Skip to content

fix: prevent empty JS file generation for CSS-only entry points#20427

Closed
DukeDeSouth wants to merge 1 commit intowebpack:mainfrom
DukeDeSouth:fix/css-only-entry-no-empty-js
Closed

fix: prevent empty JS file generation for CSS-only entry points#20427
DukeDeSouth wants to merge 1 commit intowebpack:mainfrom
DukeDeSouth:fix/css-only-entry-no-empty-js

Conversation

@DukeDeSouth
Copy link

@DukeDeSouth DukeDeSouth commented Feb 7, 2026

Human View

Summary

Fixes #11671 (5+ years old, 82 reactions).

When CSS files are used as webpack entry points, an unnecessary empty .js file is generated in the output alongside the CSS. This forces users to rely on third-party plugins like webpack-remove-empty-scripts.

Before

// webpack.config.js
entry: { styles: './src/styles.css' }
// Output: dist/styles.js (EMPTY) + dist/styles.css

After

// Output: dist/styles.css (only)

Root Cause

Two functions in JavascriptModulesPlugin.js cause this:

  1. chunkHasJs() (line 83) returns true for any chunk with entry modules, without checking their source types. CSS-only entry modules have source type "css", not "javascript".

  2. chunkHasRuntimeOrJs() (line 96) returns true when runtime modules exist. But runtime modules are always added to entry chunks — for CSS-only entries, the runtime has nothing to bootstrap.

Fix

  • chunkHasJs(): Iterate entry modules and check their source types. Only return true when at least one entry module has JAVASCRIPT_TYPE.

  • chunkHasRuntimeOrJs(): When runtime modules exist, check for JS non-entry modules first. If only runtime + entry modules, verify entry modules have JS source type. For standalone runtime chunks (optimization.runtimeChunk: true), keep JS emission since the bootstrap code is needed.

Changes

File Change
lib/javascript/JavascriptModulesPlugin.js Fix chunkHasJs and chunkHasRuntimeOrJs to check entry module source types
test/configCases/asset-modules/only-entry/test.js Update expectations: CSS-only entries no longer generate .js files

Edge Cases Verified

  • CSS-only entry (web target): No JS file emitted
  • CSS-only entry (node target): JS file still emitted (CSS modules export class names as JS on node)
  • JS entry: Still emits JS normally
  • Mixed JS+CSS entries: JS entry keeps .js, CSS entry drops empty .js
  • optimization.runtimeChunk: true: Standalone runtime chunk keeps JS for bootstrap
  • chunkHasJs as public API: Used by 5+ chunk loading modules — the change makes it more correct (CSS chunks truly don't have JS)
  • All existing split-chunks, runtime, and CSS tests pass

Testing

  • All only-entry test cases pass (7 configs)
  • vendor-only-entrypoint (runtimeChunk: true) passes
  • pure-css passes
  • no-extra-runtime-with-asset-modules passes
  • 0 new regressions (pre-existing failures are ESM infrastructure issues on main)

AI View (DCCE Protocol v1.0)

Metadata

  • Generator: Claude (Anthropic) via Cursor IDE
  • Methodology: AI-assisted development with human oversight and review

AI Contribution Summary

  • Root cause analysis through code tracing
  • Solution design and implementation
  • Edge case analysis and verification

Verification Steps Performed

  1. Reproduced the reported issue
  2. Analyzed source code to identify root cause
  3. Implemented and tested the fix

Human Review Guidance

  • Verify the root cause analysis matches your understanding of the codebase
  • Core changes are in: JavascriptModulesPlugin.js, lib/javascript/JavascriptModulesPlugin.js, test/configCases/asset-modules/only-entry/test.js
  • Verify edge case coverage is complete

Made with M7 Cursor

When CSS files are used as entry points (e.g., `entry: { styles: './styles.css' }`),
webpack generates an unnecessary empty .js file alongside the CSS output.

Root cause: `chunkHasJs()` returns true for any chunk with entry modules
without checking their source types, and `chunkHasRuntimeOrJs()` returns
true whenever runtime modules exist, even when there are no JavaScript
entry modules that need bootstrapping.

Fix:
- `chunkHasJs()`: Check entry module source types instead of blindly
  returning true for any entry module. Only return true when at least
  one entry module has JavaScript source type.
- `chunkHasRuntimeOrJs()`: When runtime modules exist but there are
  entry modules in the chunk, verify that at least one entry module
  has JavaScript source type before emitting JS. For standalone runtime
  chunks (optimization.runtimeChunk: true), keep JS emission since the
  runtime bootstrap is needed for other entry chunks.

Fixes webpack#11671

Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot
Copy link

changeset-bot bot commented Feb 7, 2026

⚠️ No Changeset found

Latest commit: fbfa382

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Feb 7, 2026

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: DukeDeSouth / name: DukeDeSouth (fbfa382)

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 8, 2026

Merging this PR will not alter performance

✅ 72 untouched benchmarks


Comparing DukeDeSouth:fix/css-only-entry-no-empty-js (fbfa382) with main (a089954)

Open in CodSpeed

@xiaoxiaojx
Copy link
Member

hi @DukeDeSouth , the code looks good. Let's fix the failing test cases first.

@alexander-akait
Copy link
Member

@xiaoxiaojx I think we will not see test fixes because this PR auto generated by AI, so feel free to send a new one with fixes tests and code improvements

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.

Generation of empty JS file in output folder from CSS only entry point

3 participants