Skip to content

fix(ui): repair scope SSR and shrink the ui pre-bundle 58MB to 24MB - #10628

Merged
GiladShoham merged 3 commits into
masterfrom
fix/ui-ssr-bundle-size-and-invalid-tag
Aug 18, 2026
Merged

fix(ui): repair scope SSR and shrink the ui pre-bundle 58MB to 24MB#10628
GiladShoham merged 3 commits into
masterfrom
fix/ui-ssr-bundle-size-and-invalid-tag

Conversation

@GiladShoham

Copy link
Copy Markdown
Member

Part 1 of #10596. Cuts the shipped @teambit/ui pre-bundle from 58 MB to 24 MB, and fixes scope SSR, which turned out to have been throwing on every request for months.

The workspace/scope single-compilation dedupe (~8 MB) is deliberately left for a follow-up PR — it needs an artifact-layout and .hash redesign, and mixing it in would make the SSR fix hard to verify.

Size: 58 MB → 24 MB

before after
ui-bundle/scope/public/bit/ssr/index.js 37 MB 6.8 MB
ui-bundle/scope 50 MB 16 MB
ui-bundle/workspace 8.2 MB 8.2 MB
total 58 MB 24 MB

Two causes, both in rspack.ssr.config.ts:

  • devtool: 'eval-cheap-module-source-map' wrapped every module in eval('…') with an inlined base64 source map — 2,566 of them, 23.4 MB, 60% of the file. It entered as 'eval-cheap-source-map', // TODO and was later retuned in chore(webpack): better development sourcemaps #7147 as a development sourcemap change; it was never a deliberate choice for a shipped artifact. Now shouldUseSourceMap ? 'source-map' : false, matching the browser config's opt-in.
  • The SSR build had no optimization.minimizer at all. It now mirrors the browser build's SwcJsMinimizerRspackPlugin.

Scope SSR was dead, and it took three fixes

Only scope.ui-root.ts sets ssr: true. The middleware catches any render error and calls next(), falling through to the static index.html — which looks completely normal in a browser. So the failure was invisible: curl / and curl '/?rendering=client' returned byte-identical 1,027-byte responses with an empty <div id="root"></div>.

Three independent defects were stacked:

  1. cjs missing from the SSR asset catch-all. The browser config excludes /\.(cjs|js|mjs|jsx|ts|tsx)$/; the SSR config omitted cjs, so every .cjs module was emitted as an asset/resource whose module value is the file's URL. A component imported from one reached React as a tag name → Minified React error #65 = "Invalid tag: /public/ssr/<hash>.cjs". Introduced by the webpack→rspack migration (feat(workspace | preview): migrate from webpack to rspack #10187), which replaced a shared base config with two hand-written ones and added cjs to only one.
  2. use-user-agent present at two versions (0.0.199 and 0.0.200). Each copy calls createContext, so ui.ui.runtime.tsx provided ssrBrowserContext on one instance while Tooltip's useUserAgent read the other, got undefined, took the browser fallback and dereferenced window on the server. Fixed by adding it to resolveAlias — the list that exists for exactly this class of bug. Aliasing the hook fixes every consumer, so the duplicated Tooltip copies need no aliasing of their own.
  3. window in a useEffect dependency arrayuseCurrentUser had }, [window.location.href]);. The effect body never runs on the server, but a dependency array is evaluated on every render, including the server one.

All routes now render server-side and the log shows zero SSR failures:

route before after
/ 1,027 B (fallback) 27,178 B
/ui/button 1,027 B (fallback) 51,726 B
/ui/button/~code 1,027 B (fallback) 61,246 B

A visible side effect: the document title was the build-time placeholder (bit-local-88bfe855) and is now the actual scope name.

Browser runtime effect

This turns SSR on for the first time in months, so it is a real runtime change. Bare scope on localhost, 7 runs, median, SSR vs ?rendering=client:

SSR client delta
FCP, warm cache 72 ms 384 ms −312 ms
FCP, cold cache 584 ms 376 ms +208 ms
TTFB 24 ms 2 ms +22 ms

SSR is ~5× faster to first paint on a repeat visit, and ~200 ms slower on a cold first load where the 6.4 MB of JS dominates and the larger HTML delays stylesheet discovery. Server startup is unchanged (~1.77 s); the first request drops from 0.79 s to 0.49 s while now doing a real render instead of throwing. Note localhost has ~0 latency, which flatters client rendering — over a network SSR's margin widens.

