module: add __esModule to require()'d ESM#52166
Closed
joyeecheung wants to merge 4 commits into
Closed
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before this PR, trying to load real ESM from transpiled ESM would throw errors like this with
--experimental-require-moduleAfter this PR it logs 'import both'.
Tooling in the ecosystem have been using the __esModule property to
recognize transpiled ESM in consuming code. For example, a 'log'
package written in ESM:
Can be transpiled as:
The consuming code may be written like this in ESM:
Which gets transpiled to:
So to allow transpiled consuming code to recognize require()'d real ESM
as ESM and pick up the default exports, we add a __esModule property by
building a source text module facade for any module that has a default
export and add .__esModule = true to the exports. We don't do this to
modules that don't have default exports to avoid the unnecessary
overhead. This maintains the enumerability of the re-exported names
and the live binding of the exports.
The source of the facade is defined as a constant per-isolate property
required_module_facade_source_string, which looks like this
And the 'original' module request is always resolved by
createRequiredModuleFacade() to wrap which is a ModuleWrap wrapping
over the original module.
This PR originally used the same trick that Bun did (h/t @Jarred-Sumner) by putting the original module namespace in the prototype chain. Upon closer examination this now switched to use a SourceTextModule facade only when the exports contain default to 1) reduce the performance impact 2) ensure that the exported names are still enumerable and can be copied by tools.
Refs: #51977 (comment)
Refs: #52134