Skip to content

feat: add server-side rendering building blocks (externals preset, SSR manifest, collected CSS) - #21444

Closed
alexander-akait wants to merge 16 commits into
mainfrom
claude/ssr-vite-rspack-webpack-hwxuzr
Closed

feat: add server-side rendering building blocks (externals preset, SSR manifest, collected CSS)#21444
alexander-akait wants to merge 16 commits into
mainfrom
claude/ssr-vite-rspack-webpack-hwxuzr

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

Adds the core building blocks for server-side rendering, aligning webpack with the SSR features of Vite and Rspack/Rsbuild. Three additive, opt-in primitives:

  • externalsPresets.nodeModules — externalize installed packages (requests resolving into node_modules) so a Node server build requires them at runtime instead of inlining them.
  • SSRManifestPlugin — emit ssr-manifest.json mapping each source module to the client assets (JS chunks + CSS) it needs, so a server can inject modulepreload/stylesheet tags for exactly what it rendered.
  • __webpack_css_server_styles__ — a module variable returning the CSS collected during a server render (from the existing server style registry), for critical-CSS inlining.

An examples/ssr example and a composition test (test/configCases/ssr/render) show the manifest and collected CSS producing a rendered document together. The related import.meta.env.SSR flag is handled separately in #21440.

What kind of change does this PR introduce?

feat — a new externals preset, a new plugin, and a new module variable.

Did you add tests for your changes?

Yes — test/configCases/externals/node-modules-preset, test/configCases/ssr/manifest, test/configCases/css/server-styles, and test/configCases/ssr/render (composition), plus an updated Defaults snapshot.

Does this PR introduce a breaking change?

No. Everything is additive and opt-in: externalsPresets.nodeModules defaults to false, and the plugin and module variable only emit runtime code when used.

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

externalsPresets.nodeModules, SSRManifestPlugin, and the __webpack_css_server_styles__ module variable should be documented on webpack.js.org (Configuration / Plugins / Module Variables). The examples/ssr README already documents end-to-end usage.

Use of AI

AI was used: implemented with Claude (Claude Code). I directed the design decisions (the resolve-based externals check, the manifest shape and concatenated-module handling, exposing the collected CSS as a module variable backed by a small runtime accessor), reviewed the diff and tests, and verified the targeted ConfigTestCases suites, tsc, and lint locally. I also verified via composition that the primitives do not all fit a single pure-node build today (the CSS server registry currently requires a universal target), which is noted for follow-up rather than forced here.

🤖 Generated with Claude Code


Generated by Claude Code


Note

Medium Risk
Changes generated CSS runtime code across node, universal, and webworker targets; incorrect branching could break CSS loading or bloat worker bundles, though coverage is broad.

Overview
Adds SSRManifestPlugin, which emits ssr-manifest.json mapping each source module to the client JS/CSS (and related chunks) needed for modulepreload / stylesheet tags after a server render. Chunk edges follow co-loaded splits and async ancestors, without pulling in unrelated routes that share a chunk.

Extends __webpack_css_server_styles__ so styles are collected on document-less Node builds (target: "node"), not only behind a runtime document check on universal bundles. CSS inject/load runtimes now branch on Node vs neutral vs webworker: Node may read emitted CSS from disk via dynamic import('fs'); webworker builds keep DOM link loading and omit the disk path.

Ships an examples/ssr dual client/server setup plus config tests for manifest shape, nested async preloads, shared chunks, Node CSS collection, and end-to-end render composition.

Reviewed by Cursor Bugbot for commit c5c7f7d. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Added SSR manifest generation mapping source modules to required JavaScript and stylesheet assets.
    • Preserved stylesheet cascade order and supported nested, shared, and asynchronously loaded modules.
    • Added server-side style collection for Node and other document-less rendering environments.
  • Documentation
    • Added a complete SSR example demonstrating client/server builds, asset preloading, CSS collection, and production workflows.
  • Bug Fixes
    • Improved stylesheet loading behavior for server-rendered pages and web workers.

Copilot AI review requested due to automatic review settings July 17, 2026 11:39

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6057986

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

This PR includes changesets to release 1 package
Name Type
webpack Minor

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 Jul 17, 2026

Copy link
Copy Markdown
Contributor

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

Install it locally:

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

