Skip to content

Migrate to Core #876

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ This is the official documentation platform for the [ReScript](https://rescript-

## System Requirements

- `node@18` or higher
- `node@20` or higher
- `npm@10` or higher

## Setup
@@ -28,10 +28,7 @@ npm i
# Initial build
npx rescript

# Only needed for initial clone (or content H2 changes)
npm run update-index

# Build the index data
# Build the index data. Only needed for initial clone (or content H2 changes)
npm run update-index

# In a new tab
46 changes: 24 additions & 22 deletions compilers/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions compilers/package.json
Original file line number Diff line number Diff line change
@@ -5,12 +5,12 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"@rescript/core": "^0.6.0",
"@rescript/core": "^1.3.0",
"@rescript/react": "^0.12.0",
"rescript-1000": "npm:rescript@10.0.0",
"rescript-1010": "npm:rescript@10.1.0",
"rescript-1100": "npm:rescript@11.0.0",
"rescript-1110": "npm:rescript@11.1.0-rc.8",
"rescript-1110": "npm:rescript@11.1.0",
"rescript-820": "npm:bs-platform@8.2.0",
"rescript-902": "npm:bs-platform@9.0.2",
"rescript-912": "npm:rescript@9.1.2"
2 changes: 1 addition & 1 deletion misc_docs/syntax/decorator_module.mdx
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ var root = Path.dirname("/User/github");
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
@module({from: "./myJson.json", with: {type_: "json", \"some-exotic-identifier": "someValue"}})
external myJson: Js.Json.t = "default"
external myJson: JSON.t = "default"
Console.log(myJson)
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
"author": "Patrick Ecker <ryyppy@users.noreply.github.com>",
"license": "MIT",
"engines": {
"node": ">=18"
"node": ">=20"
},
"type": "module",
"postcss": {
12 changes: 6 additions & 6 deletions pages/docs/manual/latest/async-await.mdx
Original file line number Diff line number Diff line change
@@ -130,15 +130,15 @@ You may use `try / catch` or `switch` to handle exceptions during async executio
```res
// For simulation purposes
let authenticate = async () => {
raise(Js.Exn.raiseRangeError("Authentication failed."))
raise(Exn.raiseRangeError("Authentication failed."))
}
let checkAuth = async () => {
try {
await authenticate()
} catch {
| Js.Exn.Error(e) =>
switch Js.Exn.message(e) {
| Exn.Error(e) =>
switch Exn.message(e) {
| Some(msg) => Console.log("JS error thrown: " ++ msg)
| None => Console.log("Some other exception has been thrown")
}
@@ -152,14 +152,14 @@ You may unify error and value handling in a single switch as well:

```res
let authenticate = async () => {
raise(Js.Exn.raiseRangeError("Authentication failed."))
raise(Exn.raiseRangeError("Authentication failed."))
}
let checkAuth = async () => {
switch await authenticate() {
| _ => Console.log("ok")
| exception Js.Exn.Error(e) =>
switch Js.Exn.message(e) {
| exception Exn.Error(e) =>
switch Exn.message(e) {
| Some(msg) => Console.log("JS error thrown: " ++ msg)
| None => Console.log("Some other exception has been thrown")
}
14 changes: 7 additions & 7 deletions pages/docs/manual/latest/exception.mdx
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ throw {

## Catching JS Exceptions

To distinguish between JavaScript exceptions and ReScript exceptions, ReScript namespaces JS exceptions under the `Js.Exn.Error(payload)` variant. To catch an exception thrown from the JS side:
To distinguish between JavaScript exceptions and ReScript exceptions, ReScript namespaces JS exceptions under the `Exn.Error(payload)` variant. To catch an exception thrown from the JS side:


Throw an exception from JS:
@@ -158,25 +158,25 @@ try {
// call the external method
someJSFunctionThatThrows()
} catch {
| Js.Exn.Error(obj) =>
switch Js.Exn.message(obj) {
| Exn.Error(obj) =>
switch Exn.message(obj) {
| Some(m) => Console.log("Caught a JS exception! Message: " ++ m)
| None => ()
}
}
```

The `obj` here is of type `Js.Exn.t`, intentionally opaque to disallow illegal operations. To operate on `obj`, do like the code above by using the standard library's [`Js.Exn`](api/js/exn) module's helpers.
The `obj` here is of type `Exn.t`, intentionally opaque to disallow illegal operations. To operate on `obj`, do like the code above by using the standard library's [`Exn`](api/js/exn) module's helpers.

## Raise a JS Exception

`raise(MyException)` raises a ReScript exception. To raise a JavaScript exception (whatever your purpose is), use `Js.Exn.raiseError`:
`raise(MyException)` raises a ReScript exception. To raise a JavaScript exception (whatever your purpose is), use `Exn.raiseError`:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let myTest = () => {
Js.Exn.raiseError("Hello!")
Exn.raiseError("Hello!")
}
```
```js
@@ -257,7 +257,7 @@ try {
} catch {
| Not_found => ... // catch a ReScript exception
| Invalid_argument(_) => ... // catch a second ReScript exception
| Js.Exn.Error(obj) => ... // catch the JS exception
| Exn.Error(obj) => ... // catch the JS exception
}
```

8 changes: 4 additions & 4 deletions pages/docs/manual/latest/import-from-export-to-js.mdx
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ var studentName = Student;
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
@module({from: "./myJson.json", with: {type_: "json", \"some-exotic-identifier": "someValue"}})
external myJson: Js.Json.t = "default"
external myJson: JSON.t = "default"
Console.log(myJson)
```
@@ -130,7 +130,7 @@ Also notice `type_`. Since `type` is a reserved keyword in ReScript, you can use
Leveraging JavaScript's [dynamic `import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) to reduce bundle size and lazy load code as needed is easy in ReScript. It's also a little bit more convenient than in regular JavaScript because you don't need to keep track of file paths manually with ReScript's module system.

### Dynamically Importing Parts of a Module
Use the `Js.import` function to dynamically import a specific part of a module. Put whatever `let` binding you want to import in there, and you'll get a `promise` back resolving to that specific binding.
Use the `import` function to dynamically import a specific part of a module. Put whatever `let` binding you want to import in there, and you'll get a `promise` back resolving to that specific binding.

Let's look at an example. Imagine the following file `MathUtils.res`:

@@ -145,7 +145,7 @@ Now let's dynamically import the add function in another module, e.g. `App.res`:
```rescript
// App.res
let main = async () => {
let add = await Js.import(MathUtils.add)
let add = await import(MathUtils.add)
let onePlusOne = add(1, 1)
Console.log(onePlusOne)
@@ -164,7 +164,7 @@ async function main() {
</CodeTab>

### Dynamically Importing an Entire Module
The syntax for importing a whole module looks a little different, since we are operating on the module syntax level; instead of using `Js.import`, you may simply `await` the module itself:
The syntax for importing a whole module looks a little different, since we are operating on the module syntax level; instead of using `import`, you may simply `await` the module itself:
<CodeTab labels={["ReScript", "JS Output (Module)"]}>
```rescript
// App.res
4 changes: 2 additions & 2 deletions pages/docs/manual/latest/primitive-types.mdx
Original file line number Diff line number Diff line change
@@ -180,7 +180,7 @@ As `bigint` is a different data type than `int`, it's necessary to open the corr
<CodeTab labels={["ReScript", "JS Output"]}>

```res example
open! Js.BigInt
open! BigInt
let a = 9007199254740991n + 9007199254740991n
let b = 2n ** 2n
@@ -198,7 +198,7 @@ It also supports all the bitwise operations, except unsigned shift right (`>>>`)
<CodeTab labels={["ReScript", "JS Output"]}>

```res example
open! Js.BigInt
open! BigInt
let a = land(1n, 1n)
let b = lor(1n, 1n)
2 changes: 1 addition & 1 deletion pages/docs/react/latest/beyond-jsx.mdx
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ type props<'className, 'children, 'ref> = {
let make = (
{?className, children, _}: props<'className, 'children, ReactRef.currentDomRef>,
ref: Js.Nullable.t<ReactRef.currentDomRef>,
ref: Nullable.t<ReactRef.currentDomRef>,
) =>
make(~className, ~children, ~ref, ())
```
12 changes: 5 additions & 7 deletions pages/docs/react/latest/forwarding-refs.mdx
Original file line number Diff line number Diff line change
@@ -56,12 +56,10 @@ module FancyInput = {
@react.component
let make = () => {
let input = React.useRef(Js.Nullable.null)
let input = React.useRef(Nullable.null)
let focusInput = () =>
input.current
->Js.Nullable.toOption
->Belt.Option.forEach(input => input->focus)
input.current->Nullable.forEach(input => input->focus)
let onClick = _ => focusInput()
@@ -96,7 +94,7 @@ module FancyInput = {
<input
type_="text"
?className
ref=?{Js.Nullable.toOption(ref)->Belt.Option.map(ReactDOM.Ref.domRef)}
ref=?{Nullable.toOption(ref)->Option.map(ReactDOM.Ref.domRef)}
/>
children
</div>
@@ -107,10 +105,10 @@ module FancyInput = {
@react.component
let make = () => {
let input = React.useRef(Js.Nullable.null)
let input = React.useRef(Nullable.null)
let focusInput = () =>
input.current->Js.Nullable.toOption->Belt.Option.forEach(input => input->focus)
input.current->Nullable.forEach(input => input->focus)
let onClick = _ => focusInput()
4 changes: 2 additions & 2 deletions pages/docs/react/latest/hooks-reducer.mdx
Original file line number Diff line number Diff line change
@@ -125,13 +125,13 @@ type state = {
let reducer = (state, action) =>
switch action {
| AddTodo(content) =>
let todos = Js.Array2.concat(
let todos = Array.concat(
state.todos,
[{id: state.nextId, content: content, completed: false}],
)
{todos: todos, nextId: state.nextId + 1}
| RemoveTodo(id) =>
let todos = Js.Array2.filter(state.todos, todo => todo.id !== id)
let todos = Array.filter(state.todos, todo => todo.id !== id)
{...state, todos: todos}
| ToggleTodo(id) =>
let todos = Belt.Array.map(state.todos, todo =>
33 changes: 13 additions & 20 deletions pages/docs/react/latest/hooks-ref.mdx
Original file line number Diff line number Diff line change
@@ -56,12 +56,10 @@ More infos on direct DOM manipulation can be found in the [Refs and the DOM](./r
@react.component
let make = () => {
let inputEl = React.useRef(Js.Nullable.null)
let inputEl = React.useRef(Nullable.null)
let onClick = _ => {
inputEl.current
->Js.Nullable.toOption
->Belt.Option.forEach(input => input->focus)
inputEl.current->Nullable.forEach(input => input->focus)
}
<>
@@ -75,13 +73,12 @@ let make = () => {
function TextInputWithFocusButton(Props) {
var inputEl = React.useRef(null);
var onClick = function (param) {
return Belt_Option.forEach(Caml_option.nullable_to_opt(inputEl.current), (function (input) {
input.focus();

}));
Core__Nullable.forEach(inputEl.current, (function (input) {
input.focus();
}));
};
return React.createElement(React.Fragment, undefined, React.createElement("input", {
ref: inputEl,
return React.createElement(React.Fragment, {}, React.createElement("input", {
ref: Caml_option.some(inputEl),
type: "text"
}), React.createElement("button", {
onClick: onClick
@@ -104,15 +101,13 @@ Reusing the example from our [Refs and the DOM](./refs-and-the-dom#callback-refs
@react.component
let make = () => {
let textInput = React.useRef(Js.Nullable.null)
let textInput = React.useRef(Nullable.null)
let setTextInputRef = element => {
textInput.current = element;
}
let focusTextInput = _ => {
textInput.current
->Js.Nullable.toOption
->Belt.Option.forEach(input => input->focus)
textInput.current->Nullable.forEach(input => input->focus)
}
<div>
@@ -129,16 +124,14 @@ function CustomTextInput(Props) {
var textInput = React.useRef(null);
var setTextInputRef = function (element) {
textInput.current = element;

};
var focusTextInput = function (param) {
return Belt_Option.forEach(Caml_option.nullable_to_opt(textInput.current), (function (input) {
input.focus();

}));
Core__Nullable.forEach(textInput.current, (function (input) {
input.focus();
}));
};
return React.createElement("div", undefined, React.createElement("input", {
ref: setTextInputRef,
ref: Caml_option.some(setTextInputRef),
type: "text"
}), React.createElement("input", {
type: "button",
4 changes: 2 additions & 2 deletions pages/docs/react/latest/lazy-components.mdx
Original file line number Diff line number Diff line change
@@ -33,12 +33,12 @@ Now we can dynamically import the `<Title/>` component by passing the result of
```rescript
// SomeOtherFile.res
module LazyTitle = {
let make = React.lazy_(() => Js.import(Title.make))
let make = React.lazy_(() => import(Title.make))
}
let titleJsx = <LazyTitle text="Hello!" />
```

That's all the code we need! The new `<LazyTitle />` component behaves exactly the same as the wrapped `<Title />` component, but will be lazy loaded via React's built-in lazy mechanism.

> You can read more about `Js.import` and dynamic import in ReScript in [this part of the documentation](/docs/manual/latest/import-from-export-to-js#dynamic-import).
> You can read more about `import` and dynamic import in ReScript in [this part of the documentation](/docs/manual/latest/import-from-export-to-js#dynamic-import).
8 changes: 4 additions & 4 deletions pages/docs/react/latest/migrate-react.mdx
Original file line number Diff line number Diff line change
@@ -148,7 +148,7 @@ module FancyInput = {
<input
type_="text"
?className
ref=?{ref_->Js.Nullable.toOption->Belt.Option.map(ReactDOM.Ref.domRef)}
ref=?{ref_->Nullable.toOption->Option.map(ReactDOM.Ref.domRef)}
/>
children
</div>
@@ -157,7 +157,7 @@ module FancyInput = {
@react.component
let make = () => {
let input = React.useRef(Js.Nullable.null)
let input = React.useRef(Nullable.null)
<div>
<FancyInput ref=input> // prop
@@ -181,7 +181,7 @@ module FancyInput = {
<input
type_="text"
?className
ref=?{ref->Js.Nullable.toOption->Belt.Option.map(ReactDOM.Ref.domRef)}
ref=?{ref->Nullable.toOption->Option.map(ReactDOM.Ref.domRef)}
/>
children
</div>
@@ -190,7 +190,7 @@ module FancyInput = {
@react.component
let make = () => {
let input = React.useRef(Js.Nullable.null)
let input = React.useRef(Nullable.null)
<div>
<FancyInput ref=input>
63 changes: 33 additions & 30 deletions pages/docs/react/latest/refs-and-the-dom.mdx
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ let value = myRef.current
```

The value of the ref differs depending on the type of the node:
- When the ref attribute is used on an HTML element, the ref passed via `ReactDOM.Ref.domRef` receives the underlying DOM element as its current property (type of `React.ref<Js.Nullable.t<Dom.element>>`)
- When the ref attribute is used on an HTML element, the ref passed via `ReactDOM.Ref.domRef` receives the underlying DOM element as its current property (type of `React.ref<Nullable.t<Dom.element>>`)
- In case of interop, when the ref attribute is used on a custom class component (based on JS classes), the ref object receives the mounted instance of the component as its current (not discussed in this document).
- **You may not use the ref attribute on component functions** because they don’t have instances (we don't expose JS classes in ReScript).

@@ -83,12 +83,12 @@ This code uses a `React.ref` to store a reference to an `input` DOM node to put
@react.component
let make = () => {
let textInput = React.useRef(Js.Nullable.null)
let textInput = React.useRef(Nullable.null)
let focusInput = () =>
switch textInput.current->Js.Nullable.toOption {
| Some(dom) => dom->focus
| None => ()
switch textInput.current {
| Value(dom) => dom->focus
| Null | Undefined => ()
}
let onClick = _ => focusInput()
@@ -103,30 +103,37 @@ let make = () => {
```js
function CustomTextInput(Props) {
var textInput = React.useRef(null);
var onClick = function (param) {
var focusInput = function () {
var dom = textInput.current;
if (!(dom == null)) {
dom.focus();
if (dom === null || dom === undefined) {
return ;
}

dom.focus();
};
return React.createElement("div", undefined, React.createElement("input", {
ref: textInput,
type: "text"
}), React.createElement("input", {
type: "button",
value: "Focus the text input",
onClick: onClick
}));
var onClick = function (param) {
focusInput();
};
return JsxRuntime.jsxs("div", {
children: [
JsxRuntime.jsx("input", {
ref: Caml_option.some(textInput),
type: "text"
}),
JsxRuntime.jsx("input", {
type: "button",
value: "Focus the text input",
onClick: onClick
})
]
});
}
```

</CodeTab>

A few things happened here, so let's break them down:

- We initialize our `textInput` ref as a `Js.Nullable.null`
- We initialize our `textInput` ref as a `Nullable.null`
- We register our `textInput` ref in our `<input>` element with `ReactDOM.Ref.domRef(textInput)`
- In our `focusInput` function, we need to first verify that our DOM element is set, and then use the `focus` binding to set the focus

@@ -148,7 +155,7 @@ module MyComp = {
@react.component
let make = () => {
let textInput = React.useRef(Js.Nullable.null)
let textInput = React.useRef(Nullable.null)
// This will **not** work
<MyComp ref={ReactDOM.Ref.domRef(textInput)} />
@@ -193,15 +200,13 @@ The example below implements a common pattern: using the ref callback to store a
@react.component
let make = () => {
let textInput = React.useRef(Js.Nullable.null)
let textInput = React.useRef(Nullable.null)
let setTextInputRef = element => {
textInput.current = element;
}
let focusTextInput = _ => {
textInput.current
->Js.Nullable.toOption
->Belt.Option.forEach(input => input->focus)
textInput.current->Nullable.forEach(input => input->focus)
}
<div>
@@ -218,16 +223,14 @@ function CustomTextInput(Props) {
var textInput = React.useRef(null);
var setTextInputRef = function (element) {
textInput.current = element;

};
var focusTextInput = function (param) {
return Belt_Option.forEach(Caml_option.nullable_to_opt(textInput.current), (function (input) {
input.focus();

}));
Core__Nullable.forEach(textInput.current, (function (input) {
input.focus();
}));
};
return React.createElement("div", undefined, React.createElement("input", {
ref: setTextInputRef,
ref: Caml_option.some(setTextInputRef),
type: "text"
}), React.createElement("input", {
type: "button",
@@ -261,7 +264,7 @@ module CustomTextInput = {
@react.component
let make = () => {
let textInput = React.useRef(Js.Nullable.null)
let textInput = React.useRef(Nullable.null)
let setInputRef = element => { textInput.current = element}
<CustomTextInput setInputRef/>
156 changes: 80 additions & 76 deletions scripts/gendocs.res
Original file line number Diff line number Diff line change
@@ -18,36 +18,36 @@ node scripts/gendocs.mjs path/to/rescript-compiler latest true
open Node
module Docgen = RescriptTools.Docgen

let args = Process.argv->Js.Array2.sliceFrom(2)
let args = Process.argv->Array.sliceToEnd(~start=2)
let dirname =
url
->URL.fileURLToPath
->Path.dirname

let compilerLibPath = switch args->Belt.Array.get(0) {
let compilerLibPath = switch args->Array.get(0) {
| Some(path) => Path.join([path, "jscomp", "others"])
| None => failwith("First argument should be path to rescript-compiler repo")
}

let corePath = switch args->Belt.Array.get(1) {
let corePath = switch args->Array.get(1) {
| Some(path) => path
| _ => failwith("Second argument should be path to rescript-core/src/RescriptCore.res")
}

let version = switch args->Belt.Array.get(2) {
let version = switch args->Array.get(2) {
| Some(version) => version
| None => failwith("Third argument should be a version, `latest`, `v10`")
}

let forceReWrite = switch args->Belt.Array.get(3) {
let forceReWrite = switch args->Array.get(3) {
| Some("true") => true
| _ => false
}

let dirVersion = Path.join([dirname, "..", "data", "api", version])

if Fs.existsSync(dirVersion) {
Js.Console.error(`Directory ${dirVersion} already exists`)
Console.error(`Directory ${dirVersion} already exists`)
if !forceReWrite {
Process.exit(1)
}
@@ -76,36 +76,36 @@ type section = {

let env = Process.env

let docsDecoded = entryPointFiles->Js.Array2.map(libFile => {
let docsDecoded = entryPointFiles->Array.map(libFile => {
let entryPointFile = Path.join2(compilerLibPath, libFile)

Js.Dict.set(env, "FROM_COMPILER", "true")
Dict.set(env, "FROM_COMPILER", "true")

let output =
ChildProcess.execSync(
`./node_modules/.bin/rescript-tools doc ${entryPointFile}`,
)->Buffer.toString

output
->Js.Json.parseExn
->JSON.parseExn
->Docgen.decodeFromJson
})

let coreDocs = {
Js.Dict.set(env, "FROM_COMPILER", "false")
Dict.set(env, "FROM_COMPILER", "false")

let output =
ChildProcess.execSync(`./node_modules/.bin/rescript-tools doc ${corePath}`)->Buffer.toString

output
->Js.Json.parseExn
->JSON.parseExn
->Docgen.decodeFromJson
}

let docsDecoded = Js.Array2.concat(docsDecoded, [coreDocs])
let docsDecoded = Array.concat(docsDecoded, [coreDocs])

let docs = docsDecoded->Js.Array2.map(doc => {
let topLevelItems = doc.items->Belt.Array.keepMap(item =>
let docs = docsDecoded->Array.map(doc => {
let topLevelItems = doc.items->Array.filterMap(item =>
switch item {
| Value(_) as item | Type(_) as item => item->Some
| _ => None
@@ -118,122 +118,126 @@ let docs = docsDecoded->Js.Array2.map(doc => {
Module({id, items, name, docstrings}) | ModuleAlias({id, items, name, docstrings}),
...rest,
} =>
if Js.Array2.includes(hiddenModules, id) {
if Array.includes(hiddenModules, id) {
getModules(rest, moduleNames)
} else {
let id = Js.String2.startsWith(id, "RescriptCore")
? Js.String2.replace(id, "RescriptCore", "Core")
let id = String.startsWith(id, "RescriptCore")
? String.replace(id, "RescriptCore", "Core")
: id
getModules(
list{...rest, ...Belt.List.fromArray(items)},
list{...rest, ...List.fromArray(items)},
list{{id, items, name, docstrings}, ...moduleNames},
)
}
| list{Type(_) | Value(_), ...rest} => getModules(rest, moduleNames)
| list{} => moduleNames
}

// let id = Js.String2.startsWith(doc.name, "RescriptCore") ? "Core" : doc.name
let id = Js.String2.startsWith(doc.name, "RescriptCore")
? Js.String2.replace(doc.name, "RescriptCore", "Core")
let id = String.startsWith(doc.name, "RescriptCore")
? String.replace(doc.name, "RescriptCore", "Core")
: doc.name

let top = {id, name: id, docstrings: doc.docstrings, items: topLevelItems}
let submodules = getModules(doc.items->Belt.List.fromArray, list{})->Belt.List.toArray
let result = [top]->Js.Array2.concat(submodules)
let submodules = getModules(doc.items->List.fromArray, list{})->List.toArray
let result = [top]->Array.concat(submodules)

(id, result)
})

let allModules = {
open Js.Json
open JSON
let encodeItem = (docItem: Docgen.item) => {
switch docItem {
| Value({id, name, docstrings, signature, ?deprecated}) => {
let id = Js.String2.startsWith(id, "RescriptCore")
? Js.String2.replace(id, "RescriptCore", "Core")
let id = String.startsWith(id, "RescriptCore")
? String.replace(id, "RescriptCore", "Core")
: id
let dict = Js.Dict.fromArray(
let dict = Dict.fromArray(
[
("id", id->string),
("kind", "value"->string),
("name", name->string),
("docstrings", docstrings->stringArray),
("signature", signature->string),
]->Js.Array2.concat(
("id", id->String),
("kind", "value"->String),
("name", name->String),
(
"docstrings",
docstrings
->Array.map(s => s->String)
->Array,
),
("signature", signature->String),
]->Array.concat(
switch deprecated {
| Some(v) => [("deprecated", v->string)]
| Some(v) => [("deprecated", v->String)]
| None => []
},
),
)
dict->object_->Some
dict->Object->Some
}

| Type({id, name, docstrings, signature, ?deprecated}) =>
let id = Js.String2.startsWith(id, "RescriptCore")
? Js.String2.replace(id, "RescriptCore", "Core")
let id = String.startsWith(id, "RescriptCore")
? String.replace(id, "RescriptCore", "Core")
: id
let dict = Js.Dict.fromArray(
let dict = Dict.fromArray(
[
("id", id->string),
("kind", "type"->string),
("name", name->string),
("docstrings", docstrings->stringArray),
("signature", signature->string),
]->Js.Array2.concat(
("id", id->String),
("kind", "type"->String),
("name", name->String),
("docstrings", docstrings->Array.map(s => s->String)->Array),
("signature", signature->String),
]->Array.concat(
switch deprecated {
| Some(v) => [("deprecated", v->string)]
| Some(v) => [("deprecated", v->String)]
| None => []
},
),
)
object_(dict)->Some
Object(dict)->Some

| _ => None
}
}

docs->Js.Array2.map(((topLevelName, modules)) => {
docs->Array.map(((topLevelName, modules)) => {
let submodules =
modules
->Js.Array2.map(mod => {
->Array.map(mod => {
let items =
mod.items
->Belt.Array.keepMap(item => encodeItem(item))
->array
->Array.filterMap(item => encodeItem(item))
->Array

let id = Js.String2.startsWith(mod.id, "RescriptCore") ? "Core" : mod.id
let id = String.startsWith(mod.id, "RescriptCore") ? "Core" : mod.id

let rest = Js.Dict.fromArray([
("id", id->string),
("name", mod.name->string),
("docstrings", mod.docstrings->stringArray),
let rest = Dict.fromArray([
("id", id->String),
("name", mod.name->String),
("docstrings", mod.docstrings->Array.map(s => s->String)->Array),
("items", items),
])
(
mod.id
->Js.String2.split(".")
->Js.Array2.joinWith("/")
->Js.String2.toLowerCase,
rest->object_,
->String.split(".")
->Array.join("/")
->String.toLowerCase,
rest->Object,
)
})
->Js.Dict.fromArray
->Dict.fromArray

(topLevelName, submodules)
})
}

let () = {
allModules->Js.Array2.forEach(((topLevelName, mod)) => {
let json = Js.Json.object_(mod)
allModules->Array.forEach(((topLevelName, mod)) => {
let json = JSON.Object(mod)

let topLevelName = Js.String2.startsWith(topLevelName, "RescriptCore") ? "Core" : topLevelName
let topLevelName = String.startsWith(topLevelName, "RescriptCore") ? "Core" : topLevelName

Fs.writeFileSync(
Path.join([dirVersion, `${topLevelName->Js.String2.toLowerCase}.json`]),
json->Js.Json.stringifyWithSpace(2),
Path.join([dirVersion, `${topLevelName->String.toLowerCase}.json`]),
json->JSON.stringify(~space=2),
)
})
}
@@ -247,20 +251,20 @@ type rec node = {
// Generate TOC modules
let () = {
let joinPath = (~path: array<string>, ~name: string) => {
Js.Array2.concat(path, [name])->Js.Array2.map(path => path->Js.String2.toLowerCase)
Array.concat(path, [name])->Array.map(path => path->String.toLowerCase)
}
let rec getModules = (lst: list<Docgen.item>, moduleNames, path) => {
switch lst {
| list{Module({id, items, name}) | ModuleAlias({id, items, name}), ...rest} =>
if Js.Array2.includes(hiddenModules, id) {
if Array.includes(hiddenModules, id) {
getModules(rest, moduleNames, path)
} else {
let itemsList = items->Belt.List.fromArray
let itemsList = items->List.fromArray
let children = getModules(itemsList, [], joinPath(~path, ~name))

getModules(
rest,
Js.Array2.concat([{name, path: joinPath(~path, ~name), children}], moduleNames),
Array.concat([{name, path: joinPath(~path, ~name), children}], moduleNames),
path,
)
}
@@ -269,16 +273,16 @@ let () = {
}
}

let tocTree = docsDecoded->Js.Array2.map(({name, items}) => {
let name = Js.String2.startsWith(name, "RescriptCore") ? "Core" : name
let path = name->Js.String2.toLowerCase
let tocTree = docsDecoded->Array.map(({name, items}) => {
let name = String.startsWith(name, "RescriptCore") ? "Core" : name
let path = name->String.toLowerCase
(
path,
{
name,
path: [path],
children: items
->Belt.List.fromArray
->List.fromArray
->getModules([], [path]),
},
)
@@ -287,8 +291,8 @@ let () = {
Fs.writeFileSync(
Path.join([dirVersion, "toc_tree.json"]),
tocTree
->Js.Dict.fromArray
->Js.Json.stringifyAny
->Belt.Option.getExn,
->Dict.fromArray
->JSON.stringifyAny
->Option.getExn,
)
}
2 changes: 1 addition & 1 deletion scripts/generate_feed.res
Original file line number Diff line number Diff line change
@@ -3,4 +3,4 @@ let content = {
getLatest()->toXmlString
}

Js.log(content)
Console.log(content)
119 changes: 59 additions & 60 deletions src/ApiDocs.res
Original file line number Diff line number Diff line change
@@ -11,13 +11,13 @@ type field = {
docstrings: array<string>,
signature: string,
optional: bool,
deprecated: Js.Null.t<string>,
deprecated: Null.t<string>,
}
type constructor = {
name: string,
docstrings: array<string>,
signature: string,
deprecated: Js.Null.t<string>,
deprecated: Null.t<string>,
}

type detail =
@@ -30,37 +30,37 @@ type item =
docstrings: array<string>,
signature: string,
name: string,
deprecated: Js.Null.t<string>,
deprecated: Null.t<string>,
})
| Type({
id: string,
docstrings: array<string>,
signature: string,
name: string,
deprecated: Js.Null.t<string>,
detail: Js.Null.t<detail>,
deprecated: Null.t<string>,
detail: Null.t<detail>,
})

module RightSidebar = {
@react.component
let make = (~items: array<item>) => {
items
->Js.Array2.map(item => {
->Array.map(item => {
switch item {
| Value({name, deprecated}) as kind | Type({name, deprecated}) as kind =>
let (icon, textColor, bgColor, href) = switch kind {
| Type(_) => ("t", "text-fire-30", "bg-fire-5", `#type-${name}`)
| Value(_) => ("v", "text-sky-30", "bg-sky-5", `#value-${name}`)
}
let deprecatedIcon = switch deprecated->Js.Null.toOption {
let deprecatedIcon = switch deprecated->Null.toOption {
| Some(_) =>
<div
className={`bg-orange-100 min-w-[20px] min-h-[20px] w-5 h-5 mr-3 flex justify-center items-center rounded-xl ml-auto`}>
<span className={"text-[10px] text-orange-400"}> {"D"->React.string} </span>
</div>->Some
| None => None
}
let title = `${Belt.Option.isSome(deprecatedIcon) ? "Deprecated " : ""}` ++ name
let title = `${Option.isSome(deprecatedIcon) ? "Deprecated " : ""}` ++ name
let result =
<li className="my-3">
<a
@@ -94,13 +94,13 @@ module SidebarTree = {

let moduleRoute =
Webapi.URL.make("file://" ++ router.asPath).pathname
->Js.String2.replace("/docs/manual/latest/api/", "")
->Js.String2.split("/")
->String.replace("/docs/manual/latest/api/", "")
->String.split("/")

let summaryClassName = "truncate py-1 md:h-auto tracking-tight text-gray-60 font-medium text-14 rounded-sm hover:bg-gray-20 hover:-ml-2 hover:py-1 hover:pl-2 "
let classNameActive = " bg-fire-5 text-red-500 -ml-2 pl-2 font-medium hover:bg-fire-70"

let subMenu = switch items->Js.Array2.length > 0 {
let subMenu = switch items->Array.length > 0 {
| true =>
<div className={"xl:hidden ml-5"}>
<ul className={"list-none py-0.5"}>
@@ -111,23 +111,22 @@ module SidebarTree = {
}

let rec renderNode = node => {
let isCurrentRoute =
Js.Array2.joinWith(moduleRoute, "/") === Js.Array2.joinWith(node.path, "/")
let isCurrentRoute = Array.join(moduleRoute, "/") === Array.join(node.path, "/")

let classNameActive = isCurrentRoute ? classNameActive : ""

let hasChildren = node.children->Js.Array2.length > 0
let href = node.path->Js.Array2.joinWith("/")
let hasChildren = node.children->Array.length > 0
let href = node.path->Array.join("/")

let tocModule = isCurrentRoute ? subMenu : React.null

switch hasChildren {
| true =>
let open_ =
node.path->Js.Array2.joinWith("/") ===
node.path->Array.join("/") ===
moduleRoute
->Js.Array2.slice(~start=0, ~end_=Js.Array2.length(moduleRoute) - 1)
->Js.Array2.joinWith("/")
->Array.slice(~start=0, ~end=Array.length(moduleRoute) - 1)
->Array.join("/")

<details key={node.name} open_>
<summary className={summaryClassName ++ classNameActive}>
@@ -139,7 +138,7 @@ module SidebarTree = {
{if hasChildren {
<ul className={"ml-5"}>
{node.children
->Js.Array2.map(renderNode)
->Array.map(renderNode)
->React.array}
</ul>
} else {
@@ -174,8 +173,8 @@ module SidebarTree = {

let targetUrl =
"/" ++
(Js.Array2.joinWith(url.base, "/") ++
("/" ++ (version ++ ("/" ++ Js.Array2.joinWith(url.pagepath, "/")))))
(Array.join(url.base, "/") ++
("/" ++ (version ++ ("/" ++ Array.join(url.pagepath, "/")))))
router->Next.Router.push(targetUrl)
}
let version = switch version {
@@ -216,16 +215,16 @@ module SidebarTree = {
</div>
<Next.Link
className={"block " ++
summaryClassName ++ (moduleRoute->Js.Array2.length == 1 ? classNameActive : "")}
href={node.path->Js.Array2.joinWith("/")}>
summaryClassName ++ (moduleRoute->Array.length == 1 ? classNameActive : "")}
href={node.path->Array.join("/")}>
{node.name->React.string}
</Next.Link>
{moduleRoute->Js.Array2.length === 1 ? subMenu : React.null}
{moduleRoute->Array.length === 1 ? subMenu : React.null}
</div>
<div className="hl-overline text-gray-80 mt-5 mb-2"> {"submodules"->React.string} </div>
{node.children
->Js.Array2.sortInPlaceWith((v1, v2) => v1.name > v2.name ? 1 : -1)
->Js.Array2.map(renderNode)
->Array.toSorted((v1, v2) => String.compare(v1.name, v2.name))
->Array.map(renderNode)
->React.array}
</aside>
</div>
@@ -235,7 +234,7 @@ module SidebarTree = {
type module_ = {
id: string,
docstrings: array<string>,
deprecated: Js.Null.t<string>,
deprecated: Null.t<string>,
name: string,
items: array<item>,
}
@@ -262,7 +261,7 @@ module MarkdownStylize = {
module DeprecatedMessage = {
@react.component
let make = (~deprecated) => {
switch deprecated->Js.Null.toOption {
switch deprecated->Null.toOption {
| Some(content) =>
<Markdown.Warn>
<h4 className={"hl-4 mb-2"}> {"Deprecated"->React.string} </h4>
@@ -279,10 +278,10 @@ module DocstringsStylize = {
let rehypePlugins =
[Rehype.WithOptions([Plugin(Rehype.slug), SlugOption({prefix: slugPrefix ++ "-"})])]->Some

let content = switch docstrings->Js.Array2.length > 1 {
| true => docstrings->Js.Array2.sliceFrom(1)
let content = switch docstrings->Array.length > 1 {
| true => docstrings->Array.sliceToEnd(~start=1)
| false => docstrings
}->Js.Array2.joinWith("\n")
}->Array.join("\n")

<div className={"mt-3"}>
<MarkdownStylize content rehypePlugins />
@@ -304,10 +303,10 @@ let default = (props: props) => {
open Markdown
switch props {
| Ok({module_: {id, name, docstrings, items}}) =>
let valuesAndType = items->Js.Array2.map(item => {
let valuesAndType = items->Array.map(item => {
switch item {
| Value({name, signature, docstrings, deprecated}) =>
let code = Js.String2.replaceByRe(signature, %re("/\\n/g"), "\n")
let code = String.replaceRegExp(signature, %re("/\\n/g"), "\n")
let slugPrefix = "value-" ++ name
<>
<H2 id=slugPrefix> {name->React.string} </H2>
@@ -316,7 +315,7 @@ let default = (props: props) => {
<DocstringsStylize docstrings slugPrefix />
</>
| Type({name, signature, docstrings, deprecated}) =>
let code = Js.String2.replaceByRe(signature, %re("/\\n/g"), "\n")
let code = String.replaceRegExp(signature, %re("/\\n/g"), "\n")
let slugPrefix = "type-" ++ name
<>
<H2 id=slugPrefix> {name->React.string} </H2>
@@ -337,7 +336,7 @@ let default = (props: props) => {
}

let rightSidebar = switch props {
| Ok({module_: {items}}) if Js.Array2.length(items) > 0 =>
| Ok({module_: {items}}) if Array.length(items) > 0 =>
<div className="hidden xl:block lg:w-1/5 md:h-auto md:relative overflow-y-visible bg-white">
<aside
className="relative top-0 pl-4 w-full block md:top-16 md:pt-16 md:sticky border-l border-gray-20 overflow-y-auto pb-24 h-[calc(100vh-4.5rem)]">
@@ -383,8 +382,8 @@ let default = (props: props) => {

module Data = {
type t = {
mainModule: Js.Dict.t<Js.Json.t>,
tree: Js.Dict.t<Js.Json.t>,
mainModule: Dict.t<JSON.t>,
tree: Dict.t<JSON.t>,
}

let dir = Node.Path.resolve("data", "api")
@@ -394,7 +393,7 @@ module Data = {

let pathModule = Path.join([dir, version, `${moduleName}.json`])

let moduleContent = Fs.readFileSync(pathModule)->Js.Json.parseExn
let moduleContent = Fs.readFileSync(pathModule)->JSON.parseExn

let content = switch moduleContent {
| Object(dict) => dict->Some
@@ -403,7 +402,7 @@ module Data = {

let toctree = switch Path.join([dir, version, "toc_tree.json"])
->Fs.readFileSync
->Js.Json.parseExn {
->JSON.parseExn {
| Object(dict) => dict->Some
| _ => None
}
@@ -419,38 +418,38 @@ let processStaticProps = (~slug: array<string>, ~version: string) => {
let moduleName = slug->Belt.Array.getExn(0)
let content = Data.getVersion(~version, ~moduleName)

let modulePath = slug->Js.Array2.joinWith("/")
let modulePath = slug->Array.join("/")

switch content {
| Some({mainModule, tree}) =>
switch mainModule->Js.Dict.get(modulePath) {
switch mainModule->Dict.get(modulePath) {
| Some(json) =>
let {items, docstrings, deprecated, name} = Docgen.decodeFromJson(json)
let id = switch json {
| Object(dict) =>
switch Js.Dict.get(dict, "id") {
switch Dict.get(dict, "id") {
| Some(String(s)) => s
| _ => ""
}
| _ => ""
}

let items = items->Js.Array2.map(item =>
let items = items->Array.map(item =>
switch item {
| Docgen.Value({id, docstrings, signature, name, ?deprecated}) =>
Value({
id,
docstrings,
signature,
name,
deprecated: deprecated->Js.Null.fromOption,
deprecated: deprecated->Null.fromOption,
})
| Type({id, docstrings, signature, name, ?deprecated, ?detail}) =>
let detail = switch detail {
| Some(kind) =>
switch kind {
| Docgen.Record({items}) =>
let items = items->Js.Array2.map(({
let items = items->Array.map(({
name,
docstrings,
signature,
@@ -462,21 +461,21 @@ let processStaticProps = (~slug: array<string>, ~version: string) => {
docstrings,
signature,
optional,
deprecated: deprecated->Js.Null.fromOption,
deprecated: deprecated->Null.fromOption,
}
})
Record({items: items})->Js.Null.return
Record({items: items})->Null.make
| Variant({items}) =>
let items = items->Js.Array2.map(({name, docstrings, signature, ?deprecated}) => {
let items = items->Array.map(({name, docstrings, signature, ?deprecated}) => {
{
name,
docstrings,
signature,
deprecated: deprecated->Js.Null.fromOption,
deprecated: deprecated->Null.fromOption,
}
})

Variant({items: items})->Js.Null.return
Variant({items: items})->Null.make
}
| None => Js.Null.empty
}
@@ -485,7 +484,7 @@ let processStaticProps = (~slug: array<string>, ~version: string) => {
docstrings,
signature,
name,
deprecated: deprecated->Js.Null.fromOption,
deprecated: deprecated->Null.fromOption,
detail,
})
| _ => assert(false)
@@ -495,11 +494,11 @@ let processStaticProps = (~slug: array<string>, ~version: string) => {
id,
name,
docstrings,
deprecated: deprecated->Js.Null.fromOption,
deprecated: deprecated->Null.fromOption,
items,
}

let toctree = tree->Js.Dict.get(moduleName)
let toctree = tree->Dict.get(moduleName)

switch toctree {
| Some(toctree) => Ok({module_, toctree: (Obj.magic(toctree): node)})
@@ -531,24 +530,24 @@ let getStaticPathsByVersion = async (~version: string) => {
let slugs =
pathDir
->Fs.readdirSync
->Js.Array2.reduce((acc, file) => {
->Array.reduce([], (acc, file) => {
switch file == "toc_tree.json" {
| true => acc
| false =>
let paths = switch Path.join2(pathDir, file)
->Fs.readFileSync
->Js.Json.parseExn {
->JSON.parseExn {
| Object(dict) =>
dict
->Js.Dict.keys
->Js.Array2.map(modPath => modPath->Js.String2.split("/"))
->Dict.keysToArray
->Array.map(modPath => modPath->String.split("/"))
| _ => acc
}
Js.Array2.concat(acc, paths)
Array.concat(acc, paths)
}
}, [])
})

let paths = slugs->Js.Array2.map(slug =>
let paths = slugs->Array.map(slug =>
{
"params": {
"slug": slug,
8 changes: 4 additions & 4 deletions src/Blog.res
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ module CategorySelector = {

<div className="text-16 w-full flex items-center justify-between text-gray-60">
{tabs
->Belt.Array.map(tab => {
->Array.map(tab => {
// Deep comparison here!
let isActive = selected == tab
let text = (tab :> string)
@@ -211,11 +211,11 @@ let default = (props: props): React.element => {
<Markdown.Warn> {React.string("This blog is currently in the works.")} </Markdown.Warn>
</div>
} else {
let result = switch Belt.Array.length(posts) {
let result = switch Array.length(posts) {
| 0 => <div> {React.string("No posts for this category available...")} </div>
| _ =>
let first = Belt.Array.getExn(posts, 0)
let rest = Js.Array2.sliceFrom(posts, 1)
let rest = Array.sliceToEnd(posts, ~start=1)

let featureBox =
<div className="w-full mb-24 lg:px-8 xl:px-0">
@@ -235,7 +235,7 @@ let default = (props: props): React.element => {
| rest =>
<div
className="px-4 md:px-8 xl:px-0 grid grid-cols-1 xs:grid-cols-2 md:grid-cols-3 gap-20 gap-y-12 md:gap-y-24 w-full">
{Js.Array2.map(rest, post => {
{Array.map(rest, post => {
let badge = post.frontmatter.badge->Null.toOption

<BlogCard
8 changes: 4 additions & 4 deletions src/BlogArticle.res
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ module BlogHeader = {
) => {
let date = DateStr.toDate(date)

let authors = Belt.Array.concat([author], co_authors)
let authors = Array.concat([author], co_authors)

<div className="flex flex-col items-center">
<div className="w-full max-w-740">
@@ -79,7 +79,7 @@ module BlogHeader = {
{React.string(Util.Date.toDayMonthYear(date))}
</div>
<h1 className="hl-title"> {React.string(title)} </h1>
{description->Belt.Option.mapWithDefault(React.null, desc =>
{description->Option.mapOr(React.null, desc =>
switch desc {
| "" => <div className="mb-8" />
| desc =>
@@ -89,7 +89,7 @@ module BlogHeader = {
}
)}
<div className="flex flex-col md:flex-row mb-12">
{Belt.Array.map(authors, author =>
{Array.map(authors, author =>
<div
key=author.username
style={ReactDOMStyle.make(~minWidth="8.1875rem", ())}
@@ -233,7 +233,7 @@ let getStaticProps: Next.GetStaticProps.t<props, Params.t> = async ctx => {
let getStaticPaths: Next.GetStaticPaths.t<Params.t> = async () => {
open Next.GetStaticPaths

let paths = BlogApi.getAllPosts()->Belt.Array.map(postData => {
let paths = BlogApi.getAllPosts()->Array.map(postData => {
params: {
Params.slug: BlogApi.blogPathToSlug(postData.path),
},
6 changes: 3 additions & 3 deletions src/ConsolePanel.res
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ let make = (~compilerState, ~runOutput) => {
switch data["type"] {
| #...logLevel as logLevel =>
let args: array<string> = data["args"]
setLogs(previous => previous->Belt.Array.concat([(logLevel, args)]))
setLogs(previous => previous->Array.concat([(logLevel, args)]))
| _ => ()
}
}
@@ -42,8 +42,8 @@ let make = (~compilerState, ~runOutput) => {
| logs =>
let content =
logs
->Belt.Array.mapWithIndex((i, (logLevel, log)) => {
let log = Js.Array2.joinWith(log, " ")
->Array.mapWithIndex(((logLevel, log), i) => {
let log = Array.join(log, " ")
<pre
key={RescriptCore.Int.toString(i)}
className={switch logLevel {
100 changes: 45 additions & 55 deletions src/Packages.res
Original file line number Diff line number Diff line change
@@ -14,14 +14,14 @@ type urlResource = {
official: bool,
}

external unsafeToUrlResource: Js.Json.t => array<urlResource> = "%identity"
external unsafeToUrlResource: JSON.t => array<urlResource> = "%identity"

type npmPackage = {
name: string,
version: string,
keywords: array<string>,
description: string,
repositoryHref: Js.Null.t<string>,
repositoryHref: Null.t<string>,
npmHref: string,
searchScore: float,
maintenanceScore: float,
@@ -44,13 +44,13 @@ module Resource = {
let shouldFilter = (res: t) => {
switch res {
| Npm(pkg) | Outdated(pkg) =>
if pkg.name->Js.String2.startsWith("@elm-react") {
if pkg.name->String.startsWith("@elm-react") {
true
} else if pkg.name->Js.String2.startsWith("bs-") {
} else if pkg.name->String.startsWith("bs-") {
true
} else if (
pkg.name->Js.String2.startsWith("@reason-react-native") ||
pkg.name->Js.String2.startsWith("reason-react-native")
pkg.name->String.startsWith("@reason-react-native") ||
pkg.name->String.startsWith("reason-react-native")
) {
true
} else {
@@ -61,8 +61,8 @@ module Resource = {
}

let filterKeywords = (keywords: array<string>): array<string> => {
Belt.Array.keep(keywords, kw => {
switch Js.String2.toLowerCase(kw) {
Array.filter(keywords, kw => {
switch String.toLowerCase(kw) {
| "reasonml"
| "reason"
| "ocaml"
@@ -78,9 +78,7 @@ module Resource = {
let isOfficial = (res: t) => {
switch res {
| Npm(pkg) | Outdated(pkg) =>
pkg.name === "rescript" ||
pkg.name->Js.String2.startsWith("@rescript/") ||
pkg.name === "gentype"
pkg.name === "rescript" || pkg.name->String.startsWith("@rescript/") || pkg.name === "gentype"
| Url(urlRes) => urlRes.official
}
}
@@ -102,7 +100,7 @@ module Resource = {

fuser
->Fuse.search(pattern)
->Js.Array2.sortInPlaceWith((a, b) => a["item"].searchScore > b["item"].searchScore ? -1 : 1)
->Array.toSorted((a, b) => Float.compare(a["item"].searchScore, b["item"].searchScore))
}

let applyUrlResourceSearch = (urls: array<urlResource>, pattern: string): array<
@@ -124,24 +122,20 @@ module Resource = {
}

let applySearch = (resources: array<t>, pattern: string): array<t> => {
let (allNpms, allUrls, allOutDated) = Belt.Array.reduce(resources, ([], [], []), (
acc,
next,
) => {
let (allNpms, allUrls, allOutDated) = Array.reduce(resources, ([], [], []), (acc, next) => {
let (npms, resources, outdated) = acc

switch next {
| Npm(pkg) => Js.Array2.push(npms, pkg)->ignore
| Url(res) => Js.Array2.push(resources, res)->ignore
| Outdated(pkg) => Js.Array2.push(outdated, pkg)->ignore
| Npm(pkg) => Array.push(npms, pkg)->ignore
| Url(res) => Array.push(resources, res)->ignore
| Outdated(pkg) => Array.push(outdated, pkg)->ignore
}
(npms, resources, outdated)
})

let filteredNpm = applyNpmSearch(allNpms, pattern)->Belt.Array.map(m => Npm(m["item"]))
let filteredUrls = applyUrlResourceSearch(allUrls, pattern)->Belt.Array.map(m => Url(m["item"]))
let filteredOutdated =
applyNpmSearch(allOutDated, pattern)->Belt.Array.map(m => Outdated(m["item"]))
let filteredNpm = applyNpmSearch(allNpms, pattern)->Array.map(m => Npm(m["item"]))
let filteredUrls = applyUrlResourceSearch(allUrls, pattern)->Array.map(m => Url(m["item"]))
let filteredOutdated = applyNpmSearch(allOutDated, pattern)->Array.map(m => Outdated(m["item"]))

Belt.Array.concatMany([filteredNpm, filteredUrls, filteredOutdated])
}
@@ -159,12 +153,12 @@ module Card = {
}
let linkBox = switch value {
| Npm(pkg) | Outdated(pkg) =>
let repositoryHref = Js.Null.toOption(pkg.repositoryHref)
let repositoryHref = Null.toOption(pkg.repositoryHref)
let repoEl = switch repositoryHref {
| Some(href) =>
let name = if Js.String2.startsWith(href, "https://github.com") {
let name = if String.startsWith(href, "https://github.com") {
"GitHub"
} else if Js.String2.startsWith(href, "https://gitlab.com") {
} else if String.startsWith(href, "https://gitlab.com") {
"GitLab"
} else {
"Repository"
@@ -183,8 +177,7 @@ module Card = {
}

let titleHref = switch value {
| Npm(pkg) | Outdated(pkg) =>
pkg.repositoryHref->Js.Null.toOption->Belt.Option.getWithDefault(pkg.npmHref)
| Npm(pkg) | Outdated(pkg) => pkg.repositoryHref->Null.toOption->Option.getOr(pkg.npmHref)
| Url(res) => res.urlHref
}

@@ -206,8 +199,8 @@ module Card = {
</div>
<div className="mt-4 text-16"> {React.string(description)} </div>
<div className="space-x-2 mt-4">
{Belt.Array.map(keywords, keyword => {
let onMouseDown = Belt.Option.map(onKeywordSelect, cb => {
{Array.map(keywords, keyword => {
let onMouseDown = Option.map(onKeywordSelect, cb => {
evt => {
ReactEvent.Mouse.preventDefault(evt)
cb(keyword)
@@ -380,9 +373,9 @@ let default = (props: props) => {
})

let allResources = {
let npms = props["packages"]->Belt.Array.map(pkg => Resource.Npm(pkg))
let urls = props["urlResources"]->Belt.Array.map(res => Resource.Url(res))
let outdated = props["unmaintained"]->Belt.Array.map(pkg => Resource.Outdated(pkg))
let npms = props["packages"]->Array.map(pkg => Resource.Npm(pkg))
let urls = props["urlResources"]->Array.map(res => Resource.Url(res))
let outdated = props["unmaintained"]->Array.map(pkg => Resource.Outdated(pkg))
Belt.Array.concatMany([npms, urls, outdated])
}

@@ -409,10 +402,7 @@ let default = (props: props) => {
setState(_ => All)
}

let (officialResources, communityResources) = Belt.Array.reduce(resources, ([], []), (
acc,
next,
) => {
let (officialResources, communityResources) = Array.reduce(resources, ([], []), (acc, next) => {
let (official, community) = acc
let isResourceIncluded = switch next {
| Npm(_) => filter.includeNpm
@@ -422,9 +412,9 @@ let default = (props: props) => {
if !isResourceIncluded {
()
} else if filter.includeOfficial && Resource.isOfficial(next) {
Js.Array2.push(official, next)->ignore
Array.push(official, next)->ignore
} else if filter.includeCommunity && !Resource.shouldFilter(next) {
Js.Array2.push(community, next)->ignore
Array.push(community, next)->ignore
}
(official, community)
})
@@ -441,7 +431,7 @@ let default = (props: props) => {
| resources =>
<Category title={Category.toString(Official)}>
<div className="space-y-4">
{Belt.Array.map(resources, res => {
{Array.map(resources, res => {
<Card key={Resource.getId(res)} onKeywordSelect value={res} />
})->React.array}
</div>
@@ -453,7 +443,7 @@ let default = (props: props) => {
| resources =>
<Category title={Category.toString(Community)}>
<div className="space-y-4">
{Belt.Array.map(resources, res => {
{Array.map(resources, res => {
<Card onKeywordSelect key={Resource.getId(res)} value={res} />
})->React.array}
</div>
@@ -472,15 +462,15 @@ let default = (props: props) => {

// On second render, this hook runs one more time to actually trigger the search.
React.useEffect(() => {
router.query->Js.Dict.get("search")->Belt.Option.forEach(onValueChange)
router.query->Dict.get("search")->Option.forEach(onValueChange)

None
}, [firstRenderDone.current])

let updateQuery = value =>
router->Next.Router.replaceObj({
pathname: router.pathname,
query: value === "" ? Js.Dict.empty() : Js.Dict.fromArray([("search", value)]),
query: value === "" ? Dict.make() : Dict.fromArray([("search", value)]),
})

// When the search term changes, update the router query accordingly.
@@ -559,14 +549,14 @@ module Response = {
@val external fetchNpmPackages: string => promise<Response.t> = "fetch"

let parsePkgs = data =>
Belt.Array.map(data["objects"], item => {
Array.map(data["objects"], item => {
let pkg = item["package"]
{
name: pkg["name"],
version: pkg["version"],
keywords: Resource.filterKeywords(pkg["keywords"])->Resource.uniqueKeywords,
description: Belt.Option.getWithDefault(pkg["description"], ""),
repositoryHref: Js.Null.fromOption(pkg["links"]["repository"]),
description: Option.getOr(pkg["description"], ""),
repositoryHref: Null.fromOption(pkg["links"]["repository"]),
npmHref: pkg["links"]["npm"],
searchScore: item["searchScore"],
maintenanceScore: item["score"]["detail"]["maintenance"],
@@ -576,13 +566,13 @@ let parsePkgs = data =>
let getStaticProps: Next.GetStaticProps.revalidate<props, unit> = async _ctx => {
let baseUrl = "https://registry.npmjs.org/-/v1/search?text=keywords:rescript&size=250&maintenance=1.0&popularity=0.5&quality=0.9"

let (one, two, three) = await Js.Promise2.all3((
let (one, two, three) = await Promise.all3((
fetchNpmPackages(baseUrl),
fetchNpmPackages(baseUrl ++ "&from=250"),
fetchNpmPackages(baseUrl ++ "&from=500"),
))

let (data1, data2, data3) = await Js.Promise2.all3((
let (data1, data2, data3) = await Promise.all3((
one->Response.json,
two->Response.json,
three->Response.json,
@@ -592,15 +582,15 @@ let getStaticProps: Next.GetStaticProps.revalidate<props, unit> = async _ctx =>

let pkges =
parsePkgs(data1)
->Js.Array2.concat(parsePkgs(data2))
->Js.Array2.concat(parsePkgs(data3))
->Js.Array2.filter(pkg => {
if packageAllowList->Js.Array2.includes(pkg.name) {
->Array.concat(parsePkgs(data2))
->Array.concat(parsePkgs(data3))
->Array.filter(pkg => {
if packageAllowList->Array.includes(pkg.name) {
true
} else if pkg.name->Js.String2.includes("reason") {
} else if pkg.name->String.includes("reason") {
false
} else if pkg.maintenanceScore < 0.3 {
let _ = unmaintained->Js.Array2.push(pkg)
let _ = unmaintained->Array.push(pkg)
false
} else {
true
@@ -611,7 +601,7 @@ let getStaticProps: Next.GetStaticProps.revalidate<props, unit> = async _ctx =>
let urlResources =
Node.Path.join2(index_data_dir, "packages_url_resources.json")
->Node.Fs.readFileSync
->Js.Json.parseExn
->JSON.parseExn
->unsafeToUrlResource
let props: props = {
"packages": pkges,
2 changes: 1 addition & 1 deletion src/Packages.resi
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ type npmPackage = {
version: string,
keywords: array<string>,
description: string,
repositoryHref: Js.Null.t<string>,
repositoryHref: Null.t<string>,
npmHref: string,
searchScore: float,
maintenanceScore: float,
221 changes: 106 additions & 115 deletions src/Playground.res

Large diffs are not rendered by default.

42 changes: 21 additions & 21 deletions src/SyntaxLookup.res
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ type itemInfo = {
}

let getAnchor = path => {
switch Js.String2.split(path, "#") {
switch String.split(path, "#") {
| [_, anchor] => Some(anchor)
| _ => None
}
@@ -114,19 +114,19 @@ module Tag = {
module DetailBox = {
@react.component
let make = (~summary: string, ~children: React.element) => {
let summaryEl = switch Js.String2.split(summary, "`") {
let summaryEl = switch String.split(summary, "`") {
| [] => React.null
| [first, second, third] =>
[
React.string(first),
<span className="text-fire"> {React.string(second)} </span>,
React.string(third),
]
->Belt.Array.mapWithIndex((i, el) => {
<span key={Belt.Int.toString(i)}> el </span>
->Array.mapWithIndex((el, i) => {
<span key={Int.toString(i)}> el </span>
})
->React.array
| more => Belt.Array.map(more, s => React.string(s))->React.array
| more => Array.map(more, s => React.string(s))->React.array
}

<div>
@@ -149,7 +149,7 @@ let scrollToTop = () => scrollTo(0, 0)
type props = {mdxSources: array<MdxRemote.output>}
type params = {slug: string}

let decode = (json: Js.Json.t) => {
let decode = (json: JSON.t) => {
open Json.Decode
let id = json->(field("id", string, _))
let keywords = json->(field("keywords", array(string, ...), _))
@@ -159,7 +159,7 @@ let decode = (json: Js.Json.t) => {
let status =
json
->optional(field("status", string, _), _)
->Belt.Option.mapWithDefault(Status.Active, Status.fromString)
->Option.mapOr(Status.Active, Status.fromString)

{
id,
@@ -174,7 +174,7 @@ let decode = (json: Js.Json.t) => {
let default = (props: props) => {
let {mdxSources} = props

let allItems = mdxSources->Js.Array2.map(mdxSource => {
let allItems = mdxSources->Array.map(mdxSource => {
let {id, keywords, category, summary, name, status} = decode(mdxSource.frontmatter)

let children =
@@ -204,14 +204,14 @@ let default = (props: props) => {
let router = Next.Router.useRouter()
let (state, setState) = React.useState(_ => ShowAll)

let findItemById = id => allItems->Js.Array2.find(item => item.id === id)
let findItemById = id => allItems->Array.find(item => item.id === id)

let findItemByExactName = name => allItems->Js.Array2.find(item => item.name === name)
let findItemByExactName = name => allItems->Array.find(item => item.name === name)

let searchItems = value =>
fuse
->Fuse.search(value)
->Belt.Array.map(m => {
->Array.map(m => {
m["item"]
})

@@ -280,7 +280,7 @@ let default = (props: props) => {
ExtensionPoints,
SpecialValues,
Other,
]->Belt.Array.map(cat => {
]->Array.map(cat => {
(cat->Category.toString, [])
})

@@ -290,15 +290,15 @@ let default = (props: props) => {
| ShowFiltered(_, items) => items
}

Belt.Array.reduce(items, Js.Dict.fromArray(initial), (acc, item) => {
Array.reduce(items, Dict.fromArray(initial), (acc, item) => {
let key = item.category->Category.toString
Js.Dict.get(acc, key)->Belt.Option.mapWithDefault(acc, items => {
Js.Array2.push(items, item)->ignore
Js.Dict.set(acc, key, items)
Dict.get(acc, key)->Option.mapOr(acc, items => {
Array.push(items, item)->ignore
Dict.set(acc, key, items)
acc
})
})
->Js.Dict.entries
->Dict.toArray
->Array.reduce([], (acc, entry) => {
let (title, items) = entry
if Array.length(items) === 0 {
@@ -320,7 +320,7 @@ let default = (props: props) => {
<div key=title className="first:mt-0 mt-12">
<Category title> {React.array(children)} </Category>
</div>
Js.Array2.push(acc, el)->ignore
Array.push(acc, el)->ignore
acc
}
})
@@ -351,7 +351,7 @@ let default = (props: props) => {
<div className="w-full" style={ReactDOM.Style.make(~maxWidth="34rem", ())}>
<SearchBox
placeholder="Enter keywords or syntax..."
completionValues={Belt.Array.map(completionItems, item => item.name)}
completionValues={Array.map(completionItems, item => item.name)}
value=searchValue
onClear=onSearchClear
onValueChange=onSearchValueChange
@@ -391,7 +391,7 @@ let default = (props: props) => {
let getStaticProps: Next.GetStaticProps.t<props, params> = async _ctx => {
let dir = Node.Path.resolve("misc_docs", "syntax")

let allFiles = Node.Fs.readdirSync(dir)->Js.Array2.map(async file => {
let allFiles = Node.Fs.readdirSync(dir)->Array.map(async file => {
let fullPath = Node.Path.join2(dir, file)
let source = fullPath->Node.Fs.readFileSync
await MdxRemote.serialize(
@@ -400,7 +400,7 @@ let getStaticProps: Next.GetStaticProps.t<props, params> = async _ctx => {
)
})

let mdxSources = await Js.Promise2.all(allFiles)
let mdxSources = await Promise.all(allFiles)

{"props": {mdxSources: mdxSources}}
}
16 changes: 6 additions & 10 deletions src/Try.res
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ let default = props => {
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)

let lazyPlayground = Next.Dynamic.dynamic(
async () => await Js.import(Playground.make),
async () => await import(Playground.make),
{
ssr: false,
loading: () => <span> {React.string("Loading...")} </span>,
@@ -32,20 +32,16 @@ let getStaticProps: Next.GetStaticProps.t<props, _> = async _ => {
let response = await Webapi.Fetch.fetch("https://cdn.rescript-lang.org/")
let text = await Webapi.Fetch.Response.text(response)
text
->Js.String2.split("\n")
->Belt.Array.keepMap(line => {
switch line->Js.String2.startsWith("<a href") {
->String.split("\n")
->Array.filterMap(line => {
switch line->String.startsWith("<a href") {
| true =>
// Adapted from https://semver.org/
let semverRe = %re(
"/v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/"
)
switch Js.Re.exec_(semverRe, line) {
| Some(result) =>
switch Js.Re.captures(result)->Belt.Array.get(0) {
| Some(str) => Js.Nullable.toOption(str)
| None => None
}
switch Re.exec(semverRe, line) {
| Some(result) => Re.Result.fullMatch(result)->Some
| None => None
}
| false => None
14 changes: 7 additions & 7 deletions src/bindings/DocSearch.res
Original file line number Diff line number Diff line change
@@ -12,20 +12,20 @@ type contentType =
type hierarchy = {
lvl0: string,
lvl1: string,
lvl2: Js.Nullable.t<string>,
lvl3: Js.Nullable.t<string>,
lvl4: Js.Nullable.t<string>,
lvl5: Js.Nullable.t<string>,
lvl6: Js.Nullable.t<string>,
lvl2: Nullable.t<string>,
lvl3: Nullable.t<string>,
lvl4: Nullable.t<string>,
lvl5: Nullable.t<string>,
lvl6: Nullable.t<string>,
}

type docSearchHit = {
objectID: string,
content: Js.Nullable.t<string>,
content: Nullable.t<string>,
url: string,
url_without_anchor: string,
@as("type") type_: contentType,
anchor: Js.Nullable.t<string>,
anchor: Nullable.t<string>,
hierarchy: hierarchy,
// NOTE: docsearch need these two fields to highlight results
_highlightResult: {.},
6 changes: 3 additions & 3 deletions src/bindings/MdxRemote.res
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
type output = {frontmatter: Js.Json.t, compiledSource: string, scope: Js.Json.t}
type output = {frontmatter: JSON.t, compiledSource: string, scope: JSON.t}

type mdxOptions = {
remarkPlugins?: array<Remark.remarkPlugin>,
@@ -24,8 +24,8 @@ let defaultMdxOptions = {

@react.component @module("next-mdx-remote")
external make: (
~frontmatter: Js.Json.t,
~frontmatter: JSON.t,
~compiledSource: string,
~scope: Js.Json.t,
~scope: JSON.t,
~components: MarkdownComponents.t=?,
) => React.element = "MDXRemote"
10 changes: 5 additions & 5 deletions src/bindings/Next.res
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ module GetServerSideProps = {
// See: https://github.com/zeit/next.js/blob/canary/packages/next/types/index.d.ts
type context<'props, 'params> = {
params: 'params,
query: Js.Dict.t<string>,
query: Dict.t<string>,
req: Req.t,
res: Res.t,
}
@@ -26,8 +26,8 @@ module GetStaticProps = {
// See: https://github.com/zeit/next.js/blob/canary/packages/next/types/index.d.ts
type context<'props, 'params> = {
params: 'params,
query: Js.Dict.t<string>,
req: Js.Nullable.t<'props>,
query: Dict.t<string>,
req: Nullable.t<'props>,
}

type t<'props, 'params> = context<'props, 'params> => promise<{"props": 'props}>
@@ -101,12 +101,12 @@ module Router = {
asPath: string,
events: Events.t,
pathname: string,
query: Js.Dict.t<string>,
query: Dict.t<string>,
}

type pathObj = {
pathname: string,
query: Js.Dict.t<string>,
query: Dict.t<string>,
}

@send external push: (router, string) => unit = "push"
10 changes: 5 additions & 5 deletions src/bindings/Next.resi
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ module GetServerSideProps: {
// See: https://github.com/zeit/next.js/blob/canary/packages/next/types/index.d.ts
type context<'props, 'params> = {
params: 'params,
query: Js.Dict.t<string>,
query: Dict.t<string>,
req: Req.t,
res: Res.t,
}
@@ -26,8 +26,8 @@ module GetStaticProps: {
// See: https://github.com/zeit/next.js/blob/canary/packages/next/types/index.d.ts
type context<'props, 'params> = {
params: 'params,
query: Js.Dict.t<string>,
req: Js.Nullable.t<'props>,
query: Dict.t<string>,
req: Nullable.t<'props>,
}

type t<'props, 'params> = context<'props, 'params> => promise<{"props": 'props}>
@@ -97,12 +97,12 @@ module Router: {
asPath: string,
events: Events.t,
pathname: string,
query: Js.Dict.t<string>,
query: Dict.t<string>,
}

type pathObj = {
pathname: string,
query: Js.Dict.t<string>,
query: Dict.t<string>,
}

let push: (router, string) => unit
2 changes: 1 addition & 1 deletion src/bindings/Node.res
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ module URL = {

module Process = {
@scope("process") external cwd: unit => string = "cwd"
@scope("process") external env: Js.Dict.t<string> = "env"
@scope("process") external env: Dict.t<string> = "env"
@scope("process") @val external argv: array<string> = "argv"
@scope("process") external exit: int => unit = "exit"
}
48 changes: 24 additions & 24 deletions src/bindings/RescriptCompilerApi.res
Original file line number Diff line number Diff line change
@@ -41,10 +41,10 @@ module Version = {

// Helps finding the right API version
let fromString = (apiVersion: string): t =>
switch Js.String2.split(apiVersion, ".")->Belt.List.fromArray {
switch String.split(apiVersion, ".")->List.fromArray {
| list{maj, min, ..._} =>
let maj = Belt.Int.fromString(maj)
let min = Belt.Int.fromString(min)
let maj = Int.fromString(maj)
let min = Int.fromString(min)

switch (maj, min) {
| (Some(maj), Some(_))
@@ -113,28 +113,28 @@ module LocMsg = {
| #E => "E"
}

`[1;31m[${prefix}] Line ${row->Belt.Int.toString}, ${column->Belt.Int.toString}:[0m ${shortMsg}`
`[1;31m[${prefix}] Line ${row->Int.toString}, ${column->Int.toString}:[0m ${shortMsg}`
}

// Creates a somewhat unique id based on the rows / cols of the locMsg
let makeId = t => {
open Belt.Int
open Int
toString(t.row) ++
("-" ++
(toString(t.endRow) ++ ("-" ++ (toString(t.column) ++ ("-" ++ toString(t.endColumn))))))
}

let dedupe = (arr: array<t>) => {
let result = Js.Dict.empty()
let result = Dict.make()

for i in 0 to Js.Array.length(arr) - 1 {
for i in 0 to Array.length(arr) - 1 {
let locMsg = Array.getUnsafe(arr, i)
let id = makeId(locMsg)

// The last element with the same id wins
result->Js.Dict.set(id, locMsg)
result->Dict.set(id, locMsg)
}
Js.Dict.values(result)
Dict.valuesToArray(result)
}
}

@@ -163,11 +163,11 @@ module Warning = {
| Warn({warnNumber, details})
| WarnErr({warnNumber, details}) =>
let {LocMsg.row: row, column, shortMsg} = details
let msg = `(Warning number ${warnNumber->Belt.Int.toString}) ${shortMsg}`
let msg = `(Warning number ${warnNumber->Int.toString}) ${shortMsg}`
(row, column, msg)
}

`[1;31m[${prefix}] Line ${row->Belt.Int.toString}, ${column->Belt.Int.toString}:[0m ${msg}`
`[1;31m[${prefix}] Line ${row->Int.toString}, ${column->Int.toString}:[0m ${msg}`
}
}

@@ -308,10 +308,10 @@ module CompilationResult = {
| Fail(CompileFail.t) // When a compilation failed with some error result
| Success(CompileSuccess.t)
| UnexpectedError(string) // Errors that slip through as uncaught exceptions of the compiler bundle
| Unknown(string, Js.Json.t)
| Unknown(string, JSON.t)

// TODO: We might change this specific api completely before launching
let decode = (~time: float, json: Js.Json.t): t => {
let decode = (~time: float, json: JSON.t): t => {
open! Json.Decode

try switch field("type", string, json) {
@@ -329,7 +329,7 @@ module ConversionResult = {
| Success(ConvertSuccess.t)
| Fail({fromLang: Lang.t, toLang: Lang.t, details: array<LocMsg.t>}) // When a compilation failed with some error result
| UnexpectedError(string) // Errors that slip through as uncaught exceptions within the playground
| Unknown(string, Js.Json.t)
| Unknown(string, JSON.t)

let decode = (~fromLang: Lang.t, ~toLang: Lang.t, json): t => {
open! Json.Decode
@@ -369,7 +369,7 @@ module Compiler = {
@get @scope("rescript") external resVersion: t => string = "version"

@send @scope("rescript")
external resCompile: (t, string) => Js.Json.t = "compile"
external resCompile: (t, string) => JSON.t = "compile"

let resCompile = (t, code): CompilationResult.t => {
let startTime = now()
@@ -380,15 +380,15 @@ module Compiler = {
}

@send @scope("rescript")
external resFormat: (t, string) => Js.Json.t = "format"
external resFormat: (t, string) => JSON.t = "format"

let resFormat = (t, code): ConversionResult.t => {
let json = resFormat(t, code)
ConversionResult.decode(~fromLang=Res, ~toLang=Res, json)
}

@send @scope("reason")
external reasonCompile: (t, string) => Js.Json.t = "compile"
external reasonCompile: (t, string) => JSON.t = "compile"
let reasonCompile = (t, code): CompilationResult.t => {
let startTime = now()
let json = reasonCompile(t, code)
@@ -398,7 +398,7 @@ module Compiler = {
}

@send @scope("reason")
external reasonFormat: (t, string) => Js.Json.t = "format"
external reasonFormat: (t, string) => JSON.t = "format"

let reasonFormat = (t, code): ConversionResult.t => {
let json = reasonFormat(t, code)
@@ -408,7 +408,7 @@ module Compiler = {
@get @scope("ocaml") external ocamlVersion: t => string = "version"

@send @scope("ocaml")
external ocamlCompile: (t, string) => Js.Json.t = "compile"
external ocamlCompile: (t, string) => JSON.t = "compile"

let ocamlCompile = (t, code): CompilationResult.t => {
let startTime = now()
@@ -436,14 +436,14 @@ module Compiler = {
| _ => None
}

Belt.Option.forEach(moduleSystem, moduleSystem => t->setModuleSystem(moduleSystem)->ignore)
Belt.Option.forEach(config.open_modules, modules => t->setOpenModules(modules)->ignore)
Option.forEach(moduleSystem, moduleSystem => t->setModuleSystem(moduleSystem)->ignore)
Option.forEach(config.open_modules, modules => t->setOpenModules(modules)->ignore)

t->setWarnFlags(config.warn_flags)->ignore
}

@send
external convertSyntax: (t, string, string, string) => Js.Json.t = "convertSyntax"
external convertSyntax: (t, string, string, string) => JSON.t = "convertSyntax"

// General format function
let convertSyntax = (~fromLang: Lang.t, ~toLang: Lang.t, ~code: string, t): ConversionResult.t =>
@@ -455,8 +455,8 @@ module Compiler = {
~fromLang,
~toLang,
) catch {
| Js.Exn.Error(obj) =>
switch Js.Exn.message(obj) {
| Exn.Error(obj) =>
switch Exn.message(obj) {
| Some(m) => ConversionResult.UnexpectedError(m)
| None => UnexpectedError("")
}
24 changes: 12 additions & 12 deletions src/bindings/RescriptCompilerApi.resi
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ module Lang: {

let toExt: t => string

let decode: Js.Json.t => t
let decode: JSON.t => t
}

module Version: {
@@ -47,7 +47,7 @@ module LocMsg: {
endColumn: int,
}

let decode: Js.Json.t => t
let decode: JSON.t => t

type prefix = [#W | #E]

@@ -65,7 +65,7 @@ module Warning: {
| Warn({warnNumber: int, details: LocMsg.t})
| WarnErr({warnNumber: int, details: LocMsg.t}) // Describes an erronous warning

let decode: Js.Json.t => t
let decode: JSON.t => t

// Useful for showing errors in a more compact format
let toCompactErrorLine: t => string
@@ -78,7 +78,7 @@ module WarningFlag: {
warn_error_flags: string,
}

let decode: Js.Json.t => t
let decode: JSON.t => t
}

module TypeHint: {
@@ -98,7 +98,7 @@ module TypeHint: {
| Binding(data)
| CoreType(data)

let decode: Js.Json.t => t
let decode: JSON.t => t
}

module CompileSuccess: {
@@ -109,7 +109,7 @@ module CompileSuccess: {
time: float, // total compilation time
}

let decode: (~time: float, Js.Json.t) => t
let decode: (~time: float, JSON.t) => t
}

module ConvertSuccess: {
@@ -119,7 +119,7 @@ module ConvertSuccess: {
toLang: Lang.t,
}

let decode: Js.Json.t => t
let decode: JSON.t => t
}

module CompileFail: {
@@ -130,28 +130,28 @@ module CompileFail: {
| WarningFlagErr(WarningFlag.t)
| OtherErr(array<LocMsg.t>)

let decode: Js.Json.t => t
let decode: JSON.t => t
}

module CompilationResult: {
type t =
| Fail(CompileFail.t) // When a compilation failed with some error result
| Success(CompileSuccess.t)
| UnexpectedError(string) // Errors that slip through as uncaught exceptions of the compiler bundle
| Unknown(string, Js.Json.t)
| Unknown(string, JSON.t)

// TODO: We might change this specific api completely before launching
let decode: (~time: float, Js.Json.t) => t
let decode: (~time: float, JSON.t) => t
}

module ConversionResult: {
type t =
| Success(ConvertSuccess.t)
| Fail({fromLang: Lang.t, toLang: Lang.t, details: array<LocMsg.t>}) // When a compilation failed with some error result
| UnexpectedError(string) // Errors that slip through as uncaught exceptions within the playground
| Unknown(string, Js.Json.t)
| Unknown(string, JSON.t)

let decode: (~fromLang: Lang.t, ~toLang: Lang.t, Js.Json.t) => t
let decode: (~fromLang: Lang.t, ~toLang: Lang.t, JSON.t) => t
}

module Config: {
4 changes: 2 additions & 2 deletions src/bindings/Webapi.res
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ module Element = {
@send external addEventListener: (Dom.element, string, unit => unit) => unit = "addEventListener"

@send
external getElementById: (Dom.element, string) => Js.nullable<Dom.element> = "getElementById"
external getElementById: (Dom.element, string) => Nullable.t<Dom.element> = "getElementById"

type contentWindow
@get external contentWindow: Dom.element => option<contentWindow> = "contentWindow"
@@ -54,7 +54,7 @@ module Fetch = {
module Response = {
type t
@send external text: t => promise<string> = "text"
@send external json: t => promise<Js.Json.t> = "json"
@send external json: t => promise<JSON.t> = "json"
}

@val external fetch: string => promise<Response.t> = "fetch"
142 changes: 70 additions & 72 deletions src/common/Ansi.res
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ module Sgr = {

let esc = `\u001B`

let isAscii = (c: string) => Js.Re.test_(%re(`/[\x40-\x7F]/`), c)
let isAscii = (c: string) => Re.test(%re(`/[\x40-\x7F]/`), c)

module Location = {
type t = {
@@ -69,15 +69,15 @@ module Location = {

let fromString = input => {input, pos: -1}

let isDone = p => p.pos >= Js.String.length(p.input)
let isDone = p => p.pos >= String.length(p.input)

let next = p =>
if !isDone(p) {
let c = Js.String2.get(p.input, p.pos + 1)
let c = String.get(p.input, p.pos + 1)->Option.getUnsafe
p.pos = p.pos + 1
c
} else {
Js.String2.get(p.input, p.pos)
String.get(p.input, p.pos)->Option.getUnsafe
}

let untilNextEsc = p => {
@@ -95,15 +95,15 @@ module Location = {
// Look is useful to look ahead without reading the character
// from the stream
let look = (p, num) => {
let length = Js.String.length(p.input)
let length = String.length(p.input)

let pos = if p.pos + num >= length {
length - 1
} else {
p.pos + num
}

Js.String2.get(p.input, pos)
String.get(p.input, pos)->Option.getUnsafe
}
}

@@ -152,7 +152,7 @@ module Lexer = {
},
content,
})
Js.Array2.push(acc, token)->ignore
Array.push(acc, token)->ignore
lex(~acc, ~state=ReadSgr({startPos: p.pos, content: c}), p)
} else if isDone(p) {
let token = Text({
@@ -162,7 +162,7 @@ module Lexer = {
},
content,
})
Js.Array2.push(acc, token)->ignore
Array.push(acc, token)->ignore
acc
} else {
let content = content ++ c
@@ -175,49 +175,47 @@ module Lexer = {
if c !== "[" && isAscii(c) {
let raw = content ++ c

let loc = {startPos, endPos: startPos + Js.String.length(raw) - 1}

let token = Js.Re.exec_(%re(`/\[([0-9;]+)([\x40-\x7F])/`), raw)->(
x =>
switch x {
| Some(result) =>
let groups = Js.Re.captures(result)
switch groups[1]->Option.flatMap(o => o->Js.Nullable.toOption) {
| Some(str) =>
switch Js.String2.split(str, ";") {
| ["0"] => ClearSgr({loc, raw})
| other =>
let params = Belt.Array.map(other, s =>
switch s {
| "1" => Bold
| "30" => Fg(Black)
| "31" => Fg(Red)
| "32" => Fg(Green)
| "33" => Fg(Yellow)
| "34" => Fg(Blue)
| "35" => Fg(Magenta)
| "36" => Fg(Cyan)
| "37" => Fg(White)
| "40" => Bg(Black)
| "41" => Bg(Red)
| "42" => Bg(Green)
| "43" => Bg(Yellow)
| "44" => Bg(Blue)
| "45" => Bg(Magenta)
| "46" => Bg(Cyan)
| "47" => Bg(White)
| o => Unknown(o)
}
)
Sgr({loc, raw, params})
let loc = {startPos, endPos: startPos + String.length(raw) - 1}

let token = switch Re.exec(%re(`/\[([0-9;]+)([\x40-\x7F])/`), raw) {
| Some(result) =>
let groups = Re.Result.matches(result)
switch groups[1] {
| Some(str) =>
switch String.split(str, ";") {
| ["0"] => ClearSgr({loc, raw})
| other =>
let params = Array.map(other, s =>
switch s {
| "1" => Bold
| "30" => Fg(Black)
| "31" => Fg(Red)
| "32" => Fg(Green)
| "33" => Fg(Yellow)
| "34" => Fg(Blue)
| "35" => Fg(Magenta)
| "36" => Fg(Cyan)
| "37" => Fg(White)
| "40" => Bg(Black)
| "41" => Bg(Red)
| "42" => Bg(Green)
| "43" => Bg(Yellow)
| "44" => Bg(Blue)
| "45" => Bg(Magenta)
| "46" => Bg(Cyan)
| "47" => Bg(White)
| o => Unknown(o)
}

| None => Sgr({loc, raw, params: []})
}
| None => Sgr({loc, raw, params: []})
)
Sgr({loc, raw, params})
}
)
Js.Array2.push(acc, token)->ignore

| None => Sgr({loc, raw, params: []})
}
| None => Sgr({loc, raw, params: []})
}

Array.push(acc, token)->ignore
lex(~acc, ~state=Scan, p)
} else {
lex(~acc, ~state=ReadSgr({startPos, content: content ++ c}), p)
@@ -238,7 +236,7 @@ let parse = (input: string) => {

let onlyText = (tokens: array<Lexer.token>) => {
open Lexer
Belt.Array.keep(tokens, x =>
Array.filter(tokens, x =>
switch x {
| Text(_) => true
| _ => false
@@ -260,7 +258,7 @@ module SgrString = {
let params = ref([])
let content = ref("")

let length = Js.Array.length(tokens)
let length = Array.length(tokens)
for i in 0 to length - 1 {
let token = Belt.Array.getExn(tokens, i)

@@ -271,20 +269,20 @@ module SgrString = {
content := content.contents ++ data.content
if isLast && content.contents !== "" {
let element = {content: content.contents, params: params.contents}
Js.Array2.push(ret, element)->ignore
Array.push(ret, element)->ignore
}
| Sgr(data) =>
// merge together specific sgr params
let (fg, bg, rest) = Belt.Array.concat(params.contents, data.params)->Belt.Array.reduce(
let (fg, bg, rest) = Array.concat(params.contents, data.params)->Array.reduce(
(None, None, []),
(acc, next) => {
let (fg, bg, other) = acc
switch next {
| Fg(_) => (Some(next), bg, other)
| Bg(_) => (fg, Some(next), other)
| o =>
if Js.Array2.find(other, o2 => o === o2) === None {
Js.Array2.push(other, next)->ignore
if Array.find(other, o2 => o === o2) === None {
Array.push(other, next)->ignore
}
(fg, bg, other)
}
@@ -293,20 +291,20 @@ module SgrString = {

if content.contents !== "" {
let element = {content: content.contents, params: params.contents}
Js.Array2.push(ret, element)->ignore
Array.push(ret, element)->ignore
content := ""
}

params :=
Belt.Array.concatMany([
Belt.Option.mapWithDefault(fg, [], v => [v]),
Belt.Option.mapWithDefault(bg, [], v => [v]),
Option.mapOr(fg, [], v => [v]),
Option.mapOr(bg, [], v => [v]),
rest,
])
| ClearSgr(_) =>
if content.contents !== "" {
let element = {content: content.contents, params: params.contents}
Js.Array2.push(ret, element)->ignore
Array.push(ret, element)->ignore

params := []
content := ""
@@ -319,10 +317,10 @@ module SgrString = {

let toString = (e: t): string => {
let content = {
open Js.String2
replaceByRe(e.content, %re("/\n/g"), "\\n")->replace(esc, "")
open String
replaceRegExp(e.content, %re("/\n/g"), "\\n")->replace(esc, "")
}
let params = Belt.Array.map(e.params, Sgr.paramToString)->Js.Array2.joinWith(", ")
let params = Array.map(e.params, Sgr.paramToString)->Array.join(", ")

`SgrString params: ${params} | content: ${content}`
}
@@ -335,24 +333,24 @@ module Printer = {
switch t {
| Text({content, loc: {startPos, endPos}}) =>
let content = {
open Js.String2
replaceByRe(content, %re("/\n/g"), "\\n")->replace(esc, "")
open String
replaceRegExp(content, %re("/\n/g"), "\\n")->replace(esc, "")
}
`Text "${content}" (${startPos->Belt.Int.toString} to ${endPos->Belt.Int.toString})`
`Text "${content}" (${startPos->Int.toString} to ${endPos->Int.toString})`
| Sgr({params, raw, loc: {startPos, endPos}}) =>
let raw = Js.String2.replace(raw, esc, "")
let params = Belt.Array.map(params, Sgr.paramToString)->Js.Array2.joinWith(", ")
`Sgr "${raw}" -> ${params} (${startPos->Belt.Int.toString} to ${endPos->Belt.Int.toString})`
let raw = String.replace(raw, esc, "")
let params = Array.map(params, Sgr.paramToString)->Array.join(", ")
`Sgr "${raw}" -> ${params} (${startPos->Int.toString} to ${endPos->Int.toString})`
| ClearSgr({loc: {startPos, endPos}, raw}) =>
let raw = Js.String2.replace(raw, esc, "")
`Clear Sgr "${raw}" (${startPos->Belt.Int.toString} to ${endPos->Belt.Int.toString})`
let raw = String.replace(raw, esc, "")
`Clear Sgr "${raw}" (${startPos->Int.toString} to ${endPos->Int.toString})`
}

let plainString = (tokens: array<token>): string =>
Belt.Array.map(tokens, x =>
Array.map(tokens, x =>
switch x {
| Lexer.Text({content}) => content
| _ => ""
}
)->Js.Array2.joinWith("")
)->Array.join("")
}
18 changes: 9 additions & 9 deletions src/common/App.res
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ type pageProps = {.}
type props = {"Component": pageComponent, "pageProps": pageProps}

@get
external frontmatter: React.component<{.}> => Js.Json.t = "frontmatter"
external frontmatter: React.component<{.}> => JSON.t = "frontmatter"

let make = (props: props): React.element => {
let component = props["Component"]
@@ -57,16 +57,16 @@ let make = (props: props): React.element => {
| {base: ["docs", "manual"], pagepath, version} =>
// check if it's an api route
<EnableCollapsibleNavbar>
{switch Belt.Array.get(pagepath, 0) {
{switch pagepath[0] {
| Some("api") =>
switch version {
| Latest =>
switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout.Docs> content </ApiOverviewLayout.Docs>
| _ => content
}
| Version("v8.0.0") =>
switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout8_0_0.Docs> content </ApiOverviewLayout8_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout8_0_0.Prose> content </JsDocsLayout8_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout8_0_0.Prose> content </BeltDocsLayout8_0_0.Prose>
@@ -76,7 +76,7 @@ let make = (props: props): React.element => {
| _ => React.null
}
| Version("v9.0.0") =>
switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout9_0_0.Docs> content </ApiOverviewLayout9_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout9_0_0.Prose> content </JsDocsLayout9_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout9_0_0.Prose> content </BeltDocsLayout9_0_0.Prose>
@@ -86,7 +86,7 @@ let make = (props: props): React.element => {
| _ => React.null
}
| Version("v10.0.0") =>
switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout10_0_0.Docs> content </ApiOverviewLayout10_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout10_0_0.Prose> content </JsDocsLayout10_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout10_0_0.Prose> content </BeltDocsLayout10_0_0.Prose>
@@ -143,7 +143,7 @@ let make = (props: props): React.element => {
</EnableCollapsibleNavbar>
// common routes
| {base} =>
switch Belt.List.fromArray(base) {
switch List.fromArray(base) {
| list{"community", ..._rest} =>
<EnableCollapsibleNavbar>
<CommunityLayout frontmatter={component->frontmatter}> content </CommunityLayout>
@@ -160,9 +160,9 @@ let make = (props: props): React.element => {
let fm = component->frontmatter->DocFrontmatter.decode
let title = switch url {
| {base: ["docs"]} => Some("Overview | ReScript Documentation")
| _ => Belt.Option.map(fm, fm => fm.title)
| _ => Option.map(fm, fm => fm.title)
}
let description = Belt.Option.flatMap(fm, fm => Js.Null.toOption(fm.description))
let description = Option.flatMap(fm, fm => Null.toOption(fm.description))
<MainLayout>
<Meta ?title ?description version=url.version />
<div className="flex justify-center">
40 changes: 20 additions & 20 deletions src/common/BlogApi.res
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@

module GrayMatter = {
type output = {
data: Js.Json.t,
data: JSON.t,
content: string,
}

@@ -39,22 +39,22 @@ type post = {
}

let blogPathToSlug = path => {
path->Js.String2.replaceByRe(%re(`/^(archive\/)?\d\d\d\d-\d\d-\d\d-(.+)\.mdx$/`), "$2")
path->String.replaceRegExp(%re(`/^(archive\/)?\d\d\d\d-\d\d-\d\d-(.+)\.mdx$/`), "$2")
}

let mdxFiles = dir => {
Node.Fs.readdirSync(dir)->Js.Array2.filter(path => Node.Path.extname(path) === ".mdx")
Node.Fs.readdirSync(dir)->Array.filter(path => Node.Path.extname(path) === ".mdx")
}

let getAllPosts = () => {
let postsDirectory = Node.Path.join2(Node.Process.cwd(), "_blogposts")
let archivedPostsDirectory = Node.Path.join2(postsDirectory, "archive")

let nonArchivedPosts = mdxFiles(postsDirectory)->Js.Array2.map(path => {
let nonArchivedPosts = mdxFiles(postsDirectory)->Array.map(path => {
let {GrayMatter.data: data} =
Node.Path.join2(postsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter
switch BlogFrontmatter.decode(data) {
| Error(msg) => Js.Exn.raiseError(msg)
| Error(msg) => Exn.raiseError(msg)
| Ok(d) => {
path,
frontmatter: d,
@@ -63,11 +63,11 @@ let getAllPosts = () => {
}
})

let archivedPosts = mdxFiles(archivedPostsDirectory)->Js.Array2.map(path => {
let archivedPosts = mdxFiles(archivedPostsDirectory)->Array.map(path => {
let {GrayMatter.data: data} =
Node.Path.join2(archivedPostsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter
switch BlogFrontmatter.decode(data) {
| Error(msg) => Js.Exn.raiseError(msg)
| Error(msg) => Exn.raiseError(msg)
| Ok(d) => {
path: Node.Path.join2("archive", path),
frontmatter: d,
@@ -84,11 +84,11 @@ let getAllPosts = () => {
let getLivePosts = () => {
let postsDirectory = Node.Path.join2(Node.Process.cwd(), "_blogposts")

let livePosts = mdxFiles(postsDirectory)->Js.Array2.map(path => {
let livePosts = mdxFiles(postsDirectory)->Array.map(path => {
let {GrayMatter.data: data} =
Node.Path.join2(postsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter
switch BlogFrontmatter.decode(data) {
| Error(msg) => Js.Exn.raiseError(msg)
| Error(msg) => Exn.raiseError(msg)
| Ok(d) => {
path,
frontmatter: d,
@@ -106,11 +106,11 @@ let getArchivedPosts = () => {
let postsDirectory = Node.Path.join2(Node.Process.cwd(), "_blogposts")
let archivedPostsDirectory = Node.Path.join2(postsDirectory, "archive")

let archivedPosts = mdxFiles(archivedPostsDirectory)->Js.Array2.map(path => {
let archivedPosts = mdxFiles(archivedPostsDirectory)->Array.map(path => {
let {GrayMatter.data: data} =
Node.Path.join2(archivedPostsDirectory, path)->Node.Fs.readFileSync->GrayMatter.matter
switch BlogFrontmatter.decode(data) {
| Error(msg) => Js.Exn.raiseError(msg)
| Error(msg) => Exn.raiseError(msg)
| Ok(d) => {
path: Node.Path.join2("archive", path),
frontmatter: d,
@@ -140,16 +140,16 @@ module RssFeed = {
title: string,
href: string,
description: string,
pubDate: Js.Date.t,
pubDate: Date.t,
}

// TODO: This is yet again a dirty approach to prevent UTC to substract too many
// hours of my local timezone so it does end up on another day, so we set the hours
// to 15 o clock. We need to reconsider the way we parse blog article dates,
// since the dates should always be parsed from a single timezone perspective
let dateToUTCString = date => {
date->Js.Date.setHours(15.0)->ignore
date->Js.Date.toUTCString
date->Date.setHours(15)->ignore
date->Date.toUTCString
}

// Retrieves the most recent [max] blog post feed items
@@ -158,7 +158,7 @@ module RssFeed = {
getAllPosts()
->Array.map(post => {
let fm = post.frontmatter
let description = Js.Null.toOption(fm.description)->Belt.Option.getWithDefault("")
let description = Null.toOption(fm.description)->Option.getOr("")
{
title: fm.title,
href: baseUrl ++ "/blog/" ++ blogPathToSlug(post.path),
@@ -172,16 +172,16 @@ module RssFeed = {

let toXmlString = (~siteTitle="ReScript Blog", ~siteDescription="", items: array<item>) => {
let latestPubDateElement =
Belt.Array.get(items, 0)
->Belt.Option.map(item => {
items[0]
->Option.map(item => {
let latestPubDateStr = item.pubDate->dateToUTCString
`<lastBuildDate>${latestPubDateStr}</lastBuildDate>`
})
->Belt.Option.getWithDefault("")
->Option.getOr("")

let itemsStr =
items
->Js.Array2.map(({title, pubDate, description, href}) => {
->Array.map(({title, pubDate, description, href}) => {
let descriptionElement = switch description {
| "" => ""
| desc =>
@@ -201,7 +201,7 @@ module RssFeed = {
<pubDate>${dateStr}</pubDate>
</item>`
})
->Js.Array2.joinWith("\n")
->Array.join("\n")

let ret = `<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
2 changes: 1 addition & 1 deletion src/common/BlogApi.resi
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ module RssFeed: {
title: string,
href: string,
description: string,
pubDate: Js.Date.t,
pubDate: Date.t,
}
let getLatest: (~max: int=?, ~baseUrl: string=?, unit) => array<item>
let toXmlString: (~siteTitle: string=?, ~siteDescription: string=?, array<item>) => string
24 changes: 12 additions & 12 deletions src/common/BlogFrontmatter.res
Original file line number Diff line number Diff line change
@@ -85,15 +85,15 @@ type t = {
author: author,
co_authors: array<author>,
date: DateStr.t,
previewImg: Js.null<string>,
articleImg: Js.null<string>,
previewImg: Null.t<string>,
articleImg: Null.t<string>,
title: string,
badge: Js.null<Badge.t>,
description: Js.null<string>,
badge: Null.t<Badge.t>,
description: Null.t<string>,
}

let decodeBadge = (str: string): Badge.t =>
switch Js.String2.toLowerCase(str) {
switch String.toLowerCase(str) {
| "release" => Release
| "testing" => Testing
| "preview" => Preview
@@ -104,32 +104,32 @@ let decodeBadge = (str: string): Badge.t =>
exception AuthorNotFound(string)

let decodeAuthor = (~fieldName: string, ~authors, username) =>
switch Js.Array2.find(authors, a => a.username === username) {
switch Array.find(authors, a => a.username === username) {
| Some(author) => author
| None => raise(AuthorNotFound(`Couldn't find author "${username}" in field ${fieldName}`))
}

let authorDecoder = (~fieldName: string, ~authors) => {
open Json.Decode

let multiple = j => array(string, j)->Belt.Array.map(a => decodeAuthor(~fieldName, ~authors, a))
let multiple = j => array(string, j)->Array.map(a => decodeAuthor(~fieldName, ~authors, a))

let single = j => [string(j)->decodeAuthor(~fieldName, ~authors)]

either(single, multiple)
}

let decode = (json: Js.Json.t): result<t, string> => {
let decode = (json: JSON.t): result<t, string> => {
open Json.Decode
switch {
author: json->field("author", string, _)->decodeAuthor(~fieldName="author", ~authors),
co_authors: json
->optional(field("co-authors", authorDecoder(~fieldName="co-authors", ~authors), ...), _)
->Belt.Option.getWithDefault([]),
->Option.getOr([]),
date: json->field("date", string, _)->DateStr.fromString,
badge: json->optional(j => field("badge", string, j)->decodeBadge, _)->Js.Null.fromOption,
previewImg: json->optional(field("previewImg", string, ...), _)->Js.Null.fromOption,
articleImg: json->optional(field("articleImg", string, ...), _)->Js.Null.fromOption,
badge: json->optional(j => field("badge", string, j)->decodeBadge, _)->Null.fromOption,
previewImg: json->optional(field("previewImg", string, ...), _)->Null.fromOption,
articleImg: json->optional(field("articleImg", string, ...), _)->Null.fromOption,
title: json->(field("title", string, _)),
description: json->(nullable(field("description", string, ...), _)),
} {
55 changes: 27 additions & 28 deletions src/common/CompilerManagerHook.res
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ module LoadScript = {
external removeScript: (~src: string) => unit = "removeScript"

let loadScriptPromise = (url: string) => {
Js.Promise2.make((~resolve, ~reject as _) => {
Promise.make((resolve, _) => {
loadScript(
~src=url,
~onSuccess=() => resolve(Ok()),
@@ -45,11 +45,11 @@ module Semver = {
*/
let parse = (versionStr: string) => {
let parsePreRelease = str => {
switch str->Js.String2.split("-") {
switch str->String.split("-") {
| [_, identifier] =>
switch identifier->Js.String2.split(".") {
switch identifier->String.split(".") {
| [name, number] =>
switch Belt.Int.fromString(number) {
switch Int.fromString(number) {
| None => None
| Some(buildIdentifier) =>
switch name {
@@ -67,15 +67,14 @@ module Semver = {
}

// Some version contain a suffix. Example: v11.0.0-alpha.5, v11.0.0-beta.1
let isPrerelease = versionStr->Js.String2.search(%re("/-/")) != -1
let isPrerelease = versionStr->String.search(%re("/-/")) != -1

// Get the first part i.e vX.Y.Z
let versionNumber =
versionStr->Js.String2.split("-")->Belt.Array.get(0)->Belt.Option.getWithDefault(versionStr)
let versionNumber = versionStr->String.split("-")->Array.get(0)->Option.getOr(versionStr)

switch versionNumber->Js.String2.replace("v", "")->Js.String2.split(".") {
switch versionNumber->String.replace("v", "")->String.split(".") {
| [major, minor, patch] =>
switch (major->Belt.Int.fromString, minor->Belt.Int.fromString, patch->Belt.Int.fromString) {
switch (major->Int.fromString, minor->Int.fromString, patch->Int.fromString) {
| (Some(major), Some(minor), Some(patch)) =>
let preReleaseIdentifier = if isPrerelease {
parsePreRelease(versionStr)
@@ -90,16 +89,16 @@ module Semver = {
}

let toString = ({major, minor, patch, preRelease}) => {
let mainVersion = `v${major->Belt.Int.toString}.${minor->Belt.Int.toString}.${patch->Belt.Int.toString}`
let mainVersion = `v${major->Int.toString}.${minor->Int.toString}.${patch->Int.toString}`

switch preRelease {
| None => mainVersion
| Some(identifier) =>
let identifier = switch identifier {
| Dev(number) => `dev.${number->Belt.Int.toString}`
| Alpha(number) => `alpha.${number->Belt.Int.toString}`
| Beta(number) => `beta.${number->Belt.Int.toString}`
| Rc(number) => `rc.${number->Belt.Int.toString}`
| Dev(number) => `dev.${number->Int.toString}`
| Alpha(number) => `alpha.${number->Int.toString}`
| Beta(number) => `beta.${number->Int.toString}`
| Rc(number) => `rc.${number->Int.toString}`
}

`${mainVersion}-${identifier}`
@@ -138,8 +137,8 @@ let getLibrariesForVersion = (~version: Semver.t): array<string> => {
// Since version 11, we ship the compiler-builtins as a separate file, and
// we also added @rescript/core as a pre-vendored package
if version.major >= 11 {
libraries->Js.Array2.push("@rescript/core")->ignore
libraries->Js.Array2.push("compiler-builtins")->ignore
libraries->Array.push("@rescript/core")->ignore
libraries->Array.push("compiler-builtins")->ignore
}

libraries
@@ -148,7 +147,7 @@ let getLibrariesForVersion = (~version: Semver.t): array<string> => {
let getOpenModules = (~apiVersion: Version.t, ~libraries: array<string>): option<array<string>> =>
switch apiVersion {
| V1 | V2 | V3 | UnknownVersion(_) => None
| V4 => libraries->Belt.Array.some(el => el === "@rescript/core") ? Some(["RescriptCore"]) : None
| V4 => libraries->Array.some(el => el === "@rescript/core") ? Some(["RescriptCore"]) : None
}

/*
@@ -176,17 +175,17 @@ let attachCompilerAndLibraries = async (~version, ~libraries: array<string>, ())
switch await LoadScript.loadScriptPromise(compilerUrl) {
| Error(_) => Error([`Could not load compiler from url ${compilerUrl}`])
| Ok(_) =>
let promises = Belt.Array.map(libraries, async lib => {
let promises = Array.map(libraries, async lib => {
let cmijUrl = CdnMeta.getLibraryCmijUrl(version, lib)
switch await LoadScript.loadScriptPromise(cmijUrl) {
| Error(_) => Error(`Could not load cmij from url ${cmijUrl}`)
| r => r
}
})

let all = await Js.Promise2.all(promises)
let all = await Promise.all(promises)

let errors = Belt.Array.keepMap(all, r => {
let errors = Array.filterMap(all, r => {
switch r {
| Error(msg) => Some(msg)
| _ => None
@@ -259,7 +258,7 @@ let useCompilerManager = (

// Dispatch method for the public interface
let dispatch = (action: action): unit => {
Belt.Option.forEach(onAction, cb => cb(action))
Option.forEach(onAction, cb => cb(action))
switch action {
| SwitchToCompiler(id) =>
switch state {
@@ -295,7 +294,7 @@ let useCompilerManager = (

let currentLang = ready.targetLang

Js.Array2.find(availableTargetLangs, l => l === lang)->Belt.Option.forEach(lang => {
Array.find(availableTargetLangs, l => l === lang)->Option.forEach(lang => {
// Try to automatically transform code
let (result, targetLang) = switch ready.selected.apiVersion {
| V1 =>
@@ -382,7 +381,7 @@ let useCompilerManager = (
| CompilerLoadingError(msg) => msg
}
switch prev {
| Ready(ready) => Ready({...ready, errors: Js.Array2.concat(ready.errors, [msg])})
| Ready(ready) => Ready({...ready, errors: Array.concat(ready.errors, [msg])})
| _ => SetupFailed(msg)
}
})
@@ -429,8 +428,8 @@ let useCompilerManager = (

let targetLang =
Version.availableLanguages(apiVersion)
->Js.Array2.find(l => l === initialLang)
->Belt.Option.getWithDefault(Version.defaultTargetLang)
->Array.find(l => l === initialLang)
->Option.getOr(Version.defaultTargetLang)

setState(_ => Ready({
code: "",
@@ -441,7 +440,7 @@ let useCompilerManager = (
result: FinalResult.Nothing,
}))
| Error(errs) =>
let msg = Js.Array2.joinWith(errs, "; ")
let msg = Array.join(errs, "; ")

dispatchError(CompilerLoadingError(msg))
}
@@ -457,7 +456,7 @@ let useCompilerManager = (
LoadScript.removeScript(~src=CdnMeta.getCompilerUrl(ready.selected.id))

// We are removing the previous libraries, therefore we use ready.selected here
Belt.Array.forEach(ready.selected.libraries, lib =>
Array.forEach(ready.selected.libraries, lib =>
LoadScript.removeScript(~src=CdnMeta.getLibraryCmijUrl(ready.selected.id, lib))
)

@@ -487,7 +486,7 @@ let useCompilerManager = (
result: FinalResult.Nothing,
}))
| Error(errs) =>
let msg = Js.Array2.joinWith(errs, "; ")
let msg = Array.join(errs, "; ")

dispatchError(CompilerLoadingError(msg))
}
6 changes: 3 additions & 3 deletions src/common/DateStr.res
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
type t = string

// Used to prevent issues with webkit based date representations
let parse = (dateStr: string): Js.Date.t =>
dateStr->Js.String2.replaceByRe(%re("/-/g"), "/")->Js.Date.fromString
let parse = (dateStr: string): Date.t =>
dateStr->String.replaceRegExp(%re("/-/g"), "/")->Date.fromString

let fromDate = date => Js.Date.toString(date)
let fromDate = date => Date.toString(date)
let toDate = dateStr => parse(dateStr)

external fromString: string => t = "%identity"
4 changes: 2 additions & 2 deletions src/common/DateStr.resi
Original file line number Diff line number Diff line change
@@ -3,5 +3,5 @@ type t

external fromString: string => t = "%identity"

let fromDate: Js.Date.t => t
let toDate: t => Js.Date.t
let fromDate: Date.t => t
let toDate: t => Date.t
12 changes: 6 additions & 6 deletions src/common/DocFrontmatter.res
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
type t = {
title: string,
metaTitle: Js.null<string>,
description: Js.null<string>,
canonical: Js.null<string>,
metaTitle: Null.t<string>,
description: Null.t<string>,
canonical: Null.t<string>,
}

let decode = json => {
open! Json.Decode
try Some({
title: field("title", string, json),
metaTitle: optional(field("metaTitle", string, ...), json)->Js.Null.fromOption,
description: optional(field("description", string, ...), json)->Js.Null.fromOption,
canonical: optional(field("canonical", string, ...), json)->Js.Null.fromOption,
metaTitle: optional(field("metaTitle", string, ...), json)->Null.fromOption,
description: optional(field("description", string, ...), json)->Null.fromOption,
canonical: optional(field("canonical", string, ...), json)->Null.fromOption,
}) catch {
| DecodeError(_errMsg) => None
}
8 changes: 4 additions & 4 deletions src/common/DocFrontmatter.resi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
type t = {
title: string,
metaTitle: Js.null<string>, // If there's no metaTitle, we will take title instead
description: Js.null<string>,
canonical: Js.null<string>,
metaTitle: Null.t<string>, // If there's no metaTitle, we will take title instead
description: Null.t<string>,
canonical: Null.t<string>,
}

let decode: Js.Json.t => option<t>
let decode: JSON.t => option<t>
2 changes: 1 addition & 1 deletion src/common/EvalIFrame.res
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ let sendOutput = code => {
let frame =
Document.document
->Element.getElementById("iframe-eval")
->Js.Nullable.toOption
->Nullable.toOption

switch frame {
| Some(element) =>
12 changes: 6 additions & 6 deletions src/common/HighlightJs.res
Original file line number Diff line number Diff line change
@@ -10,21 +10,21 @@ let renderHLJS = (~highlightedLines=[], ~darkmode=false, ~code: string, ~lang: s
// If the language couldn't be parsed, we will fall back to text
let options = {language: lang}
let (lang, highlighted) = try (lang, highlight(~code, ~options)->valueGet) catch {
| Js.Exn.Error(_) => ("text", code)
| Exn.Error(_) => ("text", code)
}

// Add line highlighting as well
let highlighted = if Belt.Array.length(highlightedLines) > 0 {
Js.String2.split(highlighted, "\n")
->Belt.Array.mapWithIndex((i, line) =>
if Js.Array2.find(highlightedLines, lnum => lnum === i + 1) !== None {
let highlighted = if Array.length(highlightedLines) > 0 {
String.split(highlighted, "\n")
->Array.mapWithIndex((line, i) =>
if Array.find(highlightedLines, lnum => lnum === i + 1) !== None {
let content = line === "" ? "&nbsp;" : line
"<span class=\"inline-block\">" ++ (content ++ "</span>")
} else {
"<span class=\"inline-block text-inherit opacity-50\">" ++ (line ++ "</span>")
}
)
->Js.Array2.joinWith("\n")
->Array.join("\n")
} else {
highlighted
}
4 changes: 2 additions & 2 deletions src/common/Mdx.res
Original file line number Diff line number Diff line change
@@ -42,9 +42,9 @@ module MdxChildren = {
let classify = (v: t): case =>
if %raw(`function (a) { return a instanceof Array}`)(v) {
Array((Obj.magic(v): array<mdxComponent>))
} else if Js.typeof(v) == "string" {
} else if typeof(v) == #string {
String((Obj.magic(v): string))
} else if Js.typeof(v) == "object" {
} else if typeof(v) == #object {
Element((Obj.magic(v): mdxComponent))
} else {
Unknown((Obj.magic(v): unknown))
10 changes: 5 additions & 5 deletions src/common/Url.res
Original file line number Diff line number Diff line change
@@ -55,9 +55,9 @@ let prettyString = (str: string) => {
}

let parse = (route: string): t => {
let fullpath = route->Js.String2.split("/")->Belt.Array.keep(s => s !== "")
let foundVersionIndex = Js.Array2.findIndex(fullpath, chunk => {
Js.Re.test_(%re(`/latest|v\d+(\.\d+)?(\.\d+)?/`), chunk)
let fullpath = route->String.split("/")->Array.filter(s => s !== "")
let foundVersionIndex = Array.findIndex(fullpath, chunk => {
Re.test(%re(`/latest|v\d+(\.\d+)?(\.\d+)?/`), chunk)
})

let (version, base, pagepath) = if foundVersionIndex == -1 {
@@ -70,8 +70,8 @@ let parse = (route: string): t => {
}
(
version,
fullpath->Js.Array2.slice(~start=0, ~end_=foundVersionIndex),
fullpath->Js.Array2.slice(~start=foundVersionIndex + 1, ~end_=Js.Array2.length(fullpath)),
fullpath->Array.slice(~start=0, ~end=foundVersionIndex),
fullpath->Array.slice(~start=foundVersionIndex + 1, ~end=Array.length(fullpath)),
)
}

9 changes: 2 additions & 7 deletions src/common/Util.res
Original file line number Diff line number Diff line change
@@ -53,11 +53,6 @@ module String = {
}")
}

module Json = {
@val @scope("JSON")
external prettyStringify: (Js.Json.t, @as(json`null`) _, @as(4) _) => string = "stringify"
}

module Url = {
let isAbsolute: string => bool = %raw(`
function(str) {
@@ -78,9 +73,9 @@ module Date = {
external dateTimeFormat: (string, {"month": string, "day": string, "year": string}) => intl =
"DateTimeFormat"

@send external format: (intl, Js.Date.t) => string = "format"
@send external format: (intl, Date.t) => string = "format"

let toDayMonthYear = (date: Js.Date.t) => {
let toDayMonthYear = (date: Date.t) => {
dateTimeFormat("en-US", {"month": "short", "day": "numeric", "year": "numeric"})->format(date)
}
}
Loading