Dec 7, 2020

ReScript 8.4

bsb improvements

Hongbo Zhang
Compiler & Build System


ReScript is a soundly typed language with an optimizing compiler focused on the JS platform. It's focused on type safety, performance and JS interop. It used to be called BuckleScript.

ReScript@8.4 is now available for testing, you can try it via

npm i bs-platform@8.4.2

The changes are listed here.

We will go through some highlighted changes.

The integrity of bsb -make-world

When we introduced bsb as a build system around four years ago, we made the assumption that dependencies are immutable, so that once it's built for the first time, we don't need to rebuild it any more. The integrity of bsb -make-world will be broken when the assumption does not hold.

In this release, we fix the integrity of bsb -make-world which allows user to change the dependencies. The fix is well implemented that people who don't do such modifications will not pay for it.

This is one of the highest desired feature request based on the user feedback, so we will expand a bit here why it is tricky to implement it without compromising performance.

In ReScript compilation scheme, dependencies as packages are treated as a black box, changes of dependencies should be transitive. The is due to that we have cross module optimizations and the binary interface itself is a hash of its dependencies. So for a package dependency chain: A -> B -> C, if A changes and B does not change, C still needs get rebuilt. Because the intermediate output of B may still change due to the change of A. To make things worse, each package comes with an installation process which is a shell script, so re-installation will make the package look like freshly built.

In this release, we track the installation in the build graph as well, and we calculate the hash of the installation of dependencies and put it in the dependent's command line flags for building binary artifacts. Such strategy benefits in such aspects:

  • The calculation of the hash of installation is almost free since it is just one stat, we don't need track all dependencies' artifacts.

  • The introduction of such hashing does not appear in parsing, so that the changes of dependencies will never trigger re-parsing.

  • Once the package installation is a no-op, the transitive rebuild graph will be cut off so that we can save some unneeded rebuild.

  • When people make changes to the dependencies, if such changes don't change the package interface, it will not trigger the build of its dependents.

Introducing pinned-dependencies

To make bsb -make-world more practical for e.g. multi-package setups (lerna, yarn workspaces, etc.), we introduced a new concept called pinned-dependencies. A pinned dependency allows you to automatically rebuild packages that are pinned by a toplevel package, like your final webapp.

Please refer to our pinned dependencies docs for more details.

More robust handling of removal of staled output

When people delete or rename ReScript files, it will introduce dangling staled output, for example, renaming src/A.res into src/B.res will bring src/A.cmi (and more intermediate outputs) stale. This is worse than it sounds like, suppose you have a local src/List.res which shadows the stdlib's List. Now we remove it, it will introduce stale List.cmi file, without the proper removal of such stale outptu, such stale List.cmi file will break the integrity of the build.

In this release, we introduced a more robust algorithm that will always remove stale output before the build so that such integrity is not broken.

Last but not the least, we continue improving the readability, debuggability of the generated output : )

Happy Hacking! -- Hongbo Zhang

Want to read more?
Back to Overview