Skip to content

Proposal: Only support ES modules in PureScript 0.15 (disallow CommonJS) #4230

@thomashoneyman

Description

@thomashoneyman

Summary

In #3791 we are moving to support ES modules with the PureScript compiler. We'll accept ES modules via the FFI, and the compiler output will be ES modules. But what are we to do with all existing code that uses CommonJS modules?

My proposal is to only support ES modules in the compiler. Specifically, we would only support ES modules in compiler output and any existing FFI written with CommonJS modules will have to be rewritten (there are tools that can help do this automatically).

This proposal has been cross-posted to the PureScript Discourse and more discussion can be found there.

Motivation

We already only emit ES modules in #3791, and I believe this is uncontroversial. However, while #3791 emits warnings for CommonJS in foreign modules, it also makes some attempts to continue supporting CommonJS via the FFI.

That said, there are issues with trying to support CommonJS foreign modules. Some include:

  1. If we allow CommonJS in the FFI, then we cannot follow through on Proposal: Remove the PureScript bundler #4226 because unbundled development for the browser is only possible using ES modules. That proposal describes several reasons why we really ought to drop our buggy bundler, especially since we can get even better bundle sizes by skipping our bundler altogether and using purity annotations and a bundler like esbuild.
  2. As far as I can tell, there isn't a good story for how to support both module formats in purs bundle. I'm not sure we even can mix and match module formats, and even if we can, I'm not aware of anyone who is interested in sinking any more time into the bundler especially when there are perfectly good industrial solutions for bundling.

There's the follow-on downside that if we can't drop the PureScript bundler (#4226), then we can't drop language-javascript either (#4227), and that means continuing to de facto maintain a JavaScript parser that already can't reliably parse ES modules (see 1, 2, 3, and so on).

For these reasons, as well as the reasons described in #4226 and #4227, I think that it's just not worth attempting to support CJS at all with the PureScript compiler once we support ES modules.

Proposal

I am proposing that we only deal with ES modules in the compiler:

  1. We only generate ES modules and we only accept ES modules via the FFI.
  2. For PureScript 0.15 we use language-javascript to emit an error if we detect you are using CJS in a foreign module and tell you to migrate to ES modules instead. Since language-javascript is buggy, this check should only be run on a successful parse. If language-javascript fails to parse, then we just disable the check.
  3. For PureScript 0.16 we can remove the check, and in fact can remove language-javascript altogether.

In the "Motivation" section I mentioned some positives, but there are drawbacks to doing this as well. The primary drawback is that all FFI files will have to be migrated from CommonJS to ES modules. It's not clear how many people this will affect, but I'd say most production PureScript applications use the FFI at least a little. That makes this a significant breaking change for a lot of people.

Fortunately, there are tools like lebab and cjstoesm that can automatically migrate a project's CommonJS modules into ES modules. They aren't perfect, so it's possible projects will still have to manually fix some files, but they appear to do a good job.

In addition, as described in this comment, you could still use CommonJS modules throughout your project if they are shimmed as ES modules -- you'd import the relevant FFI functions from a CJS module into an ES module, and then export them for the FFI from the ES module.

If we were to go through with this proposal, then we'd want to use these tools to migrate the core, contrib, node, and web libraries so that we can provide some tested advice in our migration guide.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions