# ReScript Getting Started Focused documentation for installing ReScript, editor setup, migration notes, and onboarding from JavaScript. # ReScript ReScript is a robustly typed language that compiles to efficient and human-readable JavaScript. It comes with a lightning fast compiler toolchain that scales to any codebase size. ## JavaScript Interop ReScript compiles to clean, readable, and performant JavaScript, directly runnable in browsers and Node. Your existing package managers, bundlers, frameworks, and test runners all work with ReScript. Your existing knowledge of web development transfers to ReScript projects. If you are coming from JavaScript, start with [ReScript for JavaScript Developers](./rescript-for-javascript-developers.mdx) for a quick syntax guide. ReScript code can be [imported into JavaScript code](./import-from-export-to-js.mdx#export-to-javascript), can [generate types for TypeScript](./typescript-integration.mdx), and ReScript can [import code written in JavaScript or TypeScript](./import-from-export-to-js.mdx#import-from-javascript). ## Type System - Is deliberately curated to be a simple subset most folks will have an easier time to use. - Sound type system. If a type isn't marked as nullable, the value will never be `undefined`. **ReScript code has no null/undefined errors**. - No configuration needed. The type system behaves the same way in every project. - Runs extremely fast precisely thanks to its simplicity and curation. It's one of the fastest compiler & build system toolchains for JavaScript development. - **Doesn't need type annotations**. Annotate as much or as little as you'd like. The types are inferred by the language (and, again, are guaranteed correct). ## Compiler ### Compiles to Optimized JavaScript ReScript's type system and compiler generate JavaScript that is performant by default, taking advantage of Just-In-Time optimizations (hidden classes, inline caching, avoiding deopts, etc). ### Tiny JS Output A `Hello world` ReScript program generates **20 bytes** of JS code. Additionally, the standard library pieces you require in are only included when needed. ### Fast Iteration Loop ReScript's build time is **one or two orders of magnitude** faster than alternatives. In its watcher mode, the build system usually finishes before you switch screen from the editor to the terminal tab (two digits of milliseconds). A fast iteration cycle reduces the need of keeping one's mental state around longer; this in turn allows one to stay in the flow longer and more often. ### Readable Output ReScript's JS output is very readable. This is especially important while learning, where users might want to understand how the code's compiled, and to audit for bugs. This characteristic, combined with a fully-featured JS interop system, allows ReScript code to be inserted into an existing JavaScript codebase almost unnoticed. ### Preservation of Code Structure ReScript maps one source file to one JavaScript output file. This eases the integration of existing tools such as bundlers and test runners. You can even start writing a single file without much change to your build setup. Each file's code structure is approximately preserved, too. ### High Quality Dead Code Elimination The JavaScript ecosystem is very reliant on dependencies. Shipping the final product inevitably drags in a huge amount of code, lots of which the project doesn't actually use. These regions of dead code impact loading, parsing and interpretation speed. ReScript provides powerful dead code elimination at all levels: - Function- and module-level code elimination is facilitated by the well-engineered type system and purity analysis. - At the global level, ReScript generates code that is naturally friendly to dead code elimination done by bundling tools such as [Rollup](https://github.com/rollup/rollup) and [Closure Compiler](https://developers.google.com/closure/compiler/), after its own sophisticated elimination pass. - The same applies for ReScript's own tiny runtime (which is written in ReScript itself). # Installation ## Prerequisites
- [Node.js](https://nodejs.org/) version >= 22 - One of the following package managers: - [npm](https://docs.npmjs.com/cli/) (comes with Node.js) - [yarn](https://yarnpkg.com/) - yarn versions >1 need to set `nodeLinker: node-modules` in `.yarnrc.yml` - [pnpm](https://pnpm.io/) - [bun](https://bun.sh/) - [deno](http://deno.com/) - Configure `"nodeModulesDir": "auto"` in `deno.json`
## New Project The fastest and easiest way to spin up a new ReScript project is with the [create-rescript-app](https://github.com/rescript-lang/create-rescript-app) project generator. This will get you started with a fresh Next.js or Vite app with React and Tailwind CSS. You can start it with any of the aforementioned package managers or `npx`. ```sh example npm create rescript-app@latest ``` ```sh npx create-rescript-app ``` ```sh yarn create rescript-app ``` ```sh pnpm create rescript-app ``` ```sh bun create rescript-app ``` - Follow the steps of the setup. - Trigger a ReScript build: ```sh npm run res:build ``` - If you selected the "basic" template, simply run it with: ```sh node src/Demo.res.mjs ``` That compiles your ReScript into JavaScript, then uses Node.js to run said JavaScript. **When taking your first steps with ReScript, we recommend you use our unique workflow of keeping a tab open for the generated JS file** (`.res.js`/`.res.mjs`), so that you can learn how ReScript transforms into JavaScript. Not many languages output clean JavaScript code you can inspect and learn from! With our [VS Code extension](https://marketplace.visualstudio.com/items?itemName=chenglou92.rescript-vscode), use the command "ReScript: Open the compiled JS file for this implementation file" to open the generated JS file for the currently active ReScript source file. During development, instead of running `npm run res:build` each time to compile, use `npm run res:dev` to start a watcher that recompiles automatically after file changes. ## Integrate Into an Existing JS Project If you already have a JavaScript project into which you'd like to add ReScript you can do that in the following ways: ### Quick Setup In the root directory of your project, execute: ```sh npm create rescript-app@latest ``` ```sh npx create-rescript-app ``` ```sh yarn create rescript-app ``` ```sh pnpm create rescript-app ``` ```sh bun create rescript-app ``` `create-rescript-app` will tell you that a `package.json` file has been detected and ask you if it should install ReScript into your project. Just follow the steps accordingly. ### Manual Setup - Install ReScript locally: ```sh npm install rescript ``` ```sh yarn add rescript ``` ```sh pnpm install rescript ``` ```sh bun install rescript ``` ```sh // you will need deno configured to have a node_modules folder deno install npm:rescript --allow-scripts ``` **pnpm users:** ReScript-compiled JS imports from `@rescript/runtime` directly, but pnpm's strict isolation does not expose transitive dependencies at the app root. Either install the runtime as a direct dependency (`pnpm add @rescript/runtime`), or hoist it by adding the following to `pnpm-workspace.yaml`: ```yaml publicHoistPattern: - "*@rescript/runtime*" ``` - Create a ReScript build configuration file (called `rescript.json`) at the root: ```json { "name": "your-project-name", "sources": [ { "dir": "src", // update this to wherever you're putting ReScript files "subdirs": true } ], "package-specs": [ { "module": "esmodule", "in-source": true } ], "suffix": ".res.js" } ``` See [Build Configuration](./build-configuration.mdx) for more details on `rescript.json`. - Add convenience `npm` scripts to `package.json`: ```json "scripts": { "build:res": "rescript", "dev:res": "rescript watch" } ``` Since ReScript compiles to clean readable JS files, the rest of your existing toolchain (e.g. Vite, Rspack, Rollup) should just work! Helpful guides: - [Converting from JS](./converting-from-js.mdx). - [Shared Data Types](./shared-data-types.mdx). - [Import from/Export to JS](./import-from-export-to-js.mdx). ### Integrate with a ReactJS Project To start a [rescript-react](../react/introduction.mdx) app, or to integrate ReScript into an existing ReactJS app, follow the instructions [here](../react/installation.mdx). # ReScript for JavaScript Developers If you already write JavaScript, ReScript should feel familiar quickly. This page is a compact syntax guide for the main differences you should be aware of. For a guided migration workflow, see [Converting from JS](./converting-from-js.mdx). ## What to know first - `let` bindings are immutable by default. See [Let Binding](./let-binding.mdx) and [Mutation](./mutation.mdx). - ReScript does not use `null` and `undefined` as normal control flow. Reach for `option` instead. See [Null, Undefined and Option](./null-undefined-option.mdx). - Arrays must contain values of the same type. See [Array and List](./array-and-list.mdx) and [Tuple](./tuple.mdx). - Records are not ad-hoc JS objects; they have known field names and types. See [Record](./record.mdx) and [Object](./object.mdx). - Conditionals and blocks return values, so expression-oriented code is common. See [If-Else & Loops](./control-flow.mdx). - Pattern matching replaces many ad-hoc `if` or property-check branches. See [Pattern Matching / Destructuring](./pattern-matching-destructuring.mdx). ## Quick reference | Topic | ReScript | Notes for JavaScript developers | | --------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Semicolons | `let x = 1` | Semicolons are not required. See [Overview](./overview.mdx#semicolons). | | Comments | `//`, `/* */`, `/** */` | Familiar syntax, including doc comments. See [Overview](./overview.mdx#comments). | | Variables | `let x = 5` | `let` creates an immutable binding. See [Let Binding](./let-binding.mdx). | | Mutation | `let x = ref(5)` | Mutable state is explicit through `ref` or mutable fields. See [Mutation](./mutation.mdx). | | Strings | `"hello"` | Strings use double quotes. See [Primitive Types](./primitive-types.mdx). | | String concatenation | `"hello " ++ name` | ReScript uses `++` for strings. See [Primitive Types](./primitive-types.mdx). | | Interpolation | `` `hello ${name}` `` | Template strings work similarly. See [Primitive Types](./primitive-types.mdx). | | Equality | `===`, `!==`, `==`, `!=` | No coercive equality. `==` and `!=` are structural. See [Equality and Comparison](./equality-comparison.mdx). | | Numbers | `3`, `3.14`, `2.0 * 3.0` | Arithmetic operators work for both `int` and `float`. See [Primitive Types](./primitive-types.mdx). | | Records | `{x: 30, y: 20}` | Similar object syntax, but records are typed. See [Record](./record.mdx). | | Arrays | `[1, 2, 3]` | Arrays are homogeneous. See [Array and List](./array-and-list.mdx). | | Mixed fixed-size data | `(1, "Bob", true)` | Use tuples instead of heterogeneous arrays. See [Tuple](./tuple.mdx). | | Missing values | `option<'a>` | Use `Some(value)` and `None` instead of `null` and `undefined`. See [Null, Undefined and Option](./null-undefined-option.mdx). | | Functions | `let add = (a, b) => a + b` | Familiar arrow-style syntax. See [Function](./function.mdx). | | Blocks | `{ let x = 1; x + 1 }` | The last expression is returned implicitly. See [Overview](./overview.mdx#blocks). | | Conditionals | `if cond {a} else {b}` | `if` is an expression. See [If-Else & Loops](./control-flow.mdx). | | Pattern matching | `switch value { ... }` | Use `switch` for destructuring and exhaustive branching. See [Pattern Matching / Destructuring](./pattern-matching-destructuring.mdx). | | Destructuring | `let {a, b} = data` | Works for records, arrays, tuples, and more. See [Pattern Matching / Destructuring](./pattern-matching-destructuring.mdx). | | Loops | `for i in 0 to 10 {}` | `for` and `while` exist, but collection transforms are also common. See [If-Else & Loops](./control-flow.mdx). | | Exceptions | `throw(MyException(...))` | `throw` and `try` exist, but typed data flow is preferred where possible. See [Exception](./exception.mdx). | | JSX | `` | JSX is supported directly, with a few ReScript conventions. See [JSX](./jsx.mdx). | ## Where to Go Next - For a broader syntax reference, see [Overview](./overview.mdx). - For a migration workflow inside an existing codebase, see [Converting from JS](./converting-from-js.mdx). - For JavaScript interop, see [Interop Cheatsheet](./interop-cheatsheet.mdx). - For importing and exporting JS modules, see [Import from Export to JS](./import-from-export-to-js.mdx). - For binding to JS objects and functions, see [Bind to JS Object](./bind-to-js-object.mdx) and [Bind to JS Function](./bind-to-js-function.mdx). # Editor This section is about the editor plugin for ReScript. It adds syntax highlighting, autocomplete, type hints, formatting, code navigation, code analysis for `.res` and `.resi` files. ## Plugins - [VSCode](https://marketplace.visualstudio.com/items?itemName=chenglou92.rescript-vscode) - [Vim/Neovim](https://github.com/rescript-lang/vim-rescript) - [Zed](https://github.com/rescript-lang/rescript-zed) ### Community Supported We don't officially support these; use them at your own risk! - [IntelliJ IDEA](https://github.com/Nagatatz/rescript-intellij-plugin) - [Emacs](https://github.com/jjlee/rescript-mode) ## Code analysis The code analysis provides extra checks for your ReScript project, such as detecting dead code and unhandled exceptions. It's powered by [reanalyze](https://github.com/rescript-association/reanalyze), which is built into the extension — no separate install required. ### How to Use - Open the command palette and run: `ReScript: Start Code Analyzer` - Warnings like dead code will show inline in the editor. - Suppression actions are available where applicable. - To stop analysis, click **Stop Code Analyzer** in the status bar. ### Configuration Add a `reanalyze` section to your `rescript.json` to control what the analyzer checks or ignores. You'll get autocomplete for config options in the editor. More details: [reanalyze configuration docs](https://github.com/rescript-association/reanalyze#configuration-via-bsconfigjson) ### Exception analysis The exception analysis is designed to keep track statically of the exceptions that might be thrown at runtime. It works by issuing warnings and recognizing annotations. Warnings are issued whenever an exception is thrown and not immediately caught. Annotations are used to push warnings from he local point where the exception is thrown, to the outside context: callers of the current function. Nested functions need to be annotated separately. Instructions on how to run the exception analysis using the `-exception` and `-exception-cmt` command-line arguments, or how to add `"analysis": ["exception"]` in `rescript.json` are contained in the [reanalyze configuration docs](https://github.com/rescript-association/reanalyze#configuration-via-bsconfigjson). Here's an example, where the analysis reports a warning any time an exception is thrown, and not caught: ```rescript let throws = () => throw(Not_found) ``` reports: ```sh Exception Analysis File "A.res", line 1, characters 4-10 throws might throw Not_found (A.res:1:19) and is not annotated with @throws(Not_found) ``` No warning is reported when a `@throws` annotation is added: ```rescript @throws(Not_found) let throws = () => throw(Not_found) ``` When a function throws multiple exceptions, a tuple annotation is used: ```rescript exception A exception B @throws([A, B]) let twoExceptions = (x, y) => { if (x) { throw(A) } if (y) { throw(B) } } ``` It is possible to silence the analysis by adding a `@doesNotThrow` annotation: ```rescript @throws(Invalid_argument) let stringMake1 = String.make(12, ' ') // Silence only the make function let stringMake2 = (@doesNotThrow String.make)(12, ' ') // Silence the entire call (including arguments to make) let stringMake3 = @doesNotThrow String.make(12, ' ') ``` #### Limitations - The libraries currently modeled are limited to the standard library, Belt and Js modules. Models are currently vendored in the analysis, and are easy to add (see [`analysis/reanalyze/src/ExnLib.ml`](https://github.com/rescript-lang/rescript/blob/master/analysis/reanalyze/src/ExnLib.ml)) - Generic exceptions are not understood by the analysis. For example `exn` is not recognized below (only concrete exceptions are): ```rescript try (foo()) { | exn => throw(exn) } ``` - Uses of e.g. `List.head` are interpreted as belonging to the standard library. If you re-define `List` in the local scope, the analysis it will think it's dealing with `List` from the standard library. - There is no special support for module functions. ### Guide Look - [Editor Code Analysis](./editor-code-analysis.mdx) for a more detailed guide about how to use the code analysis tool. ### Caveats - For older extension versions, cross-package dead code analysis in monorepos may be limited. ## Editor features Below are features and configurations of the editor tooling that might be good to know about. ### Pipe completions Pipes (`->`) are a huge and important part of the ReScript language, for many reasons. Because of that, extra care has gone into the editor experience for using pipes. #### Default pipe completion rules for non-builtin types By default, using `->` will give completions from the module where the type of the expression you're piping on is defined. So, if you're piping on something of the type `SomeModule.t` (like `someValue->`) then you'll get completions for all functions defined in `SomeModule` that take the type `t` as the first unlabelled argument. #### Pipe completions for builtin types For builtin types, completion will automatically happen based on the _standard library module_ for that type. So, `array` types will get completions from the `Array` module, `string` gets completions from `String`, and so on. There is a way to enhance this behavior via configuration, described further down in this document. ### Dot completion enhancements In ReScript, using a dot (`.`) normally means "access record field". The editor extends dot (`.`) to trigger completions in more scenarios beyond record field access. This behavior has the following important implications: - Improves discoverability (E.g. using a `.` will reveal important pipe completions) Below is a list of all the scenarios where using dots trigger completion in addition to the normal record field completion. #### Objects When writing a `.` on something that's a [structural object](./object.mdx), you'll get completions for those object properties. Example: ```res nocheck let obj = { "first": true, "second": false } let x = obj. // Will give the following completions for object property access: // - ["first"] // - ["second"] ``` #### Pipe completions for anything When writing `.` on _anything_, the editor will try to do pipe completion for the value on the left of the `.`. Example: ```res nocheck let arr = [1, 2, 3] let x = arr. // Will give the following pipe completions: // - ->Array.length // - ->Array.filter // - ->Array.map ``` ### `@editor.completeFrom` for drawing completions from additional modules You can configure any type you have control over to draw pipe completions from additional modules, in addition to the main module where the type is defined, via the `@editor.completeFrom` decorator. This is useful in many different scenarios: - When you, for various reasons, need to have your type definition separate from its "main module". Could be because of cyclic dependencies, a need for the type to be in a recursive type definition chain, and so on. - You have separate modules with useful functions for your type but that you don't want to (or can't) include in the main module of that type. Let's look at an example: ```res nocheck // Types.res // In this example types need to live separately in their own file, for various reasons type htmlInput // Utils.res module HtmlInput = { /** Gets the HTML input value. */ @get external value: Types.htmlInput => option = "value" } ``` In the example above, if we try and pipe on something of the type `Types.htmlInput`, we'll get no completions because there are no functions in `Types` that take `htmlInput` as its first unlabelled argument. But, better DX would be for the editor to draw completions from our util functions for `htmlInput` in the `Utils.HtmlInput` module. With `@editor.completeFrom`, we can fix this. Let's look at an updated example: ```res nocheck // Types.res @editor.completeFrom(Utils.HtmlInput) type htmlInput // Utils.res module HtmlInput = { /** Gets the HTML input value. */ @get external value: Types.htmlInput => option = "value" } ``` Now when piping on a value of the type `Types.htmlInput`, the editor tooling will know to include relevant functions from the module `Utils.HtmlInput`, and you'll get the completions you expect, even if the functions aren't located in the same module. > You can point out multiple modules to draw completions from for a type either by repeating `@editor.completeFrom` with a single module path each time, or by passing an array with all the module paths you want to include, like `@editor.completeFrom([Utils.HtmlInput, HtmlInputUtils])`. ### Configuring the editor via `editor` in `rescript.json` There's certain configuration you can do for the editor on a per project basis in `rescript.json`. Below lists all of the configuration available. #### `autocomplete` for pipe completion The `autocomplete` property of `editor` in `rescript.json` let's you map types to modules _on the project level_ that you want the editor to leverage when doing autocomplete for pipes. This is useful in scenarios like: - You have your own util module(s) for builtin types. Maybe you have an `ArrayExtra` with helpers for arrays that you want to get completions from whenever dealing with arrays. - You have your own util module(s) for types you don't control yourself (and therefore can't use `@editor.completeFrom`), like from external packages you install. To configure, you pass `autocomplete` an object where the keys are the _path to the type_ you want to target, and then an array of the path to each module you want to include for consideration for pipe completions. Let's take two examples. ##### Enhancing completion for builtin types First, let's look at including our own `ArrayExtra` in all completions for `array`: ```json { "editor": { "autocomplete": { "array": ["ArrayExtra"] } } } ``` Now, when using pipes on arrays, you'll get completions both from the standard library array functions, and also from your own `ArrayExtra` module. ```res nocheck let x = [1, 2, 3]-> // Example of what completing at the pipe might look like - Array.length - Array.map - Array.filter - ArrayExtra.someArrayFn - ArrayExtra.myOtherArrayFn ``` **Note**: generic types like `promise.t` and `result.t` do not need any additional types in the `rescript.json`: ```json "editor": { "autocomplete": { "promise": ["PromiseExt"], "result": ["ResultExt"] } } ``` ##### Enhancing completion for non-builtin types Now, let's look at an example of when you have a non-builtin type that you don't have control over. In this example, imagine this: - We're writing an app using `fastify` - We're using an external package that provides the necessary bindings in a `Fastify` module - We've got our own extra file `FastifyExtra` that has various custom util functions that operate on the main type `Fastify.t` We now want the editor to always suggest completions from the `FastifyExtra` module, in addition to the regular completions from the main `Fastify` module. Let's configure this using the `editor.autocomplete` config in `rescript.json`: ```json { "editor": { "autocomplete": { "Fastify.t": ["FastifyExt"] } } } ``` Now, when using pipes on anything of type `Fastify.t`, we'll also get completions from our custom `FastifyExtra`. ##### Enhancing completion for non-builtin types with namespaces When a project uses a namespace, this affects the internal representation of type names used in the `autocomplete` configuration. Consider the [geolocation](https://rescript-lang.github.io/experimental-rescript-webapi/apidocs/geolocation-api/#geolocation) type from the [Experimental WebAPI bindings](https://rescript-lang.github.io/experimental-rescript-webapi/). This project specifies in its `rescript.json`: ```json { "name": "@rescript/webapi", "namespace": "WebAPI" } ``` This makes the `geolocation` type internally represented as `GeolocationAPI-WebAPI.geolocation`, where: - `GeolocationAPI` is the module name - `WebAPI` is the namespace - `geolocation` is the type name **Important**: You must use this internal representation when configuring autocomplete for namespaced types: ```json { "editor": { "autocomplete": { "GeolocationAPI-WebAPI.geolocation": ["GeolocationExt"] } } } ``` # Migrate to ReScript 12 If you encounter any missing information or issues during migration, please [open an issue](https://github.com/rescript-lang/rescript-lang.org/issues/new?template=documentation_issue.md) or, even better, [send a pull request](https://github.com/rescript-lang/rescript-lang.org/) 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 `.ml` files is removed in this version. However there are ways to convert OCaml syntax with an older ReScript compiler version ([see below](#converting-generated-ml-files)). - 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: ```shell npm uninstall @rescript/std ``` this is replaced by `@rescript/runtime`, which is installed as a dependency of `rescript` now. **pnpm users:** since pnpm does not hoist transitive dependencies, you may need to install `@rescript/runtime` as a direct dependency (`pnpm add @rescript/runtime`), or hoist it by adding the following to `pnpm-workspace.yaml`: ```yaml publicHoistPattern: - "*@rescript/runtime*" ``` ## Replacements Some typical name changes include: - `Error.t` -> `JsError.t` - `raise(MyException("error"))` -> `throw(MyException("error"))` - `Js.Exn.Error` exception -> `JsExn` - `Error.make` -> `JsExn.make` - `Error.raise` -> `JsExn.raise` - `Error.message` -> `JsExn.message` - `Bool.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. ```shell npx rescript-tools migrate-all # preview the changes via rescript-tools migrate [--stdout] ``` ### Bitwise operations v11: ```res nocheck let 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: ```res nocheck let 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: ```res nocheck let x = lsl(a, b) // logical left shift let y = lsr(a, b) // logical right shift let z = asr(a, b) // unsigned right shift ``` v12: ```res nocheck let 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 nocheck
...children
``` v12: ```res nocheck
children
``` ### Attributes v11: ```res nocheck @bs.as("foo") @bs.send @bs.new @raises @genType.as ``` v12: ```res nocheck @as("foo") @send @new @throws @as ``` - `@meth` and `@bs.send.pipe` are removed. ### Assert v11: ```res nocheck assert 1 == 2 ``` v12: ```res nocheck assert(1 == 2) // Now a regular function call ``` ## Configuration Rename `bsconfig.json` to `rescript.json` and update these configuration options: - `bs-dependencies` → `dependencies` - `bs-dev-dependencies` → `dev-dependencies` - `bsc-flags` → `compiler-flags` ### jsx - Set `version` to `4` (lower versions are not supported) - Remove `mode` option (automatically set to `automatic`) ## 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](https://github.com/DZakh/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. 1. Keep ReScript 12 as the sole compiler dependency in your main project (i.e. `devDependencies.rescript` stays at `^12.0.0`). 2. 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. 3. `cd` back 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" } } ``` 4. Execute the helper script to convert your `.ml` files to `.res` files: ```console npm 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 `j` or `js` are no longer reserved, so add your own implementation whenever a tagged template expects them. https://github.com/rescript-lang/rescript-compiler/pull/6817 - `lazy` syntax was removed; use the `Lazy` module instead. https://github.com/rescript-lang/rescript-compiler/pull/6342 - All legacy `@bs.*` attributes (e.g. `@bs.as`, `@bs.send`) and `@bs.open` were 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.pipe` was removed; rewrite bindings to use `@send`. https://github.com/rescript-lang/rescript-compiler/pull/6858 https://github.com/rescript-lang/rescript-compiler/pull/6891 - OCaml `.ml` files are no longer supported anywhere: `.ml` parsing/formatting went away and the `rescript convert` CLI was removed, so convert legacy files to `.res` before 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/6860 - Some 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-mode` option 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.Vector` and `Js.List` were deleted. https://github.com/rescript-lang/rescript-compiler/pull/6900 - The legacy `js_cast.res` helpers were removed; migrate to explicit externals. https://github.com/rescript-lang/rescript-compiler/pull/7075 - `JsError` and related modules were renamed/cleaned up under `JsExn`. https://github.com/rescript-lang/rescript/pull/7408 - `BigInt.fromFloat` now returns `option` and exposes `BigInt.fromFloatOrThrow`, and the `Exn`-suffixed helpers across `Bool`, `BigInt`, `JSON`, `Option`, `Null`, `Nullable`, `Result`, and `List` now end with `OrThrow`. https://github.com/rescript-lang/rescript/pull/7419 https://github.com/rescript-lang/rescript/pull/7518 https://github.com/rescript-lang/rescript/pull/7554 - `Result.getOrThrow` throws a JS `Error` (instead of `Not_found`), and `Result.equal` / `Result.compare` now provide a comparison function for `Error` values. https://github.com/rescript-lang/rescript/pull/7630 https://github.com/rescript-lang/rescript/pull/7933 - `Iterator.forEach` now emits `Iterator.prototype.forEach`. https://github.com/rescript-lang/rescript/pull/7506 - `Date.make` uses `~day` instead of `~date`. https://github.com/rescript-lang/rescript/pull/7324 - Plain `int` multiplication is implemented as a regular int32 operation instead of `Math.imul`. https://github.com/rescript-lang/rescript/pull/7358 - The `List` API was cleaned up—several functions were renamed or removed (see the PR for the exact surface). https://github.com/rescript-lang/rescript/pull/7290 - `String.getSymbol` / `String.setSymbol` were removed; only `String.getSymbolUnsafe` remains on strings. https://github.com/rescript-lang/rescript/pull/7571 https://github.com/rescript-lang/rescript/pull/7626 - `String.charCodeAt` now returns `option` and exposes `String.charCodeAtUnsafe` for unchecked access. https://github.com/rescript-lang/rescript/pull/7877 - `Intl.*.supportedLocalesOf` bindings now return `array` and the non-portable `Intl.PluralRules.selectBigInt` / `selectRangeBigInt` were removed. https://github.com/rescript-lang/rescript/pull/7995 ### Build system & CLI - The new Rust-based `rewatch` build system now powers the `rescript` command. The old Ninja-based builder system moved behind `rescript legacy`, and `--compiler-args` became the `compiler-args` subcommand. https://github.com/rescript-lang/rescript/pull/7551 https://github.com/rescript-lang/rescript/pull/7593 https://github.com/rescript-lang/rescript/pull/7928 - `rescript format` was reimplemented in Rust, its options now use the `--check` / `--stdin` long-form spelling, and the `--all` flag 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/7752 - The `rescript dump` command was removed; call `bsc` directly if you need to inspect `.cmi` files. 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-features` key in `rescript.json` now uses kebab-case to match the other config fields. https://github.com/rescript-lang/rescript/pull/7891 - The legacy `-bs-super-errors` flag was removed. https://github.com/rescript-lang/rescript-compiler/pull/6814