diff --git a/_blogposts/2025-09-01-let-unwrap.mdx b/_blogposts/2025-09-01-let-unwrap.mdx new file mode 100644 index 000000000..7ab27af0d --- /dev/null +++ b/_blogposts/2025-09-01-let-unwrap.mdx @@ -0,0 +1,291 @@ +--- +author: rescript-team +date: "2025-10-14" +previewImg: /static/blog/rescript-12-let-unwrap.jpg +badge: roadmap +title: "Experimental Feature: let?" +description: | + 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. + +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 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) { + | 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). + +The only alternative in ReScript was always to use some specialized functions: + +```res +let getUser = id => + fetchUser(id) + ->Result.flatMapOkAsync(user => Promise.resolve(user->decodeUser)) + ->Result.flatMapOkAsync(decodedUser => ensureUserActive(decodedUser)) +``` + +**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. Furthermore, those functions result in two more function calls. + +```js +function getUser(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 getUser(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, + }; + } +} +``` + +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: + + + +```rescript +let getUser = async (id) => { + let? Ok(user) = await fetchUser(id) + let? Ok(decodedUser) = decodeUser(user) + let? Ok() = await ensureUserActive(decodedUser) + Ok(decodedUser) +} +``` + +```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; + 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. 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. + +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(...)`. + +```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. +``` + +**Note**: `result` and `option` types cannot be mixed in a `let?` function! + +### A full example with error handling + + + +```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: + +```json +{ + "experimental-features": { + "letUnwrap": true + } +} +``` + +If you still use the legacy build system, enable it with the compiler flag `-enable-experimental`: + +```json +{ + "compiler-flags": ["-enable-experimental", "LetUnwrap"] +} +``` + +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! 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 cd89fed8b..cba015e7d 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/public/static/blog/rescript-12-let-unwrap.jpg b/public/static/blog/rescript-12-let-unwrap.jpg new file mode 100644 index 000000000..537924d14 Binary files /dev/null and b/public/static/blog/rescript-12-let-unwrap.jpg differ 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 8ea95f911..9dba26182 100644 --- a/src/components/CodeExample.res +++ b/src/components/CodeExample.res @@ -157,6 +157,8 @@ module Toggle = { label: option, lang: option, code: string, + experiments: option, + version: option, } @react.component @@ -242,14 +244,24 @@ module Toggle = { | Some(tab) => let playgroundLinkButton = tab->isReScript - ? - // 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 c8afb0589..b70d39446 100644 --- a/src/components/CodeExample.resi +++ b/src/components/CodeExample.resi @@ -13,6 +13,8 @@ module Toggle: { label: option, lang: option, code: string, + experiments: option, + version: option, } @react.component diff --git a/src/components/Markdown.res b/src/components/Markdown.res index 4b4a03cdc..3b4590a49 100644 --- a/src/components/Markdown.res +++ b/src/components/Markdown.res @@ -286,8 +286,16 @@ module CodeTab = { } return element.props.metastring; }") + + let preludeRegex = RegExp.fromString(`#/prelude="([^"]*)"/`, ~flags="g") + @react.component - let make = (~children: Mdx.MdxChildren.t, ~labels: array=[]) => { + let make = ( + ~children: Mdx.MdxChildren.t, + ~labels: array=[], + ~experiments: option=?, + ~version: option=?, + ) => { let mdxElements = switch Mdx.MdxChildren.classify(children) { | Array(mdxElements) => mdxElements | Element(el) => [el] @@ -302,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) @@ -309,12 +323,21 @@ 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, code, 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 9abe49e38..5de22df88 100644 --- a/src/components/Markdown.resi +++ b/src/components/Markdown.resi @@ -99,7 +99,12 @@ 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=?, + ~version: string=?, + ) => React.element } module Blockquote: { diff --git a/src/components/MarkdownComponents.res b/src/components/MarkdownComponents.res index 784bc756f..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> => React.element, + codeTab?: CodeTab.props, string, string> => React.element, /* Common markdown elements */ p?: P.props => React.element, li?: Li.props => React.element,