Skip to content

chore(webpack): better development sourcemaps - #7147

Merged
GiladShoham merged 1 commit into
masterfrom
chore/better-dev-sourcemaps
Mar 14, 2023
Merged

chore(webpack): better development sourcemaps#7147
GiladShoham merged 1 commit into
masterfrom
chore/better-dev-sourcemaps

Conversation

@leimonio

Copy link
Copy Markdown
Contributor

Proposed Changes

Change sourcemaps to better quality

@GiladShoham
GiladShoham enabled auto-merge (squash) March 14, 2023 12:04
@GiladShoham
GiladShoham disabled auto-merge March 14, 2023 13:15
@GiladShoham
GiladShoham merged commit 98492b6 into master Mar 14, 2023
@GiladShoham
GiladShoham deleted the chore/better-dev-sourcemaps branch March 14, 2023 13:15
GiladShoham added a commit that referenced this pull request Aug 18, 2026
…10628)

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 #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/&lt;hash&gt;.cjs"*. Introduced by the webpack→rspack
migration (#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 array** — `useCurrentUser` 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants