Skip to content
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

Playground 2022 #5375

Merged
merged 13 commits into from
Jun 1, 2022
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
59 changes: 25 additions & 34 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,43 +209,38 @@ This is usually the file you want to create to test certain compile behavior wit
- Verify the output, check in the `jscomp/test/my_file_test.ml` and `jscomp/test/my_file_test.js` to version control. The checked in `.js` file is essential for verifying regressions later on.
- Eventually check in other relevant files changed during the rebuild (depends on your compiler changes).

## Contribute to the BS Playground Bundle
## Contribute to the ReScript Playground Bundle

> Note: These instructions are designed for building the 4.06 based version of ReScript (ReScript v6).

The "Playground bundle" is the BS compiler compiled to JavaScript, including all necessary dependency files (stdlib / belt etc). It is useful for building tools where you want to compile and execute arbitrary Reason / OCaml in the browser.
The "Playground bundle" is a JS version of the ReScript compiler; including all necessary dependency files (stdlib / belt etc). It is useful for building tools where you want to compile and execute arbitrary ReScript code in the browser.

The ReScript source code is compiled with a tool called [JSOO (js_of_ocaml)](https://ocsigen.org/js_of_ocaml/3.5.1/manual/overview), which uses OCaml bytecode to compile to JavaScript and is part of the bigger OCaml ecosystem. Before we can compile anything, we need to install the required tools (requires [`opam`](https://opam.ocaml.org/doc/Install.html) to be installed):
The ReScript source code is compiled with a tool called [JSOO (js_of_ocaml)](https://ocsigen.org/js_of_ocaml/4.0.0/manual/overview), which uses OCaml bytecode to compile to JavaScript and is part of the bigger OCaml ecosystem.

```sh
# Create the right switch, if not created yet (first install)
opam switch create 4.06.1

# Makes sure to be on the right switch
opam switch 4.06.1
eval `opam config env`
Install `jsoo` via `opam`:

opam install js_of_ocaml.3.5.1
```sh
opam install js_of_ocaml.4.0.0
```

### Build the Bundle
### Building the Bundle

The entry point of the JSOO bundle is located in `jscomp/main/jsoo_main.ml` and the script for running JSOO can be found in `scripts/repl.js`. A full clean build can be done like this:
The entry point of the JSOO bundle is located in `jscomp/main/jsoo_playground_main.ml`, the code for packing the compiler into a single compiler file is located in `jscomp/snapshot.ninja`, and the script for running JSOO can be found in `scripts/repl.js`. A full clean build can be done like this:

```
# We create a target directory for storing the bundle / stdlib files
mkdir playground && mkdir playground/stdlib

# We build the ReScript source code and also the bytecode for jsoo_main.ml
# We build the ReScript source code and also the bytecode for the JSOO entrypoint
node scripts/ninja.js config && node scripts/ninja.js build

# Now we run the repl.js script pointing to our playground directory (note how it needs to be relative to the repl.js file)
BS_PLAYGROUND=../playground node scripts/repl.js
PLAYGROUND=../playground node scripts/repl.js
```

_Troubleshooting: if ninja build step failed with `Error: cannot find file '+runtime.js'`, make sure `ocamlfind` is installed with `opam install ocamlfind`._

**You should now find following files:**
After a successful compilation, you will find following files in your project:

- `playground/exports.js` -> This is the ReScript compiler, which binds the ReScript API to the `window` object.
- `playground/stdlib/*.js` -> All the ReScript runtime files.
Expand All @@ -255,44 +250,40 @@ You can now use the `exports.js` file either directly by using a `<script src="/
```
$ node
> require("./exports.js");
undefined
> let compile_result = ocaml.compile(`Js.log Sys.ocaml_version`); // You can change the code here
undefined
> eval(compile_result);
> let compiler = rescript_compiler.make()
> let result = compiler.rescript.compile(`Js.log(Sys.ocaml_version)`);
> eval(result.js_code);
4.06.2+BS
undefined
```

### Playground JS bundle API

As soon as the bundle is loaded, you will get access to following functions (as seen in [`jsoo_main.ml`](jscomp/main/jsoo_main.ml)):
As soon as the bundle is loaded, you will get access to the functions exposed in [`jsoo_playground_main.ml`](jscomp/main/jsoo_playground_main.ml). Best way to check out the API is by inspecting a compiler instance it either in node, or in the browser:

- `window.ocaml`:
- `compile(code: string)`: Compiles given code
- `shake_compile(code: string)`: Compiles given code with tree-shaking
- `compile_super_errors(code: string)`: Compiles given code and outputs `super_errors` related messages on `console.error`
- `compile_super_errors_ppx_v2(code: string)`: Compiles given code with the React v2 syntax
- `compile_super_errors_ppx_v3(code: string)`: Compiles given code with the React v3 syntax
- `load_module(cmi_path: string, cmi_content: string, cmj_name: string, cmj_content: string)`: Loads a module into the compiler (see notes on `cmj` / `cmi` below)
```
$ node
require('./exports.js')

For each compile every successful operation will return `{js_code: string}`. On compile errors, the returned object will be `{js_error_msg: string}`.
> let compiler = rescript_compiler.make()
> console.log(compiler)
```

### Working on the Playground JS API

Whenever you are modifying any files in the ReScript compiler, or in the `jsoo_main.ml` file, you'll need to rebuild the source and recreate the JS bundle.
Whenever you are modifying any files in the ReScript compiler, or in the `jsoo_playground_main.ml` file, you'll need to rebuild the source and recreate the JS bundle.

```sh
node scripts/ninja.js config && node scripts/ninja.js build
BS_PLAYGROUND=../playground node scripts/repl.js
PLAYGROUND=../playground node scripts/repl.js
```

**.cmj files in the Web**

A `.cmj` file contains compile information and JS package information of ReScript build artifacts (your `.re / .ml` modules) and are generated on build (`scripts/ninja.js build`).
A `.cmj` file contains compile information and JS package information of ReScript build artifacts (your `.res / .ml` modules) and are generated on build (`scripts/ninja.js build`).

A `.cmi` file is an [OCaml originated file extension](https://waleedkhan.name/blog/ocaml-file-extensions/) and contains all interface information of a certain module without any implementation.

In this repo, these files usually sit right next to each compiled `.ml` / `.re` file. The structure of a `.cmj` file is defined in [js_cmj_format.ml](jscomp/core/js_cmj_format.ml). You can run a tool called `./jscomp/bin/cmjdump.exe [some-file.cmj]` to inspect the contents of given `.cmj` file.
In this repo, these files usually sit right next to each compiled `.ml` / `.res` file. The structure of a `.cmj` file is defined in [js_cmj_format.ml](jscomp/core/js_cmj_format.ml). You can run a tool called `./jscomp/bin/cmjdump.exe [some-file.cmj]` to inspect the contents of given `.cmj` file.

`.cmj` files are required for making ReScript compile modules (this includes modules like ReasonReact). ReScript includes a subset of modules by default, which can be found in `jscomp/stdlib-406` and `jscomp/others`. You can also find those modules listed in the `jsoo` call in `scripts/repl.js`. As you probably noticed, the generated `playground` files are all plain `.js`, so how are the `cmj` / `cmi` files embedded?

Expand Down
10 changes: 10 additions & 0 deletions jscomp/main/jsoo_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ module Js = struct
external create_file : js_string t -> js_string t -> unit = "caml_create_file"

external to_bytestring : js_string t -> string = "caml_js_to_byte_string"

type number

external number_of_float : float -> number t = "caml_js_from_float"

external bool : bool -> bool t = "caml_js_from_bool"

type 'a js_array

external array : 'a array -> 'a js_array t = "caml_js_from_array"
end

let mk_js_error (loc : Location.t) (msg : string) =
Expand Down
10 changes: 10 additions & 0 deletions jscomp/main/jsoo_common.mli
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ module Js : sig
external create_file : js_string t -> js_string t -> unit = "caml_create_file"

external to_bytestring : js_string t -> string = "caml_js_to_byte_string"

type number

external number_of_float : float -> number t = "caml_js_from_float"

external bool : bool -> bool t = "caml_js_from_bool"

type 'a js_array

external array : 'a array -> 'a js_array t = "caml_js_from_array"
end

(*
Expand Down
Loading