Skip to content

Commit 77e2f47

Browse files
committed
Fix playground infrastructure for ReScript 11 and update CONTRIBUTING instructions
1 parent deced79 commit 77e2f47

File tree

4 files changed

+85
-95
lines changed

4 files changed

+85
-95
lines changed

CONTRIBUTING.md

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ This is usually the file you want to create to test certain compile behavior wit
196196

197197
## Contribute to the ReScript Playground Bundle
198198

199-
> Note: These instructions are designed for building the 4.06 based version of ReScript (ReScript v6).
200-
201199
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.
202200

203201
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.
@@ -210,99 +208,64 @@ opam install js_of_ocaml.4.0.0
210208

211209
### Building the Bundle
212210

213-
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:
214-
215-
```
216-
# We create a target directory for storing the bundle / stdlib files
217-
mkdir playground && mkdir playground/stdlib
218-
219-
# We build the ReScript source code and also the bytecode for the JSOO entrypoint
220-
node scripts/ninja.js config && node scripts/ninja.js build
221-
222-
# Now we run the repl.js script which will create all the required artifacts in the `./playground` directory
223-
node scripts/repl.js
224-
```
225-
226-
In case you want to build the project with our default third party packages (like `@rescript/react`), prepare the `playground-bundling` project and then run `repl.js` with `BUILD_THIRD_PARTY` enabled:
211+
The entry point of the JSOO bundle is located in `jscomp/jsoo/jsoo_playground_main.ml`, the compiler and its relevant runtime cmij files can be built via make:
227212

