How to share Rescript Components between two Rescript Apps?

I have a Remix app and a Next.js app which have to share some Rescript components.
I would like to do take a monorepo approach.

monorepo/
├─ next-app/
│  ├─ components/
│  │  ├─ Component1.res (uses Shared.res)
│  ├─ node_modules/
│  ├─ bsconfig
│  ├─ package.json
├─ remix-app/
│  ├─ components/
│  │  ├─ Component2.res (uses Shared.res)
│  ├─ node_modules/
│  ├─ bsconfig
│  ├─ package.json
├─ shared/
│  ├─ Shared.res

I tried specifying bsconfig the relative path

"sources": [
    {
      "dir": "app/res",
      "subdirs": false
    },
    {
      "dir": "../shared",
      "subdirs": false
    }
  ],

but it breaks when building the app because it can’t resolve some dependencies

 ERROR: Could not resolve "rescript/lib/es6/curry.js"
ERROR: Could not resolve "rescript/lib/es6/belt_List.js"
// etc

Any ideas how to achieve this? I wonder if I make a package.json in /shared and install node_modules with just rescript.

I am not an expert in this; I was looking up the definition of “monorepo” today. But I tried to do something similar today - 2 React apps (client, admin) and a “shared” library. It seemed to work to have 3 separate folders each with its own package.json and bsconfig.json. Every package installs Rescript and whatever dependencies it needs and has its own node_modules folder. The way these projects talk to eachother is…

(1) install the “shared” library using the file: syntax, which creates a symbolic link in the node_modules folder. So my dependency in package.json looks something like…

"shared": "file:../shared"

(2) specifying shared in the bs-dependencies section of the client and admin packages bsconfig.json. Like this…

"bs-dependencies": ["shared"]

(3) might be optional, but I also turned on the namespace option in bsconfig.json.

"namespace": true,

Overall it seemed to work but I think I experienced some flakiness with the VS Code rescript extension. That message about “Do you want to start Rescript to get the latest build” popped up for each separate project.

2 Likes

You could make shared a separate standalone NPM package. That way you can put the shared package in pinned-dependencies and go from there:

{
  "bs-dependencies": ["shared"],
  "pinned-dependencies": ["shared"]
}

Building works when this approach is coupled with NPM workspaces (so that there is a single node_modules directory and the packages depend on each other correctly also on package.json level). Building might work with other monorepo systems, too, but just a warning: in my brief experience the centralized way pnpm stores node_modules is not compatible with ReScript.

Editor support for monorepos is currently not very good. The VSCode plugin is a bit more functional than the vim plugin.

1 Like

What do you mean by that? Does the VS Code plug in work for this situation? In my quick tests it seemed a little flaky but also seemed to work with those three bsconfig.json files. Are there known problems?

that’s good idea! thanks for pointing me to pinned-dependencies. This builds nicely on what @jmagaram tested out.

I am also looking into npm workspaces to complement this

I experienced assorted oddities that I assumed were caused by the monorepo setup because they were happening on npm module to npm module boundaries. I now realize it’s entirely possible these were not specific to monorepos and having similar constructs on just ReScript module to ReScript module level could’ve been the same!

My biggest problem with the vim plugin is that it is picky about the current working directory. See https://github.com/rescript-lang/vim-rescript/issues/45. It works only when cwd is the monorepo root. It is all too tempting to cd to a package directory to have a more focused view. :slight_smile: