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

When will CommonJS modules (require) be deprecated and removed? #33954

Closed
trusktr opened this issue Jun 19, 2020 · 64 comments
Closed

When will CommonJS modules (require) be deprecated and removed? #33954

trusktr opened this issue Jun 19, 2020 · 64 comments
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. experimental Issues and PRs related to experimental features. module Issues and PRs related to the module subsystem. question Issues that look for answers.

Comments

@trusktr
Copy link
Contributor

trusktr commented Jun 19, 2020

Just curious if this is planned to happen, or is it something no one is thinking about yet?

@mmarchini
Copy link
Contributor

@nodejs/modules has probably discussed this extensively, and so have many other project members.

Sharing my opinion on this: deprecation and removal of CommonJS would be extremely disruptive to the entire ecosystem, and would cause more pain to our users than Python users experienced with migrating to Python 3. As such, deprecation is unlikely to happen in the mid-term future, if at all.

@ljharb
Copy link
Member

ljharb commented Jun 19, 2020

I don’t see any point in ever doing that - CJS isn’t legacy, it’s just one of two first-class module systems in node.

@jkrems
Copy link
Contributor

jkrems commented Jun 19, 2020

I think it’s too early to come up with a clear timeline for this. We’re just approaching the first non-trivial module based apps being possible (without compiling from something else, at least rewriting imports). And there’s a huge amount of code in the ecosystem and even more so in various companies that’s based on CJS - much of which may be non-trivial to convert to ESM (since ESM lacks certain features and is fully async).

So - I wouldn’t go as far as Jordan and say “never” but I’d say “long enough that it could as well be never at this point”.

@jasnell
Copy link
Member

jasnell commented Jun 19, 2020

Still way too early to even consider, especially given that ESM is still experimental

@devsnek devsnek added esm Issues and PRs related to the ECMAScript Modules implementation. experimental Issues and PRs related to experimental features. question Issues that look for answers. labels Jun 19, 2020
@devsnek
Copy link
Member

devsnek commented Jun 19, 2020

Seems answered, closing

@devsnek devsnek closed this as completed Jun 19, 2020
@devsnek devsnek added the module Issues and PRs related to the module subsystem. label Jun 19, 2020
@jsumners
Copy link
Contributor

I hate to revive a long closed issue, but I don't think this topic is resolved. At least one major module developer is planning to remove support for CJS from their modules by the end of 2021 -- https://twitter.com/sindresorhus/status/1349294527350149121

I think without a clear signal from core that CJS is deprecated, module authors are going to have a maintenance nightmare on their hands. As a result of the above announcement, @mcollina has asked the Fastify community if we should do the same fastify/fastify#2847.

What we will have is a hodgepodge of incompatible modules and fragmentation throughout the community. CJS codebases that wish to remain CJS will have to try and find replacement modules, fork and maintain older versions of the modules they use, or refactor their code base to either load modules asynchronously or convert to ESM.

Personally, as a backend only developer, I have not seen any "oh I gotta have that" benefits to ESM that would convince me to shift everything over. But if core were to come out and say "look, keeping both around is going to be a problem so we are deprecating CJS," then I'd basically have no choice. And it would give clarity to the community at-large as to what their maintenance path forward should be.

cc: @sindresorhus for your own visibility to whatever discussion happens here.

@ljharb
Copy link
Member

ljharb commented Feb 10, 2021

It isn’t deprecated, so no such signal should exist. CJS and ESM are both first-class module systems in node.

If a package switches to only ESM, that package has chosen to exclude CJS users, and those users will either switch to ESM, or more likely find alternatives that don’t capriciously exclude them (since providing an ESM wrapper on a CJS package is relatively trivial).

@WebReflection
Copy link
Contributor

My 2c,
today I wrote this file because CommonJS can still do things ESM cannot, and work in both cases (as that file does).

There was a lot of effort to make dual packaging reality, and for personal experience I can say it works fine with everything out there (bundlers, CJS only projects, ESM ony projects), plus cache invalidation/hot reloading is still extremely hard in ESM, so that choosing to ditch CJS when there are tools (ascjs/ucjs to name two) that makes dual packaging a no-brainer, beside the only constraint that is live bindings, sounds a bit "capricious" indeed.

@mcollina
Copy link
Member

There is no plan to deprecate cjs.

@jsumners
Copy link
Contributor

There is no plan to deprecate cjs.

I am aware. My statement is that some discussion should be had about if a plan to do so should be created. In my view, there is clearly a painful future in store if it isn't deprecated.

To be clear, I have no desire to use ESM. But if having both CJS and ESM supported means there will be a fragmented community, then only one should be supported.

@ljharb
Copy link
Member

ljharb commented Feb 10, 2021

There would only be a fragmented community if authors decide to exclude one part of it by shipping a non-dual ESM package.

The solution to that fragmentation is community pressure on those authors, and/or abandoning those authors.

@jsumners
Copy link
Contributor

The solution to that fragmentation is community pressure on those authors, and/or abandoning those authors.

Sadly, I see myself doing that last part.

@ljharb
Copy link
Member

ljharb commented Feb 10, 2021

That is also an effective form of community pressure.

@jasnell
Copy link
Member

jasnell commented Mar 3, 2021

I think, ultimately, we need to come up with a new definition of "Deprecated" in Node.js. There used to be a very toxic notion in Node.js that an API or subsystem could be "locked", and that was used to justify all kinds of bad behavior around rejecting changes, fixes, and necessary improvements. I'm very happy that mentality has been pushed out of the project but we do need a formal notion of some API or subsystem that is just... done, or obsolete, even if it's not going anywhere, and even if someone wants to keep maintaining it. We have some examples of this already in core. The url.parse() API for instance. Yes it's deprecated but there's no plan on actually removing it. New code should never use it but there's no immediate reason to pull it and break existing code. I think CommonJS either is, or will quickly, get to that same point.

Whatever our personal opinions are about whether CommonJS was good enough -- it's not the standard, it's not where the ecosystem is going, it's not where JavaScript is going, and we need to just accept that fact and clearly communicate the direction we think things are heading in.

So, no, I don't think we're anywhere close to Deprecating CommonJS in Node.js... at least not from the point of view of removing it outright, but I do think we can and should draw a line in the sand and say that ESM is the direction we're going from here on out.

@jsumners
Copy link
Contributor

jsumners commented Mar 3, 2021

So, no, I don't think we're anywhere close to Deprecating CommonJS in Node.js... at least not from the point of view of removing it outright, but I do think we can and should draw a line in the sand and say that ESM is the direction we're going from here on out.

How would core communicate that, in your opinion?

@jasnell
Copy link
Member

jasnell commented Mar 3, 2021

How would core communicate that, in your opinion?

Would you take, "At this point I have no idea yet" as an answer for now? ;-)

@jsumners
Copy link
Contributor

jsumners commented Mar 3, 2021

How would core communicate that, in your opinion?

Would you take, "At this point I have no idea yet" as an answer for now? ;-)

image

@ljharb
Copy link
Member

ljharb commented Mar 3, 2021

I think CommonJS either is, or will quickly, get to that same point.

"new code should never use it" is objectively a false claim for CJS; there are a great many reasons new code should use CJS for the foreseeable future. If things change, then we can make changes at that time.

ESM is the direction we're going from here on out.

I think this would be the wrong statement for node to make - now or any time soon - and that stance would do great harm to the ecosystem.

@jsumners
Copy link
Contributor

jsumners commented Mar 3, 2021

and that stance would do great harm to the ecosystem.

The harm has already been done by making two module systems available.

@benjamingr
Copy link
Member

How would core communicate that, in your opinion?

Possibly by releasing a major version where esm is the default for files and a --module=cjs flag to opt-out of esm rather than opt in (and asking npm/yarn to make esm the default in npm init).

@robpalme
Copy link

robpalme commented Mar 3, 2021

ESM is the direction we're going from here on out.

I think this would be the wrong statement for node to make - now or any time soon - and that stance would do great harm to the ecosystem.

Maybe we should also consider the consequence of Node being perceived as directionless on the matter. Browsers are clear. Deno is clear. Modern tooling is clear.

Import Maps just landed in Chrome Stable today. We have already leveraged them to get portable/universal ESM code running across both client & server environments. The Developer Experience is amazing. The future is quickly arriving.

I think Node has a very healthy future. It's easier to sell that vision to others when we have coherent message that table-stakes features like ESM are the way forwards, even if rough edges (feature gaps) still exist today. CJS support will likely never go away, but in my personal opinion, it would weaken the Node project for it to appear confused about which way the world is going.

@DerekNonGeneric
Copy link
Contributor

/to @jasnell

I think, ultimately, we need to come up with a new definition of "Deprecated" in Node.js. There used to be a very toxic notion in Node.js that an API or subsystem could be "locked", and that was used to justify all kinds of bad behavior around rejecting changes, fixes, and necessary improvements. I'm very happy that mentality has been pushed out of the project but we do need a formal notion of some API or subsystem that is just... done, or obsolete, even if it's not going anywhere, and even if someone wants to keep maintaining it.

Perhaps the term “effectively frozen” would be a good fit here.

effectively frozen
no new changes in the API surface unless in response to a major oversight

Refs: https://twitter.com/ryzokuken/status/1359612226009616386

@ljharb
Copy link
Member

ljharb commented Mar 3, 2021

@robpalme where have browsers messaged that no new code should be written as Scripts?

I'll add, that TC39 won't be adding any language features that are ESM-only (besides ESM-related ones, ofc) - in other words, TC39 isn't stating that no new code should be written as Scripts either.

@jasnell
Copy link
Member

jasnell commented Mar 3, 2021

@ljharb -- I would push back just a bit to make it clear: I'm not saying that Node.js users shouldn't have the ability to keep writing Scripts rather than Modules... and I'm not saying that the CommonJS support should go away. What I'm arguing for is that, given the direction the ecosystem is going, and given the massive differences that exist in the models, if we continue to keep our feet firmly planted on both sides we're not going to do either well. For better or worse (and I'm in the camp that it is indeed worse) ESM is the direction the platform has chosen and we should be emphasizing that moving forward (while continuing to support the legacy approach also).

@ghost
Copy link

ghost commented Sep 26, 2022

I see some other issues here. How will UMD modules work with import [from]/export [default]? That will involve a lot of decisions to be made and mass-migrations in the entire JS ecosystem - including other transpilers, such as PureScript, ReasonML, Nim, and Elm; we may need to replace AMD, UMD, CJS, and browser globals with ESM to make the migration simpler. However, we cannot be left behind the standards; if we want to add more features (ex. decorators and JSX) to ECMAScript, we will need to eventually migrate from the other module systems to ESM.

I think we need a larger discussion as soon as possible, so we can get the headaches of migrating out of the way. I wonder if @github-staff can start a discussion about standardization and the legitimacy of ECMAScript.

@jsumners
Copy link
Contributor

I see some other issues here. How will UMD modules work with import [from]/export [default]? That will involve a lot of decisions to be made and mass-migrations in the entire JS ecosystem - including other transpilers, such as PureScript, ReasonML, Nim, and Elm; we may need to replace AMD, UMD, CJS, and browser globals with ESM to make the migration simpler. However, we cannot be left behind the standards; if we want to add more features (ex. decorators and JSX) to ECMAScript, we will need to eventually migrate from the other module systems to ESM.

None of that has anything to do with CJS and ESM: the two module types that Node.js supports.

I wonder if @github-staff can start a discussion about standardization and the legitimacy of ECMAScript.

What?

@ljharb
Copy link
Member

ljharb commented Sep 26, 2022

Additionally, UMD already works fine - it’s treated as CJS by anything that understands modern module systems.

@ghost
Copy link

ghost commented Sep 27, 2022

Never mind. 🤦‍♂️

@Haringat
Copy link

There is no plan to deprecate cjs.

CJS will probably never be removed from Node.JS as there are thousands of unmaintained packages out there in the NPM ecosystem that use it and even more packages relying on those (for better or worse).

However, CJS will lead a shadowy existence in the future as it provides no real advantage, but a number of disadvantages when compared to MJS (e.g. no top-level await, cannot use MJS libraries) so fewer and fewer people will use it for new projects. At that point it will probably become deprecated, if only because people will have problems using it due to no interoperability with MJS libs and it will be like domains. Deprecated, but probably forever in Node.JS as a relic of times past.

@ljharb
Copy link
Member

ljharb commented Feb 22, 2023

@Haringat "no real advantage" may be true in the future, and i hope it is - but it's not true now, since loaders aren't shipped yet, hot reloading still can't work without a memory leak, etc. However, time will tell if the ecosystem's long term response to ESM-only packages is "switch to ESM" or "use/create CJS-friendly packages".

@Haringat
Copy link

@ljharb "create CJS-friendly packages" is a headache, as you either simply wrap a CJS module with ESM (at which point you basically maintain a CJS package as the entire functionally relevent code is in CJS) or you have two instances of your module hanging around which is bound to cause state-problems.
And yes, the problems you described are there, but I would call those teething problems as I believe they will (hopefully soon) be solved.

@benjamingr
Copy link
Member

No one is stopping you from exporting your esm code to cjs :]

@ljharb
Copy link
Member

ljharb commented Feb 22, 2023

@Haringat or you just make a CJS-only package and you're done.

@benjamingr you can't do that synchronously.

@Haringat
Copy link

@ljharb or I just make a ESM-only package and I'm done.

@ljharb
Copy link
Member

ljharb commented Feb 22, 2023

@Haringat except that your package can't be used by the majority of the ecosystem. to me that's not "done" but certainly you're not alone in calling it thus.

