Skip to content

perf(ui): make @rozenite/ui tree-shakeable - #557

Merged
V3RON merged 6 commits into
mainfrom
fix/ui-tree-shaking
Oct 12, 2026
Merged

V3RON merged 6 commits into
mainfrom
fix/ui-tree-shaking

Conversation

@V3RON

@V3RON V3RON commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Description

@rozenite/ui is now tree-shaken in plugin builds: a panel bundles only the components it uses.

  • Pure annotations: module-scope calls that Rollup couldn't prove free of side effects are now marked /* @__PURE__ */. That covers 21 compound components built with Object.assign(Root, { … }), 11 cva(...), 6 createContext(...), the cn(...) calls nested in cva arguments, and one new Set. The annotations survive into dist/index.js.
  • sideEffects: "sideEffects": ["*.css"] in package.json, so styles.css and fonts.css stay side-effectful.
  • Regression test: src/tree-shaking.test.ts bundles the built package with Vite's production build.
    • A Button-only entry may contain only the button component, under 20 kB gzipped.
    • A tokens-and-hooks entry may contain no components at all, under 2 kB.
    • It fails against the old build with 95 extra components.

No component code or API changed.

Related Issue

Closes #556

Context

Before this, importing only Button bundled 96 of the library's 124 component parts and their @base-ui/react dependencies.

Import Before (min+gzip) After
Button 110.1 kB 11.0 kB
Dialog 110.1 kB 28.8 kB
tokens / hooks only 110 kB 0.1–0.7 kB

Real panels, gzipped JS from forced builds:

Package Before After Change
network-activity-plugin 470.3 kB 428.3 kB −9%
sqlite-plugin 455.8 kB 431.2 kB −5%
storage-plugin 336.7 kB 312.9 kB −7%
react-navigation-plugin 307.3 kB 219.2 kB −29%
rhf-plugin 200.2 kB 141.8 kB −29%
controls-plugin 198.9 kB 160.7 kB −19%
overlay-plugin 199.9 kB 161.5 kB −19%
file-system-plugin 239.3 kB 200.9 kB −16%
feature-flags-plugin 245.4 kB 210.4 kB −14%
performance-monitor-plugin 228.2 kB 201.5 kB −12%
require-profiler-plugin 236.0 kB 212.4 kB −10%
shell 181.4 kB 139.6 kB −23%
app 189.3 kB 147.5 kB −22%
vite-plugin dev host 194.4 kB 168.5 kB −13%

preserveModules (one file per component) was evaluated but not adopted.

  • For Rollup and Vite consumers, which covers every Rozenite plugin, the output is identical once the annotations are in.
  • It would help third-party Webpack or esbuild consumers: a Button-only bundle goes from 36–45 kB to about 11 kB.
  • The cost is 121 files in dist instead of 3. It can be a follow-up if those users matter.

Testing

  • git fetch origin main && pnpm checks:affected && pnpm test:affected: passed. The new test takes about 2–3 s and has a 60 s timeout, so it doesn't flake when Turborepo runs suites in parallel.
  • pnpm release:plan: passed.
  • Visual check: Storybook was built against the tree-shaken dist (not the source alias) and compared with the source build. Dialog, Tabs, Sidebar, Menu, Toast, Split, Select, ConfirmDialog, and the Design refresh panel in dark and light give identical screenshots.
  • Built panels: ten plugin panels and the shell render with no JS errors: SQLite, React Navigation, Storage, React Hook Form, Controls, Feature Flags, File System, Performance Monitor and Require Profiler.

V3RON added 6 commits October 12, 2026 17:52
Compound components are built with a top-level Object.assign(Root, {...}).
Rollup cannot prove that call side-effect free, so importing any one
component from @rozenite/ui kept every compound component and the
@base-ui/react modules behind them. Annotate those calls (and the cva /
createContext / cn calls at module scope) with /* @__PURE__ */ so bundlers can
drop the ones a consumer never uses.
cva() base classes, the Text label variant and the control surfaces still
called cn() at module scope, which kept tailwind-merge in every bundle even
for a consumer that only imports a token such as sizeHeight (13.6 kB gzip,
now 0.1 kB).
Only the stylesheets have side effects, so bundlers may drop any other module
the consumer does not use.
@V3RON
V3RON merged commit 212e6fe into main Oct 12, 2026
4 checks passed
@V3RON
V3RON deleted the fix/ui-tree-shaking branch October 12, 2026 16:14
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.

@rozenite/ui is not tree-shaken in plugin builds

1 participant