diff --git a/markdown-pages/blog/release-12-0-0.mdx b/markdown-pages/blog/release-12-0-0.mdx new file mode 100644 index 000000000..565b947b2 --- /dev/null +++ b/markdown-pages/blog/release-12-0-0.mdx @@ -0,0 +1,265 @@ +--- +author: rescript-team +date: "2025-11-25" +previewImg: /blog/compiler_release_12_0.webp +articleImg: /blog/compiler_release_12_0_article.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). + +## New Features + +### New Build System + +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 even across monorepo packages. + +### Improved Standard Library + +The new standard library [`@rescript/core`](https://github.com/rescript-lang/rescript-core) is now included in the compiler runtime. So you can get rid if the dependency on `@rescript/core` in your projects. To avoid collisions we have named the new internal library just `Stdlib`, so you can keep using it alongside `Core` if you cannot migrate yet. There have been tons of additions as well, so you can shrink your `Utils` files down a bit. Check it out here: [Stdlib](../docs/manual/api/stdlib). + +### Operator Improvements + +#### Unified Operators + +ReScript 12 finalizes the unified operator work introduced [earlier this year](./introducing-unified-operators.mdx). +Arithmetic operators (`+`, `-`, `*`, `/`, and the newly added `%` and `**`) now work consistently for `int`, `float` and `bigint`, allowing the compiler to infer the correct specialization from the left operand. You can ditch all the `+.`, `-.`, `*.`, and `/.` now! + +In addition you can also now use `+` for string concatenation, while `++` still works as before. + +#### Bitwise Operators + +ReScript now supports F#-style bitwise operators `&&&` (AND), `|||` (OR), `^^^` (XOR), and `~~~` (NOT) for both `int` and `bigint`. +Legacy OCaml-style bitwise functions such as `land`, `lor`, and `lsl` are deprecated. + +#### Shift Operators + +For shift operators there was luckily no conflict in the character space, which means ReScript now supports `<<` (logical left shift), `>>` (logical right shift) and `>>>` (unsigned right shift), just like JavaScript. + +### 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 Record Types + +Nested record definitions 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, + }, +} +``` + +### 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 and maintains compatibility with React Compiler. + +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("contact@rescript-lang.org") { + 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 => { + 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. + +## Breaking Changes + +### Build System + +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 runtime modules were moved from the main `rescript` npm package to a dedicated `@rescript/runtime` npm package. It is automatically installed as a dependency of the main `rescript` package. +The new `@rescript/runtime` package also replaces the standalone runtime package `@rescript/std` from earlier versions. + +### 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 +| 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)) +// ☝️ this is now safe even `e` is undefined or null +| 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 a future version; 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. + +## Acknowledgments + + + +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. + + diff --git a/public/blog/compiler_release_12_0.webp b/public/blog/compiler_release_12_0.webp new file mode 100644 index 000000000..179279457 Binary files /dev/null and b/public/blog/compiler_release_12_0.webp differ diff --git a/public/blog/compiler_release_12_0_article.webp b/public/blog/compiler_release_12_0_article.webp new file mode 100644 index 000000000..f10f19b1f Binary files /dev/null and b/public/blog/compiler_release_12_0_article.webp differ diff --git a/public/blog/rescript-retreat-2025.webp b/public/blog/rescript-retreat-2025.webp new file mode 100644 index 000000000..907574577 Binary files /dev/null and b/public/blog/rescript-retreat-2025.webp differ diff --git a/public/blog/rescript-team-2025.webp b/public/blog/rescript-team-2025.webp new file mode 100644 index 000000000..4691456bd Binary files /dev/null and b/public/blog/rescript-team-2025.webp differ diff --git a/src/Path.res b/src/Path.res index 8198fb62e..a3bbea2fb 100644 --- a/src/Path.res +++ b/src/Path.res @@ -155,6 +155,7 @@ type t = [ | #"/blog/release-8-4" | #"/blog/release-8-3" | #"/blog/release-8-3-2" + | #"/blog/release-12-0-0" | #"/blog/release-11-1-0" | #"/blog/release-11-0-0" | #"/blog/release-10-1" diff --git a/src/layouts/LandingPageLayout.res b/src/layouts/LandingPageLayout.res index 9d3c92e24..3d7e3d6dd 100644 --- a/src/layouts/LandingPageLayout.res +++ b/src/layouts/LandingPageLayout.res @@ -688,6 +688,13 @@ let make = (~components=MarkdownComponents.default, ~children) => {
+ + {React.string("ReScript 12 is out! Read the ")} + + {React.string("announcement blog post")} + + {React.string(".")} +