From 67c33133f6d567282066f2f2d0b6965ac0b15974 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Wed, 27 Aug 2025 19:25:28 +0200 Subject: [PATCH 01/16] Draft: let? blog post --- _blogposts/2025-09-01-let-unwrap.mdx | 75 ++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 _blogposts/2025-09-01-let-unwrap.mdx diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx new file mode 100644 index 000000000..5f08e83d1 --- /dev/null +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -0,0 +1,75 @@ +--- +author: rescript-team +date: "2025-09-01" +badge: roadmap +title: let? +description: | + A new let-unwrap syntax just landed in ReScript. +--- + +After long discussions we finally decided on an unwrap syntax for both the `option` and `result` types that we are happy with and that still matches the explicitness of ReScript we all like. + +# What is it exactly? + +`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and *early-returns* on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new “experimental features” gate. + +### Example + +```rescript +let getUser = async (id) => { + let? Ok(user) = await fetchUser(id) + let? Ok(decodedUser) = decodeUser(user) + Console.log(`Got user ${decodedUser.name}!`) + let? Ok() = await ensureUserActive(decodedUser) + Ok(decodedUser) +} +``` + +This desugars to a **sequence** of `switch`/early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await`. Same idea works for `option` with `Some(...)` (and the PR also extends support so the left pattern can be `Error(...)`/`None`, not just `Ok(...)`/`Some(...)`). ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2], [[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) + +### Where it works (and doesn’t) + +* Targets **built-ins** only: `result` and `option`. (Custom variants still need `switch`.) ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2]) +* **Block/local bindings** only; tests show top-level usage is rejected. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +* The printed JS is the straightforward if/return form (i.e., “zero cost”). ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2]) + +### How to enable it (experimental) + +* The PR adds an **experimental-features infrastructure** to the toolchain and **CLI support**: `-enable-experimental …`. Rewatch reads config from `rescript.json` and forwards the feature(s) to the compiler. (See `rewatch` config + compile changes and the new compiler flag.) ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +* There are tests and docs stubs around the experimental toggle plus “feature not enabled” error cases. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) + +> In short: add the feature in `rescript.json` via the new “experimental features” setting (per the updated `CompilerConfigurationSpec.md`), or pass `-enable-experimental` in your build; otherwise `let?` is unavailable. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) + +# Dev notes (what changed under the hood) + +* **Lexer/Parser/Printer:** new token for `let?`, grammar rules, and pretty-printer support. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +* **Frontend transform:** AST handling lowers `let?` to the equivalent `switch`/early return; tailored super-errors for misuse. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +* **Feature gate:** new module(s) to register/enable experimental features, with CLI wiring and rewatch config reading. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +* **Tests:** syntax parsing, printer snapshots, super-errors (e.g., “feature not enabled”, “top-level not allowed”, “not supported variant”, and return-type mismatch). ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) + +# Safety analysis: can this break anything when **off**? + +**Bottom line:** extremely low risk when the flag is **off**. + +Why: + +1. **Gated by default.** The feature is unreachable unless explicitly enabled; using `let?` without the gate yields a dedicated “feature not enabled” error (there are fixtures for this). That means existing codebases remain unaffected. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +2. **New syntax, not repurposed.** `let?` didn’t previously parse; recognizing it only at a `let` binding start avoids collisions with existing `?` uses (e.g., ternary) elsewhere in expressions. Parser tests were added to lock this down. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +3. **No runtime path changes.** It’s a compile-time sugar that lowers to the same `switch`/return structure you’d hand-write. If you don’t use it, nothing in your generated JS changes. ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2]) +4. **Confined scope.** Even when enabled, top-level `let?` is rejected; only local/block bindings are supported. This reduces any accidental global impact. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +5. **Tooling guarded.** The PR tracks TODOs for error coverage and editor support; with the gate off, current editor plugins behave as before. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582)][1]) + +**Potential edge considerations (still low risk off):** + +* **Tokenization side-effects:** The scanner learns the `let?` token, but the feature gate + grammar location prevents it from altering how existing, valid programs lex/parse when you don’t write `let?`. Tests cover “not enabled” and misuse shapes. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +* **Error text churn:** Some super-error messages were added (including a hint when code looks *eligible* for `let?`). That only triggers in error paths; it won’t change successful builds. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) + +**Conclusion:** With the experimental flag **off**, there are no functional or runtime changes, and parser behavior for all previously valid code paths is preserved. It’s safe to ship behind the flag. + +--- + +If you want, I can also jot down a tiny rollout checklist (enable flag in a sample app, verify editor plugin snapshot, run the super-errors suite) next. + +[1]: https://github.com/rescript-lang/rescript/pull/7582 "PoC of let? by zth · Pull Request #7582 · rescript-lang/rescript · GitHub" +[2]: https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227 "Proposing new syntax for zero-cost unwrapping options/results - Development - ReScript Forum" +[3]: https://github.com/rescript-lang/rescript/pull/7582/files "PoC of let? by zth · Pull Request #7582 · rescript-lang/rescript · GitHub" \ No newline at end of file From 9e08420bcd1a6783ee60fa02b335ff34dd0ab27c Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Thu, 28 Aug 2025 22:41:06 +0200 Subject: [PATCH 02/16] Refine let? post --- _blogposts/2025-09-01-let-unwrap.mdx | 46 +++------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 5f08e83d1..57c1edab0 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -25,51 +25,15 @@ let getUser = async (id) => { } ``` -This desugars to a **sequence** of `switch`/early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await`. Same idea works for `option` with `Some(...)` (and the PR also extends support so the left pattern can be `Error(...)`/`None`, not just `Ok(...)`/`Some(...)`). ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2], [[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) +This desugars to a **sequence** of `switch`/early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await`. Same idea works for `option` with `Some(...)` (and the PR also extends support so the left pattern can be `Error(...)`/`None`, not just `Ok(...)`/`Some(...)`). -### Where it works (and doesn’t) - -* Targets **built-ins** only: `result` and `option`. (Custom variants still need `switch`.) ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2]) -* **Block/local bindings** only; tests show top-level usage is rejected. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -* The printed JS is the straightforward if/return form (i.e., “zero cost”). ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2]) +Beware it targets built-ins only: `result` and `option`. (Custom variants still need `switch`.) And it is for block or local bindings only; top-level usage is rejected. +Compiled JS code is the straightforward if/return form (i.e., “zero cost”). ### How to enable it (experimental) -* The PR adds an **experimental-features infrastructure** to the toolchain and **CLI support**: `-enable-experimental …`. Rewatch reads config from `rescript.json` and forwards the feature(s) to the compiler. (See `rewatch` config + compile changes and the new compiler flag.) ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -* There are tests and docs stubs around the experimental toggle plus “feature not enabled” error cases. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) - -> In short: add the feature in `rescript.json` via the new “experimental features” setting (per the updated `CompilerConfigurationSpec.md`), or pass `-enable-experimental` in your build; otherwise `let?` is unavailable. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) - -# Dev notes (what changed under the hood) - -* **Lexer/Parser/Printer:** new token for `let?`, grammar rules, and pretty-printer support. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -* **Frontend transform:** AST handling lowers `let?` to the equivalent `switch`/early return; tailored super-errors for misuse. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -* **Feature gate:** new module(s) to register/enable experimental features, with CLI wiring and rewatch config reading. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -* **Tests:** syntax parsing, printer snapshots, super-errors (e.g., “feature not enabled”, “top-level not allowed”, “not supported variant”, and return-type mismatch). ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) - -# Safety analysis: can this break anything when **off**? - -**Bottom line:** extremely low risk when the flag is **off**. +We have added an **experimental-features infrastructure** to the toolchain. The corresponding compiler flag is `-enable-experimental`. This means you can enable `let?` in your `rescript.json`s `compiler-flags` and it forwards the feature to the compiler. -Why: - -1. **Gated by default.** The feature is unreachable unless explicitly enabled; using `let?` without the gate yields a dedicated “feature not enabled” error (there are fixtures for this). That means existing codebases remain unaffected. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -2. **New syntax, not repurposed.** `let?` didn’t previously parse; recognizing it only at a `let` binding start avoids collisions with existing `?` uses (e.g., ternary) elsewhere in expressions. Parser tests were added to lock this down. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -3. **No runtime path changes.** It’s a compile-time sugar that lowers to the same `switch`/return structure you’d hand-write. If you don’t use it, nothing in your generated JS changes. ([[ReScript Forum](https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227)][2]) -4. **Confined scope.** Even when enabled, top-level `let?` is rejected; only local/block bindings are supported. This reduces any accidental global impact. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -5. **Tooling guarded.** The PR tracks TODOs for error coverage and editor support; with the gate off, current editor plugins behave as before. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582)][1]) - -**Potential edge considerations (still low risk off):** - -* **Tokenization side-effects:** The scanner learns the `let?` token, but the feature gate + grammar location prevents it from altering how existing, valid programs lex/parse when you don’t write `let?`. Tests cover “not enabled” and misuse shapes. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) -* **Error text churn:** Some super-error messages were added (including a hint when code looks *eligible* for `let?`). That only triggers in error paths; it won’t change successful builds. ([[GitHub](https://github.com/rescript-lang/rescript/pull/7582/files)][3]) - -**Conclusion:** With the experimental flag **off**, there are no functional or runtime changes, and parser behavior for all previously valid code paths is preserved. It’s safe to ship behind the flag. - ---- +This is purely a syntactical change so performance is not affected. -If you want, I can also jot down a tiny rollout checklist (enable flag in a sample app, verify editor plugin snapshot, run the super-errors suite) next. -[1]: https://github.com/rescript-lang/rescript/pull/7582 "PoC of let? by zth · Pull Request #7582 · rescript-lang/rescript · GitHub" -[2]: https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227 "Proposing new syntax for zero-cost unwrapping options/results - Development - ReScript Forum" -[3]: https://github.com/rescript-lang/rescript/pull/7582/files "PoC of let? by zth · Pull Request #7582 · rescript-lang/rescript · GitHub" \ No newline at end of file From 26d84bcfef17565d5115633d8da69ecadba534a5 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Mon, 15 Sep 2025 01:13:30 +0200 Subject: [PATCH 03/16] Better example --- _blogposts/2025-09-01-let-unwrap.mdx | 45 +++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 57c1edab0..401a8071e 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -15,15 +15,58 @@ After long discussions we finally decided on an unwrap syntax for both the `opti ### Example +Before showing off this new feauture, let's explore why it is useful. Consider a chain of `async` functions that are dependent on the result of the previous one. The naive way to write this in modern ReScript with `async`/`await` is to just `switch` on the results. + +```res +let getUser = async id => + switch await fetchUser(id) { + | Error(error) => Error(error) + | Ok(res) => + switch await decodeUser(res) { + | Error(error) => Error(error) + | Ok(decodedUser) => + switch await ensureUserActive(decodedUser) { + | Error(error) => Error(error) + | Ok() => Ok(decodedUser) + } + } + } +``` + +Two observations: + 1. with every `switch` expression, this function gets nested deeper. + 2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change) + +This means even though `async`/`await` syntax is available in ReScript for some time now, it is also understandable that people created their own `ResultPromise` libraries to handle such things with less lines of code, e.g.: + +```res +module ResultPromise = { + let flatMapOk = async (p: promise<'res>, f) => + switch await p { + | Ok(x) => await f(x) + | Error(_) as res => res + } +} + +let getUserPromises = id => + fetchUser(id) + ->ResultPromise.flatMapOk(user => Promise.resolve(user->decodeUser)) + ->ResultPromise.flatMapOk(decodedUser => ensureUserActive(decodedUser)) +``` + +While this is much shorter, it is also harder to understand because we have two wrapper types here, `promise` and `result`. And we have to wrap the non-async type in a `Promise.resolve` in order to stay on the same type level. + ```rescript let getUser = async (id) => { let? Ok(user) = await fetchUser(id) let? Ok(decodedUser) = decodeUser(user) - Console.log(`Got user ${decodedUser.name}!`) let? Ok() = await ensureUserActive(decodedUser) Ok(decodedUser) } ``` +With the new `let-unwrap` syntax, `let?` in short, we now have to follow the happy-path (in the scope of the function). And it's immediately clear that `fetchUser` is an `async` function while `decodeUser` is not. There is no nesting as the `Error` is automatically mapped. But be assured the error case is also handled as the type checker will complain when you don't handle the `Error` returned by the `getUser` function. + + This desugars to a **sequence** of `switch`/early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await`. Same idea works for `option` with `Some(...)` (and the PR also extends support so the left pattern can be `Error(...)`/`None`, not just `Ok(...)`/`Some(...)`). From 156ad3c0fdb9c4d7aef08b0e8c59e6301773f009 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Fri, 10 Oct 2025 11:10:17 +0200 Subject: [PATCH 04/16] Use new internal async result methods --- _blogposts/2025-09-01-let-unwrap.mdx | 94 ++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 401a8071e..c6ef20e9f 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -4,16 +4,12 @@ date: "2025-09-01" badge: roadmap title: let? description: | - A new let-unwrap syntax just landed in ReScript. + A new let-unwrap syntax just landed in ReScript. Experimental! --- After long discussions we finally decided on an unwrap syntax for both the `option` and `result` types that we are happy with and that still matches the explicitness of ReScript we all like. -# What is it exactly? - -`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and *early-returns* on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new “experimental features” gate. - -### Example +`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and *early-returns* on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new "experimental features" gate. See below how to enable it. Before showing off this new feauture, let's explore why it is useful. Consider a chain of `async` functions that are dependent on the result of the previous one. The naive way to write this in modern ReScript with `async`/`await` is to just `switch` on the results. @@ -35,26 +31,24 @@ let getUser = async id => Two observations: 1. with every `switch` expression, this function gets nested deeper. - 2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change) + 2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change). -This means even though `async`/`await` syntax is available in ReScript for some time now, it is also understandable that people created their own `ResultPromise` libraries to handle such things with less lines of code, e.g.: +The only alternative in ReScript was always to use some specialized methods: ```res -module ResultPromise = { - let flatMapOk = async (p: promise<'res>, f) => - switch await p { - | Ok(x) => await f(x) - | Error(_) as res => res - } -} - let getUserPromises = id => fetchUser(id) - ->ResultPromise.flatMapOk(user => Promise.resolve(user->decodeUser)) - ->ResultPromise.flatMapOk(decodedUser => ensureUserActive(decodedUser)) + ->Result.flatMapOkAsync(user => Promise.resolve(user->decodeUser)) + ->Result.flatMapOkAsync(decodedUser => ensureUserActive(decodedUser)) ``` -While this is much shorter, it is also harder to understand because we have two wrapper types here, `promise` and `result`. And we have to wrap the non-async type in a `Promise.resolve` in order to stay on the same type level. +**Note**: `Result.flatMapOkAsync` among some other async result helper functions are brand new in ReScript 12 as well! + +This is arguably better, more concise, but also harder to understand because we have two wrapper types here, `promise` and `result`. And we have to wrap the non-async type in a `Promise.resolve` in order to stay on the same type level. Also we are giving up on our precious `async`/`await` syntax here. + +## Introducing `let?` + +Let's rewrite the above example again with our new syntax: ```rescript let getUser = async (id) => { @@ -64,19 +58,67 @@ let getUser = async (id) => { Ok(decodedUser) } ``` -With the new `let-unwrap` syntax, `let?` in short, we now have to follow the happy-path (in the scope of the function). And it's immediately clear that `fetchUser` is an `async` function while `decodeUser` is not. There is no nesting as the `Error` is automatically mapped. But be assured the error case is also handled as the type checker will complain when you don't handle the `Error` returned by the `getUser` function. + +This strikes a balance between conciseness and simplicity that we really think fits ReScript well. + +With `let?`, we can now safely focus on the the happy-path in the scope of the function. There is no nesting as the `Error` is automatically mapped. But be assured the error case is also handled as the type checker will complain when you don't handle the `Error` returned by the `getUser` function. + +This desugars to a **sequence** of early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await` as the example above suggests. + +Of course, it also works for `option` with `Some(...)`. + +```rescript +let getActiveUser = user => { + let? Some(name) = activeUsers->Array.get(user) + Some({name, active: true}) +} +``` + +It also works with the unhappy path, with `Error(...)` or `None` as the main type and `Ok(...)` or `Some(...)` as the implicitly mapped types. + +```rescript +let getNoUser = user => { + let? None = activeUsers->Array.get(user) + Some("No user for you!") +} + +let decodeUserWithHumanReadableError = user => { + let? Error(_e) = decodeUser(user) + Error("This could not be decoded!") +} +``` + +Beware it targets built-ins only, namely `result` and `option`. Custom variants still need `switch`. And it is for block or local bindings only; top-level usage is rejected. + +```rescript +let? Ok(user) = await fetchUser("1") +// ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings. +``` -This desugars to a **sequence** of `switch`/early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await`. Same idea works for `option` with `Some(...)` (and the PR also extends support so the left pattern can be `Error(...)`/`None`, not just `Ok(...)`/`Some(...)`). +### How to enable it + +We have added an **experimental-features infrastructure** to the toolchain. If you use the new build system that comes with ReScript 12 by default, you can enable it in `rescript.json` like so: + +```json +{ + "experimental-features": { + "letUnwrap": true + } +} +``` -Beware it targets built-ins only: `result` and `option`. (Custom variants still need `switch`.) And it is for block or local bindings only; top-level usage is rejected. -Compiled JS code is the straightforward if/return form (i.e., “zero cost”). +If you still use the legacy build system, enable it with the compiler flag `-enable-experimental`: -### How to enable it (experimental) +```json +{ + "compiler-flags": ["-enable-experimental", "LetUnwrap"] +} +``` -We have added an **experimental-features infrastructure** to the toolchain. The corresponding compiler flag is `-enable-experimental`. This means you can enable `let?` in your `rescript.json`s `compiler-flags` and it forwards the feature to the compiler. +We would love to hear your thoughts about this feature in the [forum](https://forum.rescript-lang.org/). Please try it out and tell us what you think! -This is purely a syntactical change so performance is not affected. +Happy hacking! From 43f44399b49d81e57c50ec3e7c56f1234ec6946c Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Fri, 10 Oct 2025 13:17:59 +0200 Subject: [PATCH 05/16] Format --- _blogposts/2025-09-01-let-unwrap.mdx | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index c6ef20e9f..c71fee966 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -4,12 +4,12 @@ date: "2025-09-01" badge: roadmap title: let? description: | - A new let-unwrap syntax just landed in ReScript. Experimental! + A new let-unwrap syntax just landed in ReScript. Experimental! --- After long discussions we finally decided on an unwrap syntax for both the `option` and `result` types that we are happy with and that still matches the explicitness of ReScript we all like. -`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and *early-returns* on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new "experimental features" gate. See below how to enable it. +`let?` or `let-unwrap` is a tiny syntax that unwraps `result`/`option` values and _early-returns_ on `Error`/`None`. It’s explicitly **experimental** and **disabled by default** behind a new "experimental features" gate. See below how to enable it. Before showing off this new feauture, let's explore why it is useful. Consider a chain of `async` functions that are dependent on the result of the previous one. The naive way to write this in modern ReScript with `async`/`await` is to just `switch` on the results. @@ -29,9 +29,10 @@ let getUser = async id => } ``` -Two observations: - 1. with every `switch` expression, this function gets nested deeper. - 2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change). +Two observations: + +1. with every `switch` expression, this function gets nested deeper. +2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change). The only alternative in ReScript was always to use some specialized methods: @@ -65,7 +66,7 @@ With `let?`, we can now safely focus on the the happy-path in the scope of the f This desugars to a **sequence** of early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await` as the example above suggests. -Of course, it also works for `option` with `Some(...)`. +Of course, it also works for `option` with `Some(...)`. ```rescript let getActiveUser = user => { @@ -74,7 +75,7 @@ let getActiveUser = user => { } ``` -It also works with the unhappy path, with `Error(...)` or `None` as the main type and `Ok(...)` or `Some(...)` as the implicitly mapped types. +It also works with the unhappy path, with `Error(...)` or `None` as the main type and `Ok(...)` or `Some(...)` as the implicitly mapped types. ```rescript let getNoUser = user => { @@ -88,11 +89,11 @@ let decodeUserWithHumanReadableError = user => { } ``` -Beware it targets built-ins only, namely `result` and `option`. Custom variants still need `switch`. And it is for block or local bindings only; top-level usage is rejected. +Beware it targets built-ins only, namely `result` and `option`. Custom variants still need `switch`. And it is for block or local bindings only; top-level usage is rejected. ```rescript let? Ok(user) = await fetchUser("1") -// ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings. +// ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings. ``` @@ -120,5 +121,3 @@ If you still use the legacy build system, enable it with the compiler flag `-ena We would love to hear your thoughts about this feature in the [forum](https://forum.rescript-lang.org/). Please try it out and tell us what you think! Happy hacking! - - From 35866552ab311b05e3d9929a1e63634d3e4ec776 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Fri, 10 Oct 2025 23:39:14 +0200 Subject: [PATCH 06/16] Support experimental features in CodeTabs --- src/components/CodeExample.res | 8 +++++++- src/components/CodeExample.resi | 1 + src/components/Markdown.res | 7 ++++++- src/components/Markdown.resi | 6 +++++- src/components/MarkdownComponents.res | 2 +- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/components/CodeExample.res b/src/components/CodeExample.res index 8ea95f911..8d3b38621 100644 --- a/src/components/CodeExample.res +++ b/src/components/CodeExample.res @@ -157,6 +157,7 @@ module Toggle = { label: option, lang: option, code: string, + experiments: option, } @react.component @@ -243,7 +244,12 @@ module Toggle = { let playgroundLinkButton = tab->isReScript ? experiments + | None => "" + }}`} target="_blank"> // ICON Link to PLAYGROUND , lang: option, code: string, + experiments: option, } @react.component diff --git a/src/components/Markdown.res b/src/components/Markdown.res index 4b4a03cdc..c0e0291bf 100644 --- a/src/components/Markdown.res +++ b/src/components/Markdown.res @@ -287,7 +287,11 @@ module CodeTab = { return element.props.metastring; }") @react.component - let make = (~children: Mdx.MdxChildren.t, ~labels: array=[]) => { + let make = ( + ~children: Mdx.MdxChildren.t, + ~labels: array=[], + ~experiments: option=?, + ) => { let mdxElements = switch Mdx.MdxChildren.classify(children) { | Array(mdxElements) => mdxElements | Element(el) => [el] @@ -315,6 +319,7 @@ module CodeTab = { code, label, highlightedLines: Some(Code.parseNumericRangeMeta(metastring)), + experiments, } Array.push(acc, tab)->ignore diff --git a/src/components/Markdown.resi b/src/components/Markdown.resi index 9abe49e38..23d4ab0e9 100644 --- a/src/components/Markdown.resi +++ b/src/components/Markdown.resi @@ -99,7 +99,11 @@ module Code: { module CodeTab: { @react.component - let make: (~children: Mdx.MdxChildren.t, ~labels: array=?) => React.element + let make: ( + ~children: Mdx.MdxChildren.t, + ~labels: array=?, + ~experiments: string=?, + ) => React.element } module Blockquote: { diff --git a/src/components/MarkdownComponents.res b/src/components/MarkdownComponents.res index 784bc756f..cf69e4144 100644 --- a/src/components/MarkdownComponents.res +++ b/src/components/MarkdownComponents.res @@ -20,7 +20,7 @@ type t = { @as("UrlBox") urlBox?: React.componentLike, React.element>, @as("CodeTab") - codeTab?: CodeTab.props> => React.element, + codeTab?: CodeTab.props, string> => React.element, /* Common markdown elements */ p?: P.props => React.element, li?: Li.props => React.element, From e68abfda4b1db259d4d97f83383a60d9ac206966 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Fri, 10 Oct 2025 23:39:37 +0200 Subject: [PATCH 07/16] Error handling example --- _blogposts/2025-09-01-let-unwrap.mdx | 92 +++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index c71fee966..847ce6d18 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -1,6 +1,7 @@ --- author: rescript-team -date: "2025-09-01" +date: "2025-10-14" +previewImg: /static/blog/rescript-12-let-unwrap.jpg badge: roadmap title: let? description: | @@ -55,6 +56,7 @@ Let's rewrite the above example again with our new syntax: let getUser = async (id) => { let? Ok(user) = await fetchUser(id) let? Ok(decodedUser) = decodeUser(user) + Console.log(`Got user ${decodedUser.name}!`) let? Ok() = await ensureUserActive(decodedUser) Ok(decodedUser) } @@ -96,9 +98,93 @@ let? Ok(user) = await fetchUser("1") // ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings. ``` - +### A full example with error handling -### How to enable it + + +```rescript +type user = { + id: string, + name: string, + token: string, +} + +external fetchUser: string => promise< + result #NetworkError | #UserNotFound | #Unauthorized]>, +> = "fetchUser" + +external decodeUser: JSON.t => result #DecodeError]> = "decodeUser" + +external ensureUserActive: user => promise #UserNotActive]>> = + "ensureUserActive" + +let getUser = async id => { + let? Ok(user) = await fetchUser(id) + let? Ok(decodedUser) = decodeUser(user) + Console.log(`Got user ${decodedUser.name}!`) + let? Ok() = await ensureUserActive(decodedUser) + Ok(decodedUser) +} + +// ERROR! +// You forgot to handle a possible case here, for example: +// | Error(#Unauthorized | #UserNotFound | #DecodeError | #UserNotActive) +let main = async () => { + switch await getUser("123") { + | Ok(user) => Console.log(user) + | Error(#NetworkError) => Console.error("Uh-oh, network error...") + } +} +``` + +```js +async function getUser(id) { + let e = await fetchUser(id); + if (e.TAG !== "Ok") { + return e; + } + let e$1 = decodeUser(e._0); + if (e$1.TAG !== "Ok") { + return e$1; + } + let decodedUser = e$1._0; + console.log(`Got user ` + decodedUser.name + `!`); + let e$2 = await ensureUserActive(decodedUser); + if (e$2.TAG === "Ok") { + return { + TAG: "Ok", + _0: decodedUser + }; + } else { + return e$2; + } +} + +async function main() { + let user = await getUser("123"); + if (user.TAG === "Ok") { + console.log(user._0); + return; + } + if (user._0 === "NetworkError") { + console.error("Uh-oh, network error..."); + return; + } + throw { + RE_EXN_ID: "Match_failure", + _1: [ + "playground.res", + 28, + 2 + ], + Error: new Error() + }; +} +``` + + + +## Experimental features We have added an **experimental-features infrastructure** to the toolchain. If you use the new build system that comes with ReScript 12 by default, you can enable it in `rescript.json` like so: From fd589e8c69875dde088791f1f4b056c925fe9632 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Fri, 10 Oct 2025 23:39:56 +0200 Subject: [PATCH 08/16] Blogpost image --- public/static/blog/rescript-12-let-unwrap.jpg | Bin 0 -> 49230 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/static/blog/rescript-12-let-unwrap.jpg diff --git a/public/static/blog/rescript-12-let-unwrap.jpg b/public/static/blog/rescript-12-let-unwrap.jpg new file mode 100644 index 0000000000000000000000000000000000000000..537924d14a004a0c730466a41b5c95b85c940394 GIT binary patch literal 49230 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

