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

module: support 'module.exports' interop export name in require(esm) #54563

Closed
wants to merge 1 commit into from

Conversation

guybedford
Copy link
Contributor

@guybedford guybedford commented Aug 26, 2024

This adds support for export { foo as 'module.exports' } in a CJS module to imply that this export value should always be used as the module value when requiring that ESM module under require(esm), allowing other types than just module namespaces in this interop.

This effectively splits off the non-major change from #53848 as discussed in the Node.js loaders meeting, under a rename of this flag to use the 'module.exports' pattern, where that PR will be updated to use the name that lands here on the ESM wrapper as a major change further.

The naming bikeshed took place in nodejs/loaders#221 and was determined by TSC vote.

//cc @nodejs/loaders

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/loaders

@nodejs-github-bot nodejs-github-bot added module Issues and PRs related to the module subsystem. needs-ci PRs that need a full CI run. labels Aug 26, 2024
@RedYetiDev RedYetiDev added the loaders Issues and PRs related to ES module loaders label Aug 26, 2024
Copy link

codecov bot commented Aug 26, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 88.40%. Comparing base (89a2f56) to head (78d41a2).
Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #54563   +/-   ##
=======================================
  Coverage   88.39%   88.40%           
=======================================
  Files         652      652           
  Lines      186565   186568    +3     
  Branches    36046    36043    -3     
=======================================
+ Hits       164916   164929   +13     
- Misses      14908    14911    +3     
+ Partials     6741     6728   -13     
Files with missing lines Coverage Δ
lib/internal/modules/cjs/loader.js 97.42% <100.00%> (+<0.01%) ⬆️

... and 23 files with indirect coverage changes

@guybedford guybedford changed the title module: support __cjsUnwrapDefault interop flag in require(esm) module: support cjsUnwrapDefault interop flag in require(esm) Sep 1, 2024
doc/api/modules.md Outdated Show resolved Hide resolved
@guybedford
Copy link
Contributor Author

//cc @nodejs/loaders this PR is ready for final review.

@mcollina mcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 2, 2024
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wish we used __cjsUnwrapDefault instead, but I'm ok with this as well.

@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 2, 2024
@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@joyeecheung joyeecheung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another caveat of the default live-binding being broken (compared to ESM import semantics). Though I guess that's rather an edge case that most won't care about - this feature is intended for library authors who migrate from CommonJS, which doesn't support re-assigning module.exports once it's cached anyway.

doc/api/modules.md Show resolved Hide resolved
doc/api/modules.md Outdated Show resolved Hide resolved
doc/api/modules.md Outdated Show resolved Hide resolved
@ljharb
Copy link
Member

ljharb commented Sep 3, 2024

default exports in ESM do not have live bindings, only named exports do.

@joyeecheung
Copy link
Member

joyeecheung commented Sep 4, 2024

default exports in ESM do not have live bindings, only named exports do.

I am talking about this (which can't be mimicked with native CommonJS, but then it has never been, so for CommonJS -> ESM module migration, it doesn't matter as the original CommonJS module is unlikely to expect re-assigning module.exports to be effective in consumer code. It's only a problem for existing ESM that re-assigns default exports and tries to extend support to CommonJS consumers, which should be rare):

// a.mjs
let a = 1;
export function increment() {
  a++;
}
export { a as default };

// b.mjs
import a, { increment } from './a.mjs';
increment();
console.log(a);  // 2

@ljharb
Copy link
Member

ljharb commented Sep 4, 2024

Oh interesting, you’re saying that when exported as a name, it does have live binding behavior - TIL.

@guybedford guybedford changed the title module: support cjsUnwrapDefault interop flag in require(esm) module: support __cjsUnwrapDefault interop flag in require(esm) Sep 6, 2024
Copy link
Member

@joyeecheung joyeecheung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have an alternative proposal in nodejs/loaders#221 which people seem to like over there:

function foo() {}

export { foo as "module.exports" };
export default foo;

I think we should do a poll of this v.s. what this PR currently proposes:

function foo() {}

export const __cjsUnwrapDefault = true;
export default foo;

and decide which one we should go with. I am not sure what the poll audience should be though, should it be:

  • TSC, as usual
  • collaborators, because this is a naming choice and seems fun to vote on
  • Broader user base (social media poll?)

placing a block until we have a decision.

@joyeecheung joyeecheung added the tsc-agenda Issues and PRs to discuss during the meetings of the TSC. label Sep 7, 2024
@guybedford
Copy link
Contributor Author

@joyeecheung given there's quite a bit of nuance here, I'd suggest we discuss this within the TSC. I'll make myself available for the next meeting on the 11th. I'd like us to come to a conclusion as swiftly as possible though and encourage a vote / decision in the meeting, as this has discussion has now dragged on since July 14. I'll commit to whatever decision is made there for this PR and align that result with #53848.

@jasnell
Copy link
Member

jasnell commented Sep 8, 2024

I prefer the more explicit use of __cjsUnwrapDefault = true personally as it seems less magical and more explicit about the intent.

@guybedford guybedford added backport-blocked-v22.x PRs that should land on the v22.x-staging branch but are blocked by another PR's pending backport. backport-requested-v20.x PRs awaiting manual backport to the v20.x-staging branch. backport-requested-v22.x PRs awaiting manual backport to the v22.x-staging branch. and removed backport-blocked-v22.x PRs that should land on the v22.x-staging branch but are blocked by another PR's pending backport. tsc-agenda Issues and PRs to discuss during the meetings of the TSC. needs-ci PRs that need a full CI run. labels Oct 1, 2024
@joyeecheung joyeecheung mentioned this pull request Oct 1, 2024
8 tasks
doc/api/modules.md Outdated Show resolved Hide resolved
doc/api/modules.md Show resolved Hide resolved
test/es-module/test-require-as-esm-interop.mjs Outdated Show resolved Hide resolved
@guybedford guybedford force-pushed the cjs-unwrap branch 2 times, most recently from 74f1a93 to 3f4455d Compare October 1, 2024 18:25
@guybedford
Copy link
Contributor Author

I believe all outstanding comments have been addressed. It's been a ride, we might finally be there.

Will aim to land shortly unless anyone would like to engage in further debate.

doc/api/modules.md Outdated Show resolved Hide resolved
@aduh95 aduh95 added the request-ci Add this label to start a Jenkins CI on a PR. label Oct 1, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Oct 1, 2024
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot
Copy link
Collaborator

guybedford added a commit that referenced this pull request Oct 2, 2024
PR-URL: #54563
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
@guybedford
Copy link
Contributor Author

Landed in d24c731.

@guybedford guybedford closed this Oct 2, 2024
@guybedford guybedford deleted the cjs-unwrap branch October 2, 2024 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-requested-v20.x PRs awaiting manual backport to the v20.x-staging branch. backport-requested-v22.x PRs awaiting manual backport to the v22.x-staging branch. loaders Issues and PRs related to ES module loaders module Issues and PRs related to the module subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants