Skip to content

fix: keep an empty string in sourcesContent - #314

Merged
alexander-akait merged 1 commit into
mainfrom
fix/empty-source-content
Oct 30, 2026
Merged

alexander-akait merged 1 commit into
mainfrom
fix/empty-source-content

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Summary

streamChunksOfSourceMap read each entry as (sourcesContent && sourcesContent[i]) || undefined, so an empty file's content "" became "no content". A missing entry tells a consumer to fetch the file itself, so the map loses information. Found while moving minimizer-webpack-plugin onto SourceMapSource for chaining maps; its test for exactly this case failed.

const map = { version: 3, file: "x.js", sources: ["empty.js"], sourcesContent: [""], names: [], mappings: "AAAA" };
new SourceMapSource("x", "x.js", map).map().sourcesContent;
// before: undefined
// after:  [""]

null entries still read as missing.

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes, test/SourceMapSource.js ("should keep an empty source content"): map() and sourceAndMap() with and without columns, and through an inner source map. It fails without the fix.

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

Written with Claude Code from my requirements; I reviewed the fix and the test.


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Empty strings in source-map content are now preserved instead of being treated as missing content.
    • Source-map content that is missing or not a string continues to be reported as unavailable.
    • The fix applies when retrieving source maps directly or composing them with an inner source map.

@changeset-bot

changeset-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 267476f

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

This PR includes changesets to release 1 package
Name Type
webpack-sources 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

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Walkthrough

All four source-map streaming paths now pass sourcesContent values to onSource only when they are strings. Tests verify that empty strings remain in map() and sourceAndMap() results, including when composing source maps. A changeset declares a patch release and describes retaining empty strings in sourcesContent.

Priority: Normal

Merge Risk: Low · up to 26747

The fix is mergeable with a bounded test-coverage follow-up: add a combined-map assertion with columns disabled to protect empty source content in that mode.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "fix: keep an empty string in sourcesContent" matches the main change. The PR stops streamChunksOfSourceMap from treating an empty string in sourcesContent as missing. The diff changes lib/h…
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
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.
Full details: Title check

Explanation

The title "fix: keep an empty string in sourcesContent" matches the main change. The PR stops streamChunksOfSourceMap from treating an empty string in sourcesContent as missing. The diff changes lib/helpers/streamChunksOfSourceMap.js so that onSource receives sourcesContent[i] only when it is a string. It also adds a changeset and a regression test in test/SourceMapSource.js that covers map(), sourceAndMap(), and inner source map composition. The title is specific, concise, and free of file lists or vague terms.

Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 2 files. (1 skipped: 1 unsupported.)

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

test/SourceMapSource.js

Oops! Something went wrong! :(

ESLint: 9.39.5

Error: Error while loading rule 'jest/no-deprecated-functions': Unable to detect Jest version - please ensure jest package is installed, or otherwise set version explicitly
Occurred while linting /test/SourceMapSource.js
at detectJestVersion (/.eslint-tmp/node_modules/eslint-plugin-jest/lib/rules/utils/detectJestVersion.js:22:9)
at create (/.eslint-tmp/node_modules/eslint-plugin-jest/lib/rules/no-deprecated-functions.js:31:107)
at Object.create (/.eslint-tmp/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:39:20)
at createRuleListeners (/.eslint-tmp/node_modules/eslint/lib/linter/linter.js:1019:15)
at /.eslint-tmp/node_modules/eslint/lib/linter/linter.js:1151:7
at Array.forEach ()
at runRules (/.eslint-tmp/node_modules/eslint/lib/linter/linter.js:1085:31)
at #flatVerifyWithoutProcessors (/.eslint-tmp/node_modules/eslint/lib/linter/linter.js:2115:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/.eslint-tmp/node_modules/eslint/lib/linter/linter.js:2203:43)
at Linter._verifyWithFlatConfigArray (/.eslint-tmp/node_modules/eslint/lib/linter/linter.js:2306:15)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Follow-up Comments

Nitpick Cover the combined columns: false path for empty sourcesContent.

86-89: Trivial · Maintainability & Code Quality · Quick win

Cover the combined columns: false path for empty sourcesContent.

The new assertion calls combined.map() with no options. getMap spreads those options and does not add columns, so the combined branch evaluates columns as true and uses the column-aware helpers. The sourcesContent expressions in streamChunksOfSourceMapLinesFull and streamChunksOfSourceMapLinesFinal run only when columns is false. A regression in those expressions that drops "" would not fail this test.

The file's other { columns: false } cases do not cover this path. The non-combined loop uses a source without an inner map, and map() returns that map directly. No other fixture in this file uses an empty sourcesContent entry.

Add the same check with { columns: false }:

Suggested test addition
 			assert.deepStrictEqual(
 				/** @type {RawSourceMap} */ (combined.map()).sourcesContent,
 				[""],
 			);
+			assert.deepStrictEqual(
+				/** @type {RawSourceMap} */ (combined.map({ columns: false }))
+					.sourcesContent,
+				[""],
+			);

Open the source location


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e5436731-8821-4084-b0b2-a0eafbf868bc
Commits

Reviewing files that changed from the base of the PR and between 99067d0 and 267476f.

Files selected for processing: 3
  • .changeset/keep-empty-source-content.md
  • lib/helpers/streamChunksOfSourceMap.js
  • test/SourceMapSource.js

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.10%. Comparing base (99067d0) to head (267476f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #314   +/-   ##
=======================================
  Coverage   99.10%   99.10%           
=======================================
  Files          27       27           
  Lines        6337     6345    +8     
  Branches      812      820    +8     
=======================================
+ Hits         6280     6288    +8     
  Misses         56       56           
  Partials        1        1           
Flag Coverage Δ
integration 99.10% <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.

@codspeed

codspeed Bot commented Oct 30, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 19.62%

2 warnings

⚠️ 1 benchmark is dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

⚡ 5 improved benchmarks
❌ 28 regressed benchmarks
✅ 179 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Memory cached-source memory: warm sourceAndMap() returns cached references 8 B 992 B -99.19%
❌ CPU Simulation original-source: new OriginalSource(string) 183 µs 278.2 µs -34.24%
❌ CPU Simulation original-source: new OriginalSource(buffer) 183.7 µs 278.8 µs -34.08%
❌ CPU Simulation original-source: source() 200.4 µs 295.2 µs -32.1%
❌ CPU Simulation original-source: buffers() (from buffer) 217.3 µs 310.6 µs -30.03%
❌ CPU Simulation original-source: getName() 256.9 µs 348.5 µs -26.28%
❌ CPU Simulation raw-source: new RawSource(buffer, true) 212.5 µs 286.6 µs -25.86%
❌ CPU Simulation raw-source: new RawSource(buffer) 212.8 µs 286.3 µs -25.67%
❌ CPU Simulation raw-source: new RawSource(string) 216.7 µs 291.1 µs -25.54%
❌ CPU Simulation raw-source: buffer() (from buffer) 230.6 µs 303.9 µs -24.12%
❌ CPU Simulation raw-source: source() (string) 234.1 µs 307.8 µs -23.95%
❌ CPU Simulation raw-source: buffers() (from buffer) 246 µs 319.4 µs -22.96%
❌ CPU Simulation raw-source: map() 235.8 µs 305.8 µs -22.88%
❌ CPU Simulation helpers/stringBufferUtils: enter/exit interning range 339.5 µs 434.9 µs -21.92%
❌ CPU Simulation prefix-source: new PrefixSource(str, string) 297 µs 379.8 µs -21.79%
❌ CPU Simulation prefix-source: new PrefixSource(str, Source) 309.5 µs 392.9 µs -21.21%
❌ CPU Simulation raw-source: sourceAndMap() 279.5 µs 350.1 µs -20.16%
❌ CPU Simulation raw-source: isBuffer() 286.6 µs 358.4 µs -20.02%
❌ CPU Simulation raw-source: source() cached 301.8 µs 375.8 µs -19.7%
❌ CPU Simulation raw-source: buffers() cached 411 µs 482.3 µs -14.79%
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

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


Comparing fix/empty-source-content (267476f) with main (99067d0)

Open in CodSpeed

Copy link
Copy Markdown
Member Author

CodSpeed Performance Analysis is red, but the regressions aren't from this PR:

  • The regressed benchmarks are constructors and accessors this diff doesn't touch (new RawSource(string), new OriginalSource(buffer), new PrefixSource(...), raw-source: map()). The only change is a typeof check run once per source in streamChunksOfSourceMap.
  • CodSpeed reports the base and head ran on different environments: CPU AMD EPYC 7763 → Intel Xeon, Node 24.21.0 → 24.22.1.
  • Control: feat: add util.sourceMap.originalPositionFor #313 adds only a new file nothing on these paths loads, and against the same base it shows the same pattern (23 regressions, cached-source memory: warm sourceAndMap() 8 B → 992 B, new OriginalSource(buffer) −43%).

No fix to port; it needs the regressions acknowledged on CodSpeed or a base run on the same runner type.


Generated by Claude Code

@alexander-akait
alexander-akait merged commit 9272057 into main Oct 30, 2026
36 of 37 checks passed
@alexander-akait
alexander-akait deleted the fix/empty-source-content branch October 30, 2026 11:20
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