Skip to content

refactor(ranvi): extract the builder into its own published package - #408

Merged
chaxus merged 3 commits into
mainfrom
refactor/extract-ranview
Sep 13, 2026
Merged

chaxus merged 3 commits into
mainfrom
refactor/extract-ranview

Conversation

@chaxus

@chaxus chaxus commented Sep 13, 2026

Copy link
Copy Markdown
Owner

The builder had grown into a general-purpose library wearing a component library's clothes: 1,831 lines, a 434-line manual, 345 tests, and exactly one external import. Anyone who wanted framework-free reactive DOM had to install ranui — and with it dash.js, hls.js, mpegts.js, mermaid and temml: 242 MB of transitive dependencies for a file that depends on one function. ranpress could not use it at all for that reason.

It is ranvi now, depending only on ranuts.

ranui's behaviour is unchanged — that was the acceptance test

All 1,641 remaining ranui tests pass without a single edit.

before after
ranui unit 1,732 1,565
ranui ssr 254 76
ranvi — 345
total 1,986 1,986

None lost. The one further difference — 1,566 → 1,565 — is docs.references, which generates a case per doc file in ranui, and ranui now has one fewer doc.

How the surface was kept identical

  • 84 of the 90 internal imports go through @/utils/builder, so that path stays and re-exports ranvi. Not one of those files changed.
  • The six that reached past it (./builder/core, ./builder/signal, ./builder/env) now name ranvi directly.
  • builder.ts, the published ranui/builder entry, keeps enumerating every export by name rather than forwarding wholesale — a star would also carry whatever ranvi adds next, quietly widening ranui's public API. (package-exports.source enforces this, and caught it when the word appeared in a comment.)

Build output, checked in both shapes

They differ, and both had to be right:

  • ES build externalises ranvi — it is a runtime dependency, so consumers get it through npm. dist/builder.js is now a 1.4 kB re-export.
  • IIFE bundles inline it, as they must. Verified no standalone bundle contains a bare import of it; the only occurrence of the string is inside an error message.

Packaging

ranvi exports TypeScript source to workspace consumers and swaps in dist through publishConfig at pack time, so pnpm -F ranui build needs no build-order dependency on it.

That does mean consumers type-check this source — which immediately found a bare import.meta.env.DEV that only compiles where Vite's ambient types are declared. It is a local helper now. This is the kind of thing that would otherwise have been discovered by the first person to install the package.

Found on the way

  • ShadowBuilder kept a private options field that nothing ever read — taken in the constructor, assigned, never used.
  • Error strings still said "ranui": a cyclic-dependency message and a duplicate-key warning now name the package they come from.
  • test/readme.test.ts is new. The manual's import lines were rewritten from ranui/builder to ranvi mechanically, and nothing was checking that the names in them exist. A manual that tells a reader to import something that does not exist is worse than no manual — they assume they are holding it wrong. 21 names checked.

Verification

tsc across 8 packages, 3,145 tests, verify:design across 3 packages, verify-packages (14 declared), prettier, and both ranui and ranvi builds.

🤖 Generated with Claude Code

https://claude.ai/code/session_019pjSL6jGCw2wmBLhCAW35d

The builder had grown into a general-purpose library wearing a component
library's clothes: 1,831 lines, a 434-line manual, 345 tests, and exactly one
external import. Anyone who wanted framework-free reactive DOM had to install
ranui — and with it dash.js, hls.js, mpegts.js, mermaid and temml: 242 MB of
transitive dependencies for a file that depends on one function. ranpress could
not use it at all for that reason.

It is `ranvi` now, depending only on ranuts.

**ranui's behaviour is unchanged, and that was the acceptance test.** All 1,641
of its remaining tests pass without a single edit. The count moved from
1,732 + 254 to 1,565 + 76 because 344 builder tests moved to the new package;
1,986 before, 1,986 after, none lost. The one further difference —
1,566 → 1,565 — is `docs.references`, which generates a case per doc file in
ranui, and ranui now has one fewer doc.

How the surface was kept identical:

  - 84 of the 90 internal imports go through `@/utils/builder`, so that path
    stays and re-exports `ranvi`. Not one of those files changed.
  - The six that reached past it (`./builder/core`, `./builder/signal`,
    `./builder/env`) now name `ranvi` directly.
  - `builder.ts`, the published `ranui/builder` entry, keeps enumerating every
    export by name rather than forwarding wholesale — a star would also carry
    whatever ranvi adds next, quietly widening ranui's public API.

Build output was checked in both shapes, because they differ: the ES build
externalises `ranvi` (it is a runtime dependency, so consumers get it through
npm), while the IIFE bundles inline it, as they must — verified that no
standalone bundle contains a bare import of it.

`ranvi` exports TypeScript source to workspace consumers and swaps in `dist`
through `publishConfig` at pack time, so `pnpm -F ranui build` needs no
build-order dependency on it. That does mean consumers type-check this source,
which immediately found a bare `import.meta.env.DEV` that only compiles where
Vite's ambient types are declared; it is a local helper now.

Two other things surfaced on the way. `ShadowBuilder` kept a private `options`
field that nothing ever read — taken in the constructor, assigned, never used.
And the error strings still said "ranui": a cyclic-dependency message and a
duplicate-key warning that now name the package they come from.

`test/readme.test.ts` is new: the manual's import lines were rewritten from
`ranui/builder` to `ranvi` mechanically, and nothing was checking that the
*names* in them exist. A manual that tells a reader to import something that
does not exist is worse than no manual — they assume they are holding it wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019pjSL6jGCw2wmBLhCAW35d
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Deploying ran with  Cloudflare Pages  Cloudflare Pages

Latest commit: e09f322
Status: ✅  Deploy successful!
Preview URL: https://a95ba4f7.ran-4ty.pages.dev
Branch Preview URL: https://refactor-extract-ranview.ran-4ty.pages.dev

View logs

chaxus and others added 2 commits September 13, 2026 12:09
Moving a file breaks whatever pointed at it, and the repo's dead-link check
found thirteen — which is the check doing exactly its job.

  - `docs/BUILDER.md` was linked from all eight ranui READMEs and both
    `utils/README*.md`. They now point at ranvi's README, which is where the
    manual went.
  - `SSR_DESIGN.md` cited `utils/builder/mocks.ts#L227` for the serialisation
    contract; that file is `ranvi/src/mocks.ts` now.
  - ranvi's own README carried two relative links to `COMPONENTS.md` and
    `DESIGN.md`. They resolved while it lived in `ranui/docs/` and stopped the
    moment it did not — both name ranui's copies explicitly now.

Also removes a `readFileSync` import left unused in `design-bundle.ts` by an
earlier commit of mine. It was a lint warning rather than the failure, but it
was mine and it was noise in the same log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019pjSL6jGCw2wmBLhCAW35d
The previous run sat queued for 51 minutes without a runner being allocated —
GitHub reported all systems operational, the repository is public so standard
runners are unmetered, nothing else was consuming concurrency, and no workflow
declares a concurrency group. Cancelled and re-triggered; no source change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019pjSL6jGCw2wmBLhCAW35d
@chaxus
chaxus merged commit 8da0df0 into main Sep 13, 2026
11 checks passed
@chaxus
chaxus deleted the refactor/extract-ranview branch October 3, 2026 11:52
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