feat(css): add __webpack_css_server_styles__ to read server-collected CSS - #21576
Conversation
webpack already collects CSS into a global registry when the neutral platform runs without a DOM, but reading it meant scanning globalThis for an internal key derived from output.uniqueName. Expose the registry as a string through a module variable instead. Also route the registry through the __webpack_require__.g polyfill on targets without globalThis, so it works on the supported node baseline.
🦋 Changeset detectedLatest commit: 5a97898 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
This PR is packaged and the instant preview is available (d3e5a0b). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@d3e5a0b
yarn add -D webpack@https://pkg.pr.new/webpack@d3e5a0b
pnpm add -D webpack@https://pkg.pr.new/webpack@d3e5a0b |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21576 +/- ##
==========================================
+ Coverage 94.00% 94.01% +0.01%
==========================================
Files 623 624 +1
Lines 75771 75801 +30
Branches 22077 22086 +9
==========================================
+ Hits 71229 71267 +38
+ Misses 4542 4534 -8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Also regenerate types.d.ts, which went stale when the runtime global was reordered by lint.
Merging this PR will degrade performance by 5.69%
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
The no-DOM branch keyed the style registry by raw module id. Integer-like keys enumerate in ascending numeric order, so reading the registry back returned the styles sorted by module id instead of by the order they were applied, silently reversing the cascade.
Nothing built the server style registry with `environment.globalThis` enabled, so the direct-`globalThis` branch was never emitted.
An earlier lint autofix reordered the new export and left `getChunkUpdateScriptFilename` documented as the CSS reader.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4a759f0. Configure here.
The helper assigns onto `__webpack_require__.cs`, but nothing pulled in the require scope, so a bundle whose only runtime consumer was `__webpack_css_server_styles__` threw before reaching the entry. The `globalThis` polyfill hid this whenever the target lacked `globalThis`.
Unable to generate the flame graphsThe performance report has correctly been generated, but there was an internal error while generating the flame graphs for this run. We're working on fixing the issue. Feel free to contact us on Discord or at support@codspeed.io if the issue persists. |

Summary
When the neutral platform (
target: ["web", "node"]) runs without a DOM, webpack already collects the styles it would have injected into a global registry — but reading them meant scanningglobalThisfor an internal key derived fromoutput.uniqueName(webpack's ownexport-type-style-universaltest does exactly that). This exposes the registry as a single string via__webpack_css_server_styles__, so the CSS can be inlined into generated HTML. The registry now also goes through the__webpack_require__.gpolyfill on targets withoutglobalThis, so it works on the supported node baseline.What kind of change does this PR introduce?
feat
Did you add tests for your changes?
Yes —
test/configCases/css/css-server-styles, asserting the collected CSS is returned when there is no DOM and that it is empty when the styles went into the document instead.Does this PR introduce a breaking change?
No. The module variable is new, and the registry expression only changes on targets without
globalThis, where it previously would have thrown.If relevant, what needs to be documented once your changes are merged or what have you already documented?
__webpack_css_server_styles__should be added to the module variables page, noting it is only populated on the neutral platform when rendering without a DOM.Use of AI
Claude Code was used to implement the runtime module and wiring, write the test case, and verify the behaviour against scratch builds (universal, browser-only, and a static page generator). All output was reviewed before committing.
Generated by Claude Code
Note
Low Risk
Additive minor feature with new runtime/API surface; behavior changes are scoped to no-DOM universal CSS injection and registry key format for ordering.
Overview
Adds
__webpack_css_server_styles__as a public module variable that returns one concatenated string of CSS collected when universal (web+node) bundles run without a DOM (SSR/SSG). In the browser it stays empty because styles are injected into the document instead.Wiring includes
RuntimeGlobals.getCssServerStyles(__webpack_require__.cs),CssServerStylesRuntimeModule, andAPIPlugin/ TypeScript declarations.cssServerStyleRegistry()now usesglobalThiswhen supported, otherwise__webpack_require__.g, with runtime requirements updated inCssModulesPluginandRuntimePlugin.CssInjectStyleRuntimeModulestores registry entries under"module-" + identifierso enumeration follows application order, not ascending numeric module ids; the getter walks the registry in that order. Config-case tests cover basic collection, ordering, prerender inlining,globalThis, and using the API without CSS imports.Reviewed by Cursor Bugbot for commit 5a97898. Bugbot is set up for automated code reviews on this repo. Configure here.