Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7948b23
refactor(bestax-migrate): hoist the alias registry and literal helper…
allxsmith Sep 5, 2026
9e7c84b
feat(bestax-migrate): add bloomer as a migration source
allxsmith Sep 5, 2026
cff4ca4
test(bestax-migrate): add the bloomer fixtures, kitchen-sink e2e and …
allxsmith Sep 5, 2026
16e7765
docs(bestax-migrate): document the bloomer source
allxsmith Sep 5, 2026
8043fe5
fix(create-bestax): name bloomer in the scaffolded skill roster
allxsmith Sep 5, 2026
fad9e01
fix(bestax-migrate): keep bloomer's markup where bestax's defaults di…
allxsmith Sep 5, 2026
54ffe06
fix(bestax-migrate): honour inline type imports and flag what bloomer…
allxsmith Sep 5, 2026
e2cf7da
fix(bestax-migrate): keep bloomer's markup and public names through t…
allxsmith Sep 5, 2026
dcac5a0
fix(bestax-migrate): read a falsy href and a false helper the way blo…
allxsmith Sep 5, 2026
6f305bc
fix(bestax-migrate): turn bloomer's prop-less modifiers into their Bu…
allxsmith Sep 5, 2026
11e1b7a
fix(bestax-migrate): keep an icon's own name over one parsed from its…
allxsmith Sep 5, 2026
afd2274
fix(bestax-migrate): read a falsy PanelBlock href as no anchor, like …
allxsmith Sep 5, 2026
748d673
fix(bestax-migrate): keep spread order through folds and drop a falsy…
allxsmith Sep 5, 2026
bde01de
fix(bestax-migrate): let the Bulma class win over a spread, and name …
allxsmith Sep 5, 2026
c1528d0
fix(bestax-migrate): keep every attribute's place on a plain element,…
allxsmith Sep 5, 2026
7973889
fix(bestax-migrate): mirror bloomer's PageControl direction rule exactly
allxsmith Sep 5, 2026
95c0693
fix(bestax-migrate): carry bloomer's markup through nine cases a deep…
allxsmith Sep 6, 2026
dc5541e
fix(bestax-migrate): leave a class-hostile element alone when it carr…
allxsmith Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Building with an AI agent (Claude Code, Cursor, Copilot)? bestax-bulma ships LLM
| `bestax-custom-component` | Building a new custom component beyond stock Bulma, the bestax way |
| `bestax-icons` | Adding icons — Icon/IconText and the five supported icon libraries |
| `bestax-optimize` | Shrinking the built CSS — flavor builds, modular Sass, import hygiene |
| `bestax-migrate` | Moving an app off react-bulma-components (v4) onto bestax-bulma |
| `bestax-migrate` | Moving an app off react-bulma-components, rbx or bloomer onto bestax-bulma |

```bash
npx skills add https://github.com/allxsmith/bestax --skill bestax-layout-scaffold
Expand Down
21 changes: 18 additions & 3 deletions bestax-mcp/data/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,29 @@
},
{
"name": "bestax-migrate",
"description": "Migrate an existing app from an unmaintained React Bulma library — react-bulma-components (v4) or rbx (v2) — to @allxsmith/bestax-bulma on Bulma v1. Run the bestax-migrate codemod, then resolve every TODO(bestax-migrate) comment it leaves using the per-source mapping references. Use when a repo imports react-bulma-components or rbx and wants to move to bestax-bulma, when TODO(bestax-migrate) comments are present in a codebase, or when asked to migrate off an unmaintained React Bulma library.",
"description": "Migrate an existing app from an unmaintained React Bulma library — react-bulma-components (v4), rbx (v2) or bloomer (0.6) — to @allxsmith/bestax-bulma on Bulma v1. Run the bestax-migrate codemod, then resolve every TODO(bestax-migrate) comment it leaves using the per-source mapping references. Use when a repo imports react-bulma-components, rbx or bloomer and wants to move to bestax-bulma, when TODO(bestax-migrate) comments are present in a codebase, or when asked to migrate off an unmaintained React Bulma library.",
"promptName": "migrate",
"dir": "bestax-migrate",
"references": [
{
"id": "bloomer-component-map",
"file": "references/bloomer/component-map.md",
"bytes": 12033
},
{
"id": "bloomer-prop-map",
"file": "references/bloomer/prop-map.md",
"bytes": 22758
},
{
"id": "bloomer-unmappables",
"file": "references/bloomer/unmappables.md",
"bytes": 12915
},
{
"id": "css-migration",
"file": "references/css-migration.md",
"bytes": 8555
"bytes": 8622
},
{
"id": "rbx-component-map",
Expand All @@ -103,7 +118,7 @@
{
"id": "rbx-unmappables",
"file": "references/rbx/unmappables.md",
"bytes": 10780
"bytes": 10931
},
{
"id": "react-bulma-components-component-map",
Expand Down
37 changes: 23 additions & 14 deletions bestax-migrate/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@

jscodeshift-based CLI (`pnpm dlx bestax-migrate <source> <paths…>`) that migrates existing
apps from other React Bulma libraries to `@allxsmith/bestax-bulma`. Multi-source by design:
each source library registers in `src/sources/registry.ts`. Two are shipped —
`react-bulma-components` (v4 only) and `rbx` (v2 only).
each source library registers in `src/sources/registry.ts`. Three are shipped —
`react-bulma-components` (v4 only), `rbx` (v2 only) and `bloomer` (0.6 only).

## Hard rules

- **No source library is EVER installed anywhere in this repo** (supply-chain policy) —
not react-bulma-components, not rbx. Fixtures are read as _text_ (`__testfixtures__`,
`fixtures/kitchen-sink`, `fixtures/rbx-kitchen-sink`); the migration _input_ is never
not react-bulma-components, not rbx, not bloomer. Fixtures are read as _text_ (`__testfixtures__`,
`fixtures/kitchen-sink`, `fixtures/rbx-kitchen-sink`, `fixtures/bloomer-kitchen-sink`); the migration _input_ is never
typechecked or executed. Validation typechecks the migrated _output_ against the
workspace `@allxsmith/bestax-bulma`. The two kitchen-sink manifests are named
workspace `@allxsmith/bestax-bulma`. The kitchen-sink manifests are named
`package.input.json`, not `package.json`: Dependabot's alert graph treats any file with
that name as a real manifest whatever the lockfile says, and opened a security-update PR
against the rbx fixture's deliberately-old `node-sass` (#615). The e2e renames its copy
back inside `.e2e-tmp`, so the pass under test still sees a genuine `package.json`.
- Mapping-table first: each source's `mapping.ts` is its single source of truth. Every
export of that library must have an entry (`mapped`/`partial`/`todo`) — the
`mapping-coverage` test walks the vendored `RBC_EXPORTS` / `RBX_EXPORTS` list against it,
`mapping-coverage` test walks the vendored `RBC_EXPORTS` / `RBX_EXPORTS` / `BLOOMER_EXPORTS` list against it,
in both directions, so the table cannot grow an entry for something the library never
exported either. New coverage is a table edit (plus a `special` handler in `specials.ts`
when structure changes).
- **A new source is a directory plus one registry line.** `src/sources/_shared/` holds
everything library-agnostic: `jsx-utils.ts` (AST helpers + `TransformContext`),
`props.ts` (the `PropAction` interpreter — its universal table is a parameter, not an
import), `imports.ts` (binding collection and import aliasing), `specials-utils.ts`
(`alignTarget`, `mergeClassName`, `parseIconClasses`, and the `stripModifierProps`
factory), and `make-styles-transform.ts` (the whole Bulma 0.9→v1 stylesheet transform,
(`alignTarget`, `mergeClassName`, `parseIconClasses`, `modifierClass`, `restrictAsToTargets`,
the `stripModifierProps` factory and the `makeStructuralHelpers` factory behind
`replaceWithPlain`/`collapseOntoChild`), `viewports.ts` (Bulma's nine viewports → bestax's
prop suffixes), and `make-styles-transform.ts` (the whole Bulma 0.9→v1 stylesheet transform,
parameterised by the source package's own specifiers). What stays per-source is the data
— `mapping.ts`, `specials.ts`, `responsive.ts`, `deps.ts` — plus a `transform.ts` that
orchestrates them.
orchestrates them. bloomer's is the plain one: its exports are flat, so it has no
destructuring pass, no alias registry and no wrapping pass; the rbx and RBC copies still
carry all three.
- `'<source>'` must be added to `MIGRATE_SOURCE_VALUES` in `telemetry-worker/src/schema.ts`
or its events are dropped at ingest; `check:conformance --only=telemetry-allowlists`
fails until it is. That worker deploys to production on merge, so the two land together.
Expand All @@ -43,12 +47,14 @@ each source library registers in `src/sources/registry.ts`. Two are shipped —
- **A TODO message is a claim about bestax; read the component before writing it.** Four
shipped on #613 were false (Modal "always closes on Escape"; a Field TODO naming `isGrouped`).
The rbx e2e fails on an undocumented rule (RBC's does not); nothing checks the guidance is true.
- **A defect in one source's `transform.ts` is almost certainly in the other.** Nine fixes were
ported RBC↔rbx on #613 and review kept finding the unported half. Fix the sibling in the same
commit, or move the logic into `_shared/`.
- **A defect in one source's `transform.ts` is almost certainly in the others.** Nine fixes were
ported RBC↔rbx on #613 and review kept finding the unported half. Fix the siblings in the same
commit, or move the logic into `_shared/` (the alias registry, the literal/object helpers
and the structural-handler helpers went there on #410 for exactly this reason).
- **Resolve references by binding, not by identifier text.** Every serious bug on #613 came
from a name-keyed map (`function F(Header)` became `F(Card.Header)`). Use `aliasAt` in each
`transform.ts` (duplicated; hoist it): ancestors decide ownership, `scope.lookup` vetoes a nearer binding.
from a name-keyed map (`function F(Header)` became `F(Card.Header)`). Use
`makeAliasRegistry` from `_shared/imports.ts`: ancestors decide ownership, `scope.lookup`
vetoes a nearer binding.
- **The report may only describe what actually happened.** The manifest headline said "bumped
bulma" in most manifest shapes where nothing was bumped. Track each mutation; phrase from it.
- **The stylesheet and manifest passes must agree on what is removable.** Both report rather
Expand Down Expand Up @@ -107,6 +113,9 @@ each source library registers in `src/sources/registry.ts`. Two are shipped —
script extracts the `<Playground>` blocks out of its 43 `*.docs.mdx` pages (254 blocks)
and rebuilds each page as one synthetic module written the way a consumer writes rbx.
Output lands in `.e2e-tmp/corpus-out-rbx/`.
- `validate:corpus:bloomer` — bloomer's own MIT docs: 39 React "Scene" `.tsx` files that
render the library through a relative `src` import, migrated as written after that
specifier becomes `'bloomer'`. Output lands in `.e2e-tmp/corpus-out-bloomer/`.

The corpus is the only check that sees breadth; the kitchen-sink e2e is the only one that
sees bestax's _real_ prop names. Both are needed — the rbx e2e's typecheck caught six
Expand Down
22 changes: 12 additions & 10 deletions bestax-migrate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ Codemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https:/

Currently supported source libraries:

| Source | Status |
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |
| [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) |
| Source | Status |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) |
| [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) |
| [`bloomer`](https://github.com/AlgusDark/bloomer) (0.6) | ✅ All 108 exports mapped (Font Awesome 4 icons and Bulma 0.4's `Nav` are flagged) |

Pass the source library as the first argument: `bestax-migrate <source> <paths…>`.

Expand All @@ -33,8 +34,9 @@ the app being migrated — the source is read as text and is never executed.
```bash
# Preview what would change (no writes)
pnpm dlx bestax-migrate react-bulma-components src/ --dry
# …or, for an rbx app
# …or, for an rbx or bloomer app
pnpm dlx bestax-migrate rbx src/ --dry
pnpm dlx bestax-migrate bloomer src/ --dry

# Apply the migration
pnpm dlx bestax-migrate react-bulma-components src/
Expand All @@ -45,14 +47,14 @@ npm and yarn work too: `npx bestax-migrate …` / `yarn dlx bestax-migrate …`.
The codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to:

- rewrite the source library's imports to `@allxsmith/bestax-bulma` (including destructuring and namespace imports)
- rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …)
- rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …) — and, for bloomer, its flat names onto bestax's compounds (`CardHeaderTitle` → `Card.Header.Title`)
- convert props (`renderAs` → `as`, `loading` → `isLoading`, numeric spacing/text sizes → string unions, `textAlign="center"` → `textAlign="centered"`, …)
- flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`)
- flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`) and bloomer's `isDisplay`/`isHidden` strings, arrays and objects (`isHidden="touch"` → `visibilityTouch="hidden"`)
- restructure patterns bestax models differently (`Table.Container`, Navbar dropdowns, `Form.Help`, icon-font children → `<Icon name=…>`)
- migrate stylesheets: CSS imports converge on `@allxsmith/bestax-bulma/bestax.css`, and SCSS files move from Bulma 0.9's `@import` + `$var !default` overrides to `@use 'bulma/sass' with (…)` plus `@use '@allxsmith/bestax-bulma/scss/extras'`
- update `package.json`: remove the source library, add `@allxsmith/bestax-bulma`, raise a declared pre-1.0 `bulma` to `^1` (adding it only when your sources import `bulma/…` directly — otherwise it arrives transitively), and swap the dead `node-sass` for dart `sass` (no install is ever run). Migrating **rbx** is what frees an app from Bulma 0.7.5: rbx pinned it as a _direct_ dependency, so the app could not choose its own Bulma version at all. The four Bulma extensions rbx also pulled in (`bulma-badge`, `bulma-divider`, `bulma-pageloader`, `bulma-tooltip`) are **reported rather than removed** — bestax replaces all four, but a manifest entry is a deliberate declaration and your own Sass may import them
- update `package.json`: remove the source library, add `@allxsmith/bestax-bulma`, raise a declared pre-1.0 `bulma` to `^1` (adding it only when your sources import `bulma/…` directly — otherwise it arrives transitively), and swap the dead `node-sass` for dart `sass` (no install is ever run). Migrating **rbx** is what frees an app from Bulma 0.7.5: rbx pinned it as a _direct_ dependency, so the app could not choose its own Bulma version at all. The four Bulma extensions rbx also pulled in (`bulma-badge`, `bulma-divider`, `bulma-pageloader`, `bulma-tooltip`) are **reported rather than removed** — bestax replaces all four, but a manifest entry is a deliberate declaration and your own Sass may import them. A **bloomer** app declared its own Bulma 0.6, which is what gets bumped

Anything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the migration guide ([react-bulma-components](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), [rbx](https://bestax.io/docs/guides/getting-started/migration/rbx)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:
Anything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the migration guide ([react-bulma-components](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), [rbx](https://bestax.io/docs/guides/getting-started/migration/rbx), [bloomer](https://bestax.io/docs/guides/getting-started/migration/bloomer)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you:

```bash
npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate
Expand All @@ -74,7 +76,7 @@ npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate
2. Search for `TODO(bestax-migrate)` and resolve each comment
3. Typecheck/build and review the rendered app

Full walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components) · [rbx migration guide](https://bestax.io/docs/guides/getting-started/migration/rbx).
Full walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components) · [rbx migration guide](https://bestax.io/docs/guides/getting-started/migration/rbx) · [bloomer migration guide](https://bestax.io/docs/guides/getting-started/migration/bloomer).

## Hardened by default

Expand Down
Loading
Loading