228-
```
229-
# Prepare the `playground-bundling` project to allow building of the third party cmij packages
230-
npm link
231-
cd packages/playground-bundling
232-
npm install
233-
npm link rescript
234-
235-
BUILD_THIRD_PARTY=true node scripts/repl.js
213+
```sh
214+
make playground
215+
make playground-cmijs
236216
```
237217

238-
_Troubleshooting: if ninja build step failed with `Error: cannot find file '+runtime.js'`, make sure `ocamlfind` is installed with `opam install ocamlfind`._
218+
Note that building the cmijs is based on the dependencies defined in `packages/playground-bundling/package.json`. In case you want to build some different version of e.g. `@rescript/react` or just want to add a new package, change the definition within the `package.json` file and re-run `make playground-cmijs` again.
239219

240220
After a successful compilation, you will find following files in your project:
241221

242222
- `playground/compiler.js` -> This is the ReScript compiler, which binds the ReScript API to the `window` object.
243-
- `playground/stdlib/*.js` -> All the ReScript runtime files.
244223
- `playground/packages` -> Contains third party deps with cmij.js files (as defined in `packages/playground-bundling/bsconfig.json`)
224+
- `playground/compilerCmij.js` -> The compiler base cmij containing all the relevant core modules (`Js`, `Belt`, `Pervasives`, etc.)
245225

246-
You can now use the `compiler.js` file either directly by using a `<script src="/path/to/compiler.js"/>` inside a html file, use a browser bundler infrastructure to optimize it, or you can even use it with `nodejs`:
226+
You can now use the `compiler.js` file either directly by using a `<script src="/path/to/compiler.js"/>` and `<script src="/path/to/packages/compilerCmij.js"/>` inside a html file, use a browser bundler infrastructure to optimize it, or use `nodejs` to run it on a command line:
247227

248228
```
249229
$ node
250230
> require("./compiler.js");
231+
> require("./packages/compilerCmij.js")
251232
> let compiler = rescript_compiler.make()
252233
> let result = compiler.rescript.compile(`Js.log(Sys.ocaml_version)`);
253234
> eval(result.js_code);
254235
4.06.2+BS
255236
```
256237

257-
You can also run `node playground/playground_test.js` for a quick sanity check to see if all the build artifacts are working together correctly.
258-
259-
### Playground JS bundle API
238+
### Testing the Playground bundle
260239

261-
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:
262-
263-
```
264-
$ node
265-
require('./compiler.js')
266-
267-
> let compiler = rescript_compiler.make()
268-
> console.log(compiler)
269-
```
240+
Run `node playground/playground_test.js` for a quick sanity check to see if all the build artifacts are working together correctly. When releasing the playground bundle, the test will always be executed before publishing to catch regressions.
270241

271242
### Working on the Playground JS API
272243

273244
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.
274245

275-
```sh
276-
node scripts/ninja.js config && node scripts/ninja.js build
277-
node scripts/repl.js
278246
```
247+
make playground
279248
280-
**.cmj files in the Web**
281-
282-
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`).
283-
284-
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.
285-
286-
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.
287-
288-
`.cmj` files are required to compile modules (this includes modules like RescriptReact). 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?
289-
290-
JSOO offers an `build-fs` subcommand that takes a list of `.cmi` and `.cmj` files and creates a `cmij.js` file that can be loaded by the JS runtime **after** the `compiler.js` bundle has been loaded (either via a `require()` call in Node, or via `<link/>` directive in an HTML file). Since we are shipping our playground with third party modules like `RescriptReact`, we created a utility directory `packages/playground-bundling` that comes with a utility script to do the `cmij.js` file creation for us. Check out `packages/playground-bundling/scripts/generate_cmijs.js` for details.
249+
# optionally run your test / arbitrary node script to verify your changes
250+
node playground/playground_test.js
251+
```
291252

292253
### Publishing the Playground Bundle on our KeyCDN
293254

294255
> Note: If you want to publish from your local machine, make sure to set the `KEYCDN_USER` and `KEYCDN_PASSWORD` environment variables accordingly (credentials currently managed by @ryyppy). Our CI servers / GH Action servers are already pre-configured with the right env variable values.
295256
296257
Our `compiler.js` and third-party packages bundles are hosted on [KeyCDN](https://www.keycdn.com) and uploaded via FTPS.
297258

298-
After a successful bundle build, run our upload script to publish the build artifacts to our server:
259+
The full release can be executed with the following make script:
299260

300261
```
301-
playground/upload_bundle.sh
262+
make playground-release
302263
```
303264

304265
The script will automatically detect the ReScript version from the `compiler.js` bundle and automatically create the correct directory structure on our CDN ftp server.
305266

267+
Note that there's currently still a manual step involved on [rescript-lang.org](https://rescript-lang.org) to make the uploaded playground version publicly available.
268+
306269
## Contribute to the API Reference
307270

308271
The API reference is generated from doc comments in the source code. [Here](https://github.com/rescript-lang/rescript-compiler/blob/99650/jscomp/others/js_re.mli#L146-L161)'s a good example.

Makefile

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ lib: build node_modules/.bin/semver
5353
artifacts: lib
5454
./scripts/makeArtifactList.js
5555

56+
# Builds the core playground bundle (without the relevant cmijs files for the runtime)
57+
playground:
58+
dune build --profile browser
59+
cp ./_build/default/jscomp/jsoo/jsoo_playground_main.bc.js playground/compiler.js
60+
61+
# Creates all the relevant core and third party cmij files to side-load together with the playground bundle
62+
playground-cmijs: artifacts
63+
node packages/playground-bundling/scripts/generate_cmijs.js
64+
65+
# Builds the playground, runs some e2e tests and releases the playground to the
66+
# CDN (requires KEYCDN_USER and KEYCDN_PASSWORD set in the env variables)
67+
playground-release: playground playground-cmijs
68+
node playground/playground_test.js
69+
sh playground/upload_bundle.sh
70+
5671
format:
5772
dune build @fmt --auto-promote
5873

@@ -70,4 +85,4 @@ clean-all: clean clean-gentype
7085

7186
.DEFAULT_GOAL := build
7287

73-
.PHONY: build watch ninja bench dce test test-syntax test-syntax-roundtrip test-gentype test-all lib artifacts format checkformat clean-gentype clean clean-all
88+
.PHONY: build watch ninja bench dce test test-syntax test-syntax-roundtrip test-gentype test-all lib playground playground-cmijs test-playground playground-release artifacts format checkformat clean-gentype clean clean-all

packages/playground-bundling/scripts/generate_cmijs.js

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@
55
* project. Or in other words: You need to build cmij files with the same
66
* rescript version as the compiler bundle.
77
*
8-
* This script extracts all cmi / cmj files of all dependencies listed in the
9-
* project root's bsconfig.json, creates cmij.js files for each library and
10-
* puts them in the compiler playground directory.
8+
* This script extracts all cmi / cmj files of the rescript/lib/ocaml and all
9+
* dependencies listed in the project root's bsconfig.json, creates cmij.js
10+
* files for each library and puts them in the compiler playground directory.
1111
*
1212
* The cmij files are representing the marshaled dependencies that can be used with the ReScript
1313
* playground bundle.
14-
*
15-
* This script is intended to be called by the `repl.js` script, but it can be independently run
16-
* by either running it within the compiler repo, or by providing a proper `PLAYGROUND_DIR` environment
17-
* flag pointing to the target folder the artifacts will be created.
1814
*/
1915

2016
const child_process = require("child_process");
@@ -23,13 +19,20 @@ const path = require("path");
2319