* DO NOT MODIFY BY HAND.
* Run `yarn fix:special` to update
*/
const r=/^(?:[A-Za-z]:[\\/]|\\\\|\/)/;function e(t,{instancePath:n="",parentData:a,parentDataProperty:o,rootData:s=t}={}){if(!t||"object"!=typeof t||Array.isArray(t))return e.errors=[{params:{type:"object"}}],!1;{const n=0;for(const r in t)if("context"!==r&&"filename"!==r)return e.errors=[{params:{additionalProperty:r}}],!1;if(0===n){if(void 0!==t.context){let n=t.context;const a=0;if(0===a){if("string"!=typeof n)return e.errors=[{params:{type:"string"}}],!1;if(n.includes("!")||!0!==r.test(n))return e.errors=[{params:{}}],!1}var i=0===a}else i=!0;if(i)if(void 0!==t.filename){let n=t.filename;const a=0;if(0===a){if("string"!=typeof n)return e.errors=[{params:{type:"string"}}],!1;if(n.includes("!")||!1!==r.test(n))return e.errors=[{params:{}}],!1;if(n.length<1)return e.errors=[{params:{}}],!1}i=0===a}else i=!0}}return e.errors=null,!0}module.exports=e,module.exports.default=e; No newline at end of 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.

This file is auto-generated (schemas/**/*.check.js) — its header reads "This file was automatically generated. DO NOT MODIFY BY HAND. Run yarn fix:special". The flagged dead store is emitted by the ajv schema compiler, so it can't be hand-edited (any change would be reverted on the next yarn fix:special), and it's benign codegen shared by all the precompiled validators. No action here.


Generated by Claude Code

* DO NOT MODIFY BY HAND.
* Run `yarn fix:special` to update
*/
const r=/^(?:[A-Za-z]:[\\/]|\\\\|\/)/;function e(t,{instancePath:n="",parentData:a,parentDataProperty:o,rootData:s=t}={}){if(!t||"object"!=typeof t||Array.isArray(t))return e.errors=[{params:{type:"object"}}],!1;{const n=0;for(const r in t)if("context"!==r&&"filename"!==r)return e.errors=[{params:{additionalProperty:r}}],!1;if(0===n){if(void 0!==t.context){let n=t.context;const a=0;if(0===a){if("string"!=typeof n)return e.errors=[{params:{type:"string"}}],!1;if(n.includes("!")||!0!==r.test(n))return e.errors=[{params:{}}],!1}var i=0===a}else i=!0;if(i)if(void 0!==t.filename){let n=t.filename;const a=0;if(0===a){if("string"!=typeof n)return e.errors=[{params:{type:"string"}}],!1;if(n.includes("!")||!1!==r.test(n))return e.errors=[{params:{}}],!1;if(n.length<1)return e.errors=[{params:{}}],!1}i=0===a}else i=!0}}return e.errors=null,!0}module.exports=e,module.exports.default=e; No newline at end of 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.

Same as the sibling thread: this is the auto-generated ajv validator (yarn fix:special output, "DO NOT MODIFY BY HAND"), so the dead store can't be hand-edited and is benign compiler codegen. No action.


Generated by Claude Code

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.07692% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.10%. Comparing base (b6ccff1) to head (6057986).

Files with missing lines Patch % Lines
lib/css/CssModulesPlugin.js 89.28% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #21444    +/-   ##
========================================
  Coverage   95.10%   95.10%            
========================================
  Files         703      704     +1     
  Lines       90753    90896   +143     
  Branches    27396    27438    +42     
========================================
+ Hits        86309    86450   +141     
- Misses       4444     4446     +2     
Flag Coverage Δ
css-parsing 24.77% <6.97%> (-0.01%) ⬇️
html5lib 29.87% <10.00%> (-0.01%) ⬇️
integration 88.65% <98.07%> (+0.01%) ⬆️
syntax-equivalence 79.75% <ø> (ø)
test262 44.55% <10.00%> (-0.02%) ⬇️
unit 56.75% <10.00%> (-0.02%) ⬇️

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.

Copilot AI review requested due to automatic review settings July 17, 2026 12:04

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 31.4%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 3 improved benchmarks
✅ 325 untouched benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 1,208.2 KB 785.7 KB +53.78%
Memory benchmark "many-modules-interop-runtime", scenario '{"name":"mode-production","mode":"production"}', measure 'exec' 24.4 KB 19.9 KB +22.82%
Memory benchmark "asset-modules-bytes", scenario '{"name":"mode-production","mode":"production"}' 7.4 MB 6.2 MB +20.13%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/ssr-vite-rspack-webpack-hwxuzr (6057986) with main (b6ccff1)

Open in CodSpeed

Copilot AI review requested due to automatic review settings July 17, 2026 12:24

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 17, 2026 12:56

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 17, 2026 13:13

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread lib/SSRManifestPlugin.js Outdated
Comment thread examples/ssr/server.js
Comment thread lib/SSRManifestPlugin.js

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0c0966b. Configure here.

Comment thread lib/css/CssLoadingRuntimeModule.js Outdated
…manifest

Maps each source module to the client chunk JS, CSS and asset files it needs,
so a server-side renderer can inject modulepreload/stylesheet links and avoid
FOUC during hydration (webpack's analog to Vite's build.ssrManifest).
Emitting at PROCESS_ASSETS_STAGE_SUMMARIZE let RealContentHashPlugin rewrite the
hashed filenames inside the already-emitted manifest, which invalidated the
persistent cache pack. Emit at PROCESS_ASSETS_STAGE_REPORT instead so filenames
are final and the manifest is not rewritten.
Drive a real universal SSR render that reads the SSR manifest for modulepreload
links and __webpack_css_server_styles__ for critical css, then assert the
composed HTML document ties both primitives together.
require("fs") in the universal module bundle resolved through the createRequire
external, which is not reliably callable in the ESM test runner on Node 10-18.
Use dynamic import(), matching the CSS SSR runtime's fs access.
Demonstrate SSRManifestPlugin, __webpack_css_server_styles__ and
externalsPresets.nodeModules composing into a server-side rendering flow.
Native CSS minification now applies in production, so the example's
recorded asset sizes changed.
`output.publicPath: "auto"` resolves in the browser from the script URL,
which a build-time manifest cannot do. Emitting the bare filename made
injected preloads resolve against the rendered page's URL, so a nested
route requested the wrong asset. Map it to "/" as `ManifestPlugin` does.

Also drop the redundant `experiments` from the example: `css` defaults to
"auto" and the universal target already implies `outputModule`.
Each module was mapped only to the chunk it lives in, so a route sharing
a split-out chunk left the client to discover that chunk after fetching
the route. Follow dependencies (never importers, which would drag in
unrelated routes) and async ancestors, matching what Vite's SSR manifest
describes.
Split the example into a client build and a Node render build so every SSR
building block is exercised end to end: both manifests side by side, the
CSS collected while rendering without a DOM, the node/nodeModules externals
presets, non-emitting asset modules, and import.meta.env.
alexander-akait and others added 7 commits September 1, 2026 22:22
A `target: "node"` build that opts into emitting stylesheets
(`generator.css.exportsOnly: false`) crashed with "document is not
defined": the no-DOM branches of the CSS loading and inject runtimes were
gated on the neutral platform alone, so a target known to have no DOM fell
through to the link/style-element code. Select the no-DOM branch statically
there instead, and require the global that backs the style registry whenever
that branch is emitted.

Lets the SSR example build for node, where `import.meta.env.SSR` is true.
The nested-async case exercises a route reached through another async chunk,
where the manifest has to list the ancestor the browser loads it through;
the validation case covers the plugin's schema-rejection path.
They are deliberately the wrong type, so the types-test project rejected the
literal.
getRequiredChunks walked every chunk module's outgoing connections per
chunk, so a module shared by N chunks had its connections iterated N
times. Collapse the module graph to chunk-to-chunk edges in a single pass
and walk those instead: 29.1ms -> 23.4ms on a 201-chunk build whose 800
shared modules each land in 200 chunks.
The no-DOM branch was selected from `environment.document` alone, which is
also false for `target: "webworker"` — routing a worker's CSS chunk loading
through `import('fs')`, which a worker cannot resolve. Require the node
platform as well, so every other document-less target keeps the link code it
had before.
The manifest sorted every file alphabetically, so a route split across two
stylesheets handed the server an arbitrary cascade — `alpha.css` before the
`page.css` its source imports first. Stylesheets now keep the order the
extracted HTML writes them in, and a config case pins that a `<link>` a server
printed is adopted by the browser runtime rather than requested again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8n6KpLAPpLZdLrovcDnAu
@alexander-akait
alexander-akait force-pushed the claude/ssr-vite-rspack-webpack-hwxuzr branch from c5c7f7d to 6057986 Compare September 1, 2026 22:39
@github-actions github-actions Bot added area: config Options, defaults, validation (lib/config, schemas/) area: css CSS support (lib/css) area: types types.d.ts, JSDoc annotations, hand-maintained declarations labels Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging claude/ssr-vite-rspack-webpack-hwxuzr into main will be
99.23%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
bin
   webpack.js98.82%100%100%98.82%103
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.js100%100%100%100%
examples/markdown
   webpack.config.mjs100%100%100%100%
examples/module-federation
   test.filter.js100%100%100%100%
examples/reexport-components
   test.filter.js100%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-emscripten
   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%42
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CircularModulesPlugin.js99.35%100%100%99.35%244
   CleanPlugin.js99.12%100%100%99.12%212, 232
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.53%100%100%98.53%1736, 2055, 2062, 2070, 2092, 2095, 3034, 3513–3514, 3546, 4380, 4413, 4466–4467, 4471, 4476, 4492–4493, 4507–4508, 4513–4514, 5036, 5062, 5872, 5904, 5921, 5939, 5955, 5970, 5995–5996, 5998, 6333, 6338, 6344, 6347, 6354, 6366, 6368, 6372, 6390, 6405, 6439, 6495, 6519, 6635, 818–819
   Compiler.js99.56%100%100%99.56%1174–1175, 1183
   ConcatenationScope.js99.10%100%100%99.10%293
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js99.88%100%100%99.88%1554
   ContextModuleFactory.js97.29%100%100%97.29%289, 465, 486, 491, 532, 543, 545, 549, 558–559
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js99.13%100%100%99.13%1124, 197–198, 214, 233, 307
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.54%100%100%98.54%498, 545
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DotenvPlugin.js98.07%100%100%98.07%395, 443, 456–457
   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.js99.26%100%100%99.26%415, 431, 763, 860, 878, 922, 927
   ExportsInfoApiPlugin.js100%100%100%100%
   ExternalModule.js98.76%100%100%98.76%1340, 1343, 590–594, 596, 756
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.53%100%100%99.53%187, 2464–2465, 2468, 2479, 2490, 2501, 285, 3944, 3959, 3983
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.36%100%100%98.36%505, 514, 517, 521, 533
   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%
   LazyBarrel.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.51%100%100%98.51%1317, 1322, 1382, 1396, 1458, 1467
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.90%100%100%98.90%111, 113
   ModuleGraph.js99.78%100%100%99.78%1180
   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.72%100%100%99.72%729
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.99%100%100%97.99%1040, 1043, 1060, 1077, 1325, 1359, 1375, 1830, 2126, 2131–2141, 29
   NormalModuleFactory.js99.01%100%100%99.01%1341, 1790, 1801, 1811, 1862–1864, 1871, 732, 744
   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.js99.80%100%100%99.80%694
   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.js95.78%100%100%95.78%314, 380, 389, 392, 416, 434, 455–456, 479, 499–500, 536–537, 560, 573–574, 646, 659, 680, 699
   RuntimeTemplate.js99.63%100%100%99.63%350, 3684, 4284, 4296, 4301, 4303, 4308
   SSRManifestPlugin.js100%100%100%100%
   SelfModuleFactory.js100%100%100%100%
   SingleEntryPlugin.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js98.63%100%100%98.63%229, 233, 235, 429, 440, 899
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.48%100%100%99.48%366–367
   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%375
   cli.js98.63%100%100%98.63%10, 117, 547, 579, 629, 903
   index.js99.73%100%100%99.73%184
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js97.12%100%100%97.12%10, 274, 296, 298
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModule.js100%100%100%100%
   AssetModulesPlugin.js98.15%100%100%98.15%338, 362, 365, 495, 57, 62
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
   WebManifestGenerator.js100%100%100%100%
   WebManifestParser.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
   isGeneratorLowered.js100%100%100%100%
lib/bun
   BunTargetPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%75, 87, 95
   MemoryCachePlugin.js92%100%100%92%33, 42
   MemoryWithGcCachePlugin.js93.42%100%100%93.42%107, 121–122, 131, 89
   PackFileCacheStrategy.js96.52%100%100%96.52%1317, 1417, 1421, 1483, 1719, 1803, 1826, 1858,

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 6b982ac0-5110-44a8-84a6-d279dd8eb476

📥 Commits

Reviewing files that changed from the base of the PR and between b6ccff1 and 6057986.

