Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ All CommonJS, JSON, and C++ modules can be used with `import`.
Modules loaded this way will only be loaded once, even if their query
or fragment string differs between `import` statements.

When loaded via `import` these modules will provide a single `default` export
representing the value of `module.exports` at the time they finished evaluating.
When loaded via `import` these modules will provide a `default` export
representing the value of `module.exports` at the time they finished evaluating,
and named exports for each key of `module.exports`.
Copy link
Member

Choose a reason for hiding this comment

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

I would add a sentence explaining what will not be supported, i.e. modifying module.exports with an asynchronous operations will not update that property in ESM. It seems an edge case, but I do not see a way to provide a good error message for it we should be careful in how we document.


```js
import fs from 'fs';
Expand All @@ -97,6 +98,11 @@ fs.readFile('./foo.txt', (err, body) => {
});
```

```js
import { readFileSync } from 'fs';
console.log(readFileSync('./foo.txt').toString());
```

## Loader hooks

<!-- type=misc -->
Expand Down
33 changes: 20 additions & 13 deletions lib/internal/loader/ModuleRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,38 @@ 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);
Copy link
Member

Choose a reason for hiding this comment

The 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 keys = Object.keys(exports);
return createDynamicModule(['default', ...keys], url, (reflect) => {
reflect.exports.default.set(exports);
for (const key of keys) reflect.exports[key].set(exports[key]);
});
});

// 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) => {
Copy link
Member

Choose a reason for hiding this comment

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

It's not documented that builtin modules still have a default export.

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps for these native module keys we should add a filter - Object.keys(exports).filter(name => name.startsWith('_') === false)? This would avoid private keys such as import { _makeLong } from 'path' being on the public API that will end up in the list for type hinting systems such as TypeScript.

Copy link
Member

Choose a reason for hiding this comment

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

Please do not; _ may be used to convey hopeful privacy, but it’s always fully public.

reflect.exports.default.set(exports);
for (const key of keys) reflect.exports[key].set(exports[key]);
});
});

loaders.set('addon', async (url) => {
const ctx = createDynamicModule(['default'], url, (reflect) => {
debug(`Loading NativeModule ${url}`);
const module = { exports: {} };
const pathname = internalURLModule.getPathFromURL(new URL(url));
process.dlopen(module, _makeLong(pathname));
debug(`Loading NativeModule ${url}`);
const module = { exports: {} };
const pathname = internalURLModule.getPathFromURL(new URL(url));
process.dlopen(module, _makeLong(pathname));
const keys = Object.keys(module.exports);
const ctx = createDynamicModule(['default', ...keys], url, (reflect) => {
reflect.exports.default.set(module.exports);
for (const key of keys) reflect.exports[key].set(module.exports[key]);
});
return ctx;
});
Expand Down
6 changes: 4 additions & 2 deletions test/es-module/test-esm-namespace.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Flags: --experimental-modules
/* eslint-disable required-modules */

import * as fs from 'fs';
import assert from 'assert';
import fs, { readFile } from 'fs';

assert.deepStrictEqual(Object.keys(fs), ['default']);
assert(fs);
assert(fs.readFile);
assert(readFile);
Copy link
Member

Choose a reason for hiding this comment

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

can you test that readFile  is the same as fs.readFile?

8 changes: 8 additions & 0 deletions test/es-module/test-reserved-keywords.mjs
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 { delete as d } from
'../fixtures/es-module-loaders/reserved-keywords.js';

assert(d);
5 changes: 5 additions & 0 deletions test/fixtures/es-module-loaders/reserved-keywords.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
enum: 'enum',
class: 'class',
delete: 'delete',
};