Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: New package-spec "dual" #6209

Open
Tracked by #6210
cometkim opened this issue Apr 26, 2023 · 9 comments
Open
Tracked by #6210

RFC: New package-spec "dual" #6209

cometkim opened this issue Apr 26, 2023 · 9 comments
Milestone

Comments

@cometkim
Copy link
Member

Motivation

Deciding on the module format is one of the most important parts of getting a JavaScript library published on NPM.

A leaf project usually uses only one format, but a library does not. There are several ways:

  • CommonJS-only: CJS is still the most common module format in Node.js. It is also compatible with projects using ESM. But the problem is that it is not standard and therefore breaks interoperability with other JS platforms like Deno, Bun, Browsers.
  • ESM-only: ESM is standard. Adoption rates are growing, but there are still many gaps. One major problem is that it is not compatible with projects using CJS.
  • Dual-package publish: To resolve the problems, Node.js has adopted a relaxed module resolution mechanism since v16+. Libraries can support both CJS/ESM projects with that in mind.

I've introduced the dual-package publish method using bundler on a forum: Bundle ReScript libraries with dual-package exports

However, the ReScript compiler today only supports one package-spec at a time and doesn't offer enough customization options, so we have to rely on external tools to do that.

Babel has env options for transpile multiple target once, TypeScript can customize project config file, so users can do that by just run tsc twice.

But it's still difficult, even with external tools. I've automated it to some extent, but there are tricky tasks like fixing the bs package-spec to es6 for compatibility, adjusting the module resolution in the output, and linking the correct libraries, etc.

I want ReScript to be a good option for libraries as well. It will eventually lead to ReScript-first projects and increased adoption.

Design

Introduce a new package-spec mode dual to support dual-package libraries.

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

Which means ReScript should emit both CommonJS and ESM. To adjust the extension of the outputs, the suffix option must also be expanded.

{
  "suffix": {
    "commonjs": ".cjs",
    "esmodule": ".mjs"
  }
}

To use dual, suffix must be specified as an object, a unique and valid extension for each format must be specified.

As a result, the A.res module has A.cjs and A.mjs outputs at the same time. This is the minimum requirement for publishing a dual package and should be sufficient to use ReScript as the only toolchain for a library.

@cristianoc
Copy link
Collaborator

Screenshot 2023-04-26 at 12 11 01

Sounds like a thumbs up to me.

@anmonteiro
Copy link
Contributor

This isn't very hard to implement. You need to be careful about detecting conflicts (2 specs shouldn't have the same extension, etc.).

I explored this in melange but never ended up merging it. melange-re/melange#158

It's now, however, trivially supported with our dune integration. Check out module_systems here if you're curious: https://dune.readthedocs.io/en/latest/melange.html#melange-emit.

@anmonteiro
Copy link
Contributor

(Note that ReScript already supports this, btw. It just doesn't support both to output in-source)

@mununki
Copy link
Member

mununki commented Apr 26, 2023

Once this feature is implemented in the compiler, it seems that it will also affect the module_systems for Dynamic import and should be taken into consideration.

@cristianoc cristianoc added this to the v12 milestone Apr 26, 2023
@cometkim
Copy link
Member Author

cometkim commented Apr 26, 2023

it seems that it will also affect the module_systems for Dynamic import and should be taken into consideration.

The easy way is not to support it. Dynamic import is semantic of ESM and there is no way to do it properly in CommonJS.

CommonJS is specific to Node.js, and Node.js allows dynamic imports within CommonJS modules. Probably no changes are needed.

@cometkim cometkim changed the title RFC: New package-spec "dual" [RFC] New package-spec "dual" Jan 3, 2024
@cometkim cometkim changed the title [RFC] New package-spec "dual" RFC: New package-spec "dual" Jan 25, 2024
@cknitt
Copy link
Member

cknitt commented Aug 11, 2024

Do we really need "dual"?

  1. I think package-specs can already be an array today, and we could just explicitly list commonjs and esmodule there?
  2. What if we need to add a third format in the future for some reason? Then "dual" doesn't really make sense anymore.

@cometkim
Copy link
Member Author

cometkim commented Aug 11, 2024

I think package-specs can already be an array today, and we could just explicitly list commonjs and esmodule there?

Right. I considered that too. Then it need to fuse the suffix (and others.. maybe genType config too) into the package-specs

What if we need to add a third format in the future for some reason? Then "dual" doesn't really make sense anymore.

Yes, for example, TypeScript could be added as an new backend (why not?) The point of dual is that we should always be able to support multiple targets simultaneously. It is important for multi target libraries.

@cknitt
Copy link
Member

cknitt commented Aug 11, 2024

Right. I considered that too. Then it need to fuse the suffix (and others.. maybe genType config too) into the package-specs

The suffix is already there according to build-schema.json (module-format-object):

https://github.com/rescript-lang/rescript-compiler/blob/8a28dafe71082da9d40eb53ea182dc2eb85fc5f8/docs/docson/build-schema.json#L22

@DZakh
Copy link
Contributor

DZakh commented Aug 12, 2024

Also, I think for the library mode there should be some sort of an idiomatic suffix (or default one), so all libraries use the same one to allow depending on them without bundlers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: RFC
Development

No branches or pull requests

6 participants