Skip to content

Commit af0b0c5

Browse files
authored
Explain dual-instantiation
Resolve the open issue of what happens when a file is requested to be loaded in both modes: ESM and CommonJS. See GeoffreyBooth#1 for discussion.
1 parent 63a27e5 commit af0b0c5

File tree

1 file changed

+4
-8
lines changed

1 file changed

+4
-8
lines changed

README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,18 +281,14 @@ To preserve backward compatibility, we expect that `node file.js` will continue
281281

282282
A package can be “dual mode” if its `package.json` contains both a `"main"` field and an `"exports"` field (or some other ESM-signifying field). An `import` statement of such a package will treat the package as ESM and ignore the `"main"` field. To explicitly import a dual-mode package via its CommonJS entry point, [`module.createRequireFromPath`][nodejs-docs-modules-create-require-from-path] could be used.
283283

284-
The ESM and CommonJS versions of a dual-mode package are really two distinct packages, and would be treated as such by Node if both were imported. It is an implementation detail to be worked out what Node should do in such a situation. Of particular concern are race conditions that could occur if it might be unpredictable which version (ESM or CommonJS) of a dual-mode package gets loaded first.
284+
The ESM and CommonJS module systems have independent namespaces. Therefore the ESM and CommonJS versions of a file have separate identities when instantiated. The module loader's previous invariant was: a file will only be loaded at-most once within a Node application. This invariant now becomes: a version of a file (ESM or CommonJS) will only be loaded at-most once within a Node application.
285+
286+
Users that only rely on Node's interpretation of a module, via `import` and `import()`, will never trigger simultaneous instantiation of both versions. Today, the only way to encounter this dual-instantiation scenario is if some part of the application uses `import`/`import()` to load a module **and** some other part of the application overrides Node's interpretation by using `require()` or [`module.createRequireFromPath`][nodejs-docs-modules-create-require-from-path] to load that same module.
285287

286-
Different modes of the same package _should_ be importable into different package scopes, for example if a user’s project imports ESM `lodash` and that project has a dependency which itself imports CommonJS `lodash`. This corresponds with how different package scopes today can import different versions of the same package, such a user’s project having dependencies of `lodash@2` and `request`, with `request` then having a dependency of `lodash@1`, and both the user’s project and `request` each receive the version of `lodash` that they expect.
288+
The choice to allow dual-instantiation was made to provide well-defined determinstic behaviour. Alternative behaviours, such as throwing a runtime exception upon encountering the scenario, were deemed brittle and likely to cause user frustration. Nevertheless, dual instantiation is not an encouraged pattern. Users should ideally avoid dual-instantiation by migrating consumers away from `require` to use `import` or `import()`.
287289

288290
#### Further Considerations
289291

290-
<details><summary>“Double importing” of files</summary>
291-
292-
There is the possibility of `import` and `createRequireFromPath` both importing the same file into the same package scope, potentially the former as ESM and the latter as CommonJS. Allowing this would likely cause issues, and a solution would need to be worked out to handle this situation.
293-
294-
</details>
295-
296292
<details><summary>“Loose” CommonJS files (files outside of packages)</summary>
297293

298294
Currently, `module.createRequireFromPath` can be used to import CommonJS files that aren’t inside a CommonJS package scope. To import the file via an `import` statement, a symlink could also be created from inside a CommonJS package scope to the desired “loose” CommonJS file, or the file could simply be moved inside a CommonJS package scope. Seeing as there is low user demand for ESM files importing CommonJS files outside of CommonJS packages, we feel that these options are sufficient.

0 commit comments

Comments
 (0)