Purpose, alternatives to next-transpile-modules in next ReScript projects

I haven’t read deep on this issue yet. From my rough understanding, we want to run transpile-modules on bs-platform libraries and any ReScript npm deps to convert them from commonjs to es6 modules, when we use es6 in our bsconfig?

If that is true, is there some simpler transformation that I can run once after yarn install, to perform the transformation once. I am curious about this because it would be nice to not have to worry about transpile-modules clashing with nextjs upgrades, since it needs to be updated any time a nextjs release changes the webpack configuration setup.

1 Like

You need this plugin to be able to transpile es6 based files in a specific set of node_modules folders. We use the es6 target for our app, so ReScript compiles to es6 artifacts in each bs-dep and the bs-platform folder.

Don’t worry about it too much. Our site had 3 big Next upgrades, and there has never really be any problem with that setup.

My guess is that at some point NextJS will lobby for a plain es6 module approach as soon as es6 modules are more broadly adopted in the JS / webpack ecosystem. Just a matter of time until we can gracefully drop the transpile plugin.

1 Like

As predicted, I am getting errors upgrading this now. I haven’t looked into why, will dig in later ):

Error: next-transpile-modules - an unexpected error happened when trying to resolve “bs-platform”
18:22:06 Error: Can’t resolve ‘bs-platform’ in ‘/vercel/workpath0’
18:22:06 at getPackageRootDirectory (/vercel/workpath0/node_modules/next-transpile-modules/src/next-transpile-modules.js:70:11)

what did you upgrade? don’t forget to clean and build-world whenever messing with npm packages, since npm likes to wipe your compiled ReScript dependency js artifacts.

Bumps next-transpile-modules from 4.1.0 to 6.0.0.

What was the motivation of upgrading it?

It seems like they change the way how module resolution work, so there might be some changes needed in the bs-platform’s package.json as well?

Changelog: https://github.com/martpie/next-transpile-modules/releases

Besides, upgrading a package, no matter what kind of dependency, by 2 major version just asks for troubles due to breaking changes. In opam those errors are easy to spot ahead of time, but in JS there is no such guarantee.

No reason, just watching dependabot proactively, so that I can upgrade later on whenever it becomes necessary

Confirmed, when I do the following, the build gets passed bs-platform and then trips on reason-react:

cd node_modules/bs-platform && touch index.js && sed -i 's/^{/{ "main": "index.js",/' package.json

Sounds like next-transpile-modules should expose an extra parameter to explicitly provide a directory when the library is out of the user’s control. Alternatively, I could bother each rescript library author to add “main-is”…

2 Likes

For reference, this comment was left in a PR by the first person who started using next-transpile-modules:

we can now return a promise from getInitialProps. (trick was to ask bucklescript to output es6 and not commonjs)

I just realized that in order to update to webpack5 in NextJS we have to use the latest next-transpile-modules as mentioned here

  • When using next-transpile-modules make sure you use the latest version which includes this patch

Some goodies about upgrading webpack are:

  • Improved Disk Caching: next build is significantly faster on subsequent builds
  • Improved Fast Refresh: Fast Refresh work is prioritized
  • Improved Long Term Caching of Assets: Deterministic code output that is less likely to change between builds
  • Improved Tree Shaking
  • Support for assets using new URL("file.png", import.meta.url)
  • Support for web workers using new Worker(new URL("worker.js", import.meta.url))
  • Support for exports/imports field in package.json

In this GitHub issue I reported that next-transpile-modules need rescript to have an entry point in order to be compatible with next-transpile-modules v5+. But @Hongbo stated that rescript doesn’t have the concept of entry point.

I’m a bit bummed and concerned that this might be the end of the road for a while regarding upgrading to the latest webpack v5 in NextJS.

A solution could be to fork next-transpile-modules and bend it to our needs.

1 Like

Okay nice! I am interested in this. Shouldn’t be too hard to find a solution… I will have a look at it for the rescript nextjs template

1 Like

@jorge I got my template working with webpack5 and next-transpile-modules just right now.

Took me a while to understand what’s the issue, but there’s currently two things that need to be fixed:

  1. As you stated correctly, the package.json of bs-platform (and @rescript/react) needs either a main field pointing to a file, or an empty index.js within the root folder, otherwise the webpack resolver will not find the package

  2. There seems to be an issue with the webpack rule in next-transpile-module that causes webpack to not treat the packages within lib/es6/X.js as a esm module (Reference Error: module is not defined). To fix this, I had to adapt the rule on line 224 to the following:

config.module.rules.push({
  test: /\.+(js|jsx|mjs|ts|tsx)$/,
  use: options.defaultLoaders.babel,
  include: matcher,
+  type: "javascript/auto",
});

Not sure if this is a bug.

2 Likes

this is great to hear @ryyppy!
for #1 could it point to a readme file? or does it has to be .js?
I think it would be misleading to point to a fake entry file. On the other hand it is important to support webpack 5.

Did you open an issue for #2?

I am honestly not sure why this is needed in the first place. next-transpile-modules uses Webpack’s enhanced-resolve functionality to resolve the package root dir… and for some reason it won’t recognize it if there’s not a main file (which I find surprising).

Not yet because I am not entirely sure if my change would be in the interest of the package maintainer.

1 Like

I am trying to add the main and empty index.js but I just saw that it is not only bs-platform but all the bs dependencies.

Seems like #2 is fixed in ntm v7

Running this patch in next.config.js gets the job done (quick and dirty).

const bsconfig = require('./bsconfig.json');
const withPlugins = require('next-compose-plugins');

const fs = require('fs');

function patchResDeps() {
  ['bs-platform'].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),
    );
  });
}
patchResDeps(); // update package.json and create empty `index.js` before transpiling

const transpileModules = ['bs-platform', 'three'].concat(
  bsconfig['bs-dependencies'],
);

const withTM = require('next-transpile-modules')(transpileModules);

module.exports = withTM({
  future: {
    webpack5: true,
  },
//....
})
2 Likes

Nice, this actually helped me upgrading the rescript-lang .org codebase to the newest ES6 module setup.

Let’s see if we can figure out a solution for the next.config.js later on. For now this must suffice.

2 Likes

@ryyppy Good to hear! Nice teamwork :raised_hands:

side-question: did you replace bs-fetch with src/common/XmlHttpRequest.res?

XmlHttpRequest is useful for React components with useEffect… for fetching done on server-side, I just create an adhoc fetch binding like here.

Not sure why bs-fetch was in there… probably forgot to remove it in the past.

1 Like

I just updated to the latest next-transpile-modules@v7.1.1 without using patchResDeps and it worked :raised_hands: