Skip to content
Merged
Show file tree
Hide file tree
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
14 changes: 6 additions & 8 deletions apps/docs/markdown-pages/docs/manual/build-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,24 @@ Note that only sources marked with `"type":"dev"` will be able to resolve module
## js-post-build

Hook that's invoked every time a file is recompiled. Good for JS build system interop, but please use it **sparingly**. Calling your custom command for every recompiled file slows down your build and worsens the building experience for even third-party users of your lib.
Hook that's invoked for every JavaScript file emitted by a build. It can be useful for JavaScript build-system interop, but use it **sparingly**. Starting another process for every recompiled file can noticeably slow down builds for both applications and libraries.

Example:

```json
{
"js-post-build": {
"cmd": "/path/to/node ../../postProcessTheFile.js"
"cmd": "node ./postProcess.mjs"
}
}
```

Note that the path resolution for the command (`node` in this case) is done so:
The build system appends the absolute path to the generated JavaScript file as the command's final argument. The path points:

- `/myCommand` is resolved into `/myCommand`
- `package/myCommand` is resolved into `node_modules/package/myCommand`
- `./myCommand` is resolved into `myProjectRoot/myCommand`
- `myCommand` is just called as `myCommand`, aka a globally available executable. But note that ReScript doesn't read into your shell's environment, so if you put e.g. `node`, it won't find it unless you specify an absolute path. Alternatively, add `#!/usr/local/bin/node` to the top of your script to directly call it without prepending `node`.
- next to the source file when `"in-source": true`
- into the corresponding `lib/<module>/` output directory when `"in-source": false`

The command itself is called from inside `lib/bs`.
Since ReScript 12.2, the command runs from the directory containing the `rescript.json` where the hook is defined, and output written to stdout or stderr is forwarded to the build output. This makes relative paths predictable in nested packages and monorepos.

## jsx

Expand Down
72 changes: 40 additions & 32 deletions apps/docs/markdown-pages/docs/manual/build-overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,77 +9,85 @@ order: 1

# Build System Overview

