-
Notifications
You must be signed in to change notification settings - Fork 18
ADR: CommonJS and ESM decision #323
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
base: master
Are you sure you want to change the base?
Changes from all commits
2667117
cca6311
d8c9118
cda116a
b8d5a59
7fa2531
0f72b27
8007ef6
ca5dcb0
43b0ed8
166e5f8
9fe1075
4ca69bc
01d4903
68eb3d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,85 @@ | ||||||||||
# ADR: Export the libraries specifically in ESM format | ||||||||||
|
||||||||||
## Status | ||||||||||
|
||||||||||
Proposed | ||||||||||
|
||||||||||
## Submitters | ||||||||||
|
||||||||||
- @kjugi | ||||||||||
- @wesleytodd | ||||||||||
- @ctcpip | ||||||||||
|
||||||||||
## Decision Owners | ||||||||||
|
||||||||||
- @expressjs/express-tc | ||||||||||
|
||||||||||
## Context | ||||||||||
|
||||||||||
The document's objective is to gather all notable comments and thoughts in one place and track potential changes in this topic. We have noticed that it is repeated frequently in many issues from the community and we need to take action. | ||||||||||
|
||||||||||
We have acknowledged the need and discussion around it touched on multiple scenarios. Including: | ||||||||||
- rethinking the process and exposing both options (ESM and CommonJS) for all libraries | ||||||||||
- expose both options (ESM and CommonJS) for selected libraries | ||||||||||
- keeping default settings as the main target is on the server | ||||||||||
|
||||||||||
**Why do we need this decision?** | ||||||||||
We aimed to consolidate the Technical Committee's (TC) opinion on this topic. It is important to emphasize that Express is an HTTP framework specifically designed for Node.js. Over the years, technology has evolved, and new runtimes have emerged. Additionally, some of our libraries are being utilized by the community in other environments. | ||||||||||
|
||||||||||
**What problem does it solve or avoid?** | ||||||||||
Ambiguity and uncertainty for the community, alongside clear guidance for repository maintainers and contributors. | ||||||||||
|
||||||||||
**Are there any existing issues/discussions/pull requests related to this?** | ||||||||||
- https://github.com/pillarjs/router/issues/128 | ||||||||||
- https://github.com/expressjs/discussions/issues/297 | ||||||||||
- https://github.com/expressjs/express/discussions/6051 | ||||||||||
- https://github.com/jshttp/cookie/issues/211 | ||||||||||
|
||||||||||
## Decision | ||||||||||
|
||||||||||
During the [working session](https://github.com/expressjs/discussions/issues/320), we had an in-depth discussion about this topic. After careful consideration, we concluded that we will not make a dedicated effort to export our libraries in the ESM format. Instead, we will continue exporting the libraries as we have done historically. | ||||||||||
|
||||||||||
This decision is motivated by the lack of resources to maintain such an effort in the long term. It is also worth noting that Express was specifically designed to run with Node.js. While some of our libraries can run in other runtimes, this can currently be classified as an "unofficial and untested feature." Consequently, our CI systems do not include other runtimes as part of their testing workflows. | ||||||||||
|
||||||||||
At present, our libraries function seamlessly in Node.js, supporting both CommonJS and ESM. Transitioning to support additional scenarios, such as direct ESM exports, would require significant changes to our CI systems and additional maintenance overhead. | ||||||||||
|
||||||||||
**TL;DR**: Dedicated ESM exports will not be available for [expressjs](https://github.com/expressjs), [pillarjs](https://github.com/pillarjs), or [jshttp](https://github.com/jshttp) packages. PRs with such a change will not be accepted. | ||||||||||
|
||||||||||
**What will be done?** | ||||||||||
|
||||||||||
Future issues can be closed with a link to this document. | ||||||||||
|
||||||||||
## Rationale | ||||||||||
|
||||||||||
CommonJS is the default syntax in Node.js. While the JavaScript ecosystem has increasingly moved toward ESM due to its compatibility with browsers, enhanced tree-shaking capabilities (coming from bundler tools), and support for dynamic imports, there are still complexities and challenges associated with ESM. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is any document that supports the "default syntax" statement, it would be beneficial to link it. It seems to me that in Node, CJS is the de facto standard because of its history but it's not set in stone. Especially with syntax detection enabled by default in all LTS versions after April 2025 (when 18 is gone) - Node 20.19 has it enabled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. node’s own docs, the default value of the “type” field, the default value of node’s input-type arg… does it really require evidence when it’s so overwhelmingly the case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This says absolutely nothing about what the project considers the default. But you know why? Let's go down the rabbit hole. Back when the toggle was first added it already had In author's own words:
Want more examples?
Commit message says:
Now, this only shows intent from authors. But is much better case (in my opinion) than the toggle. I'd rather this decision was based on evidence and not coincidence that some toggle defaults to this or that state. And this could as well be a "bug" to fix with the docs page. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default is whatever happens without configuration with .js files, which is always CJS. it is not a bug, it is an intentional decision that is unlikely to ever change because it would break too much of the ecosystem. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Let's do a test. ❯ mkdir testesm && cd testesm
❯ echo 'import {version} from "node:process"; console.log(version);' > test.js
❯ node test.js Which will fail on node 18:
But will have ABSOLUTELY NO ISSUE on 20+.
Therefore it's not always CJS. Let's extend the test further: ❯ echo '{"name": "testesm"}' > package.json
Even with poorly packaged code your statement does not hold true and node.js does not default to CJS. Nothing breaks in the ecosystem. The only actual evidence of "default" would be here: https://nodejs.org/docs/latest/api/packages.html#introduction
And the
Which is not true, because omitting
which breaks ESM inside This shows that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, that's fair that the default is now "syntax detection" for files. However, for the REPL, it's still CJS, and i think this sentence remains correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @slagiewka, If you disagree with the wording, please suggest an alternative and we can decide if it is a better fit. Otherwise I think we can resolve this thread, because I don't think nitpicking this wording choice is worth it if the goal is not to pick other words. Especially since these were chosen intentionally and are technically correct enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm yet to see it be proven technically correct. There is no default standard in node.js and there is no suggested syntax for libraries to use. Granted, the state of lesser complexity is fairly new.
This is the leading statement that serves as the rationale for the decision. And at the time of writing is already hard to sustain. It may skew the decision towards suboptimal choice. However, I sense that everyone's mind is already set anyway.
Given the above, there are a couple of options:
Suggested change
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I said, it's the default syntax in the repl, which should be sufficient to establish that it's the default syntax for node. Also, node 18 isn 't EOL quite yet, and it will be years after that that libraries can painlessly transition to ESM. |
||||||||||
|
||||||||||
Adopting ESM for our libraries would require a significant investment of time and resources to ensure proper implementation and long-term maintenance. While it is not impossible to achieve, it represents a considerable effort. Moreover, the majority of our users already utilize our libraries in their projects, relying on bundlers to handle the necessary transformations without issues. | ||||||||||
|
||||||||||
- **Alternatives Considered:** | ||||||||||
- Alternative 1: Add ESM export to our libraries. CommonJS format is accepted by all most popular bundlers. | ||||||||||
- **Why is this decision the best option?** Time and energy can be shifted to other topics. | ||||||||||
|
||||||||||
## Consequences | ||||||||||
|
||||||||||
- **Positive Impact**: It does not require to support another set of tools and one more major (or at least big) release. | ||||||||||
- **Negative Impact**: | ||||||||||
wesleytodd marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
- No guarantee the packages work in browser environments. | ||||||||||
- Potential community library fork (to make it ESM-friendly) might lack security updates over time | ||||||||||
- OSS library authors that use our packages in ESM native libs might suffer from a lack of support | ||||||||||
- **Mitigations**: Potential decision update for selected libraries (not specified yet) and exposing both types (CJS and ESM) | ||||||||||
|
||||||||||
## References | ||||||||||
|
||||||||||
Support for commonjs imports in ESM code is available in the node. Described in docs: | ||||||||||
- https://nodejs.org/api/esm.html#interoperability-with-commonjs | ||||||||||
|
||||||||||
Support for ESM modules imports in commonjs is available since node v20 behind the experimental flag and node v23 without a flag. Docs: | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is no longer true. Adding this to LTS shows commitment to the feature and the following is an example:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Lets just drop the moving target and reference the upstream docs. |
||||||||||
- https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require | ||||||||||
|
||||||||||
## Changelog | ||||||||||
|
||||||||||
Track changes or updates to this ADR over time. Include the date, author, and a brief description of each change. | ||||||||||
|
||||||||||
- **[2025-01-15]**: [@kjugi] - document init | ||||||||||
- **[2025-01-18]**: [@kjugi] - applied code review suggestions | ||||||||||
- **[2025-02-02]**: [@kjugi] - applied second part of code review suggestions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part needs polishing. Soften the language and consider exporting ESM when it requires little effort. Include libraries maintained by @blakeembrey, which now include ESM export. It should be clear that our CI tests do not cover this for today.
"CommonJS is a requirement, but ESM is not"