Testing

New e2e e2e/harmony/ui-ssr.e2e.ts asserts on the served HTML, since a browser cannot distinguish a working SSR render from the fallback.

It runs bit start --rebuild, which matters: getBundleUiPath resolves through getAspectDirFromBvm, so without --rebuild the server serves the pre-bundle from the installed bvm version and the test would assert on whatever bit release happens to be installed rather than on this code. HttpHelper gained an optional extra-args parameter for this.

Verified the test actually catches the bug — with the cjs fix reverted, 2 of the 3 assertions fail. (The third, an assertion that the scope name appears anywhere in the document, passed in both states because --rebuild puts the scope name in the static <title>; it now asserts inside #root instead.)

Manual verification on both UI roots, with the freshly built artifact swapped into the bvm install and the released .hash files preserved (a .hash mismatch makes bit start silently rebuild locally instead of serving the artifact). Confirmed the new bundle was genuinely served by diffing content-hashed asset names against the originals. Both roots checked in a real browser: workspace and scope home, component page, code tab, API reference — no console errors. The one error found (componentChanged subscription missing on a bare scope) reproduces identically on the original bundle and is pre-existing.

Bundle analysis tooling

BIT_UI_BUNDLE_STATS=1 makes the UI build write an rspack stats file per compilation, and scripts/analyze-bundle.mjs (npm run analyze-bundle) summarizes assets plus the heaviest packages and workspace scopes. No new dependency. This is what found defect 2 above.

It also surfaced that 81–86 packages appear at more than one version in a single bundle (@teambit/design.ui.tooltip at eight). That is only ~0.6 MB (~3% of module bytes), so it is a React-context correctness risk rather than a size lever — worth tracking separately.

Not in scope

The six env preview pre-bundles (@teambit/{react,node,mdx,env,aspect,readme}/artifacts/env-template) are 37.9 MB with only 6.4 MB of unique content — 31.5 MB is byte-identical across the six. That is now the largest single remaining item, bigger than the workspace/scope dedupe, and is not covered by #10596 as written.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix scope SSR failures and shrink shipped UI SSR bundle

🐞 Bug fix ✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Fix scope SSR by excluding .cjs from asset emission, aliasing use-user-agent, and guarding
 window access.
• Reduce shipped SSR bundle size by removing eval-* sourcemaps and enabling SWC minification.
• Add opt-in rspack bundle stats + analyzer script and an e2e test asserting SSR-rendered HTML.
Diagram

graph TD
  T(["E2E SSR test"]) --> S["bit start --rebuild"] --> B["UiMain build()"]; 
  B --> C[["rspack.ssr.config"]] --> O[("SSR bundle artifacts")] --> R["Scope SSR middleware"]; 
  B --> X[("bundle-stats/*.json")] --> A[["analyze-bundle.mjs"]];

  subgraph Legend
    direction LR
    _test(["Test"]) ~~~ _proc["Process"] ~~~ _art[("Artifact")] ~~~ _tool[["Tool/script"]]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Enforce single versions via package-manager overrides
  • ➕ Eliminates duplicate-context bugs without maintaining bundler aliases
  • ➕ Can reduce bundle variance across roots/compilations
  • ➖ May be hard to roll out safely across all workspaces/consumers
  • ➖ Doesn’t protect against future duplication via different entrypoints/exports conditions
2. Share a single base rspack config for browser+SSR
  • ➕ Prevents config drift (e.g., .cjs exclusion mismatch)
  • ➕ Makes future optimizations easier to apply consistently
  • ➖ Requires refactor of config composition; higher change surface
  • ➖ Harder to isolate SSR-specific differences and validate fixes quickly
3. Make consumers SSR-safe even when context is missing
  • ➕ More resilient to duplication/ordering issues
  • ➕ Can reduce reliance on global aliasing
  • ➖ Still leaves correctness risk when providers/consumers split contexts
  • ➖ Can hide real dependency duplication problems

Recommendation: The PR’s approach is appropriate for a targeted, verifiable SSR repair: fix the .cjs rule mismatch, alias the context-carrying hook package, and guard window usage. As follow-ups, consider consolidating browser/SSR config composition to prevent future drift, and evaluate package-manager overrides if duplicate versions remain a recurring source of React context split-brain.

Files changed (9) +270 / -6

Enhancement (3) +161 / -0
bundle-stats.tsAdd opt-in rspack stats writer for bundle diagnostics +40/-0

Add opt-in rspack stats writer for bundle diagnostics

• Adds 'BIT_UI_BUNDLE_STATS' support to write rspack stats JSON per compilation to a directory outside shipped artifacts. Exposes helpers to resolve the output dir and write stats safely.

scopes/ui-foundation/ui/rspack/bundle-stats.ts

ui.main.runtime.tsEmit optional bundle stats for browser and SSR builds +16/-0

Emit optional bundle stats for browser and SSR builds

• Integrates stats writing into the UI build pipeline for both browser and SSR compilations. Uses a best-effort 'try/catch' so diagnostics never fail a build, and logs the written stats path when enabled.

scopes/ui-foundation/ui/ui.main.runtime.ts

analyze-bundle.mjsAdd CLI to summarize rspack bundle stats and largest dependency buckets +105/-0

Add CLI to summarize rspack bundle stats and largest dependency buckets

• Adds a Node CLI that reads rspack stats JSON, prints largest assets, and attributes module sizes to actionable buckets (npm packages or workspace scopes). Helps identify both size drivers and multi-version duplication hotspots.

scripts/analyze-bundle.mjs

Bug fix (3) +36 / -4
use-current-user.tsFix SSR crash by removing 'window' from effect dependency evaluation +7/-2

Fix SSR crash by removing 'window' from effect dependency evaluation

• Moves 'window.location.href' access out of the 'useEffect' dependency array and guards it for server renders. The effect now exits early when no browser 'window' exists.

scopes/cloud/hooks/use-current-user/use-current-user.ts

rspack.common.tsAlias 'use-user-agent' hook package to prevent React context duplication +5/-0

Alias 'use-user-agent' hook package to prevent React context duplication

• Adds a resolve alias for '@teambit/ui-foundation.ui.hooks.use-user-agent' to force a single resolved copy in the UI graph. Prevents provider/consumer context splits that can lead to SSR 'window' dereferences.

scopes/ui-foundation/ui/rspack/rspack.common.ts

rspack.ssr.config.tsFix SSR bundling: drop eval sourcemaps, enable minimization, exclude '.cjs' from asset rule +24/-2

Fix SSR bundling: drop eval sourcemaps, enable minimization, exclude '.cjs' from asset rule

• Changes SSR devtool to opt-in 'source-map' (otherwise disabled) to avoid shipping 'eval-*' inlined maps. Adds SWC JS minimizer mirroring the browser build. Fixes the catch-all asset rule to exclude '.cjs', preventing '.cjs' modules from being emitted as asset URLs and reaching React as invalid tag names.

scopes/ui-foundation/ui/rspack/rspack.ssr.config.ts

Tests (2) +72 / -2
ui-ssr.e2e.tsAdd e2e coverage asserting scope SSR renders real HTML +64/-0

Add e2e coverage asserting scope SSR renders real HTML

• Introduces a dedicated e2e test that fetches '/' HTML and asserts SSR markup exists under '#root', and that no emitted SSR asset URL reaches React as a tag name. Runs 'bit start' with '--rebuild' to ensure the bundle is built from the repo under test.

e2e/harmony/ui-ssr.e2e.ts

http-helper.tsAllow passing extra 'bit start' flags from e2e tests +8/-2

Allow passing extra 'bit start' flags from e2e tests

• Extends HttpHelper to accept extra CLI args and appends them to the 'bit start' invocation. Enables tests to force '--rebuild' so assertions apply to the current branch’s UI bundle output.

e2e/http-helper.ts

Other (1) +1 / -0
package.jsonAdd 'analyze-bundle' npm script +1/-0

Add 'analyze-bundle' npm script

• Registers 'npm run analyze-bundle' to execute the new bundle stats summarizer script.

package.json

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 18, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Ad-hoc chalk in writeStats 📘 Rule violation ⚙ Maintainability
Description
The new writeStats() CLI output uses direct chalk formatting and a hardcoded [Rspack] prefix
instead of the repository’s shared CLI output formatting toolkit. This risks inconsistent CLI output
styling across commands and bypasses the documented style guide.
Code

scopes/ui-foundation/ui/ui.main.runtime.ts[R311-312]

+      const filePath = writeBundleStats(stats, name);
+      if (filePath) this.logger.console(`${chalk.magenta('[Rspack]')} wrote bundle stats to ${chalk.cyan(filePath)}`);
Evidence
PR Compliance ID 1 requires CLI output to follow the style guide and use the shared output
formatting utilities instead of ad-hoc chalk styling. The added log line formats output with
direct chalk calls and a hardcoded prefix, bypassing the shared formatter.

CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit: CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit: CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit: CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit
scopes/ui-foundation/ui/ui.main.runtime.ts[309-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`UiMain.writeStats()` prints CLI output using ad-hoc `chalk` formatting (e.g. `chalk.magenta('[Rspack]')`) rather than using the shared CLI output formatting toolkit.
## Issue Context
The repo requires CLI output to follow `scopes/harmony/cli/cli-output-style-guide.md` and use the shared formatter utilities from `@teambit/cli` (`scopes/harmony/cli/output-formatter.ts`) to keep output consistent and maintainable.
## Fix Focus Areas
- scopes/ui-foundation/ui/ui.main.runtime.ts[309-315]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Stats path can break 🐞 Bug ◔ Observability
Description
writeBundleStats() builds the output path with the unsanitized name, so names containing path
separators can create an unintended nested path and make writeFileSync() fail (ENOENT).
UiMain.writeStats() swallows that failure (debug-only), so enabling BIT_UI_BUNDLE_STATS may produce
no stats files without any visible signal.
Code

scopes/ui-foundation/ui/rspack/bundle-stats.ts[R37-38]

+  const filePath = join(dir, `${name}.stats.json`);
+  writeFileSync(filePath, JSON.stringify(json));
Evidence
The stats writer only mkdirp’s the base dir, then writes to a filePath derived directly from
name; if name contains separators, the parent path won’t exist and the write will throw.
UiMain.writeStats wraps writeBundleStats in a try/catch and only logs at debug level, so the failure
is silent in normal output.

scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`writeBundleStats(stats, name)` uses `join(dir, `${name}.stats.json`)` and only creates `dir`. If `name` contains `/` or `\\`, the resulting `filePath` includes intermediate directories that do not exist, causing `writeFileSync()` to throw. The caller (`UiMain.writeStats`) catches and logs only at debug level, making the failure effectively silent when diagnostics are explicitly enabled.
## Issue Context
This is an opt-in diagnostics path (`BIT_UI_BUNDLE_STATS`), so it must be robust to odd names and should reliably write a file when enabled.
## Fix Focus Areas
- scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
- scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]
## Suggested fix
- Sanitize `name` into a filename-safe value (e.g., replace `/` and `\\` with `_`, and prevent `..` segments).
- Ensure the parent directory of `filePath` exists (e.g., `mkdirpSync(dirname(filePath))`) before writing.
- (Optional) If writing fails, consider logging a `warn` (still not failing the build) so the user who opted in understands why no file appeared.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more'

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit c1f9286 ⚖️ Balanced

Results up to commit 557eef4


🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)


