-
-
Notifications
You must be signed in to change notification settings - Fork 254
Rough outline of general v12 blog post #1130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
185ff54
Rough outline of general v12 blog post
nojaf f9a86ce
Remove ocaml toolchain
nojaf 9307078
V12 article image
fhammerschmidt 5bc85d5
Don't use v12 pic as article image
fhammerschmidt 3ff6006
Merge remote-tracking branch 'rescript/master' into v12-post
fhammerschmidt 52720f3
Rename release blogpost file
fhammerschmidt dcd59ed
Merge remote-tracking branch 'rescript/master' into v12-post
fhammerschmidt 5cb662e
Link to migration guide
fhammerschmidt 63e7efd
Remove accidentally committed changelog
fhammerschmidt dcdec79
Fix links and images
fhammerschmidt 3a861c3
Set date to the 25th
fhammerschmidt 8152fc5
Add v12 release banner
fhammerschmidt 84c5e4b
Better quality retreat photo
fhammerschmidt 113b84f
Update markdown-pages/blog/release-12-0-0.mdx
nojaf 00eec8c
Update markdown-pages/blog/release-12-0-0.mdx
nojaf 889b8bc
Fix previewImg url
fhammerschmidt 50aef14
Format
fhammerschmidt 2f8ae85
Merge remote-tracking branch 'rescript/master' into v12-post
fhammerschmidt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,253 @@ | ||
| --- | ||
| author: rescript-team | ||
| date: "2025-11-25" | ||
| previewImg: /static/blog/compiler_release_12_0.webp | ||
| badge: release | ||
| title: "Announcing ReScript 12" | ||
| description: | | ||
| ReScript 12 arrives with a redesigned build toolchain, a modular runtime, and a wave of ergonomic language features. | ||
| --- | ||
|
|
||
| ## Introduction | ||
|
|
||
| ReScript 12 is now available. | ||
| This release completes the multi-year effort to separate the compiler from legacy constraints, refreshes critical developer workflows, and delivers a modern feature set that is grounded in real-world feedback. | ||
| The headline upgrades include a rewritten build system, a standalone runtime package, unified operator support, JSX preserve mode, and numerous syntax improvements that make ReScript code easier to write and maintain. | ||
|
|
||
| Teams upgrading from ReScript 11 should review the highlighted breaking changes and schedule time for migration. | ||
| The new tooling provides focused commands, better diagnostics, and smaller downloads, while the language additions flatten sharp edges that many of you encountered in larger codebases. | ||
|
|
||
| You can find the complete migration guide [here](../docs/manual/migrate-to-v12.mdx). | ||
|
|
||
| ## Breaking Changes | ||
|
|
||
| ### Build system redesign | ||
|
|
||
| ReScript 12 ships with the new build system introduced earlier this month in the [Reforging the Build System](./reforging-build-system.mdx) preview. | ||
| The tooling now relies on a modern architecture that tracks dependencies more precisely, avoids unnecessary recompilations, and integrates with incremental workflows. | ||
| The old build system remains available through `rescript-legacy`, but active projects should switch to the new commands to benefit from faster feedback loops and clearer output. | ||
|
|
||
| ### Runtime package split | ||
|
|
||
| The compiler no longer embeds runtime modules. | ||
| Instead, the runtime lives in the dedicated `@rescript/runtime` npm package, which contains Belt, Stdlib, Js, primitive modules, and supporting artifacts compiled in both ES Module and CommonJS formats. | ||
| Earlier versions bundled these files inside the `rescript` package and exposed an optional `@rescript/std` helper. | ||
| ReScript 12 removes `@rescript/std`; install `rescript` and `@rescript/runtime` together to keep projects in sync. | ||
| Projects that previously published custom runtime shims should revisit their setup to ensure the new package structure remains discoverable. | ||
|
|
||
| ### Updated bitwise operator surface | ||
|
|
||
| Legacy OCaml-style bitwise functions such as `land`, `lor`, and `lsl` are deprecated. | ||
| They continue to work in v12 with warnings but will be removed in v13. | ||
| ReScript now prefers unified operator syntax: `&&&`, `|||`, `^^^`, and `~~~` cover AND, OR, XOR, and NOT for both `int` and `bigint`. | ||
|
|
||
| ### API naming alignment | ||
|
|
||
| APIs that previously ended with `Exn` now end with `OrThrow`. | ||
| Examples include `Option.getOrThrow`, `List.headOrThrow`, `BigInt.fromStringOrThrow`, and `JSON.parseOrThrow`. | ||
| The change clarifies that these functions throw JavaScript errors, aligning the naming with the language’s semantics. | ||
| The deprecated `*Exn` variants remain available in v12 to ease the transition, and the codemod bundled with the migration tool can perform a mechanical rename. | ||
| Note that `Result.getOrThrow` now throws a JavaScript exception, so please update any exception-handling logic that relied on OCaml exception names. We also revamped the API around JS exceptions and errors. | ||
|
|
||
| We’ve renamed JS errors to `JsError.t` to better distinguish them from the `Result.Error` variant. Since JavaScript allows throwing anything (not only proper Error objects), the previous way of catching JS exceptions was unsafe: | ||
| ```res | ||
| let foo = switch myUnsafeJsResult() { | ||
| | exception Exn.Error(e) => Error(e->Error.message) | ||
| // ☝️ this will crash if `e` is undefined or null, | ||
| // or return undefined if `e` has no `message` property | ||
| | myRes => Ok(myRes) | ||
| } | ||
| ``` | ||
| Additionally, the coexistence of `Result.Error` and the `Error` module caused confusion. | ||
|
|
||
| The new recommended pattern is: | ||
| ```res | ||
| let foo = switch myUnsafeJsResult() { | ||
| | exception JsExn(e) => Error(e->JsExn.message->Option.getOr("[no message]")) | ||
| | myRes => Ok(myRes) | ||
| } | ||
| ``` | ||
| Utility helpers for working with JS errors are now in `JsError`, eg: | ||
| ```res | ||
| JsError.throw(JsError.RangeError.make("the payload should be below 16")) | ||
| ``` | ||
|
|
||
| ### JSX version requirement | ||
|
|
||
| JSX v3 has been removed. | ||
| ReScript 12 requires JSX v4 configuration in `rescript.json`, using the `"jsx": { "version": 4 }` schema. | ||
| ReScript React projects must update their configuration before moving to v12. | ||
| Projects attempting to compile with v3 will receive an explicit error, ensuring that your codebase uses the current transform and associated tooling. | ||
|
|
||
| ### Deprecation of OCaml compatibility helpers | ||
|
|
||
| The standard library continues its shift away from OCaml-specific aliases. | ||
| Functions such as `succ`, `pred`, `abs_float`, `string_of_int`, `fst`, `raise`, and the `char` type are now deprecated. | ||
| The recommended replacements are the JavaScript-aligned counterparts in `Int`, `Float`, `Bool`, `Pair`, and related modules, alongside the `throw` keyword for exceptions. | ||
| References to the OCaml composition operators (`|>`, `@@`) now warn and will be removed in v13; the ReScript pipe operator `->` replaces them. | ||
| The migration tool highlights deprecated usage, and incremental cleanups are encouraged so your codebase is ready before the next major release. | ||
|
|
||
| ## New features | ||
|
|
||
| ### Unified operators | ||
|
|
||
| ReScript 12 finalizes the unified operator work introduced [earlier this year](./introducing-unified-operators.mdx). | ||
| Arithmetic, comparison, and bitwise operators now behave consistently across `int` and `bigint`, allowing the compiler to infer the correct specialization from the left operand. | ||
|
|
||
| ### Expanded bitwise capabilities | ||
|
|
||
| In addition to deprecating the OCaml-style helpers, ReScript 12 adds new operator spellings. | ||
| JavaScript-style bitwise operators (`&`, `|`, `^`, `~`) and shift operators (`<<`, `>>`, `>>>`) are first-class citizens that compile directly to their JavaScript equivalents. | ||
| Combined with the unified F#-style operators, developers can select the syntax that best fits their code policies without sacrificing performance or type safety. | ||
|
|
||
| ### Dict literals and pattern matching | ||
|
|
||
| The language now supports dictionary literals (`dict{"foo": "bar"}`) that compile to plain JavaScript objects. | ||
| Dict literals work with variables, multi-line formatting, and optional entries, and they drastically reduce the boilerplate compared to `Dict.fromArray`. | ||
| Pattern matching also understands dicts, enabling concise destructuring of JSON payloads and configuration objects. | ||
| The compiler emits the same optimized JavaScript while preserving ReScript's type guarantees. | ||
|
|
||
| ```rescript | ||
| let user = dict{"name": "Ada", "role": "engineer"} | ||
|
|
||
| switch user { | ||
| | dict{"name": name, "role": role} => Console.log2(name, role) | ||
| | _ => Console.log("missing user metadata") | ||
| } | ||
| ``` | ||
|
|
||
| ### Nested and inline record types | ||
|
|
||
| Nested record definitions and inline record payloads remove the need for auxiliary type declarations. | ||
| You can define optional nested structures directly inside records, or attach record payloads to variant constructors without creating standalone types. | ||
| The feature supports mutable fields, type parameters, and record spreading, providing better locality for complex domain models with no runtime penalty. | ||
|
|
||
| ```rescript | ||
| type profile = { | ||
| name: string, | ||
| extra?: { | ||
| location: {city: string, country: string}, | ||
| mutable note: option<string>, | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| ### Variant pattern spreads | ||
|
|
||
| Pattern spreads (`| ...SomeVariant as value =>`) allow you to reuse handlers for entire subsets of constructors. | ||
| When a variant extends another variant through spreads, you can match the shared constructors in one branch and delegate to helper functions, keeping exhaustive matches concise even as the hierarchy grows. | ||
| The compiler enforces subtype relationships and ensures that runtime representations remain compatible. | ||
|
|
||
| ```rescript | ||
| type base = Start | Stop | Pause | ||
| type extended = | ...base | Resume | ||
|
|
||
| let handle = (event: extended) => | ||
| switch event { | ||
| | ...base as core => Console.log2("base", core) | ||
| | Resume => Console.log("resuming") | ||
| } | ||
| ``` | ||
|
|
||
| ### JSX preserve mode | ||
|
|
||
| Projects that rely on downstream JSX tooling can enable [preserve mode](../docs/manual/jsx.mdx#preserve-mode) via `"jsx": { "version": 4, "preserve": true }`. | ||
| The compiler will emit JSX syntax directly instead of transforming elements to `react/jsx-runtime` calls, allowing bundlers such as ESBuild, SWC, or Babel to apply their own transforms. | ||
| This mode keeps JSX readable in the output, retains spread props, and maintains compatibility with React Server Components. | ||
| React classic mode is no longer supported, so projects must use the JSX v4 transform regardless of preserve mode settings. | ||
| When preserve mode is disabled, the compiler continues to output the optimized runtime calls you are accustomed to. | ||
|
|
||
| ### Function-level directives | ||
|
|
||
| The new `@directive` attribute emits JavaScript directive strings at the top of generated functions. | ||
| Use it to mark server actions with `'use server'`, memoized handlers with `'use memo'`, or any other directive that your framework requires. | ||
| The attribute works on synchronous and asynchronous functions, supports labeled parameters, and removes the need for `%raw` blocks. | ||
| Combined with JSX preserve mode, this enables clean integration with [React Server Components](../docs/react/server-components.mdx) and other directive-based runtimes. | ||
|
|
||
| ### Regex literals | ||
|
|
||
| ReScript now understands JavaScript-style regular expression literals (`/pattern/flags`). | ||
| They are full equivalents of `%re` expressions, supporting all ECMAScript flags, Unicode character classes, and sticky searches. | ||
| The literals compile directly to JavaScript regex objects, so existing APIs like `RegExp.exec` and `RegExp.test` continue to work exactly as before, but with clearer syntax and better editor support. | ||
|
|
||
| ```rescript | ||
| let emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i | ||
|
|
||
| if emailPattern->RegExp.test("[email protected]") { | ||
| Console.log("Valid email") | ||
| } | ||
|
|
||
| switch emailPattern->RegExp.exec("invalid") { | ||
| | Some(match) => Console.log(match->RegExp.Result.fullMatch) | ||
| | None => Console.log("No match") | ||
| } | ||
| ``` | ||
|
|
||
| ### Experimental `let?` syntax | ||
|
|
||
| This release introduces an experimental `let?` syntax for zero-cost unwrapping of `option` and `result` values. | ||
| The syntax remains behind an opt-in flag while the community evaluates its ergonomics. | ||
| Refer to the forum discussion at https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227 for the current proposal, examples, and feedback guidelines. | ||
|
|
||
| ```rescript | ||
| type user = { | ||
| address?: { | ||
| city?: string, | ||
| }, | ||
| } | ||
|
|
||
| let userCity = (user: user): option<string> => { | ||
| let? Some(address) = user.address | ||
| let? Some(city) = address.city | ||
| Some(city) | ||
| } | ||
| ``` | ||
|
|
||
| ## Platform and tooling improvements | ||
|
|
||
| ### Cleaner JavaScript output | ||
|
|
||
| The printer now emits compact arrow functions and streamlines anonymous function expressions, making generated JavaScript easier to audit. | ||
| These changes preserve semantics while aligning the output with modern JavaScript style. | ||
|
|
||
| ### Internal architecture updates | ||
|
|
||
| The compiler cleans up its internal abstract syntax tree, removes unused OCaml-era nodes, and tracks async and partial function metadata directly on AST nodes. | ||
| These changes simplify future feature work and reduce maintenance overhead. | ||
|
|
||
| ### ESM-first distribution | ||
|
|
||
| The `rescript` npm package declares `"type": "module"` and ships ESM code across the CLI. | ||
| Import statements replace CommonJS `require` usage, improving compatibility with contemporary bundlers and enabling better tree-shaking. | ||
|
|
||
| ### Platform-specific binaries | ||
|
|
||
| Installer footprints shrink thanks to platform-specific binary packages such as `@rescript/darwin-arm64`, `@rescript/linux-x64`, and `@rescript/win32-x64`. | ||
| npm installs only the binary that matches your operating system and architecture, delivering substantially faster installs and smaller cache footprints for CI pipelines. | ||
| The primary `rescript` package loads the appropriate binary at runtime and surfaces clear error messages if the matching package is missing. | ||
|
|
||
| ## Acknowledgments | ||
|
|
||
| <Image | ||
| src="/blog/rescript-retreat-2025.webp" | ||
| withShadow={true} | ||
| caption="Kick-off of this year's ReScript Retreat in Vienna" | ||
| /> | ||
|
|
||
| Thank you to every contributor, tester, and partner who helped shape ReScript 12. | ||
| The core team gathered in Vienna earlier this year to map out this release, and your feedback guided every decision. | ||
| Community pull requests, bug reports, and experiments across the ecosystem gave us the confidence to complete large refactors and deprecations. | ||
|
|
||
| ## Reach out | ||
|
|
||
| Join the conversation on the community forum if you have migration questions or want to share what you build with ReScript 12. | ||
| Businesses that rely on ReScript can contact the association at https://rescript-association.org/contact to explore support, sponsorship, or collaboration. | ||
| Funding enables the team to ship features like the new runtime architecture faster, so every contribution, financial or otherwise, helps the language move forward. | ||
|
|
||
| We look forward to hearing what you create with ReScript 12. | ||
|
|
||
| <Image | ||
| src="/blog/rescript-team-2025.webp" | ||
| withShadow={true} | ||
| caption="The ReScript team at the 2025 retreat in Vienna" | ||
| /> | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.