2420
const bsconfig = require("../bsconfig.json");
2521

26-
const PLAYGROUND_DIR =
27-
process.env.PLAYGROUND ||
28-
path.join(__dirname, "..", "..", "..", "playground");
22+
const RESCRIPT_COMPILER_ROOT_DIR = path.join(__dirname, "..", "..", "..");
23+
const PLAYGROUND_DIR = path.join(RESCRIPT_COMPILER_ROOT_DIR, "playground");
2924

25+
// The playground-bundling root dir
3026
const PROJECT_ROOT_DIR = path.join(__dirname, "..");
27+
28+
// Final target output directory where all the cmijs will be stored
3129
const PACKAGES_DIR = path.join(PLAYGROUND_DIR, "packages");
3230

31+
// Making sure this directory exists, since it's not checked in to git
32+
if (!fs.existsSync(PACKAGES_DIR)) {
33+
fs.mkdirSync(PACKAGES_DIR, { recursive: true });
34+
}
35+
3336
const config = {
3437
cwd: PROJECT_ROOT_DIR,
3538
encoding: "utf8",
@@ -43,6 +46,9 @@ function e(cmd) {
4346
console.log(`<<<<<<`);
4447
}
4548

49+
e(`npm install`);
50+
e(`npm link ${RESCRIPT_COMPILER_ROOT_DIR}`);
51+
4652
const packages = bsconfig["bs-dependencies"];
4753

4854
// We need to build the compiler's builtin modules as a separate cmij.
@@ -63,35 +69,40 @@ function buildCompilerCmij() {
6369
);
6470
}
6571

66-
packages.forEach(function installLib(package) {
67-
const libOcamlFolder = path.join(
68-
PROJECT_ROOT_DIR,
69-
"node_modules",
70-
package,
71-
"lib",
72-
"ocaml"
73-
);
74-
const libEs6Folder = path.join(
75-
PROJECT_ROOT_DIR,
76-
"node_modules",
77-
package,
78-
"lib",
79-
"es6"
80-
);
81-
const outputFolder = path.join(PACKAGES_DIR, package);
72+
function buildThirdPartyCmijs() {
73+
packages.forEach(function installLib(package) {
74+
const libOcamlFolder = path.join(
75+
PROJECT_ROOT_DIR,
76+
"node_modules",
77+
package,
78+
"lib",
79+
"ocaml"
80+
);
81+
const libEs6Folder = path.join(
82+
PROJECT_ROOT_DIR,
83+
"node_modules",
84+
package,
85+
"lib",
86+
"es6"
87+
);
88+
const outputFolder = path.join(PACKAGES_DIR, package);
8289

83-
const cmijFile = path.join(outputFolder, `cmij.js`);
90+
const cmijFile = path.join(outputFolder, `cmij.js`);
8491

85-
if (!fs.existsSync(PLAYGROUND_DIR)) {
86-
console.error(`PLAYGROUND_DIR "${PLAYGROUND_DIR}" does not exist`);
87-
process.exit(1);
88-
}
92+
if (!fs.existsSync(PLAYGROUND_DIR)) {
93+
console.error(`PLAYGROUND_DIR "${PLAYGROUND_DIR}" does not exist`);
94+
process.exit(1);
95+
}
8996

90-
if (!fs.existsSync(outputFolder)) {
91-
fs.mkdirSync(outputFolder, { recursive: true });
92-
}
93-
e(`find ${libEs6Folder} -name '*.js' -exec cp {} ${outputFolder} \\;`);
94-
e(
95-
`find ${libOcamlFolder} -name "*.cmi" -or -name "*.cmj" | xargs -n1 basename | xargs js_of_ocaml build-fs -o ${cmijFile} -I ${libOcamlFolder}`
96-
);
97-
});
97+
if (!fs.existsSync(outputFolder)) {
98+
fs.mkdirSync(outputFolder, { recursive: true });
99+
}
100+
e(`find ${libEs6Folder} -name '*.js' -exec cp {} ${outputFolder} \\;`);
101+
e(
102+
`find ${libOcamlFolder} -name "*.cmi" -or -name "*.cmj" | xargs -n1 basename | xargs js_of_ocaml build-fs -o ${cmijFile} -I ${libOcamlFolder}`
103+
);
104+
});
105+
}
106+
107+
buildCompilerCmij();
108+
buildThirdPartyCmijs();

playground/playground_test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require("./compiler.js")
2+
require("./packages/compilerCmij.js")
23
require("./packages/@rescript/react/cmij.js")
34

45
let compiler = rescript_compiler.make()

0 commit comments

Comments
 (0)