Remediation recommended
1. Ad-hoc chalk in writeStats 📘 Rule violation ⚙ Maintainability
Description
The new writeStats() CLI output uses direct chalk formatting and a hardcoded [Rspack] prefix
instead of the repository’s shared CLI output formatting toolkit. This risks inconsistent CLI output
styling across commands and bypasses the documented style guide.
Code

scopes/ui-foundation/ui/ui.main.runtime.ts[R311-312]

+      const filePath = writeBundleStats(stats, name);
+      if (filePath) this.logger.console(`${chalk.magenta('[Rspack]')} wrote bundle stats to ${chalk.cyan(filePath)}`);
Evidence
PR Compliance ID 1 requires CLI output to follow the style guide and use the shared output
formatting utilities instead of ad-hoc chalk styling. The added log line formats output with
direct chalk calls and a hardcoded prefix, bypassing the shared formatter.

CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit: CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit
scopes/ui-foundation/ui/ui.main.runtime.ts[309-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`UiMain.writeStats()` prints CLI output using ad-hoc `chalk` formatting (e.g. `chalk.magenta('[Rspack]')`) rather than using the shared CLI output formatting toolkit.
## Issue Context
The repo requires CLI output to follow `scopes/harmony/cli/cli-output-style-guide.md` and use the shared formatter utilities from `@teambit/cli` (`scopes/harmony/cli/output-formatter.ts`) to keep output consistent and maintainable.
## Fix Focus Areas
- scopes/ui-foundation/ui/ui.main.runtime.ts[309-315]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Stats path can break 🐞 Bug ◔ Observability
Description
writeBundleStats() builds the output path with the unsanitized name, so names containing path
separators can create an unintended nested path and make writeFileSync() fail (ENOENT).
UiMain.writeStats() swallows that failure (debug-only), so enabling BIT_UI_BUNDLE_STATS may produce
no stats files without any visible signal.
Code

scopes/ui-foundation/ui/rspack/bundle-stats.ts[R37-38]

+  const filePath = join(dir, `${name}.stats.json`);
+  writeFileSync(filePath, JSON.stringify(json));
Evidence
The stats writer only mkdirp’s the base dir, then writes to a filePath derived directly from
name; if name contains separators, the parent path won’t exist and the write will throw.
UiMain.writeStats wraps writeBundleStats in a try/catch and only logs at debug level, so the failure
is silent in normal output.

scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`writeBundleStats(stats, name)` uses `join(dir, `${name}.stats.json`)` and only creates `dir`. If `name` contains `/` or `\\`, the resulting `filePath` includes intermediate directories that do not exist, causing `writeFileSync()` to throw. The caller (`UiMain.writeStats`) catches and logs only at debug level, making the failure effectively silent when diagnostics are explicitly enabled.
## Issue Context
This is an opt-in diagnostics path (`BIT_UI_BUNDLE_STATS`), so it must be robust to odd names and should reliably write a file when enabled.
## Fix Focus Areas
- scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
- scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]
## Suggested fix
- Sanitize `name` into a filename-safe value (e.g., replace `/` and `\\` with `_`, and prevent `..` segments).
- Ensure the parent directory of `filePath` exists (e.g., `mkdirpSync(dirname(filePath))`) before writing.
- (Optional) If writing fails, consider logging a `warn` (still not failing the build) so the user who opted in understands why no file appeared.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit ea3def7


🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)


Remediation recommended
1. Ad-hoc chalk in writeStats 📘 Rule violation ⚙ Maintainability
Description
The new writeStats() CLI output uses direct chalk formatting and a hardcoded [Rspack] prefix
instead of the repository’s shared CLI output formatting toolkit. This risks inconsistent CLI output
styling across commands and bypasses the documented style guide.
Code

scopes/ui-foundation/ui/ui.main.runtime.ts[R311-312]

+      const filePath = writeBundleStats(stats, name);
+      if (filePath) this.logger.console(`${chalk.magenta('[Rspack]')} wrote bundle stats to ${chalk.cyan(filePath)}`);
Evidence
PR Compliance ID 1 requires CLI output to follow the style guide and use the shared output
formatting utilities instead of ad-hoc chalk styling. The added log line formats output with
direct chalk calls and a hardcoded prefix, bypassing the shared formatter.

CLAUDE.md: CLI Output Must Follow the Repository CLI Output Style Guide and Shared Formatting Toolkit
scopes/ui-foundation/ui/ui.main.runtime.ts[309-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`UiMain.writeStats()` prints CLI output using ad-hoc `chalk` formatting (e.g. `chalk.magenta('[Rspack]')`) rather than using the shared CLI output formatting toolkit.

## Issue Context
The repo requires CLI output to follow `scopes/harmony/cli/cli-output-style-guide.md` and use the shared formatter utilities from `@teambit/cli` (`scopes/harmony/cli/output-formatter.ts`) to keep output consistent and maintainable.

## Fix Focus Areas
- scopes/ui-foundation/ui/ui.main.runtime.ts[309-315]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Stats path can break 🐞 Bug ◔ Observability
Description
writeBundleStats() builds the output path with the unsanitized name, so names containing path
separators can create an unintended nested path and make writeFileSync() fail (ENOENT).
UiMain.writeStats() swallows that failure (debug-only), so enabling BIT_UI_BUNDLE_STATS may produce
no stats files without any visible signal.
Code

scopes/ui-foundation/ui/rspack/bundle-stats.ts[R37-38]

+  const filePath = join(dir, `${name}.stats.json`);
+  writeFileSync(filePath, JSON.stringify(json));
Evidence
The stats writer only mkdirp’s the base dir, then writes to a filePath derived directly from
name; if name contains separators, the parent path won’t exist and the write will throw.
UiMain.writeStats wraps writeBundleStats in a try/catch and only logs at debug level, so the failure
is silent in normal output.

scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`writeBundleStats(stats, name)` uses `join(dir, `${name}.stats.json`)` and only creates `dir`. If `name` contains `/` or `\\`, the resulting `filePath` includes intermediate directories that do not exist, causing `writeFileSync()` to throw. The caller (`UiMain.writeStats`) catches and logs only at debug level, making the failure effectively silent when diagnostics are explicitly enabled.

## Issue Context
This is an opt-in diagnostics path (`BIT_UI_BUNDLE_STATS`), so it must be robust to odd names and should reliably write a file when enabled.

## Fix Focus Areas
- scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
- scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]

