Skip to content

fix(build): re-link component stylesheets so consumers can load them - #1

Merged
inureyes merged 1 commit into
mainfrom
fix/component-css-reachability
Aug 5, 2026
Merged

fix(build): re-link component stylesheets so consumers can load them#1
inureyes merged 1 commit into
mainfrom
fix/component-css-reachability

Conversation

@inureyes

@inureyes inureyes commented Aug 5, 2026

Copy link
Copy Markdown
Member

What

@lablup/ui-common@0.1.0-alpha.0 ships every component unstyled, and a consumer has no supported way to fix it. This re-links the stylesheets and adds the two guards that would have caught it.

The defect

In library mode Rollup strips import "./Button.css" out of the chunk and leaves an "empty css" marker, because an application build would have injected a link tag instead. Nothing puts the import back, so the published tarball carries:

  • 20 component stylesheets under dist/assets/
  • zero .css references in any emitted .js
  • no exports entry naming assets/*, so import "@lablup/ui-common/assets/components/Button/Button.css" fails with ERR_PACKAGE_PATH_NOT_EXPORTED

styles/base.css is only the 113 tokens, so importing it does not compensate. Measured against the install fixture, the published tarball produces a 4.52 kB consumer CSS bundle where this build produces 21.06 kB.

This blocks Backend.AI GO's migration onto the packages (backend.ai-go#4033 Phase 4): moving a call site to the package would silently drop that component's styling, and neither pnpm build nor CI would notice.

The fix

linkComponentStyles in vite.config.ts re-attaches each emitted stylesheet to the chunk whose module imported it. Call sites are unchanged, and README's "component CSS travels with the component" becomes true.

Two details worth review:

  • It reads the module graph (getModuleInfo().importedIds), not filenames. Pairing Button.css with Button.tsx by name agrees on every component today and would break silently on the first one that departs from the convention. The stripped import is gone from chunk.moduleIds, but the edge survives in the graph.
  • Imports are appended rather than prepended. ES modules hoist them, so execution order is unchanged while every existing line keeps its position and the sourcemap emitted moments earlier stays accurate.

The guards

Both existing checks were green across the whole defect, because each asked only whether advertised paths resolve, never whether a shipped file is reachable.

Guard Catches Verified against alpha.0
check:pack reachability a packed stylesheet no module imports and no exports entry names reports all 20
check-fixture-styles.mjs in CI rules missing from the consumer's own bundle fails on Button and Drawer, one per import shape

The fixture markers are read out of the packed stylesheet rather than hardcoded, so renaming a class is not a false failure.

Verification

  • pnpm run verify green
  • Fixture built against the packed tarball: Consumer stylesheets present: Button, Drawer.
  • Negative test both ways: with the plugin disabled check:pack fails on all 20; with the published alpha.0 tarball installed the fixture check fails on both components

Release

Version bumped to 0.1.0-alpha.1. Publishing needs a v0.1.0-alpha.1 release, since publish.yml asserts the tag matches package.json.

lablup/ui-ai has the identical build configuration and the same defect (7 stylesheet imports in src, no CSS export path). It needs the same change before Phase 4 reaches ChatMessage.

0.1.0-alpha.0 shipped every component unstyled. In library mode Rollup strips
`import "./Button.css"` out of the chunk and leaves an "empty css" marker,
because an application build would have injected a link tag instead. Nothing
put the import back, so all 20 component stylesheets were packed under
`dist/assets/` with no chunk importing them and no exports entry naming them.
A consumer had no supported way to load them at all: `assets/*` is not in the
exports map, so a deep import fails with ERR_PACKAGE_PATH_NOT_EXPORTED, and
`styles/base.css` carries only the 113 tokens.

Measured against the install fixture: the published tarball produces a 4.52 kB
consumer bundle (tokens only) where this build produces 21.06 kB.

`linkComponentStyles` re-attaches each emitted stylesheet to the chunk whose
module imported it, which is what makes README's "component CSS travels with
the component" true and leaves every call site unchanged. It reads the module
graph rather than pairing `Button.css` with `Button.tsx` by filename, since
that convention holds for every component today and would break silently on
the first one that departs from it. Imports are appended, not prepended: ES
modules hoist them, so execution order is unchanged while existing lines keep
their positions and the sourcemap stays accurate.

Both existing checks were green throughout the defect, because each asked only
whether advertised paths resolve, never whether a shipped file was reachable.
Two guards close that:

- `check:pack` now fails on a packed stylesheet that no module imports and no
  exports entry names. Against the alpha.0 build it reports all 20.
- CI asserts the fixture's own bundle carries the rules, using markers read out
  of the packed stylesheet so a class rename is not a false failure. Against
  the published tarball it fails on Button and Drawer, one per import shape.
@inureyes
inureyes merged commit 99632b5 into main Aug 5, 2026
3 checks passed
@inureyes
inureyes deleted the fix/component-css-reachability branch August 5, 2026 12:47
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.

1 participant