Migrate to ReScript 12
If you encounter any missing information or issues during migration, please open an issue or, even better, send a pull request to help improve this guide.
Recommended Migration
Prerequisites
ReScript V11 project.
Uncurried mode must be enabled (i.e. you have not opted-out from it).
Your project must not contain any OCaml source code anymore, as support for
.mlfiles is removed in this version. However there are ways to convert OCaml syntax with an older ReScript compiler version (see below).Minimum supported Node.js version is 20.11.0.
Standard Library Changes
In V12, the new standard library ships with the compiler, so you can uninstall and remove the @rescript/core dependency from your rescript.json
CONSOLE$ npm remove @rescript/core
DIFF {
"bs-dependencies": [
- "@rescript/core"
]
}
Also remove auto opening of RescriptCore.
DIFF {
"bsc-flags": [
- "-open RescriptCore",
]
}
if you had @rescript/std installed, remove it as well:
SHELLnpm uninstall @rescript/std
this is replaced by @rescript/runtime, which is a installed as a dependency of rescript now.
Replacements
Some typical name changes include:
Error.t->JsError.traise(MyException("error"))->throw(MyException("error"))Js.Exn.Errorexception ->JsExnError.make->JsExn.makeError.raise->JsExn.raiseError.message->JsExn.messageBool.fromStringExn("true")->Bool.fromStringOrThrow("true")Int.Bitwise.lsl->Int.shiftLeft
Tip: You can use the migration tool to automatically replace these with the new functions.
SHELLnpx rescript-tools migrate-all# preview the changes via rescript-tools migrate [--stdout]
Bitwise operations
v11:
RESlet w = lnot(a) // bitwise NOT
let x = lxor(a, b) // bitwise XOR
let y = land(a, b) // bitwise AND
let z = lor(a, b) // bitwise OR
v12:
RESlet w = ~~~a // bitwise NOT
let x = a ^^^ b // bitwise XOR
let y = a &&& b // bitwise AND
let z = a ||| b // bitwise OR
Shift operations
v11:
RESlet x = lsl(a, b) // logical left shift
let y = lsr(a, b) // logical right shift
let z = asr(a, b) // unsigned right shift
v12:
RESlet x = a << b // logical left shift
let y = a >> b // logical right shift
let z = a >>> b // unsigned right shift
JSX children spread
v11:
RES<div> ...children </div>
v12:
RES<div> children </div>
Attributes
v11:
RES@bs.as("foo")
@bs.send
@bs.new
@raises
@genType.as
v12:
RES@as("foo")
@send
@new
@throws
@as
@methand@bs.send.pipeare removed.
Assert
v11:
RESassert 1 == 2
v12:
RESassert(1 == 2) // Now a regular function call
Configuration
Rename bsconfig.json to rescript.json and update these configuration options:
bs-dependencies→dependenciesbs-dev-dependencies→dev-dependenciesbsc-flags→compiler-flags
jsx
Set
versionto4(lower versions are not supported)Remove
modeoption (automatically set toautomatic)
Build System Changes
The build system has been completely rewritten in v12.0.0.
In v11, we had:
SHELL# build rescript build # watch build rescript build -w # format rescript format -all
in v12, this becomes:
SHELL# build rescript # watch build rescript watch # format rescript format
Converting generated .ml files
Note: This setup is an escape hatch. It keeps legacy generators like atdgen working but it also forces you to maintain two compiler versions. Whenever possible migrate such things to modern ReScript tooling such as Sury.
Some projects still rely on tools such as atdgen that emit .ml files. ReScript 12 cannot compile those files directly, so you must keep using ReScript 11 only to convert the generated .ml files back to .res files before you run the v12 build.
Keep ReScript 12 as the sole compiler dependency in your main project (i.e.
devDependencies.rescriptstays at^12.0.0).Install ReScript 11 in a dedicated subfolder (so its binaries never replace the v12 ones in
node_modules/.bin). A simple option is to store it under a subfolder, e.g.tools(if you're using workspaces, keep this folder out of the root workspace list so hoisting can't swap the v12 shims):
cd into tools and run npm create rescript-app and select the basic template and a v11 version of ReScript. You can name it rescript-11 for instance.
cdback into the root of your project and add a helper script that references the compiler from that folder (adapt the path accordingly):JSON{ "scripts": { "convert-ml": "tools/rescript-11/node_modules/.bin/rescript convert src/*.ml" } }Execute the helper script to convert your
.mlfiles to.resfiles:
CONSOLEnpm run convert-ml
List of all breaking changes
Below is a consolidated excerpt of all the breaking changes from the compiler changelog.
Language & syntax
Tag functions named
jorjsare no longer reserved, so add your own implementation whenever a tagged template expects them. https://github.com/rescript-lang/rescript-compiler/pull/6817lazysyntax was removed; use theLazymodule instead. https://github.com/rescript-lang/rescript-compiler/pull/6342All legacy
@bs.*attributes (e.g.@bs.as,@bs.send) and@bs.openwere removed; use their prefix-free successors (@as,@send,@open, …). https://github.com/rescript-lang/rescript-compiler/pull/6643 https://github.com/rescript-lang/rescript-compiler/pull/6629@bs.send.pipewas removed; rewrite bindings to use@send. https://github.com/rescript-lang/rescript-compiler/pull/6858 https://github.com/rescript-lang/rescript-compiler/pull/6891OCaml
.mlfiles are no longer supported anywhere:.mlparsing/formatting went away and therescript convertCLI was removed, so convert legacy files to.resbefore upgrading. https://github.com/rescript-lang/rescript-compiler/pull/6848 https://github.com/rescript-lang/rescript-compiler/pull/6852 https://github.com/rescript-lang/rescript-compiler/pull/6860Some global names and old keywords are no longer automatically prefixed during JS emission; update any code that was relying on the mangled names. https://github.com/rescript-lang/rescript-compiler/pull/6831
JSX v3 and the
-bs-jsx-modeoption were removed and JSX children spreads are no longer valid; JSX v4 semantics are now the only supported mode. https://github.com/rescript-lang/rescript-compiler/pull/7072 https://github.com/rescript-lang/rescript/pull/7327 https://github.com/rescript-lang/rescript/pull/7869
Standard library & runtime
OCaml compatibility layers in the stdlib and primitives were removed/deprecated. https://github.com/rescript-lang/rescript-compiler/pull/6984
Deprecated modules
Js.VectorandJs.Listwere deleted. https://github.com/rescript-lang/rescript-compiler/pull/6900The legacy
js_cast.reshelpers were removed; migrate to explicit externals. https://github.com/rescript-lang/rescript-compiler/pull/7075JsErrorand related modules were renamed/cleaned up underJsExn. https://github.com/rescript-lang/rescript/pull/7408BigInt.fromFloatnow returnsoptionand exposesBigInt.fromFloatOrThrow, and theExn-suffixed helpers acrossBool,BigInt,JSON,Option,Null,Nullable,Result, andListnow end withOrThrow. https://github.com/rescript-lang/rescript/pull/7419 https://github.com/rescript-lang/rescript/pull/7518 https://github.com/rescript-lang/rescript/pull/7554Result.getOrThrowthrows a JSError(instead ofNot_found), andResult.equal/Result.comparenow provide a comparison function forErrorvalues. https://github.com/rescript-lang/rescript/pull/7630 https://github.com/rescript-lang/rescript/pull/7933Iterator.forEachnow emitsIterator.prototype.forEach. https://github.com/rescript-lang/rescript/pull/7506Date.makeuses~dayinstead of~date. https://github.com/rescript-lang/rescript/pull/7324Plain
intmultiplication is implemented as a regular int32 operation instead ofMath.imul. https://github.com/rescript-lang/rescript/pull/7358The
ListAPI was cleaned up—several functions were renamed or removed (see the PR for the exact surface). https://github.com/rescript-lang/rescript/pull/7290String.getSymbol/String.setSymbolwere removed; onlyString.getSymbolUnsaferemains on strings. https://github.com/rescript-lang/rescript/pull/7571 https://github.com/rescript-lang/rescript/pull/7626String.charCodeAtnow returnsoption<int>and exposesString.charCodeAtUnsafefor unchecked access. https://github.com/rescript-lang/rescript/pull/7877Intl.*.supportedLocalesOfbindings now returnarray<string>and the non-portableIntl.PluralRules.selectBigInt/selectRangeBigIntwere removed. https://github.com/rescript-lang/rescript/pull/7995
Build system & CLI
The new Rust-based
rewatchbuild system now powers therescriptcommand. The old Ninja-based builder system moved behindrescript legacy, and--compiler-argsbecame thecompiler-argssubcommand. https://github.com/rescript-lang/rescript/pull/7551 https://github.com/rescript-lang/rescript/pull/7593 https://github.com/rescript-lang/rescript/pull/7928rescript formatwas reimplemented in Rust, its options now use the--check/--stdinlong-form spelling, and the--allflag was removed because every tracked file (non-dev by default) is formatted automatically. https://github.com/rescript-lang/rescript/pull/7603 https://github.com/rescript-lang/rescript/pull/7752The
rescript dumpcommand was removed; callbscdirectly if you need to inspect.cmifiles. https://github.com/rescript-lang/rescript/pull/7710
Configuration & platform
The minimum supported Node.js version is now 20.11.0. https://github.com/rescript-lang/rescript/pull/7354
The
experimental-featureskey inrescript.jsonnow uses kebab-case to match the other config fields. https://github.com/rescript-lang/rescript/pull/7891The legacy
-bs-super-errorsflag was removed. https://github.com/rescript-lang/rescript-compiler/pull/6814