nodejs / modules

Node.js Modules Team

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature: Multi-mode packages

GeoffreyBooth opened this issue · comments

  • I want to write an ESM package that imports WASM.
  • My package can be imported into Node or in the browser.
  • I want to be able to gradually convert my package from CommonJS to ESM.

Use cases 19, 39.

I'd like to propose merging feature #93 into this one, under this name, as multi-mode seems like a clearer term to me than dual-goal (which I would associate with source texts over packages).

Could you define "multi-mode"?

Base on #93, "dual goal" would be:

a package that can be imported as native ESM or required as CommonJS.

(I believe it could also be reduced to "a module that can...." depending on the implementation)

This can work in two ways:

  • There is a single file, and it can be treated as either an ES or CommonJS module. (support both import "cjs" and require("esm"))
  • There are two files and the "right" file is picked based on the import mechanism

The first message in #93 is not that clear about the interpretation, but the discussion below seems to be about the second interpretation ("consumer-based resolution": the actually resolved file depends on the kind of import mechanism used by the consumer).

My understanding of "multi-mode" is that it's about supporting new JS parse goal or different file types for module implementations, but once it's part of the dependency graph it behaves either like an ES modules or a commonJS module. The effect is "local".

A solution to the multi-mode use-case may also solve the dual-goal use case, but these use cases feel different to me.

I don't understand what multi-mode means, but in my own usage of this terminology I've used it to exactly refer to "dual-goal" packages.

From a terminology perspective it seems important to be very clear here on meaning, and pick a term here so we can all be on the same page.

In terms of understanding this feature, the first point sounds like it is covered by the "support importing WASM" feature, and the second point seems like it is covered by "allow code to run in Node.js and the browser".

That leaves "I want to be able to gradually convert my package from CommonJS to ESM", which seems like it is covered by "gradual migration from CommonJS to ESM (#99)".

(In terms of terminology, I could happily go with either "dual-goal packages" or "multi-mode packages" to refer to this feature, I just think it's important to pick one!)

In fact, #99 even seems like a duplicate of this same issue to me.

Perhaps the two features are:

  1. Support packages consisting of both ESM and CJS internally.
  2. Support loading a package as either ESM or CJS

(1) seems like it is covered by #99.
(2) may even be covered by "consumer agnostic imports".

Further suggestions re clarity here are welcome.

I definitely agree that there is an ambiguity here and that we need clear definitions: I also don't know what "multi-mode" means.
I started to work a bit on the terminology issue but did not get around finishing it. Glad that you're going over these issues and working on it.

At the moment, I consider the "dual-goal" feature to be better defined. The feature in this use case mixes many things: WASM support, browser compat and progressive migration.

Here is how I see it:
At the dependency graph level there are only two kinds of modules: ESM and CJS, and there only two import mechanisms: CJS require and ESM imports.
If you include the "I don't know/any" case, it makes 3³ = 27 cases for each edge of the dependency graph: consumer module kind, import mechanism, dependency module kind. "dual-goal" works at this level.
For example:

  • "Agnostic ESM consumer" can be defined as supporting "Any consumer kind, ESM import mechanism, unknown dependency kind" (and having the possibility to get the same value).
  • "require in ESM" can be defined as "ESM consumer, CJS require mechanism, any dependency kind"

Now, going from a blob to a module is a distinct problem. There are many file types: application/node, text/javascript; goal=esm, application/json, application/wasm, .node files, JS with some new goal, etc. but ultimately they all reduce to acting either as ESM or CJS modules. It involves disambiguating JS files and extension-less files.
I consider "multi-mode" to be at this level: supporting more file type or JS parse goals.


"dual-goal" seems to be relatively wide-spread but I am not convinced this is the best term because the "goal" only makes sense for JS files but the use case also covers a consumer importing a library implemented either as a .node (CJS-like) or .wasm (ESM-like) file.

"multi-mode" seems more fuzzy and unless we define it clearly somewhere, I also consider it confusing and would prefer to use "support WASM and additional JS goals", and qualifying it as needed ("such that it works out of the box in browsers", "such that it is an implementation detail", etc.)


  1. Support packages consisting of both ESM and CJS internally.
  2. Support loading a package as either ESM or CJS
    (1) seems like it is covered by #99.
    (2) may even be covered by "consumer agnostic imports".

Agreed for 1, ok for 2 based on the context of this discussion.

Agree with the previous post.

qualifying it as needed

Reading terms like consumer agnostic import, I think to myself, of what? I almost feel like uses of the word “import” should be followed by “of X into Y,” like “consumer agnostic import of CommonJS into ESM.”