`j{kg3? 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 Date: Fri, 10 Oct 2025 23:44:04 +0200 Subject: [PATCH 09/16] Format Format Format --- _blogposts/2025-09-01-let-unwrap.mdx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 847ce6d18..9c689b28b 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -127,7 +127,7 @@ let getUser = async id => { } // ERROR! -// You forgot to handle a possible case here, for example: +// You forgot to handle a possible case here, for example: // | Error(#Unauthorized | #UserNotFound | #DecodeError | #UserNotActive) let main = async () => { switch await getUser("123") { @@ -153,7 +153,7 @@ async function getUser(id) { if (e$2.TAG === "Ok") { return { TAG: "Ok", - _0: decodedUser + _0: decodedUser, }; } else { return e$2; @@ -172,12 +172,8 @@ async function main() { } throw { RE_EXN_ID: "Match_failure", - _1: [ - "playground.res", - 28, - 2 - ], - Error: new Error() + _1: ["playground.res", 28, 2], + Error: new Error(), }; } ``` From ca364ed95a0b3cc0c45e09af0a9095a6a5727e50 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Tue, 14 Oct 2025 00:10:57 +0200 Subject: [PATCH 10/16] Review --- _blogposts/2025-09-01-let-unwrap.mdx | 82 +++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 9c689b28b..13f39a3cc 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -35,7 +35,7 @@ Two observations: 1. with every `switch` expression, this function gets nested deeper. 2. The `Error` branch of every `switch` is just an identity mapper (neither wrapper nor contents change). -The only alternative in ReScript was always to use some specialized methods: +The only alternative in ReScript was always to use some specialized functions: ```res let getUserPromises = id => @@ -46,12 +46,60 @@ let getUserPromises = id => **Note**: `Result.flatMapOkAsync` among some other async result helper functions are brand new in ReScript 12 as well! -This is arguably better, more concise, but also harder to understand because we have two wrapper types here, `promise` and `result`. And we have to wrap the non-async type in a `Promise.resolve` in order to stay on the same type level. Also we are giving up on our precious `async`/`await` syntax here. +This is arguably better, more concise, but also harder to understand because we have two wrapper types here, `promise` and `result`. And we have to wrap the non-async type in a `Promise.resolve` in order to stay on the same type level. Also we are giving up on our precious `async`/`await` syntax here. Furthermore, those functions result in two more function calls. + +```js +function getUserPromises(id) { + return Stdlib_Result.flatMapOkAsync( + Stdlib_Result.flatMapOkAsync(fetchUser(id), (user) => + Promise.resolve(decodeUser(user)) + ), + ensureUserActive + ); +} +``` + +Let's have a look at the generated JS from the initial example in comparison: + +```js +async function getUserNestedSwitches(id) { + let error = await fetchUser(id); + if (error.TAG !== "Ok") { + return { + TAG: "Error", + _0: error._0, + }; + } + let error$1 = decodeUser(error._0); + if (error$1.TAG !== "Ok") { + return { + TAG: "Error", + _0: error$1._0, + }; + } + let decodedUser = error$1._0; + let error$2 = await ensureUserActive(decodedUser); + if (error$2.TAG === "Ok") { + return { + TAG: "Ok", + _0: decodedUser, + }; + } else { + return { + TAG: "Error", + _0: error$2._0, + }; + } +} +``` + ## Introducing `let?` Let's rewrite the above example again with our new syntax: + + ```rescript let getUser = async (id) => { let? Ok(user) = await fetchUser(id) @@ -62,11 +110,37 @@ let getUser = async (id) => { } ``` +```js +async function getUser(id) { + let e = await fetchUser(id); + if (e.TAG !== "Ok") { + return e; + } + let e$1 = decodeUser(e._0); + if (e$1.TAG !== "Ok") { + return e$1; + } + let decodedUser = e$1._0; + console.log(`Got user ` + decodedUser.name + `!`); + let e$2 = await ensureUserActive(decodedUser); + if (e$2.TAG === "Ok") { + return { + TAG: "Ok", + _0: decodedUser, + }; + } else { + return e$2; + } +} +``` + + + This strikes a balance between conciseness and simplicity that we really think fits ReScript well. With `let?`, we can now safely focus on the the happy-path in the scope of the function. There is no nesting as the `Error` is automatically mapped. But be assured the error case is also handled as the type checker will complain when you don't handle the `Error` returned by the `getUser` function. -This desugars to a **sequence** of early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await` as the example above suggests. +This desugars to a **sequence** of early-returns that you’d otherwise write by hand, so there’s **no extra runtime cost** and it plays nicely with `async/await` as the example above suggests. Check the `JS Output` tab to see for yourself! Of course, it also works for `option` with `Some(...)`. @@ -98,6 +172,8 @@ let? Ok(user) = await fetchUser("1") // ^^^^^^^ ERROR: `let?` is not allowed for top-level bindings. ``` +**Note**: `result` and `option` types cannot be mixed in a `let?` function! + ### A full example with error handling From 5088f8286be1863f14df1e3587bc6ebd8a430cb4 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Tue, 14 Oct 2025 00:15:33 +0200 Subject: [PATCH 11/16] Add note about it still being experimental --- _blogposts/2025-09-01-let-unwrap.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 13f39a3cc..42f032377 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -14,6 +14,8 @@ After long discussions we finally decided on an unwrap syntax for both the `opti Before showing off this new feauture, let's explore why it is useful. Consider a chain of `async` functions that are dependent on the result of the previous one. The naive way to write this in modern ReScript with `async`/`await` is to just `switch` on the results. +**Note**: While we are cautiously optimistic with this implementation of `let?`, we still consider it experimental and thus hide it behind a compiler flag that the user explicitly needs to activate. It might change so use at your own risk. + ```res let getUser = async id => switch await fetchUser(id) { @@ -52,9 +54,9 @@ This is arguably better, more concise, but also harder to understand because we function getUserPromises(id) { return Stdlib_Result.flatMapOkAsync( Stdlib_Result.flatMapOkAsync(fetchUser(id), (user) => - Promise.resolve(decodeUser(user)) + Promise.resolve(decodeUser(user)), ), - ensureUserActive + ensureUserActive, ); } ``` @@ -93,7 +95,6 @@ async function getUserNestedSwitches(id) { } ``` - ## Introducing `let?` Let's rewrite the above example again with our new syntax: From b9b9def3f94c055fd1a917105908c21c0f093ae0 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Tue, 14 Oct 2025 11:05:39 +0200 Subject: [PATCH 12/16] Cleanup --- _blogposts/2025-09-01-let-unwrap.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index 42f032377..a9f1eceba 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -64,7 +64,7 @@ function getUserPromises(id) { Let's have a look at the generated JS from the initial example in comparison: ```js -async function getUserNestedSwitches(id) { +async function getUser(id) { let error = await fetchUser(id); if (error.TAG !== "Ok") { return { @@ -95,6 +95,8 @@ async function getUserNestedSwitches(id) { } ``` +As you can see, there is no extra calls to the standard library, but it's a little verbose. + ## Introducing `let?` Let's rewrite the above example again with our new syntax: @@ -105,7 +107,6 @@ Let's rewrite the above example again with our new syntax: let getUser = async (id) => { let? Ok(user) = await fetchUser(id) let? Ok(decodedUser) = decodeUser(user) - Console.log(`Got user ${decodedUser.name}!`) let? Ok() = await ensureUserActive(decodedUser) Ok(decodedUser) } @@ -122,7 +123,6 @@ async function getUser(id) { return e$1; } let decodedUser = e$1._0; - console.log(`Got user ` + decodedUser.name + `!`); let e$2 = await ensureUserActive(decodedUser); if (e$2.TAG === "Ok") { return { @@ -137,7 +137,7 @@ async function getUser(id) { -This strikes a balance between conciseness and simplicity that we really think fits ReScript well. +This strikes a balance between conciseness and simplicity that we really think fits ReScript well. Also the emitted JS is more concise than the initial example because we got rid of the manual error mapping. With `let?`, we can now safely focus on the the happy-path in the scope of the function. There is no nesting as the `Error` is automatically mapped. But be assured the error case is also handled as the type checker will complain when you don't handle the `Error` returned by the `getUser` function. From d6bcaab089b01f2a04c68ff0596cf7e4853824c8 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Tue, 14 Oct 2025 11:06:40 +0200 Subject: [PATCH 13/16] Better heading --- _blogposts/2025-09-01-let-unwrap.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index a9f1eceba..d4b4e4b4b 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -3,7 +3,7 @@ author: rescript-team date: "2025-10-14" previewImg: /static/blog/rescript-12-let-unwrap.jpg badge: roadmap -title: let? +title: Experimental Feauture: let? description: | A new let-unwrap syntax just landed in ReScript. Experimental! --- From 2c64d0a48e568531a750aeabda1081c8ea50d322 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Tue, 14 Oct 2025 13:02:57 +0200 Subject: [PATCH 14/16] Typo --- _blogposts/2025-09-01-let-unwrap.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index d4b4e4b4b..baa3ac195 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -3,7 +3,7 @@ author: rescript-team date: "2025-10-14" previewImg: /static/blog/rescript-12-let-unwrap.jpg badge: roadmap -title: Experimental Feauture: let? +title: "Experimental Feature: let?" description: | A new let-unwrap syntax just landed in ReScript. Experimental! --- From d2f3ea4b30d6a707fd58405f2f422621b57bae63 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Tue, 14 Oct 2025 13:16:07 +0200 Subject: [PATCH 15/16] Note about minimum version --- _blogposts/2025-09-01-let-unwrap.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index baa3ac195..cc6dcbdbe 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -259,7 +259,7 @@ async function main() { ## Experimental features -We have added an **experimental-features infrastructure** to the toolchain. If you use the new build system that comes with ReScript 12 by default, you can enable it in `rescript.json` like so: +We have added an `experimental-features` infrastructure to the toolchain. If you use the new build system that comes with ReScript 12 by default, you can enable it in `rescript.json` like so: ```json { @@ -277,6 +277,8 @@ If you still use the legacy build system, enable it with the compiler flag `-ena } ``` -We would love to hear your thoughts about this feature in the [forum](https://forum.rescript-lang.org/). Please try it out and tell us what you think! +Both `experimental-feautures` and `let?` are available in [ReScript 12.0.0-beta.9](https://github.com/rescript-lang/rescript/blob/master/CHANGELOG.md#1200-beta9) or later. Bear in mind `let?` is subject to change or might even be removed entirely if it can be superseded by something else. + +We would love to hear your thoughts about these features in the [forum](https://forum.rescript-lang.org/). Please try it out and tell us what you think! Happy hacking! From 7c30336b06955b39978d92acb36d33a44d800c46 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Fri, 17 Oct 2025 10:45:45 +0200 Subject: [PATCH 16/16] WIP: Support preludes in playground links --- _blogposts/2025-09-01-let-unwrap.mdx | 15 +- package-lock.json | 539 ++------------------------ package.json | 1 + src/common/CompilerManagerHook.res | 8 +- src/common/CompilerManagerHook.resi | 2 + src/common/MarkdownParser.res | 36 +- src/components/CodeExample.res | 32 +- src/components/CodeExample.resi | 1 + src/components/Markdown.res | 18 + src/components/Markdown.resi | 1 + src/components/MarkdownComponents.res | 2 +- 11 files changed, 130 insertions(+), 525 deletions(-) diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx index cc6dcbdbe..7ab27af0d 100644 --- a/_blogposts/2025-09-01-let-unwrap.mdx +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -16,6 +16,13 @@ Before showing off this new feauture, let's explore why it is useful. Consider a **Note**: While we are cautiously optimistic with this implementation of `let?`, we still consider it experimental and thus hide it behind a compiler flag that the user explicitly needs to activate. It might change so use at your own risk. +```res prelude +type user = { id: string, name: string, token: string} +external fetchUser: string => promise #NetworkError | #UserNotFound | #Unauthorized]>> = "fetchUser" +external decodeUser: JSON.t => result #DecodeError]> = "decodeUser" +external ensureUserActive: user => promise #UserNotActive]>> = "ensureUserActive" +``` + ```res let getUser = async id => switch await fetchUser(id) { @@ -40,7 +47,7 @@ Two observations: The only alternative in ReScript was always to use some specialized functions: ```res -let getUserPromises = id => +let getUser = id => fetchUser(id) ->Result.flatMapOkAsync(user => Promise.resolve(user->decodeUser)) ->Result.flatMapOkAsync(decodedUser => ensureUserActive(decodedUser)) @@ -51,7 +58,7 @@ let getUserPromises = id => This is arguably better, more concise, but also harder to understand because we have two wrapper types here, `promise` and `result`. And we have to wrap the non-async type in a `Promise.resolve` in order to stay on the same type level. Also we are giving up on our precious `async`/`await` syntax here. Furthermore, those functions result in two more function calls. ```js -function getUserPromises(id) { +function getUser(id) { return Stdlib_Result.flatMapOkAsync( Stdlib_Result.flatMapOkAsync(fetchUser(id), (user) => Promise.resolve(decodeUser(user)), @@ -101,7 +108,7 @@ As you can see, there is no extra calls to the standard library, but it's a litt Let's rewrite the above example again with our new syntax: - + ```rescript let getUser = async (id) => { @@ -177,7 +184,7 @@ let? Ok(user) = await fetchUser("1") ### A full example with error handling - + ```rescript type user = { diff --git a/package-lock.json b/package-lock.json index 7ee2e439a..7680e124e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "rescript": "^12.0.0-beta.6", "stringify-object": "^3.3.0", "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", "vfile-matter": "^5.0.0" }, "devDependencies": { @@ -1646,48 +1647,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/@mdx-js/mdx/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -8720,35 +8679,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-frontmatter/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-gfm": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", @@ -9422,35 +9352,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-footnote/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-gfm-strikethrough": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", @@ -10025,35 +9926,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-strikethrough/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-gfm-table": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", @@ -10630,35 +10502,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-table/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-gfm-task-list-item": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", @@ -11234,35 +11077,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-task-list-item/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-gfm/node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -11822,35 +11636,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-mdx": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", @@ -12427,33 +12212,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx-expression/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-mdx-jsx": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz", @@ -13020,33 +12778,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-mdx/node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -13606,35 +13337,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-mdxjs-esm": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", @@ -14194,33 +13896,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdxjs-esm/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-to-hast": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.0.2.tgz", @@ -14345,45 +14020,6 @@ } ] }, - "node_modules/mdast-util-to-hast/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -17775,45 +17411,6 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" }, - "node_modules/react-markdown/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/react-markdown/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/react-markdown/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -17987,48 +17584,6 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, - "node_modules/rehype-slug/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-slug/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-slug/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/rehype-stringify": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", @@ -18943,35 +18498,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-stringify/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -20554,10 +20080,11 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" }, - "node_modules/unist-util-remove-position/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" }, @@ -20566,10 +20093,17 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-remove-position/node_modules/unist-util-visit": { + "node_modules/unist-util-stringify-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", @@ -20580,23 +20114,30 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-remove-position/node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -20606,20 +20147,14 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-stringify-position/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "node_modules/unist-util-visit/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, "funding": { "type": "opencollective", diff --git a/package.json b/package.json index 397f27727..753a0b52f 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "rescript": "^12.0.0-beta.6", "stringify-object": "^3.3.0", "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", "vfile-matter": "^5.0.0" }, "scripts": { diff --git a/src/common/CompilerManagerHook.res b/src/common/CompilerManagerHook.res index d5a4e2176..80ae1975e 100644 --- a/src/common/CompilerManagerHook.res +++ b/src/common/CompilerManagerHook.res @@ -200,6 +200,9 @@ type queryParams = | @as("experiments") Experiments | @as("code") Code +let createQuerystring = (params: array<(queryParams, string)>) => + params->Array.map(((key, value)) => (key :> string) + "=" + value)->Array.join("&") + let createUrl = (pathName, ready) => { let params = switch ready.targetLang { | Res => [] @@ -221,10 +224,7 @@ let createUrl = (pathName, ready) => { // Put code last as it is the longest param. Array.push(params, (Code, ready.code->LzString.compressToEncodedURIComponent)) - let querystring = - params->Array.map(((key, value)) => (key :> string) ++ "=" ++ value)->Array.join("&") - let url = pathName ++ "?" ++ querystring - url + pathName + "?" + params->createQuerystring } let defaultModuleSystem = "esmodule" diff --git a/src/common/CompilerManagerHook.resi b/src/common/CompilerManagerHook.resi index 02d302df8..ed9f263cf 100644 --- a/src/common/CompilerManagerHook.resi +++ b/src/common/CompilerManagerHook.resi @@ -72,4 +72,6 @@ let useCompilerManager: ( unit, ) => (state, action => unit) +let createQuerystring: array<(queryParams, string)> => string + let createUrl: (string, ready) => string diff --git a/src/common/MarkdownParser.res b/src/common/MarkdownParser.res index 3a0a756ab..8889fb30c 100644 --- a/src/common/MarkdownParser.res +++ b/src/common/MarkdownParser.res @@ -1,10 +1,12 @@ type t +type tree type plugin type vfile<'a> = {..} as 'a external makePlugin: 'a => plugin = "%identity" @module("unified") external make: unit => t = "unified" +@module("unist-util-visit") external visit: (tree, string, {..} => unit) => unit = "visit" @module("remark-parse") external remarkParse: plugin = "default" @module("remark-gfm") external remarkGfm: plugin = "default" @@ -27,14 +29,46 @@ type result = { let vfileMatterPlugin = makePlugin(_options => (_tree, vfile) => vfileMatter(vfile)) +let remarkReScriptPrelude = tree => { + let prelude = ref("") + + visit(tree, "code", node => + if node["lang"] === "res prelude" { + prelude := prelude.contents + "\n" + node["value"] + } + ) + + if prelude.contents->String.trim !== "" { + visit(tree, "code", node => { + if node["lang"] === "res" { + let metaString = switch node["meta"]->Nullable.make { + | Value(value) => value + | _ => "" + } + + node["meta"] = + metaString + + JSON.stringifyAny(prelude.contents)->Option.mapOr("", prelude => " prelude=" + prelude) + + Console.log2("⇢ Added meta to code block:", node["meta"]) + } + }) + } +} + +let remarkReScriptPreludePlugin = makePlugin(_options => + (tree, _vfile) => remarkReScriptPrelude(tree) +) + let parser = make() ->use(remarkParse) - ->use(remarkStringify) ->use(remarkGfm) ->use(remarkComment) ->useOptions(remarkFrontmatter, [{"type": "yaml", "marker": "-"}]) ->use(vfileMatterPlugin) + ->use(remarkReScriptPreludePlugin) + ->use(remarkStringify) let parseSync = content => { let vfile = parser->processSync(content) diff --git a/src/components/CodeExample.res b/src/components/CodeExample.res index 8d3b38621..9dba26182 100644 --- a/src/components/CodeExample.res +++ b/src/components/CodeExample.res @@ -158,6 +158,7 @@ module Toggle = { lang: option, code: string, experiments: option, + version: option, } @react.component @@ -243,19 +244,24 @@ module Toggle = { | Some(tab) => let playgroundLinkButton = tab->isReScript - ? experiments - | None => "" - }}`} - target="_blank"> - // ICON Link to PLAYGROUND - - + ? { + let params = [] + tab.version->Option.forEach(version => + params->Array.push((CompilerManagerHook.Version, "v" ++ version)) + ) + tab.experiments->Option.forEach(experiments => + params->Array.push((CompilerManagerHook.Experiments, experiments)) + ) + params->Array.push((Code, tab.code->LzString.compressToEncodedURIComponent)) + + CompilerManagerHook.createQuerystring}`} target="_blank"> + // ICON Link to PLAYGROUND + + + } : React.null let copyButton = diff --git a/src/components/CodeExample.resi b/src/components/CodeExample.resi index 5a6e3c717..b70d39446 100644 --- a/src/components/CodeExample.resi +++ b/src/components/CodeExample.resi @@ -14,6 +14,7 @@ module Toggle: { lang: option, code: string, experiments: option, + version: option, } @react.component diff --git a/src/components/Markdown.res b/src/components/Markdown.res index c0e0291bf..3b4590a49 100644 --- a/src/components/Markdown.res +++ b/src/components/Markdown.res @@ -286,11 +286,15 @@ module CodeTab = { } return element.props.metastring; }") + + let preludeRegex = RegExp.fromString(`#/prelude="([^"]*)"/`, ~flags="g") + @react.component let make = ( ~children: Mdx.MdxChildren.t, ~labels: array=[], ~experiments: option=?, + ~version: option=?, ) => { let mdxElements = switch Mdx.MdxChildren.classify(children) { | Array(mdxElements) => mdxElements @@ -306,6 +310,12 @@ module CodeTab = { let className = Mdx.getMdxClassName(codeEl)->Option.getOr("") let metastring = getMdxMetastring(codeEl)->Option.getOr("") + Js.log2("metastring", metastring) + + let prelude = switch RegExp.exec(preludeRegex, metastring) { + | Some([_, Some(p)]) => p + | _ => "" + } let lang = switch String.split(className, "-") { | ["language", lang] => Some(lang) @@ -313,6 +323,13 @@ module CodeTab = { } let code = String.make(Mdx.MdxChildren.getMdxChildren(codeEl)) + + let code = if prelude->String.trim === "" { + code + } else { + prelude ++ "\n" ++ code + } + let label = labels[i] let tab = { CodeExample.Toggle.lang, @@ -320,6 +337,7 @@ module CodeTab = { label, highlightedLines: Some(Code.parseNumericRangeMeta(metastring)), experiments, + version, } Array.push(acc, tab)->ignore diff --git a/src/components/Markdown.resi b/src/components/Markdown.resi index 23d4ab0e9..5de22df88 100644 --- a/src/components/Markdown.resi +++ b/src/components/Markdown.resi @@ -103,6 +103,7 @@ module CodeTab: { ~children: Mdx.MdxChildren.t, ~labels: array=?, ~experiments: string=?, + ~version: string=?, ) => React.element } diff --git a/src/components/MarkdownComponents.res b/src/components/MarkdownComponents.res index cf69e4144..2659a1f26 100644 --- a/src/components/MarkdownComponents.res +++ b/src/components/MarkdownComponents.res @@ -20,7 +20,7 @@ type t = { @as("UrlBox") urlBox?: React.componentLike, React.element>, @as("CodeTab") - codeTab?: CodeTab.props, string> => React.element, + codeTab?: CodeTab.props, string, string> => React.element, /* Common markdown elements */ p?: P.props => React.element, li?: Li.props => React.element,