Yarn Workspaces Example

Working with workspaces via yarn seems to be pretty finicky in rescript, any resources out there that can better explain how to best setup a repo that has multiple modules that are separated by project within a single repo?

We have UI libraries that we would like shared across multiple apps, and we have started experiencing issues now that we are using a module for a second time in a separate app that is using this nextjs template.

Lots of random modules are showing as not found and its hard to even start to track down what the potential issues could be.

Just to be sure: Did you check out the Pinned Dependencies docs already?

1 Like

Ah this is EXACTLY what I was looking for.

I will give this a try but I am also guessing that the nextjs example with workspaces is at the root of these issues with its webpack config hard coding the node modules directory.

Any examples or further explanation on the important note about file watcher not working across the packages?

So we still have the same issues, this must be an issue with the nestjs package.

/my-app
/app (able to use wonka just fine - no issues)
/wonka
/totan (nextjs)

Within totan we are trying to use the wonka module which has a dependency DineroJs (@bettercart/rescript-dinero - npm) and we are now getting this error when the nextjs app runs:


or:

I should note that I changed some of the lines in the next.config.js for webpack from ./node_modules to ../node_modules and also had to change package specs in bsconfig from:

"package-specs": {
    "module": "es6",
    "in-source": true
  },

to:

"package-specs": {
    "module": "commonjs",
    "in-source": true
  },

FormattedMoney.bs.js

Before this change, rescript would throw errors saying modules were not found…

My hunch is that these lines of code in next.config.js are causing these issues?

function patchResDeps() {
  ["rescript"].concat(bsconfig["bs-dependencies"]).forEach((bsDep) => {
    fs.writeFileSync(`../node_modules/${bsDep}/index.js`, "");
    const json = require(`../node_modules/${bsDep}/package.json`);
    json.main = "index.js";
    fs.writeFileSync(
      `../node_modules/${bsDep}/package.json`,
      JSON.stringify(json, null, 2)
    );
  });
}

EDIT:
I have also just recently noticed that this error no longer exists if these components from Wonka are rendered client side rather than SSR - But the only reason we are using nextjs is to get these Wonka components rendered SSR.

EDIT:

Well we are still receiving this same error even after removing this code entirely…

Well we narrowed this down to our DineroJs binding, when we comment this out, we are working perfectly!!

Any idea why this binding is causing issues? - GitHub - bettercart/rescript-dinero

Might be that your external binding to a default export might be wrong, depending on if you are using commonjs or ES6 import style (those are subtly different unfortunately, and the ReScript user needs to write bindings specifically for the relevant compile target).

I guess this is the problem

So every package in the monorepo is exporting for commonjs and the other package has no issues using the DineroJS binding via the wonka package (within in /app) - we are only seeing this issue in the nextjs package (/totan). Any ideas why the binding would work in one package but not the other?

Thanks for all your help @ryyppy

same issue here. mimic the document for pinned-dependencies,but not work. always prompt me

Error: package common not found or built

  • Did you install it?
  • If you did, did you run rescript build -with-deps?

I have rethink do I really need workspaces or lerna?

I just wanna make a React components library, some project only import subset component, and I don’t want to install full of dependency and make my bundle larger.

If rescript have good dead code elimination and tree-shake friendly, So my purpose is satisfied.

Can someone answer me, thanks?

1 Like

So is your Next app configured to compile to commonjs as well, or not? My assumption is that you are currently trying to use my nextjs-template which is configured to compile to es6 by default. If that is the case, your external will not work, you’d need to have a separate binding that handles the ES6 import correctly because commonjs default export !== es6 default export.

If you are asking this question, then the answer is most likely “no”. Workspaces and all the micro-package jazz in the JS ecosystem is adding so much maintenance overhead that they had to invent extra tools (yarn workspaces / lerna), just to be able to deal with the complexity.

Keep it simple, if possible.

