Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 36.4k
loader, docs, test: named exports from commonjs modules#16675
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
81fef20d3647dde8478e2116b4701a53fd1ac2b4a14a1c95a49add2b28f58f6c6da7111220d06fe73e26c4a6c27File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -19,6 +19,8 @@ const search = require('internal/loader/search'); | ||||
| const asyncReadFile = require('util').promisify(require('fs').readFile); | ||||
| const debug = require('util').debuglog('esm'); | ||||
| const esModuleInterop = Symbol.for('esModuleInterop'); | ||||
| const realpathCache = new Map(); | ||||
| const loaders = new Map(); | ||||
| @@ -36,24 +38,39 @@ loaders.set('esm', async (url) => { | ||||
| // Strategy for loading a node-style CommonJS module | ||||
| loaders.set('cjs', async (url) => { | ||||
| return createDynamicModule(['default'], url, (reflect) => { | ||||
| debug(`Loading CJSModule ${url}`); | ||||
| const CJSModule = require('module'); | ||||
| const pathname = internalURLModule.getPathFromURL(new URL(url)); | ||||
| CJSModule._load(pathname); | ||||
| debug(`Loading CJSModule ${url}`); | ||||
| const CJSModule = require('module'); | ||||
| const pathname = internalURLModule.getPathFromURL(new URL(url)); | ||||
| const exports = CJSModule._load(pathname); | ||||
Member 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 changes CJS to always evaluate prior to linking ESM which reorders imports in odd ways | ||||
| const es = exports[esModuleInterop] !== undefined ? | ||||
| exports[esModuleInterop] : exports.__esModule; | ||||
| const keys = es ? Object.keys(exports) : ['default']; | ||||
| return createDynamicModule(keys, url, (reflect) => { | ||||
Contributor 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. Looks like Member 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. It maps names to safeguard against this already: node/lib/internal/loader/ModuleWrap.js Line 18 in f5a7a9e
Contributor 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. Ah so it does, my mistake. Missed the | ||||
| if (es) { | ||||
Contributor 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. Is branching like this faster? Otherwise seems like the else branch does exactly what the
| ||||
| for (var i = 0; i < keys.length; i++) | ||||
| reflect.exports[keys[i]].set(exports[keys[i]]); | ||||
| } else { | ||||
| reflect.exports.default.set(exports); | ||||
| } | ||||
| }); | ||||
| }); | ||||
| // Strategy for loading a node builtin CommonJS module that isn't | ||||
| // through normal resolution | ||||
| loaders.set('builtin', async (url) => { | ||||
| return createDynamicModule(['default'], url, (reflect) => { | ||||
| debug(`Loading BuiltinModule ${url}`); | ||||
| const exports = NativeModule.require(url.substr(5)); | ||||
| debug(`Loading BuiltinModule ${url}`); | ||||
| const exports = NativeModule.require(url.substr(5)); | ||||
| const keys = Object.keys(exports); | ||||
| return createDynamicModule(['default', ...keys], url, (reflect) => { | ||||
Member 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. It's not documented that builtin modules still have a default export. Contributor 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. Perhaps for these native module keys we should add a filter - Member 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. Please do not; | ||||
| reflect.exports.default.set(exports); | ||||
| for (var i = 0; i < keys.length; i++) | ||||
| reflect.exports[keys[i]].set(exports[keys[i]]); | ||||
| }); | ||||
| }); | ||||
| // Strategy for loading a native addon module | ||||
| // Named exports will not be parsed from these - see | ||||
| // https://github.com/nodejs/abi-stable-node/issues/256#issuecomment-325138872 | ||||
Member 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. Fwiw, another thing I’d like to see (not necessarily in this PR) is keeping parity between existing addons and CJS modules. I agree with the linked comment in that we don’t need any extra wiring for ESM, at least for now, but I don’t see any good reason to let wrapping for CJS and native addons diverge. MemberAuthor 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 read this as napi wanting to keep a single export point such that they don't need to worry about if its Member 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. It’s certainly not that important for addons, but … if Member 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. @addaleax the | ||||
| loaders.set('addon', async (url) => { | ||||
| const ctx = createDynamicModule(['default'], url, (reflect) => { | ||||
| debug(`Loading NativeModule ${url}`); | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // Flags: --experimental-modules | ||
| /* eslint-disable required-modules */ | ||
| import assert from 'assert'; | ||
| import eightyfour, { fourtytwo } from | ||
| '../fixtures/es-module-loaders/babel-to-esm.js'; | ||
| assert.strictEqual(eightyfour, 84); | ||
| assert.strictEqual(fourtytwo, 42); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // Flags: --experimental-modules | ||
| /* eslint-disable required-modules */ | ||
| // eslint-disable-next-line no-unused-vars | ||
| import eightyfour, { fourtytwo } | ||
| from '../fixtures/es-module-loaders/babel-to-esm-override.js'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,14 @@ | ||
| // Flags: --experimental-modules | ||
| /* eslint-disable required-modules */ | ||
| import * as fs from 'fs'; | ||
| import assert from 'assert'; | ||
| import fs, { readFile } from 'fs'; | ||
| import main, { named } from | ||
| '../fixtures/es-module-loaders/cjs-to-es-namespace.js'; | ||
| assert.deepStrictEqual(Object.keys(fs), ['default']); | ||
| assert(fs); | ||
| assert(fs.readFile); | ||
| assert.strictEqual(fs.readFile, readFile); | ||
| assert.strictEqual(main, 'default'); | ||
| assert.strictEqual(named, 'named'); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // Flags: --experimental-modules | ||
| /* eslint-disable required-modules */ | ||
| import assert from 'assert'; | ||
| import { enum as e } from | ||
| '../fixtures/es-module-loaders/reserved-keywords.js'; | ||
| assert(e); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| "use strict"; | ||
| /* | ||
| created by babel with es2015 preset | ||
| ``` | ||
| export const fourtytwo = 42; | ||
| export default 84; | ||
| ``` | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| var fourtytwo = exports.fourtytwo = 42; | ||
| exports.default = 84; | ||
| // added after babel compile | ||
| exports[Symbol.for('esModuleInterop')] = false |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| "use strict"; | ||
| /* | ||
| created by babel with es2015 preset | ||
| ``` | ||
| export const fourtytwo = 42; | ||
| export default 84; | ||
| ``` | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| var fourtytwo = exports.fourtytwo = 42; | ||
| exports.default = 84; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| exports.named = 'named'; | ||
| exports.default = 'default'; | ||
| exports[Symbol.for('esModuleInterop')] = true; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| module.exports = { | ||
| enum: 'enum', | ||
| class: 'class', | ||
| delete: 'delete', | ||
| [Symbol.for('esModuleInterop')]: true, | ||
| ||
| }; | ||
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.
Is this a snapshot of the values, or just a snapshot of the names?
The latter is necessary, but the former may not be.
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.
I thought just saying
exportswas ok since its both the names and the valuesThere 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.
Right - I’m saying that there’s no need for the values to be snapshotted; just the names.
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.
technically it isn't a snapshot, it's just a useful term to describe how it becomes static when assigned in reflection with es imports.
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.
It’s pretty important to be precise here :-) i think “a snapshot of the names of the exports”, and indicating that if the values are updated, the resulting imports will update as well (a requirement for APM-like use cases, i understand)