Migrate v1 → v2
DSL v2 is a more regular, more honest grammar — and it’s the version the new
TypeScript/IDE tooling (@reactra/language-tools, reactra-tsc) understands. Migrating
is one command plus a quick read of the handful of changes it can’t make for you
(there are very few).
Why migrate? Two reasons. (1) v2 unlocks real type-checking in your editor and CI. (2) Some v1 constructs (
cleanup, bareeffect {}, the re-listedpreserved state Xform) were retired and no longer compile — if your app uses them, it’s already broken on the current compiler, and the codemod is what fixes it.
1. Run the codemod
Section titled “1. Run the codemod”bun scripts/migrate.mjs examples/your-appIt rewrites your DSL files in place, prints a per-file summary of what it changed, and is
idempotent — running it again on an already-migrated app does nothing. Commit, then
verify (bun run dev, click around). Migrate one app at a time so any surprise is
isolated.
2. What it changes (and why)
Section titled “2. What it changes (and why)”| # | Transform | Before | After |
|---|---|---|---|
| T1 | Honest value imports for stores/services | import type { cartStore } | import { cartStore } |
| T2 | Explicit lifetime word on exported stores | export store settings { | export app store settings { |
| T3 | Inline preserved (the re-list form was retired) | state notes = "" … preserved state notes | preserved state notes = "" |
| T4 | x = expr is the write form, in handlers too | onChange={e => setNote(e.target.value)} | onChange={e => { note = e.target.value }} |
| T5 | cleanup retired → teardown via return | mount {…} cleanup {…} | mount { … return () => {…} } |
| T6 | ; terminators on declaration lines | state count = 0 | state count = 0; |
setX is not gone — it’s a typed escape hatch you can still reach for (handing a
setter to a non-Reactra React child, or setX(prev => …)). T4 only rewrites the common
setX(value) call form to the clearer x = value.
3. Errors you’ll see if you don’t migrate
Section titled “3. Errors you’ll see if you don’t migrate”On the v2 compiler, the retired constructs no longer parse. You now get a clear, actionable message instead of a cryptic one:
R027: `cleanup` was retired in Reactra v2. Return a teardown function from `mount { … return () => { … } }` or `effect on(dep) { … return () => { … } }` instead.
R027: bare `effect {}` (no `on` clause) was retired in Reactra v2. Use `mount { … }` for run-once side effects, or `effect on(dep) { … }` to watch a dependency.The re-listed preserved state X form (declaring state X and separately marking it
preserved) is also retired — fold it into one inline preserved state X = init. The
codemod (T3) does this for you.
4. The one thing the codemod can’t infer: services
Section titled “4. The one thing the codemod can’t infer: services”If your app declares a service, wire the generated service list into main.tsx once:
import { configureServices } from "@reactra/service";import { SERVICES } from "./routeManifest.generated";
configureServices({ services: SERVICES });The codemod adds this for apps that have services; double-check it landed. Apps with no
service need nothing here.
5. Turn on type-checking
Section titled “5. Turn on type-checking”Once migrated, point reactra-tsc at your app so type errors fail the build:
{ "scripts": { "type-check": "reactra-tsc --noEmit -p tsconfig.json" } }and install the editor extension (VS Code/Cursor) or the standalone LSP (reactra-lsp-server,
for Zed/neovim) for live feedback. See packages/language-tools/README.md.
Honest status: a few constructs still lower to
unknownin the shadow today (uses <behaviour>injected names,query T[], complexresourcefetchers), soreactra-tscmay report advisory diagnostics on them. They don’t block correct code and are being closed.