Skip to content

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, bare effect {}, the re-listed preserved state X form) 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.


Terminal window
bun scripts/migrate.mjs examples/your-app

It 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.


#TransformBeforeAfter
T1Honest value imports for stores/servicesimport type { cartStore }import { cartStore }
T2Explicit lifetime word on exported storesexport store settings {export app store settings {
T3Inline preserved (the re-list form was retired)state notes = ""preserved state notespreserved state notes = ""
T4x = expr is the write form, in handlers tooonChange={e => setNote(e.target.value)}onChange={e => { note = e.target.value }}
T5cleanup retired → teardown via returnmount {…} cleanup {…}mount { … return () => {…} }
T6; terminators on declaration linesstate count = 0state 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.


Once migrated, point reactra-tsc at your app so type errors fail the build:

package.json
{ "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 unknown in the shadow today (uses <behaviour> injected names, query T[], complex resource fetchers), so reactra-tsc may report advisory diagnostics on them. They don’t block correct code and are being closed.