## Suggested fix
- Sanitize `name` into a filename-safe value (e.g., replace `/` and `\\` with `_`, and prevent `..` segments).
- Ensure the parent directory of `filePath` exists (e.g., `mkdirpSync(dirname(filePath))`) before writing.
- (Optional) If writing fails, consider logging a `warn` (still not failing the build) so the user who opted in understands why no file appeared.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +311 to +312
const filePath = writeBundleStats(stats, name);
if (filePath) this.logger.console(`${chalk.magenta('[Rspack]')} wrote bundle stats to ${chalk.cyan(filePath)}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Ad-hoc chalk in writestats 📘 Rule violation ⚙ Maintainability

The new writeStats() CLI output uses direct chalk formatting and a hardcoded [Rspack] prefix
instead of the repository’s shared CLI output formatting toolkit. This risks inconsistent CLI output
styling across commands and bypasses the documented style guide.
Agent Prompt
## Issue description
`UiMain.writeStats()` prints CLI output using ad-hoc `chalk` formatting (e.g. `chalk.magenta('[Rspack]')`) rather than using the shared CLI output formatting toolkit.

## Issue Context
The repo requires CLI output to follow `scopes/harmony/cli/cli-output-style-guide.md` and use the shared formatter utilities from `@teambit/cli` (`scopes/harmony/cli/output-formatter.ts`) to keep output consistent and maintainable.

## Fix Focus Areas
- scopes/ui-foundation/ui/ui.main.runtime.ts[309-315]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +37 to +38
const filePath = join(dir, `${name}.stats.json`);
writeFileSync(filePath, JSON.stringify(json));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Stats path can break 🐞 Bug ◔ Observability

writeBundleStats() builds the output path with the unsanitized name, so names containing path
separators can create an unintended nested path and make writeFileSync() fail (ENOENT).
UiMain.writeStats() swallows that failure (debug-only), so enabling BIT_UI_BUNDLE_STATS may produce
no stats files without any visible signal.
Agent Prompt
## Issue description
`writeBundleStats(stats, name)` uses `join(dir, `${name}.stats.json`)` and only creates `dir`. If `name` contains `/` or `\\`, the resulting `filePath` includes intermediate directories that do not exist, causing `writeFileSync()` to throw. The caller (`UiMain.writeStats`) catches and logs only at debug level, making the failure effectively silent when diagnostics are explicitly enabled.

## Issue Context
This is an opt-in diagnostics path (`BIT_UI_BUNDLE_STATS`), so it must be robust to odd names and should reliably write a file when enabled.

## Fix Focus Areas
- scopes/ui-foundation/ui/rspack/bundle-stats.ts[24-39]
- scopes/ui-foundation/ui/ui.main.runtime.ts[305-316]

## Suggested fix
- Sanitize `name` into a filename-safe value (e.g., replace `/` and `\\` with `_`, and prevent `..` segments).
- Ensure the parent directory of `filePath` exists (e.g., `mkdirpSync(dirname(filePath))`) before writing.
- (Optional) If writing fails, consider logging a `warn` (still not failing the build) so the user who opted in understands why no file appeared.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@GiladShoham
GiladShoham enabled auto-merge (squash) August 18, 2026 18:00
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 557eef4

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit c1f9286

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.

2 participants