ReScript comes with a build system, [`rescript`](https://www.npmjs.com/package/rescript), that's fast, lean and used as the authoritative build system of the community.
ReScript comes with a fast, native build system through the [`rescript`](https://www.npmjs.com/package/rescript) package.

Every ReScript project needs a build description file, `rescript.json`.

## Options
The current build system was introduced in ReScript 12. It tracks dependencies across packages and schedules only the work affected by a change. See [Reforging the Build System](../../blog/reforging-build-system.mdx) for a deeper look at its architecture.

See `rescript help`:
## Commands

Run `rescript help` to see the available commands:

```
❯ rescript help
Usage: rescript <options> <subcommand>
`rescript` is equivalent to `rescript build`
Options:
-v, -version display version number
-h, -help display help
Subcommands:
build
clean
format
convert
dump
help
Run `rescript <subcommand> -h` for subcommand help. Examples:
rescript build -h
rescript format -h
Usage: rescript [OPTIONS] <COMMAND>
Commands:
build Build the project (default command)
watch Build, then start a watcher
clean Clean the build artifacts
format Format ReScript files
compiler-args Print the compiler arguments for a ReScript source file
help Print this message or the help of the given subcommand(s)
```

## Build Project
Use `rescript help <command>` for the options of a particular command.

## Build a Project

Each build will create build artifacts from your project's source files.

**To build a project (including its dependencies)**, run:
To build a project, including its dependencies, run:

```sh
rescript
```

Which is an alias for `rescript build`.
This is an alias for `rescript build`.

To keep a build watcher, run:
To build once and then watch for changes, run:

```sh
rescript -w
rescript watch
```

Any new file change will be picked up and the build will re-run.

**Note**: third-party libraries in `node_modules` aren't watched, as doing so may exceed the node.js watcher count limit.
Third-party libraries in `node_modules` aren't watched.

For monorepo setup in ReScript 12+, see [Setting up a monorepo](./build-monorepo-setup.mdx).

## Clean Project
### Incremental Builds

The build system recompiles only files affected by a change. Interface files (`.resi`) can reduce this work further: if an implementation change doesn't alter the corresponding interface, dependent modules don't need to be recompiled. See [Module signatures](./module.mdx#signatures) and [Build Performance](./build-performance.mdx).

## Clean a Project

If you ever get into a stale build for edge-case reasons, use:

```sh
rescript clean
```

## Compile with stricter errors in CI
## Format Source Files

Format the project's ReScript files with:

```sh
rescript format
```

Use `rescript format --check` in CI to check formatting without writing changes. You can also format selected files by passing their paths, or format standard input with `--stdin .res` or `--stdin .resi`.

## Compile with Stricter Errors in CI

**Since 11.1**

You may want to compile your project with stricter rules for production, than when developing. With the `-warn-error` build flag, this can easily be done, for instance in a continuous integration script. E.g.:
You may want to compile your project with stricter rules in CI than during development. Use the `--warn-error` build flag:

```sh
rescript -warn-error +110
rescript build --warn-error "+110"
```

Here, warning number 110, which is triggered when a [`%todo`](../../syntax-lookup/extension_todo.mdx) has been found, gets promoted to an error. The full list of warning numbers can be found [here](./warning-numbers.mdx).
15 changes: 15 additions & 0 deletions apps/docs/markdown-pages/docs/manual/build-performance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ order: 7

ReScript considers performance at install time, build time and run time as a serious feature; it's one of those things you don't notice until you realize it's missing.

## Measuring Build Times

When investigating a slowdown, compare the same kinds of build under the same conditions:

- a clean build
- a no-op build, with no source changes
- an incremental build after changing a leaf module
- an incremental build after changing a widely used module

Build times depend on the dependency graph, hardware, filesystem, and surrounding tools. Compare ReScript's build time with the duration of the entire development command: bundling, code generation, and tests can take more time than compilation itself.

## Under the Hood

Since ReScript v12, `rescript build` uses a native build system written in Rust. It reads `rescript.json`, tracks dependencies between modules and packages, and invokes the compiler directly. See [Reforging the ReScript Build System](../../blog/reforging-build-system.mdx) for more about its architecture.
Expand Down Expand Up @@ -77,6 +88,10 @@ Unfortunately, JS build systems are usually the bottleneck for building a JS pro
- Convert more files into ReScript =). Fewer files going through fewer parts of the JS pipeline helps a ton.
- Careful with bringing in more dependencies: libraries, syntax transforms (e.g. the unofficially supported PPX), build step loaders, etc. The bulk of these dragging down the editing & building experience might out-weight the API benefits they provide.

Keep compiler artifacts between builds unless you specifically need a clean build. Cleaning before every compilation prevents incremental builds from doing their job.

Commands configured through [`js-post-build`](./build-configuration.mdx#js-post-build) run once per emitted JavaScript file. Starting another process for each file can slow down an otherwise fast incremental build.

## Hot Reloading

Hot reloading refers to maintaining a dev server and listening to file changes in a way that allows the server to pipe some delta changes right into the currently running browser page. This provides a relatively fast iteration workflow while working in specific frameworks.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ReScript offers a unique project conversion methodology which:

## Step 1: Install ReScript

Run `npm install rescript` on your project, then imitate our [New Project](./installation.mdx#new-project) workflow by adding a `rescript.json` at the root. Then start `npx rescript -w`.
Run `npm install rescript` in your project, then follow the [New Project](./installation.mdx#new-project) workflow by adding a `rescript.json` at the root. Start the compiler in watch mode with `npx rescript watch`.

## Step 2: Copy Paste the Entire JS File

Expand Down
52 changes: 45 additions & 7 deletions apps/docs/markdown-pages/docs/manual/editor-code-analysis.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Dead code refers to code that's present in your codebase but is never executed.
- Confusion during development
- Misleading assumptions about functionality

ReScript’s language design allows for accurate and efficient dead code analysis using the **ReScript Code Analyzer**, available via the official VSCode extension.
ReScript’s language design allows for accurate and efficient dead code analysis using the **ReScript Code Analyzer**. It is available through the official VS Code extension and the `rescript-tools` executable bundled with ReScript.

### Prerequisites

Expand All @@ -37,9 +37,23 @@ ReScript’s language design allows for accurate and efficient dead code analysi

- The “Problems” pane populates with dead code warnings and suggestions.

### Reactive Updates (New)
### Command-Line Analysis

Reactive dead code updates are a newer enhancement of Editor Code Analysis and require ReScript VSCode extension v1.73.9 or higher (pre-release).
To run the analyses configured in `rescript.json` from the command line:

```sh
rescript-tools reanalyze -config
```

ReScript 12.1 added a reactive mode that caches unchanged analysis data:

```sh
rescript-tools reanalyze -config -reactive
```

The diagnostic flags `-runs <n>` and `-churn <n>` can benchmark cache effectiveness and incremental correctness. `-mermaid` prints a Mermaid diagram of the reactive pipeline.

`reanalyze-server` keeps the analysis process alive between requests. JSON analysis requests delegate to it transparently, so integrations can use `rescript-tools reanalyze -json` without managing the server themselves.

## Real-World Use Cases

Expand Down Expand Up @@ -135,26 +149,50 @@ type user = {name: string}
let getUserName = user => user.name
```

### Scoped `@@live` and `@@dead`

ReScript 12.2 added standalone forms that set the default for the remainder of the current file or module:

```res
module PublicApi = {
@@live
let load = () => "loaded"
let save = _value => ()
}
module DeferredCleanup = {
@@dead
let oldParser = input => input
let oldSerializer = value => value
}
```

Items under `@@live` are treated as live roots and can keep their dependencies alive. Items under `@@dead` are suppressed but don't keep their dependencies alive. A later `@@live` or `@@dead` changes the default again, and nested modules inherit the active setting.

## Configuration

Add to your `rescript.json`:

```json
"reanalyze": {
"analysis": ["dce"],
"suppress": ["src/bindings", "src/stories", "src/routes"],
"unsuppress": [],
"suppress": ["src/generated/**", "src/stories/**"],
"unsuppress": ["src/generated/handwritten/**"],
"transitive": false
}
```

### Options:

- **analysis**: Enables dead code analysis (`"dce"`)
- **suppress**: Silences reporting for paths (still analyzes)
- **unsuppress**: Re-enables reports within suppressed paths
- **suppress**: Silences reporting for matching paths (they are still analyzed)
- **unsuppress**: Re-enables reports for matching paths within a suppressed area
- **transitive**: Controls reporting of indirectly dead code

Since ReScript 12.3, `suppress` and `unsuppress` accept glob patterns such as `src/generated/**`. Plain path prefixes continue to work.

**Recommendation:** Set `transitive: false` for incremental cleanup.

## Summary
Expand Down
25 changes: 25 additions & 0 deletions apps/docs/markdown-pages/docs/manual/function.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ export { greet, greetMore };

If your function has no argument, just write `let greetMore = () => {...}`.

## Function Directives

Use `@directive` directly before a function expression to emit a JavaScript directive at the start of that function. This works with synchronous and asynchronous functions, including functions with labeled arguments.

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

```res
let greet = @directive("'use server'") async name => {
"Hello " + name
}
```

```js
async function greet(name) {
"use server";
return "Hello " + name;
}

export { greet };
```

</CodeTab>

The attribute belongs to the function expression, after the `=` rather than before the `let` binding. File-level directives use the separate `@@directive` form. See the [`@directive` syntax reference](../../syntax-lookup/decorator_directive.mdx).

## Labeled Arguments

<div className="hidden">
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/markdown-pages/docs/manual/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ bun create rescript-app

</CodeTab>

ReScript's native executables are distributed as optional, platform-specific packages such as `@rescript/darwin-arm64`, `@rescript/linux-x64`, and `@rescript/win32-x64`. Package managers install the matching package automatically. Make sure optional dependencies are enabled; if ReScript reports that its platform package is missing, reinstall dependencies without an option such as npm's `--omit=optional`.

<Info>
**pnpm users:** ReScript-compiled JS imports from `@rescript/runtime` directly, but pnpm's strict isolation does not expose transitive dependencies at the app root. Either install the runtime as a direct dependency (`pnpm add @rescript/runtime`), or hoist it by adding the following to `pnpm-workspace.yaml`:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ This will:

Loaders are indeed troublesome; in the meantime, please use e.g. `%raw("require('./myStyles.css')")` at the top of your file. This just uses [`raw`](./embed-raw-javascript.mdx) to compile the snippet into an actual JS require.

## Getting Project's Dependencies

`rescript` generates one `MyFile.d` file per `MyFile` source file; you'll find them in `lib/bs`. These are human readable, machine-friendly list of the dependencies of said `MyFile`. You can read into them for your purpose (though mind the IO overhead). Use these files instead of creating your own dependency graph; we did the hard work of tracking the dependencies as best as possible (including inner modules, `open`s, module names overlap, etc).

## Run Script Per File Built

See [js-post-build](./build-configuration.mdx#js-post-build). Though please use it sparingly; if you hook up a node.js script after each file built, you'll incur the node startup time per file!
2 changes: 1 addition & 1 deletion apps/docs/markdown-pages/docs/manual/migrate-to-v12.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ Below is a consolidated excerpt of all the breaking changes from the compiler ch

### Build system & CLI

- The new Rust-based `rewatch` build system now powers the `rescript` command. The old Ninja-based builder system moved behind `rescript legacy`, and `--compiler-args` became the `compiler-args` subcommand. https://github.com/rescript-lang/rescript/pull/7551 https://github.com/rescript-lang/rescript/pull/7593 https://github.com/rescript-lang/rescript/pull/7928
- The new Rust-based `rewatch` build system now powers the `rescript` command. The old Ninja-based builder is available through the separate `rescript-legacy` executable, and `--compiler-args` became the `compiler-args` subcommand. https://github.com/rescript-lang/rescript/pull/7551 https://github.com/rescript-lang/rescript/pull/7593 https://github.com/rescript-lang/rescript/pull/7928
- `rescript format` was reimplemented in Rust, its options now use the `--check` / `--stdin` long-form spelling, and the `--all` flag was removed because every tracked file (non-dev by default) is formatted automatically. https://github.com/rescript-lang/rescript/pull/7603 https://github.com/rescript-lang/rescript/pull/7752
- The `rescript dump` command was removed; call `bsc` directly if you need to inspect `.cmi` files. https://github.com/rescript-lang/rescript/pull/7710

Expand Down
33 changes: 32 additions & 1 deletion apps/docs/markdown-pages/docs/manual/module.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,38 @@ This also means that the compiler can't make assumptions about the type.
In certain cases, when working with abstract types and `option` for example, the compiler doesn't know whether
the abstract type can be the JavaScript value `undefined` or not. This can lead to less optimal code being generated.
For this reason, you can use the `@notUndefined` decorator to tell the compiler that the abstract type can never be `undefined`
(use with caution and see the `@notUndefined` decorator documentation for more details and caveats).
(use with caution and see the [`@notUndefined` decorator documentation](../../syntax-lookup/decorator_not_undefined.mdx) for more details and caveats).

### Refining Module Types with Constraints

A `with type` constraint can refine an abstract type in an existing module type:

```res prelude
module type Storage = {
type t
let make: string => t
let serialize: t => string
}
module type PrivateStringStorage = Storage with type t = private string
```

The `private` constraint exposes that `t` is represented as `string`, so a `t` can be coerced to `string`, while preventing callers from constructing a `t` from any string. Only functions exposed by the module, such as `make`, can create one.

```res
module StringStorage: PrivateStringStorage = {
type t = string
let make = value => value
let serialize = value => value
}
let unwrap = value => {
let _ = StringStorage.serialize(value)
(value :> string)
}
```

The call to `serialize` is enough for the compiler to infer `value` as `StringStorage.t`; ReScript 12 allows that inferred type variable to be resolved while checking the coercion.

### Extending module signatures

Expand Down
12 changes: 12 additions & 0 deletions apps/docs/markdown-pages/docs/manual/polymorphic-variant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,18 @@ export { myColor, theCompany, message };

**Note**: for the coercion to work, the poly variant type needs to be closed; you'd need to annotate it, since otherwise, `theCompany` would be inferred as `[> #Apple]`.

Since ReScript 12, a closed polymorphic variant can also be coerced to a regular variant when their possible constructors have matching runtime representations:

```res
type command = [#Start | #Stop]
type event = Start | Stop | Pause
let command: command = #Start
let event: event = (command :> event)
```

Here, `#Start` and `Start` are both represented by the string `"Start"`, and every value in `command` can be represented by `event`. The coercion therefore has no runtime cost.

## Tips & Tricks

### Variant vs Polymorphic Variant
Expand Down
Loading
Loading