@benjamingr
Copy link
Member

@ljharb what do you mean? What's stopping you from createRequire in and then exporting whatever you want from there?

@Haringat
Copy link

@benjamingr That is the wrong direction. That is for consuming CJS files from ESM files. You were talking about exporting ESM to CJS.

@ljharb
Copy link
Member

ljharb commented Feb 22, 2023

@benjamingr i'm confused, createRequire is used to make a require function, which can't pull in ESM - afaik the only way to consume ESM in a CJS module is import(), which is async.

@aquapi
Copy link

aquapi commented Oct 8, 2023

CommonJS starts faster.
ESM is meant to be used with browser, which means that it is async.
Async does cost some microticks and overhead.

@Haringat
Copy link

Haringat commented Oct 8, 2023

CommonJS starts faster.

Not always, but it can be the case (especially when everything is on the FS)

ESM is meant to be used with browser, which means that it is async.

It is async but not because it was meant to be a browser-technology, but because it was meant to work everywhere.

@Haringat
Copy link

Haringat commented Oct 8, 2023

@xieyuheng Well it is not really about it being technically impossible to support both, but rather about it becoming annoying in the long run. For the moment the call is definitely to support both, simply because there are literally thousands of cjs packages out there.

I predict that in a few years there will be two kinds of packages: Those that use mjs and those that have not seen an update in years by then. And then the argument to keep supporting cjs only to not break some unmaintained packages will not be too strong.

@ljharb
Copy link
Member

ljharb commented Oct 8, 2023

No, it’s technically impossible - the way bun did it is to disable a language feature, top-level await, and likely to violate some of the timing guarantees in the spec (i haven’t investigated yet to be certain).

@jsumners
Copy link
Contributor

jsumners commented Oct 8, 2023

Why are we talking about what Bun is doing in a thread about what Node.js should or shouldn't do?

@ljharb
Copy link
Member

ljharb commented Oct 8, 2023

I totally agree - node has different constraints, and “but not-node does X” isn’t relevant to whether node can or should do X.

@tbjers
Copy link

tbjers commented Oct 9, 2023

if there is not competition to nodejs, your prediction might be right.

but since there is competing server runtime supporting both.

people forced by nodejs to remove all commonjs code from their codebase maybe will simply choose the competing runtime.

if it is your team and your app, what you would do?

The fact that this thread is still open for comments is a testament to the overwhelming patience and tolerance of the steering committee and the Node core team.

@Haringat
Copy link

Haringat commented Oct 9, 2023

@xieyuheng

millions not thousands.

Okay, more thousands than I thought 😉

if there is not competition to nodejs, your prediction might be right.

but since there is competing server runtime supporting both.

AFAIK there are two competing JavaScript runtimes: deno and bun.

Deno promotes ESM as it is typescript-first. Bun supports both as you mentioned.

I fail to see your point...

people forced by nodejs to remove all commonjs code from their codebase
maybe will simply choose the competing runtime.

My guess would be that if node ever removed cjs (which they would only do after one hell of a deprecation period), people who would still depend on it would either move on to ESM or stay on a node version that supports cjs.

if it is your team and your app, what you would do?

See my prediction above.

@jsumners
Copy link
Contributor

jsumners commented Oct 9, 2023

Please take whatever this conversation is to another thread.

@Offroaders123
Copy link

My 2c, today I wrote this file because CommonJS can still do things ESM cannot, and work in both cases (as that file does).

There was a lot of effort to make dual packaging reality, and for personal experience I can say it works fine with everything out there (bundlers, CJS only projects, ESM ony projects), plus cache invalidation/hot reloading is still extremely hard in ESM, so that choosing to ditch CJS when there are tools (ascjs/ucjs to name two) that makes dual packaging a no-brainer, beside the only constraint that is live bindings, sounds a bit "capricious" indeed.

My demo might be incorrect, but I feel like it may still be possible, doing so like this maybe?

If you instead compare module.exports = to that of export * from, rather than export default, then yeah I think you're right, you can't have conditionally-imported named exports from another module.

/* c8 ignore start */
let canvas;

try {
  canvas = await import('canvas');
} catch (fallback) {
  canvas = await import('./canvas-shim.cjs');
}

export default canvas;
/* c8 ignore stop */

Come to think of it, I think this pattern may be similar as well.

/* c8 ignore start */
export default await import('canvas').catch(() => import('./canvas-shim.cjs'));
/* c8 ignore stop */

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. experimental Issues and PRs related to experimental features. module Issues and PRs related to the module subsystem. question Issues that look for answers.
Projects
None yet
Development

No branches or pull requests