ReScript compiles to tree-shakeable ES6 modules (if configured to es6 output). From there on, it is the responsibility of the bundlers (webpack, vite, rollup, esbuild, etc) to do the tree-shaking business. Also worth mentioning: Tree-shaking has nothing to do with yarn workspaces / lerna.

2 Likes

I have switched the nextjs template to use “commonjs”. So not sure that is the issue? Every package and app is using commonjs. This binding is being used in 2 other packages without any issues. I am only having issue within the nextjs app. Any other ideas?


Thanks in advance @ryyppy

More issues with commonjs vs es6 imports

This one is from re-formality and re-debouncer these are out of my control so I am not sure why this is now just becoming an issue when using nextjs?

Thanks! :pray:

Okay this is probably the strangest thing ever.

If I slowly introduce the code (I know this is super odd) - it will work. Let me explain.

First the File looks as so:

@react.component
let make = (~amount: Money.t) => {
  Js.log(Dinero.create)
  /* let dinero = Dinero.create( */
  /*   Dinero.options(~amount=amount.amount, ~currency=amount.currency->Money.currencyToJs, ()), */
  /* ) */

  /* <> {dinero.toFormat(. "$0,0.00", #HALF_AWAY_FROM_ZERO)->React.string} </> */
  "Money"->React.string
}

Then I uncomment just a little:

@react.component
let make = (~amount: Money.t) => {
  Js.log(amount)
  Js.log(Dinero.create)
  /* let dinero = Dinero.create( */
  /*   Dinero.options(~amount=amount.amount, ~currency=amount.currency->Money.currencyToJs, ()), */
  /* ) */

  /* <> {dinero.toFormat(. "$0,0.00", #HALF_AWAY_FROM_ZERO)->React.string} </> */
  "Money"->React.string
}

Then a little more:

@react.component
let make = (~amount: Money.t) => {
  Js.log(amount)
  Js.log(Dinero.create)
  let dinero = Dinero.create(
    Dinero.options(~amount=amount.amount, ~currency=amount.currency->Money.currencyToJs, ()),
  )
  Js.log(dinero)

  /* <> {dinero.toFormat(. "$0,0.00", #HALF_AWAY_FROM_ZERO)->React.string} </> */
  "Money"->React.string
}

Then finally

@react.component
let make = (~amount: Money.t) => {
  let dinero = Dinero.create(
    Dinero.options(~amount=amount.amount, ~currency=amount.currency->Money.currencyToJs, ()),
  )

  <> {dinero.toFormat(. "$0,0.00", #HALF_AWAY_FROM_ZERO)->React.string} </>
}

And the nextjs app that uses this file as a dependency will not error and be displayed properly! This is beyond weird.

This issue is 100% caused by the nextjs template. This package is working in multiple places with zero issues.

As soon as its used via the wonka package within the nextjs app it breaks. Why would this be different in the two different places? The webpack build process? I am completely out of ideas.

It almost seems like this is an issue when doing SSR vs. CSR - When I remove the .default from the require() statement I will see the initial page render for a second or two, then when client does rendering it crashes. If the I leave the .default on the require() statement in the generated bs.js file. It will crash before rendering that initial render completely.

var DineroJs = require("dinero.js").default;

//vs

var DineroJs = require("dinero.js");

This actually probably supports the post above that causes the rendering to work if I slowly introduce the code. It probably does a SSR build without the module import. Then once I start introducing the code, the autoreload partially compiles and only causes CSR to run, allowing the page to fully load since it is now only being included on the CSR step and not both steps.

After upgrading to 9.1.4 we were able to get es6 working, which allows up to now use .mjs files so that nextjs knows these are es6 module files with es6 imports.

Now the only problem is since we are using yarn workspaces, the nextjs config is causing the .bs.js files to be replaced with .mjs files in the shared package causing the build to break in the other non nextjs app that uses normal .bs.js files.

Anyway around this?

const transpileModules = ["rescript"].concat(bsconfig["bs-dependencies"]);
const withTM = require("next-transpile-modules")(transpileModules);