⛔ Files ignored due to path filters (5)
  • declarations/plugins/SSRManifestPlugin.d.ts is excluded by !declarations/**
  • schemas/plugins/SSRManifestPlugin.check.d.ts is excluded by !schemas/**/*.check.d.ts
  • schemas/plugins/SSRManifestPlugin.check.js is excluded by !schemas/**/*.check.js
  • test/configCases/ssr/manifest/logo.svg is excluded by !**/*.svg
  • types.d.ts is excluded by !types.d.ts
📒 Files selected for processing (59)
  • .changeset/019-ssr-manifest-plugin.md
  • cspell.json
  • examples/ssr/README.md
  • examples/ssr/build.js
  • examples/ssr/example.js
  • examples/ssr/page.css
  • examples/ssr/page.js
  • examples/ssr/server.js
  • examples/ssr/style.css
  • examples/ssr/template.md
  • examples/ssr/webpack.config.js
  • lib/SSRManifestPlugin.js
  • lib/css/CssInjectStyleRuntimeModule.js
  • lib/css/CssLoadingRuntimeModule.js
  • lib/css/CssModulesPlugin.js
  • lib/index.js
  • schemas/plugins/SSRManifestPlugin.json
  • test/Validation.test.js
  • test/configCases/css/css-server-styles-node-inject/index.js
  • test/configCases/css/css-server-styles-node-inject/style.css
  • test/configCases/css/css-server-styles-node-inject/webpack.config.js
  • test/configCases/css/css-server-styles-node/button.css
  • test/configCases/css/css-server-styles-node/button.js
  • test/configCases/css/css-server-styles-node/index.js
  • test/configCases/css/css-server-styles-node/webpack.config.js
  • test/configCases/css/css-webworker-no-fs/index.js
  • test/configCases/css/css-webworker-no-fs/styled.css
  • test/configCases/css/css-webworker-no-fs/styled.js
  • test/configCases/css/css-webworker-no-fs/webpack.config.js
  • test/configCases/css/server-rendered-stylesheet/button.css
  • test/configCases/css/server-rendered-stylesheet/button.js
  • test/configCases/css/server-rendered-stylesheet/index.js
  • test/configCases/css/server-rendered-stylesheet/webpack.config.js
  • test/configCases/ssr/manifest-css-order/alpha.css
  • test/configCases/ssr/manifest-css-order/index.js
  • test/configCases/ssr/manifest-css-order/page.js
  • test/configCases/ssr/manifest-css-order/shared.js
  • test/configCases/ssr/manifest-css-order/test.config.js
  • test/configCases/ssr/manifest-css-order/test.js
  • test/configCases/ssr/manifest-css-order/webpack.config.js
  • test/configCases/ssr/manifest-css-order/zebra.css
  • test/configCases/ssr/manifest-nested-async/index.js
  • test/configCases/ssr/manifest-nested-async/inner.js
  • test/configCases/ssr/manifest-nested-async/outer.js
  • test/configCases/ssr/manifest-nested-async/webpack.config.js
  • test/configCases/ssr/manifest-shared-chunk/a.js
  • test/configCases/ssr/manifest-shared-chunk/b.js
  • test/configCases/ssr/manifest-shared-chunk/index.js
  • test/configCases/ssr/manifest-shared-chunk/shared.js
  • test/configCases/ssr/manifest-shared-chunk/webpack.config.js
  • test/configCases/ssr/manifest/async.js
  • test/configCases/ssr/manifest/helper.js
  • test/configCases/ssr/manifest/index.js
  • test/configCases/ssr/manifest/style.css
  • test/configCases/ssr/manifest/webpack.config.js
  • test/configCases/ssr/render/index.js
  • test/configCases/ssr/render/page.css
  • test/configCases/ssr/render/page.js
  • test/configCases/ssr/render/webpack.config.js

📝 Walkthrough

Walkthrough

Changes

The pull request adds SSRManifestPlugin, maps source modules to client assets, preserves CSS cascade order, and supports server-side CSS collection on document-less targets. It adds CSS runtime regression tests and an SSR example with client and Node builds.

SSR runtime and manifest support

Layer / File(s) Summary
Document-less CSS runtime behavior
lib/css/*, test/configCases/css/*
Node runtimes collect CSS from injected and asynchronously loaded stylesheets. Webworker output retains DOM loading without filesystem reads.
SSR manifest generation
lib/SSRManifestPlugin.js, lib/css/CssModulesPlugin.js, lib/index.js, schemas/plugins/SSRManifestPlugin.json, test/Validation.test.js
SSRManifestPlugin validates options, resolves required chunks, orders CSS assets, expands concatenated modules, and emits a sorted JSON manifest.
Manifest dependency coverage
test/configCases/ssr/manifest*
Tests cover entry assets, async chunks, nested async dependencies, shared chunks, and stylesheet cascade order.
Client and Node SSR example
examples/ssr/*, test/configCases/ssr/render/*
The example builds browser and Node bundles, renders a route, collects server CSS, and generates stylesheet and modulepreload tags from the SSR manifest.

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.

Copy link
Copy Markdown
Member Author

Superseded by #21913, which carries the SSR manifest and the document-less CSS collection rebased onto current main, plus cascade-ordered stylesheets and a case covering the <link> reuse a server render relies on. The other two primitives from this PR shipped separately in 5.110 (externalsPresets.nodeModules in #21569, __webpack_css_server_styles__ in #21576). Closing in favour of the fresh one.


Generated by Claude Code

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Generated code size

Comparing 6057986 merged into b6ccff1 against b6ccff1. Merging this pull request adds 34 new asset(s).

Changed New Deleted Unchanged Gzip change Raw change Gzip new/gone Raw new/gone
Cases 0 9 0 1989 +26.78 KiB +95.46 KiB
Assets 0 34 0 7311 +26.78 KiB +95.46 KiB
Runtimes 0 9 0 2291

Gzip change decides — it is what a user downloads, and a re-encoding can cut raw bytes while costing wire bytes. Raw change is the tiebreak: it is what the generator wrote, so it is what has to be decompressed and parsed. Both are over assets both runs emit; bytes an added or deleted case brings with it are counted apart, under new/gone. Brotli and zstd are per asset in the table below.

No asset that both runs emit changed size — everything below is new or deleted.

34 asset(s) this pull request adds, biggest 20 by raw size
Asset Raw Gzip (9) Brotli (11) Zstd (19)
css/server-rendered-stylesheet bundle0.js 16.47 KiB 3.86 KiB 3.31 KiB 3.76 KiB
ssr/manifest-css-order main.js 14.25 KiB 3.09 KiB 2.67 KiB 3.01 KiB
ssr/render bundle0.mjs 13.21 KiB 3.54 KiB 3.06 KiB 3.46 KiB
css/css-webworker-no-fs bundle0.mjs 11.58 KiB 3.03 KiB 2.64 KiB 2.98 KiB
css/css-server-styles-node bundle0.mjs 9.36 KiB 2.32 KiB 2.06 KiB 2.29 KiB
ssr/manifest bundle0.js 6.20 KiB 1.81 KiB 1.58 KiB 1.80 KiB
ssr/manifest-nested-async bundle0.js 5.92 KiB 1.73 KiB 1.50 KiB 1.71 KiB
ssr/manifest-shared-chunk bundle0.js 5.70 KiB 1.66 KiB 1.43 KiB 1.66 KiB
css/css-server-styles-node-inject bundle0.mjs 4.60 KiB 1.37 KiB 1.16 KiB 1.36 KiB
ssr/manifest-css-order page.js 1.12 KiB 427 B 365 B 423 B
css/css-server-styles-node button_js.bundle0.mjs 565 B 271 B 226 B 266 B
css/server-rendered-stylesheet button.js 560 B 283 B 236 B 276 B
ssr/render page_js.bundle0.mjs 525 B 267 B 213 B 262 B
ssr/manifest-shared-chunk a_js.bundle0.js 523 B 282 B 233 B 271 B
ssr/manifest-shared-chunk b_js.bundle0.js 523 B 283 B 233 B 271 B
ssr/manifest-css-order test.js 504 B 332 B 264 B 332 B
css/css-webworker-no-fs styled_js.bundle0.mjs 499 B 229 B 199 B 225 B
ssr/manifest-nested-async outer_js.bundle0.js 427 B 232 B 202 B 224 B
ssr/manifest 138.bundle0.js 390 B 261 B 211 B 249 B
ssr/manifest-nested-async inner_js.bundle0.js 379 B 202 B 178 B 195 B
… 14 more, see the uploaded report

No runtime that both runs build changed which runtime modules it carries.

9 runtime(s) this pull request adds or no longer builds
Runtime Modules
css/server-rendered-stylesheet main 11
ssr/manifest-css-order main 10
css/css-server-styles-node main 8
ssr/manifest main 8
ssr/render main 8
css/css-webworker-no-fs main 6
ssr/manifest-nested-async main 6
ssr/manifest-shared-chunk main 6
css/css-server-styles-node-inject main 4

Built test/configCases with the defaults a user gets: 1998 case(s), 7345 asset(s), 78 emitted nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: config Options, defaults, validation (lib/config, schemas/) area: css CSS support (lib/css) area: types types.d.ts, JSDoc annotations, hand-maintained declarations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Sponsor
SponsoredKunjungi sekarang
Promo