RFC: Only support ES modules in PureScript 0.15

:wave: I’m back with another proposal! This time, I’m proposing that the PureScript 0.15 compiler only support ES modules, which would mean that CommonJS modules will no longer be allowed in the FFI. There are many reasons why this would be a good thing from the perspective of the compiler, and I encourage all interested parties to read the proposal and weigh in over on the compiler repo!

If CommonJS support were removed entirely, that means that all applications and libraries using the FFI will have to be updated to use ES modules instead of CommonJS modules. If you would be affected by this, then there is hope:

  1. Tools like lebab and cjstoesm ca be used to automatically migrate the majority of CJS modules to ES modules
  2. CommonJS can still be used, but if you have written some code in a CJS module that you want to provide to PureScript via the FFI, you will have to import that code into an ES modules file first before exporting it for the FFI. There is more detail about this in a comment on the ES modules PR.

Still, it’s undeniably a hassle to have to migrate FFI files. If you anticipate having difficulty with this, then please share your thoughts here or on the issue on the compiler repository! If this proposal is accepted, then we’ll need to make sure to provide as smooth a migration path as possible and hearing about potential roadblocks will be a huge help or may even make it clear that this is too big of a breaking change to go forward with.

Thank you for your time!


Just wanted to add:

If cjs is really needed :point_up: can even be done automatically using a bundler like esbuild.

Secondly, I think migrating ffi files sounds more complicated than it actually is. Apart from the automation tools mentioned by Thomas (btw, VS Code also provides one) to do the migration, in most cases it is rather simple like e.g. the changes in simple-json. We have done this already for a bunch of repos in our working group package set if you want to have a further look.

I have an interesting hack in one of my projects where my PureScript code is integrating with a JS program (screeps) that expects me to expose the guts of my code via an export named loop using CJS, and so I have a little FFI for this that looks like

exports.setLoop = function (loop) { 
  module.exports.loop = loop;
foreign import setLoop :: EffectFn1 (Effect Unit) Unit

Then I bundle it all up with spago bundle-app. Would this proposal effect the ability to do that? I’m guessing it would still work if I just changed the syntax for exporting setLoop, and even if not, it’s just a toy project for a game. It just seemed like an interesting use case and I’m just curious.

1 Like

Yes, this is explicitly supported in ES modules with export var ….

I don’t think that the program I’m integrating with supports ES modules (I could be wrong about that though and experiment with it). I think in my final bundled JS I need to include a module.exports.loop = ... statement. If I had

export var setLoop = function (loop) { 
  module.exports.loop = loop; 

and bundled it with spago bundle-app, would the compiler complain, and would my final index.js include a module.exports.loop = ... line?
Right now I can see in my bundled index.js:

(function(exports) {
  exports.setLoop = function(loop) { 
    module.exports.loop = loop;
})(PS["Main"] = PS["Main"] || {});

Sorry, a more complete example would be:

export var loop;
export function setLoop(newLoop) {
  loop = newLoop;

ESM allows circular and mutable export bindings.


Oh no. The platform we run our application on does not fully support ES6. Even the 2020 model year only partially supports modules.

The compiler isn’t going to start emitting ES6 code aside from imports/exports, so if you use a tool like esbuild to bundle things it should mean things will continue to work for you.

1 Like

I think if we just include some hints on how to convert or a short snippet of how to use some tool this should be fine and probably a good way to move forward.