Skip to content

HMR: adding the very first native CSS import never loads the stylesheet (css hmr runtime not registered) #14747

Description

@stormslowly

System Info

rspack main (also reproduces on the feat/hmr-precise-css-updates branch, PR #14682)

Details

When a compilation contains no native CSS module at all, CssPlugin does not inject CssLoadingRuntimeModule (and with it the __webpack_require__.hmrC.css download handler): the injection is gated on HAS_CSS_MODULES | CSS_INJECT_STYLE | CSS_STYLE_SHEET (crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs).

If a hot update then adds the first native CSS import, the new compilation emits the stylesheet asset (and, with #14682, lists the chunk in the manifest's css.c), but hotCheck enumerates the hmrC handlers of the old runtime — updated runtime modules only take effect in the apply phase. Since the old runtime never registered hmrC.css, nobody requests the stylesheet: the JS part of the update applies fine while the styles silently never show up until a full reload.

Note the asymmetry: CssExtractRspackPlugin injects its css loading runtime whenever HMR_DOWNLOAD_UPDATE_HANDLERS (or ENSURE_CHUNK_HANDLERS) is required (crates/rspack_plugin_extract_css/src/plugin.rs), i.e. always under HMR, so the same "first stylesheet ever" scenario works for extracted CSS. The native CSS runtime should follow the same rule: when HMR is enabled, register the css HMR handler even if the current compilation has no CSS modules yet.

Reproduce

rspack.config.js:

const { rspack } = require('@rspack/core');

module.exports = {
  context: __dirname,
  mode: 'development',
  entry: { main: './src/index.js' },
  plugins: [new rspack.HtmlRspackPlugin({ template: './src/index.html' })],
  module: {
    rules: [{ test: /\.css$/, type: 'css' }],
  },
};

src/index.js (no css import yet):

document.getElementById('root').textContent = 'no css yet';

import.meta.webpackHot.accept();

src/blue.css:

body {
  background-color: rgb(163, 255, 255);
}
  1. rspack dev, open the page — no stylesheet, as expected.
  2. Add the first css import to src/index.js:
+import './blue.css';
 document.getElementById('root').textContent = 'no css yet';

Expected: the hot update loads main.css and the background turns blue (this is what happens with CssExtractRspackPlugin in the same scenario).

Actual: the JS update applies, no CSS request is made, the page stays unstyled until a manual reload.

A playwright assertion against this setup fails with the background stuck at rgba(0, 0, 0, 0) while the same test shape passes for CssExtractRspackPlugin (see tests/e2e/cases/css/hmr-css-added on #14682).

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions