{"id":22899006,"url":"https://github.com/dzakh/rescript-stdlib-vendorer","last_synced_at":"2025-05-12T20:16:43.340Z","repository":{"id":62075118,"uuid":"551020518","full_name":"DZakh/rescript-stdlib-vendorer","owner":"DZakh","description":"Tool to support usage of a vendored standard library in ReScript","archived":false,"fork":false,"pushed_at":"2023-11-23T20:40:32.000Z","size":1101,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-07T10:34:32.494Z","etag":null,"topics":["cli","linter","rescript","stdlib"],"latest_commit_sha":null,"homepage":"","language":"ReScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DZakh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-13T17:56:18.000Z","updated_at":"2025-02-23T20:01:46.000Z","dependencies_parsed_at":"2024-10-06T21:32:26.402Z","dependency_job_id":null,"html_url":"https://github.com/DZakh/rescript-stdlib-vendorer","commit_stats":{"total_commits":52,"total_committers":1,"mean_commits":52.0,"dds":0.0,"last_synced_commit":"679ee8096af0fe3eb3602818439b3207c7a473d9"},"previous_names":["dzakh/rescript-stdlib-cli"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DZakh%2Frescript-stdlib-vendorer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DZakh%2Frescript-stdlib-vendorer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DZakh%2Frescript-stdlib-vendorer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DZakh%2Frescript-stdlib-vendorer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DZakh","download_url":"https://codeload.github.com/DZakh/rescript-stdlib-vendorer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253815008,"owners_count":21968562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","linter","rescript","stdlib"],"created_at":"2024-12-14T00:35:38.952Z","updated_at":"2025-05-12T20:16:43.317Z","avatar_url":"https://github.com/DZakh.png","language":"ReScript","readme":"[![CI](https://github.com/DZakh/rescript-stdlib-vendorer/actions/workflows/ci.yml/badge.svg)](https://github.com/DZakh/rescript-stdlib-vendorer/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/DZakh/rescript-stdlib-vendorer/branch/main/graph/badge.svg?token=40G6YKKD6J)](https://codecov.io/gh/DZakh/rescript-stdlib-vendorer)\n[![npm](https://img.shields.io/npm/dm/rescript-stdlib-vendorer)](https://www.npmjs.com/package/rescript-stdlib-vendorer)\n\n# The ultimate answer to Belt vs Js in ReScript\n\nProbably every developer that comes to [ReScript](https://rescript-lang.org) stumbles upon a dilemma about which module from the standard library they should use to work with JavaScript API.\n\n- Should I take the [Js](https://rescript-lang.org/docs/manual/latest/api/js) module with a familiar design for JavaScript developers and runtime-free bindings? 🧐\n- Or should I take the more powerful [Belt](https://rescript-lang.org/docs/manual/latest/api/belt)? 🤔\n- Wait, I heard something about [rescript-js](https://github.com/bloodyowl/rescript-js)! Maybe that’s what I need? 😅\n\nThose are pretty familiar thoughts, right? I’ve seen a lot of discussions about which one you should use, but there were never solid answers, and the result was either “it depends” or “I personally like one over another because of X”.\n\nMy lovely [Carla](https://www.carla.se/) colleague [Daggala](https://twitter.com/daggala) has written a very detailed [article](https://www.daggala.com/belt_vs_js_array_in_rescript/) about the problem, so I won’t repeat her and continue.\n\n## Problem 2: Multiple sources of truth\n\nNo matter what we choose, sometimes we still need to use another module. It’s because some functions which exist in `Js` don’t exist in `Belt` and vise-versa.\n\n![Multiple sources of truth](./assets/multiple-sources-of-truth.png)\n\nBut it might happen that a function doesn’t exist in both of the modules, eg infamous [padStart](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart). At [Carla](https://www.carla.se/), we used to solve the problem by creating a lot of `StringExtra`, `OptionExtra`, and `WhateverExtra` modules to add missing helper functions.\n\nThis leads to another problem when developers don’t know where they can find the desired helper:\n\n- Will it be in `Belt` that we agreed to use as default, or the helper doesn’t exist there, and I should use `Js` instead, or maybe `StringExtra`?\n\nA few times, we even end up using `Pervasives` by mistake.\n\n## Problem 3: Creating guidelines\n\nDepending on the project, we may have rules for the team to follow. For example, `Belt.Option.getExn` raises a ReScript exception which is very difficult to trace, so at [Carla](https://www.carla.se/) we decided to use our own `OptionExtra.getExnWithMessage` instead. Although there's an agreement, it's very easy to forget about and continue using `Belt.Option.getExn`. We want a way to prevent the usage of `Belt.Option.getExn` whatsoever.\n\n## Solution\n\nI will not create intrigue and say that the solution I suggest is to create your own vendored standard library and enforce its usage across the codebase. You can reuse existing modules like `Js`, `Belt`, or `RescriptJs`, and adjust them for our needs.\n\nThe enforcing is the most crucial part here because if we don't automate it with CI, our colleagues and even ourselves will continue using all different modules instead of the single vendored one.\n\nAnd to solve the problem, I've created [rescript-stdlib-vendorer](https://github.com/DZakh/rescript-stdlib-vendorer), an easy-to-use linter to support the usage of a vendored standard library.\n\n### Linter\n\nThe linter does a straightforward thing - It checks all project files and detects the usage of `Js`, `Belt`, and `ReScriptJs` modules. So you can easily find and replace them with your `Stdlib`.\n\nTo start using the linter in your project, install it as a dev dependency:\n\n```sh\nnpm install -D rescript-stdlib-vendorer\n```\n\nLet's also add an npm run script for convenience and immediately use it:\n\n```sh\nnpm pkg set scripts.lint:stdlib=\"rescript-stdlib-vendorer lint\"\nnpm run lint:stdlib\n```\n\nSo if we have a project created from the [official template repository](https://github.com/rescript-lang/rescript-project-template), which has a single ReScript file with `Js.log(\"Hello, World!\")`, we’ll get the following error:\n\n```\n~/rescript-project-template/src/Demo.res:1\n  Found \"Js\" module usage.\n\nUse the vendored standard library instead. Read more at: https://github.com/DZakh/rescript-stdlib-vendorer\n```\n\nTo fix it, let’s create a directory `stdlib` and add the first vendored module:\n\n```sh\ncd src\nmkdir stdlib\ncd stdlib\necho 'include Js.Console' \u003e\u003e Console.res\n```\n\nWe can now update the `Demo.res` by replacing `Js.log` with `Console.log`.\n\nLet’s run the linter again and see that there’s no error:\n\n```sh\nnpm run lint:stdlib\n```\n\nIn the following way, you can create reexports for other modules and adjust them to make them better suit you.\n\nFor example, for the `Array` module, you can redefine some functions with `Belt`’s implementation to make the code safer:\n\n```rescript\n// src/stdlib/Array.res\ninclude Js.Array\nlet get = Belt.Array.get\n```\n\nThis way, the usage of square brackets to get an array item will return `option`, which’s more correct:\n\n```rescript\nlet array = [1, 2, 3]\nlet item = array[3]\n// The item will have the option\u003cint\u003e type instead of the default int one\nConsole.log(item)\n```\n\nReturning to the `Belt.Option.getExn`, mentioned in the third problem, instead of reexporting all functions from `Js`/`Belt`, we can explicitly reexport only the functions we need and replace the ones we consider harmful:\n\n```rescript\n// src/stdlib/Option.res\nlet forEach = Belt.Option.forEach\nlet mapWithDefault = Belt.Option.mapWithDefault\nlet map = Belt.Option.map\nlet flatMap = Belt.Option.flatMap\nlet isSome = Belt.Option.isSome\nlet isNone = Belt.Option.isNone\n\nlet getExnWithMessage = (x, message) =\u003e\n  switch x {\n  | Some(x) =\u003e x\n  | None =\u003e Js.Exn.raiseError(message)\n  }\n```\n\n## Reusing the vendored stdlib\n\nI’ve shown how to start vendoring stdlib in a short way. But having multiple projects following the same guidelines, you’d soon want to start reusing the vendored stdlib. It’s very easy to do by moving the code from the `stdlib` directory featured above to a separate package.\n\nFor my personal projects, I copied [Gabriel](https://twitter.com/___zth___)'s repository with a proposal for a new ReScript stdlib, which did not burn out. Afterward, I modified it to suit my taste better and published it to npm, making it easy to use.\n\n\u003e I recommend taking a look at it as a reference [@dzakh/rescript-stdlib](https://github.com/DZakh/rescript-stdlib), but I don’t bring it to your own projects. You'll lose a very good part of vendoring - full control over the code.\n\nAt [Carla](https://www.carla.se/), we had a different situation. Having a huge codebase with `Js`, `Belt`, and `WhateverExtra` all over the place, it would be too much work to take some existing customized stdlibs like [rescript-js](https://github.com/bloodyowl/rescript-js) or [@dzakh/rescript-stdlib](https://github.com/DZakh/rescript-stdlib) and update old code with them.\n\n\u003e First of all, since at [Carla](https://www.carla.se/) we have different guidelines compared to my personal projects, it would be a bad idea to use [@dzakh/rescript-stdlib](https://github.com/DZakh/rescript-stdlib) from the get-go. As I said before, it'd lose the whole point of vendoring.\n\nSo, to migrate the whole codebase, we’ve started with creating a small stdlib package in our pnpm mono repository that simply reexported functions from `Belt` or `Js`. And started updating file by file, replacing `open Belt` with `open Stdlib` and `Js.Array2` with `Array`, etc.\n\nAlso, you can use a tool like [Comby](https://comby.dev/) to transform the whole codebase in one go.\n\n\u003e Before you start the migration to the vendored stdlib, I highly recommend replacing `Js.String` and `Pervasives.String` with `Js.String2`. Since some of the functions have the same API, but different logic, there’s a chance of missing one of them and getting a runtime error.\n\nBut we didn’t rush with the migration, and to avoid regressions of the process, we ran the linter script in CI with the `--ignore-without-stdlib-open` flag to skip files not containing `open Stdlib`.\n\nAfter every file was updated, we removed `open Stdlib` from the beginning of the files and opened it globally via `bsconfig.json`. When it was done, we could finally start gradually adjusting the `Stdlib` to make the process of writing ReScript code more convenient and reliable.\n\nIf you have any questions feel free to ping me on [Twitter](https://twitter.com/dzakh_dev).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdzakh%2Frescript-stdlib-vendorer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdzakh%2Frescript-stdlib-vendorer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdzakh%2Frescript-stdlib-vendorer/lists"}