From 3ac21d99968cae42eff6203fe84631b1d916cde9 Mon Sep 17 00:00:00 2001 From: guybedford Date: Tue, 28 Aug 2018 17:28:46 +0200 Subject: [PATCH 1/7] esm: remove .json support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/modules/pull/180 PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Myles Borins Reviewed-By: John-David Dalton Reviewed-By: Michaël Zasso --- doc/api/esm.md | 3 +-- lib/internal/modules/esm/default_resolve.js | 1 - lib/internal/modules/esm/translators.js | 22 +-------------------- src/module_wrap.cc | 2 +- test/es-module/test-esm-json.mjs | 8 -------- test/fixtures/es-modules/json.json | 3 --- 6 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 test/es-module/test-esm-json.mjs delete mode 100644 test/fixtures/es-modules/json.json diff --git a/doc/api/esm.md b/doc/api/esm.md index 459f877718..a1dfae6931 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -86,7 +86,7 @@ For now, only modules using the `file:` protocol can be loaded. ## Interop with existing modules -All CommonJS, JSON, and C++ modules can be used with `import`. +CommonJS 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. @@ -176,7 +176,6 @@ module. This can be one of the following: | `'esm'` | Load a standard JavaScript module | | `'cjs'` | Load a node-style CommonJS module | | `'builtin'` | Load a node builtin CommonJS module | -| `'json'` | Load a JSON file | | `'addon'` | Load a [C++ Addon][addons] | | `'dynamic'` | Use a [dynamic instantiate hook][] | diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index 875c560cb1..293667ce80 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -46,7 +46,6 @@ function search(target, base) { const extensionFormatMap = { '__proto__': null, '.mjs': 'esm', - '.json': 'json', '.node': 'addon', '.js': 'cjs' }; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index aaf35ed461..7dbe4ee6d6 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -3,8 +3,7 @@ const { NativeModule, internalBinding } = require('internal/bootstrap/loaders'); const { ModuleWrap } = internalBinding('module_wrap'); const { - stripShebang, - stripBOM + stripShebang } = require('internal/modules/cjs/helpers'); const CJSModule = require('internal/modules/cjs/loader'); const internalURLModule = require('internal/url'); @@ -16,9 +15,7 @@ const { SafeMap } = require('internal/safe_globals'); const { URL } = require('url'); const { debuglog, promisify } = require('util'); const readFileAsync = promisify(fs.readFile); -const readFileSync = fs.readFileSync; const StringReplace = Function.call.bind(String.prototype.replace); -const JsonParse = JSON.parse; const debug = debuglog('esm'); @@ -86,20 +83,3 @@ translators.set('addon', async (url) => { reflect.exports.default.set(module.exports); }); }); - -// Strategy for loading a JSON file -translators.set('json', async (url) => { - debug(`Translating JSONModule ${url}`); - return createDynamicModule(['default'], url, (reflect) => { - debug(`Loading JSONModule ${url}`); - const pathname = internalURLModule.fileURLToPath(new URL(url)); - const content = readFileSync(pathname, 'utf8'); - try { - const exports = JsonParse(stripBOM(content)); - reflect.exports.default.set(exports); - } catch (err) { - err.message = pathname + ': ' + err.message; - throw err; - } - }); -}); diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 1ef22b270d..7237b9712a 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -42,7 +42,7 @@ using v8::TryCatch; using v8::Undefined; using v8::Value; -static const char* const EXTENSIONS[] = {".mjs", ".js", ".json", ".node"}; +static const char* const EXTENSIONS[] = {".mjs", ".js", ".node"}; ModuleWrap::ModuleWrap(Environment* env, Local object, diff --git a/test/es-module/test-esm-json.mjs b/test/es-module/test-esm-json.mjs deleted file mode 100644 index a7146d19a9..0000000000 --- a/test/es-module/test-esm-json.mjs +++ /dev/null @@ -1,8 +0,0 @@ -// Flags: --experimental-modules -import '../common'; -import assert from 'assert'; -import ok from '../fixtures/es-modules/test-esm-ok.mjs'; -import json from '../fixtures/es-modules/json.json'; - -assert(ok); -assert.strictEqual(json.val, 42); diff --git a/test/fixtures/es-modules/json.json b/test/fixtures/es-modules/json.json deleted file mode 100644 index 8288d42e2b..0000000000 --- a/test/fixtures/es-modules/json.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "val": 42 -} From fc0a8f3736cbd42f7795604d7dec7234f17f7d42 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Wed, 12 Sep 2018 17:51:28 -0400 Subject: [PATCH 2/7] esm: remove .node support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/modules/pull/180 PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Guy Bedford Reviewed-By: John-David Dalton Reviewed-By: Michaël Zasso --- doc/api/esm.md | 2 -- lib/internal/modules/esm/default_resolve.js | 1 - lib/internal/modules/esm/translators.js | 13 ------------- src/module_wrap.cc | 2 +- test/addons/hello-world-esm/binding.cc | 13 ------------- test/addons/hello-world-esm/binding.gyp | 9 --------- test/addons/hello-world-esm/test.js | 20 -------------------- test/addons/hello-world-esm/test.mjs | 6 ------ 8 files changed, 1 insertion(+), 65 deletions(-) delete mode 100644 test/addons/hello-world-esm/binding.cc delete mode 100644 test/addons/hello-world-esm/binding.gyp delete mode 100644 test/addons/hello-world-esm/test.js delete mode 100644 test/addons/hello-world-esm/test.mjs diff --git a/doc/api/esm.md b/doc/api/esm.md index a1dfae6931..2ec7ddefce 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -176,7 +176,6 @@ module. This can be one of the following: | `'esm'` | Load a standard JavaScript module | | `'cjs'` | Load a node-style CommonJS module | | `'builtin'` | Load a node builtin CommonJS module | -| `'addon'` | Load a [C++ Addon][addons] | | `'dynamic'` | Use a [dynamic instantiate hook][] | For example, a dummy loader to load JavaScript restricted to browser resolution @@ -253,5 +252,4 @@ then be called at the exact point of module evaluation order for that module in the import tree. [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md -[addons]: addons.html [dynamic instantiate hook]: #esm_dynamic_instantiate_hook diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index 293667ce80..ed6361918b 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -46,7 +46,6 @@ function search(target, base) { const extensionFormatMap = { '__proto__': null, '.mjs': 'esm', - '.node': 'addon', '.js': 'cjs' }; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 7dbe4ee6d6..0f343dadad 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -10,7 +10,6 @@ const internalURLModule = require('internal/url'); const createDynamicModule = require( 'internal/modules/esm/create_dynamic_module'); const fs = require('fs'); -const { _makeLong } = require('path'); const { SafeMap } = require('internal/safe_globals'); const { URL } = require('url'); const { debuglog, promisify } = require('util'); @@ -71,15 +70,3 @@ translators.set('builtin', async (url) => { reflect.exports.default.set(module.exports); }); }); - -// Strategy for loading a node native module -translators.set('addon', async (url) => { - debug(`Translating NativeModule ${url}`); - return createDynamicModule(['default'], url, (reflect) => { - debug(`Loading NativeModule ${url}`); - const module = { exports: {} }; - const pathname = internalURLModule.fileURLToPath(new URL(url)); - process.dlopen(module, _makeLong(pathname)); - reflect.exports.default.set(module.exports); - }); -}); diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 7237b9712a..bd1f20201d 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -42,7 +42,7 @@ using v8::TryCatch; using v8::Undefined; using v8::Value; -static const char* const EXTENSIONS[] = {".mjs", ".js", ".node"}; +static const char* const EXTENSIONS[] = {".mjs", ".js"}; ModuleWrap::ModuleWrap(Environment* env, Local object, diff --git a/test/addons/hello-world-esm/binding.cc b/test/addons/hello-world-esm/binding.cc deleted file mode 100644 index 944f563195..0000000000 --- a/test/addons/hello-world-esm/binding.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -void Method(const v8::FunctionCallbackInfo& args) { - v8::Isolate* isolate = args.GetIsolate(); - args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world")); -} - -void init(v8::Local exports) { - NODE_SET_METHOD(exports, "hello", Method); -} - -NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/hello-world-esm/binding.gyp b/test/addons/hello-world-esm/binding.gyp deleted file mode 100644 index 7ede63d94a..0000000000 --- a/test/addons/hello-world-esm/binding.gyp +++ /dev/null @@ -1,9 +0,0 @@ -{ - 'targets': [ - { - 'target_name': 'binding', - 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], - 'sources': [ 'binding.cc' ] - } - ] -} diff --git a/test/addons/hello-world-esm/test.js b/test/addons/hello-world-esm/test.js deleted file mode 100644 index d0faf65540..0000000000 --- a/test/addons/hello-world-esm/test.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; -const common = require('../../common'); - -const assert = require('assert'); -const { spawnSync } = require('child_process'); -const { copyFileSync } = require('fs'); -const { join } = require('path'); - -const buildDir = join(__dirname, 'build'); - -copyFileSync(join(buildDir, common.buildType, 'binding.node'), - join(buildDir, 'binding.node')); - -const result = spawnSync(process.execPath, - ['--experimental-modules', `${__dirname}/test.mjs`]); - -assert.ifError(result.error); -// TODO: Uncomment this once ESM is no longer experimental. -// assert.strictEqual(result.stderr.toString().trim(), ''); -assert.strictEqual(result.stdout.toString().trim(), 'binding.hello() = world'); diff --git a/test/addons/hello-world-esm/test.mjs b/test/addons/hello-world-esm/test.mjs deleted file mode 100644 index d98de5bf87..0000000000 --- a/test/addons/hello-world-esm/test.mjs +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable node-core/required-modules */ - -import assert from 'assert'; -import binding from './build/binding.node'; -assert.strictEqual(binding.hello(), 'world'); -console.log('binding.hello() =', binding.hello()); From 4fc51761dc2999dbae140674f3253a211fc41bf2 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Mon, 1 Oct 2018 23:27:30 -0400 Subject: [PATCH 3/7] esm: remove .js support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/modules/pull/180 PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Guy Bedford Reviewed-By: John-David Dalton Reviewed-By: Michaël Zasso --- doc/api/esm.md | 20 +++++++++++++------ lib/internal/modules/esm/default_resolve.js | 3 +-- src/module_wrap.cc | 2 +- test/common/index.mjs | 14 +++++++++++-- test/es-module/test-esm-double-encoding.mjs | 2 +- test/es-module/test-esm-dynamic-import.js | 1 + test/es-module/test-esm-error-cache.js | 6 +++--- test/es-module/test-esm-require-cache.mjs | 10 +++++++--- test/es-module/test-esm-shared-loader-dep.mjs | 6 ++++-- test/es-module/test-esm-snapshot.mjs | 7 ------- .../es-module-loaders/loader-shared-dep.mjs | 6 +++++- .../es-module-loaders/loader-with-dep.mjs | 6 +++++- .../es-modules/esm-snapshot-mutator.js | 4 ---- test/fixtures/es-modules/esm-snapshot.js | 2 -- test/fixtures/es-modules/pjson-main/main.js | 1 - test/fixtures/es-modules/pjson-main/main.mjs | 1 + .../es-modules/pjson-main/package.json | 2 +- ...=> test-esm-double-encoding-native%20.mjs} | 2 +- test/fixtures/syntax/bad_syntax.mjs | 1 + 19 files changed, 58 insertions(+), 38 deletions(-) delete mode 100644 test/es-module/test-esm-snapshot.mjs delete mode 100644 test/fixtures/es-modules/esm-snapshot-mutator.js delete mode 100644 test/fixtures/es-modules/esm-snapshot.js delete mode 100644 test/fixtures/es-modules/pjson-main/main.js create mode 100644 test/fixtures/es-modules/pjson-main/main.mjs rename test/fixtures/es-modules/{test-esm-double-encoding-native%20.js => test-esm-double-encoding-native%20.mjs} (86%) create mode 100644 test/fixtures/syntax/bad_syntax.mjs diff --git a/doc/api/esm.md b/doc/api/esm.md index 2ec7ddefce..81ccf4846b 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -54,6 +54,10 @@ property: ## Notable differences between `import` and `require` +### Only Support for .mjs + +ESM must have the `.mjs` extension. + ### No NODE_PATH `NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks @@ -95,12 +99,17 @@ When loaded via `import` these modules will provide a single `default` export representing the value of `module.exports` at the time they finished evaluating. ```js -// foo.js -module.exports = { one: 1 }; +// cjs.js +module.exports = 'cjs'; + +// esm.mjs +import { createRequireFromPath as createRequire } from 'module'; +import { fileURLToPath as fromPath } from 'url'; + +const require = createRequire(fromPath(import.meta.url)); -// bar.js -import foo from './foo.js'; -foo.one === 1; // true +const cjs = require('./cjs'); +cjs === 'cjs'; // true ``` Builtin modules will provide named exports of their public API, as well as a @@ -174,7 +183,6 @@ module. This can be one of the following: | `format` | Description | | --- | --- | | `'esm'` | Load a standard JavaScript module | -| `'cjs'` | Load a node-style CommonJS module | | `'builtin'` | Load a node builtin CommonJS module | | `'dynamic'` | Use a [dynamic instantiate hook][] | diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index ed6361918b..fec279722d 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -45,8 +45,7 @@ function search(target, base) { const extensionFormatMap = { '__proto__': null, - '.mjs': 'esm', - '.js': 'cjs' + '.mjs': 'esm' }; function resolve(specifier, parentURL) { diff --git a/src/module_wrap.cc b/src/module_wrap.cc index bd1f20201d..b2c76e2d3d 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -42,7 +42,7 @@ using v8::TryCatch; using v8::Undefined; using v8::Value; -static const char* const EXTENSIONS[] = {".mjs", ".js"}; +static const char* const EXTENSIONS[] = {".mjs"}; ModuleWrap::ModuleWrap(Environment* env, Local object, diff --git a/test/common/index.mjs b/test/common/index.mjs index 832f68a9ee..f2f28b8779 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -1,6 +1,15 @@ // Flags: --experimental-modules /* eslint-disable node-core/required-modules */ -import common from './index.js'; + +import { createRequireFromPath } from 'module'; +import { fileURLToPath as toPath } from 'url'; + +function createRequire(metaUrl) { + return createRequireFromPath(toPath(metaUrl)); +} + +const require = createRequire(import.meta.url); +const common = require('./index.js'); const { isMainThread, @@ -97,5 +106,6 @@ export { getBufferSources, disableCrashOnUnhandledRejection, getTTYfd, - runWithInvalidFD + runWithInvalidFD, + createRequire }; diff --git a/test/es-module/test-esm-double-encoding.mjs b/test/es-module/test-esm-double-encoding.mjs index c81d0530d3..240104dd7f 100644 --- a/test/es-module/test-esm-double-encoding.mjs +++ b/test/es-module/test-esm-double-encoding.mjs @@ -3,4 +3,4 @@ import '../common'; // Assert we can import files with `%` in their pathname. -import '../fixtures/es-modules/test-esm-double-encoding-native%2520.js'; +import '../fixtures/es-modules/test-esm-double-encoding-native%2520.mjs'; diff --git a/test/es-module/test-esm-dynamic-import.js b/test/es-module/test-esm-dynamic-import.js index 6a80da4947..841f0bdb83 100644 --- a/test/es-module/test-esm-dynamic-import.js +++ b/test/es-module/test-esm-dynamic-import.js @@ -1,4 +1,5 @@ // Flags: --experimental-modules + 'use strict'; const common = require('../common'); const assert = require('assert'); diff --git a/test/es-module/test-esm-error-cache.js b/test/es-module/test-esm-error-cache.js index 98244615ef..79f76357ec 100644 --- a/test/es-module/test-esm-error-cache.js +++ b/test/es-module/test-esm-error-cache.js @@ -1,11 +1,11 @@ -'use strict'; - // Flags: --experimental-modules +'use strict'; + require('../common'); const assert = require('assert'); -const file = '../fixtures/syntax/bad_syntax.js'; +const file = '../fixtures/syntax/bad_syntax.mjs'; let error; (async () => { diff --git a/test/es-module/test-esm-require-cache.mjs b/test/es-module/test-esm-require-cache.mjs index ff32cde36f..1228013eee 100644 --- a/test/es-module/test-esm-require-cache.mjs +++ b/test/es-module/test-esm-require-cache.mjs @@ -1,7 +1,11 @@ // Flags: --experimental-modules -import '../common'; -import '../fixtures/es-module-require-cache/preload.js'; -import '../fixtures/es-module-require-cache/counter.js'; +import { createRequire } from '../common'; import assert from 'assert'; +// +const require = createRequire(import.meta.url); + +require('../fixtures/es-module-require-cache/preload.js'); +require('../fixtures/es-module-require-cache/counter.js'); + assert.strictEqual(global.counter, 1); delete global.counter; diff --git a/test/es-module/test-esm-shared-loader-dep.mjs b/test/es-module/test-esm-shared-loader-dep.mjs index 5c274d835c..637fbe3893 100644 --- a/test/es-module/test-esm-shared-loader-dep.mjs +++ b/test/es-module/test-esm-shared-loader-dep.mjs @@ -1,7 +1,9 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-shared-dep.mjs -import '../common'; +import { createRequire } from '../common'; import assert from 'assert'; import '../fixtures/es-modules/test-esm-ok.mjs'; -import dep from '../fixtures/es-module-loaders/loader-dep.js'; + +const require = createRequire(import.meta.url); +const dep = require('../fixtures/es-module-loaders/loader-dep.js'); assert.strictEqual(dep.format, 'esm'); diff --git a/test/es-module/test-esm-snapshot.mjs b/test/es-module/test-esm-snapshot.mjs deleted file mode 100644 index 3d4b44bbdd..0000000000 --- a/test/es-module/test-esm-snapshot.mjs +++ /dev/null @@ -1,7 +0,0 @@ -// Flags: --experimental-modules -import '../common'; -import '../fixtures/es-modules/esm-snapshot-mutator'; -import one from '../fixtures/es-modules/esm-snapshot'; -import assert from 'assert'; - -assert.strictEqual(one, 1); diff --git a/test/fixtures/es-module-loaders/loader-shared-dep.mjs b/test/fixtures/es-module-loaders/loader-shared-dep.mjs index 1a19e4c892..f7e15baf47 100644 --- a/test/fixtures/es-module-loaders/loader-shared-dep.mjs +++ b/test/fixtures/es-module-loaders/loader-shared-dep.mjs @@ -1,6 +1,10 @@ -import dep from './loader-dep.js'; import assert from 'assert'; +import {createRequire} from '../../common'; + +const require = createRequire(import.meta.url); +const dep = require('./loader-dep.js'); + export function resolve(specifier, base, defaultResolve) { assert.strictEqual(dep.format, 'esm'); return defaultResolve(specifier, base); diff --git a/test/fixtures/es-module-loaders/loader-with-dep.mjs b/test/fixtures/es-module-loaders/loader-with-dep.mjs index 944e6e438c..21798d446a 100644 --- a/test/fixtures/es-module-loaders/loader-with-dep.mjs +++ b/test/fixtures/es-module-loaders/loader-with-dep.mjs @@ -1,4 +1,8 @@ -import dep from './loader-dep.js'; +import {createRequire} from '../../common'; + +const require = createRequire(import.meta.url); +const dep = require('./loader-dep.js'); + export function resolve (specifier, base, defaultResolve) { return { url: defaultResolve(specifier, base).url, diff --git a/test/fixtures/es-modules/esm-snapshot-mutator.js b/test/fixtures/es-modules/esm-snapshot-mutator.js deleted file mode 100644 index ee52c270f6..0000000000 --- a/test/fixtures/es-modules/esm-snapshot-mutator.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; -const shouldSnapshotFilePath = require.resolve('./esm-snapshot.js'); -require('./esm-snapshot.js'); -require.cache[shouldSnapshotFilePath].exports++; diff --git a/test/fixtures/es-modules/esm-snapshot.js b/test/fixtures/es-modules/esm-snapshot.js deleted file mode 100644 index 329a0ca3f4..0000000000 --- a/test/fixtures/es-modules/esm-snapshot.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict'; -module.exports = 1; diff --git a/test/fixtures/es-modules/pjson-main/main.js b/test/fixtures/es-modules/pjson-main/main.js deleted file mode 100644 index dfdd47b877..0000000000 --- a/test/fixtures/es-modules/pjson-main/main.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'main'; diff --git a/test/fixtures/es-modules/pjson-main/main.mjs b/test/fixtures/es-modules/pjson-main/main.mjs new file mode 100644 index 0000000000..3d1d2b15c9 --- /dev/null +++ b/test/fixtures/es-modules/pjson-main/main.mjs @@ -0,0 +1 @@ +export default 'main'; diff --git a/test/fixtures/es-modules/pjson-main/package.json b/test/fixtures/es-modules/pjson-main/package.json index c13b8cf6ac..ea9b784692 100644 --- a/test/fixtures/es-modules/pjson-main/package.json +++ b/test/fixtures/es-modules/pjson-main/package.json @@ -1,3 +1,3 @@ { - "main": "main.js" + "main": "main.mjs" } diff --git a/test/fixtures/es-modules/test-esm-double-encoding-native%20.js b/test/fixtures/es-modules/test-esm-double-encoding-native%20.mjs similarity index 86% rename from test/fixtures/es-modules/test-esm-double-encoding-native%20.js rename to test/fixtures/es-modules/test-esm-double-encoding-native%20.mjs index ea1caa81be..a3bfe972e5 100644 --- a/test/fixtures/es-modules/test-esm-double-encoding-native%20.js +++ b/test/fixtures/es-modules/test-esm-double-encoding-native%20.mjs @@ -3,4 +3,4 @@ // Trivial test to assert we can load files with `%` in their pathname. // Imported by `test-esm-double-encoding.mjs`. -module.exports = 42; +export default 42; diff --git a/test/fixtures/syntax/bad_syntax.mjs b/test/fixtures/syntax/bad_syntax.mjs new file mode 100644 index 0000000000..c2cd118b23 --- /dev/null +++ b/test/fixtures/syntax/bad_syntax.mjs @@ -0,0 +1 @@ +var foo bar; From e6767f709fd0bba4213a039465689686891a2ab9 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 2 Oct 2018 00:41:36 -0400 Subject: [PATCH 4/7] esm: remove node specifier resolution algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/modules/pull/180 PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Guy Bedford Reviewed-By: John-David Dalton Reviewed-By: Michaël Zasso --- doc/api/esm.md | 25 ++-- lib/internal/modules/cjs/loader.js | 7 +- src/module_wrap.cc | 110 ++---------------- src/module_wrap.h | 8 +- test/es-module/test-esm-basic-imports.mjs | 3 +- .../test-esm-cyclic-dynamic-import.mjs | 5 +- test/es-module/test-esm-double-encoding.mjs | 3 +- test/es-module/test-esm-encoded-path.mjs | 3 +- test/es-module/test-esm-forbidden-globals.mjs | 3 +- test/es-module/test-esm-import-meta.mjs | 3 +- test/es-module/test-esm-live-binding.mjs | 3 +- .../test-esm-loader-invalid-format.mjs | 3 +- .../es-module/test-esm-loader-invalid-url.mjs | 4 +- ...oader-missing-dynamic-instantiate-hook.mjs | 3 +- test/es-module/test-esm-main-lookup.mjs | 26 ++++- test/es-module/test-esm-named-exports.mjs | 3 +- test/es-module/test-esm-namespace.mjs | 4 +- .../test-esm-preserve-symlinks-not-found.mjs | 2 +- test/es-module/test-esm-require-cache.mjs | 3 +- test/es-module/test-esm-shared-loader-dep.mjs | 4 +- test/es-module/test-esm-shebang.mjs | 3 +- test/es-module/test-esm-symlink-main.js | 2 +- test/es-module/test-esm-symlink.js | 10 +- test/es-module/test-esm-throw-undefined.mjs | 6 +- .../es-module-loaders/loader-invalid-url.mjs | 1 + .../es-module-loaders/loader-shared-dep.mjs | 2 +- .../es-module-loaders/loader-with-dep.mjs | 2 +- .../es-module-loaders/syntax-error-import.mjs | 2 +- .../es-module-loaders/throw-undefined.mjs | 1 + test/fixtures/es-modules/loop.mjs | 2 +- test/fixtures/es-modules/pjson-main/main.mjs | 2 +- .../esm_display_syntax_error_import.mjs | 6 +- .../esm_display_syntax_error_import.out | 2 +- ...esm_display_syntax_error_import_module.mjs | 5 +- ...esm_display_syntax_error_import_module.out | 4 +- .../esm_display_syntax_error_module.mjs | 5 +- .../test-module-main-extension-lookup.js | 2 +- 37 files changed, 120 insertions(+), 162 deletions(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index 81ccf4846b..988436f849 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -58,6 +58,14 @@ property: ESM must have the `.mjs` extension. +### Mandatory file extensions + +You must provide a file extension when using the `import` keyword. + +### No importing directories + +There is no support for importing directories. + ### No NODE_PATH `NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks @@ -82,21 +90,15 @@ Modules will be loaded multiple times if the `import` specifier used to resolve them have a different query or fragment. ```js -import './foo?query=1'; // loads ./foo with query of "?query=1" -import './foo?query=2'; // loads ./foo with query of "?query=2" +import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1" +import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2" ``` For now, only modules using the `file:` protocol can be loaded. -## Interop with existing modules +## CommonJS, JSON, and Native Modules -CommonJS 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. +CommonJS, JSON, and Native modules can be used with [`module.createRequireFromPath()`][`module.createRequireFromPath()`]. ```js // cjs.js @@ -112,6 +114,8 @@ const cjs = require('./cjs'); cjs === 'cjs'; // true ``` +## Builtin modules + Builtin modules will provide named exports of their public API, as well as a default export which can be used for, among other things, modifying the named exports. Named exports of builtin modules are updated when the corresponding @@ -261,3 +265,4 @@ in the import tree. [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md [dynamic instantiate hook]: #esm_dynamic_instantiate_hook +[`module.createRequireFromPath()`]: modules.html#modules_module_createrequirefrompath_filename diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index fba416d4db..c91ba91d91 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -43,6 +43,7 @@ const { const preserveSymlinks = !!process.binding('config').preserveSymlinks; const preserveSymlinksMain = !!process.binding('config').preserveSymlinksMain; const experimentalModules = !!process.binding('config').experimentalModules; +const hasLoader = !!process.binding('config').userLoader; const { ERR_INVALID_ARG_TYPE, @@ -742,7 +743,11 @@ if (experimentalModules) { // bootstrap main module. Module.runMain = function() { // Load the main module--the command line argument. - if (experimentalModules) { + const base = path.basename(process.argv[1]); + const ext = path.extname(base); + const isESM = ext === '.mjs'; + + if (experimentalModules && (isESM || hasLoader)) { if (asyncESM === undefined) lazyLoadESM(); asyncESM.loaderPromise.then((loader) => { return loader.import(pathToFileURL(process.argv[1]).pathname); diff --git a/src/module_wrap.cc b/src/module_wrap.cc index b2c76e2d3d..b67504a2ff 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -26,7 +26,6 @@ using v8::HandleScope; using v8::Integer; using v8::IntegrityLevel; using v8::Isolate; -using v8::JSON; using v8::Just; using v8::Local; using v8::Maybe; @@ -489,70 +488,17 @@ Maybe CheckFile(const std::string& path, return Just(fd); } -using Exists = PackageConfig::Exists; -using IsValid = PackageConfig::IsValid; -using HasMain = PackageConfig::HasMain; - -const PackageConfig& GetPackageConfig(Environment* env, - const std::string& path) { - auto existing = env->package_json_cache.find(path); - if (existing != env->package_json_cache.end()) { - return existing->second; - } - Maybe check = CheckFile(path, LEAVE_OPEN_AFTER_CHECK); - if (check.IsNothing()) { - auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" }); - return entry.first->second; - } - - Isolate* isolate = env->isolate(); - v8::HandleScope handle_scope(isolate); - - std::string pkg_src = ReadFile(check.FromJust()); - uv_fs_t fs_req; - CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr)); - uv_fs_req_cleanup(&fs_req); - - Local src; - if (!String::NewFromUtf8(isolate, - pkg_src.c_str(), - v8::NewStringType::kNormal, - pkg_src.length()).ToLocal(&src)) { - auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" }); - return entry.first->second; - } - - Local pkg_json_v; - Local pkg_json; - - if (!JSON::Parse(env->context(), src).ToLocal(&pkg_json_v) || - !pkg_json_v->ToObject(env->context()).ToLocal(&pkg_json)) { - auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "" }); - return entry.first->second; - } - - Local pkg_main; - HasMain has_main = HasMain::No; - std::string main_std; - if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) { - has_main = HasMain::Yes; - Utf8Value main_utf8(isolate, pkg_main); - main_std.assign(std::string(*main_utf8, main_utf8.length())); - } - - auto entry = env->package_json_cache.emplace(path, - PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std }); - return entry.first->second; -} - enum ResolveExtensionsOptions { TRY_EXACT_NAME, ONLY_VIA_EXTENSIONS }; +inline bool ResolvesToFile(const URL& search) { + std::string filePath = search.ToFilePath(); + Maybe check = CheckFile(filePath); + return !check.IsNothing(); +} + template Maybe ResolveExtensions(const URL& search) { if (options == TRY_EXACT_NAME) { @@ -578,24 +524,6 @@ inline Maybe ResolveIndex(const URL& search) { return ResolveExtensions(URL("index", search)); } -Maybe ResolveMain(Environment* env, const URL& search) { - URL pkg("package.json", &search); - - const PackageConfig& pjson = - GetPackageConfig(env, pkg.ToFilePath()); - // Note invalid package.json should throw in resolver - // currently we silently ignore which is incorrect - if (pjson.exists == Exists::No || - pjson.is_valid == IsValid::No || - pjson.has_main == HasMain::No) { - return Nothing(); - } - if (!ShouldBeTreatedAsRelativeOrAbsolutePath(pjson.main)) { - return Resolve(env, "./" + pjson.main, search, IgnoreMain); - } - return Resolve(env, pjson.main, search, IgnoreMain); -} - Maybe ResolveModule(Environment* env, const std::string& specifier, const URL& base) { @@ -604,7 +532,7 @@ Maybe ResolveModule(Environment* env, do { dir = parent; Maybe check = - Resolve(env, "./node_modules/" + specifier, dir, CheckMain); + Resolve(env, "./node_modules/" + specifier, dir); if (!check.IsNothing()) { const size_t limit = specifier.find('/'); const size_t spec_len = @@ -624,23 +552,11 @@ Maybe ResolveModule(Environment* env, return Nothing(); } -Maybe ResolveDirectory(Environment* env, - const URL& search, - PackageMainCheck check_pjson_main) { - if (check_pjson_main) { - Maybe main = ResolveMain(env, search); - if (!main.IsNothing()) - return main; - } - return ResolveIndex(search); -} - } // anonymous namespace Maybe Resolve(Environment* env, const std::string& specifier, - const URL& base, - PackageMainCheck check_pjson_main) { + const URL& base) { URL pure_url(specifier); if (!(pure_url.flags() & URL_FLAGS_FAILED)) { // just check existence, without altering @@ -655,13 +571,9 @@ Maybe Resolve(Environment* env, } if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { URL resolved(specifier, base); - Maybe file = ResolveExtensions(resolved); - if (!file.IsNothing()) - return file; - if (specifier.back() != '/') { - resolved = URL(specifier + "/", base); - } - return ResolveDirectory(env, resolved, check_pjson_main); + if (ResolvesToFile(resolved)) + return Just(resolved); + return Nothing(); } else { return ResolveModule(env, specifier, base); } diff --git a/src/module_wrap.h b/src/module_wrap.h index 3e19b6c9eb..c0be3d125b 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -12,15 +12,9 @@ namespace node { namespace loader { -enum PackageMainCheck : bool { - CheckMain = true, - IgnoreMain = false -}; - v8::Maybe Resolve(Environment* env, const std::string& specifier, - const url::URL& base, - PackageMainCheck read_pkg_json = CheckMain); + const url::URL& base); class ModuleWrap : public BaseObject { public: diff --git a/test/es-module/test-esm-basic-imports.mjs b/test/es-module/test-esm-basic-imports.mjs index 78a4106f94..d9bb22be0a 100644 --- a/test/es-module/test-esm-basic-imports.mjs +++ b/test/es-module/test-esm-basic-imports.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; import assert from 'assert'; import ok from '../fixtures/es-modules/test-esm-ok.mjs'; import okShebang from './test-esm-shebang.mjs'; diff --git a/test/es-module/test-esm-cyclic-dynamic-import.mjs b/test/es-module/test-esm-cyclic-dynamic-import.mjs index c8dfff919c..a207efc73e 100644 --- a/test/es-module/test-esm-cyclic-dynamic-import.mjs +++ b/test/es-module/test-esm-cyclic-dynamic-import.mjs @@ -1,3 +1,4 @@ // Flags: --experimental-modules -import '../common'; -import('./test-esm-cyclic-dynamic-import'); +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; +import('./test-esm-cyclic-dynamic-import.mjs'); diff --git a/test/es-module/test-esm-double-encoding.mjs b/test/es-module/test-esm-double-encoding.mjs index 240104dd7f..9366d4bd6b 100644 --- a/test/es-module/test-esm-double-encoding.mjs +++ b/test/es-module/test-esm-double-encoding.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; // Assert we can import files with `%` in their pathname. diff --git a/test/es-module/test-esm-encoded-path.mjs b/test/es-module/test-esm-encoded-path.mjs index 365a425afa..2cabfdacff 100644 --- a/test/es-module/test-esm-encoded-path.mjs +++ b/test/es-module/test-esm-encoded-path.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; import assert from 'assert'; // ./test-esm-ok.mjs import ok from '../fixtures/es-modules/test-%65%73%6d-ok.mjs'; diff --git a/test/es-module/test-esm-forbidden-globals.mjs b/test/es-module/test-esm-forbidden-globals.mjs index 4e777412a3..cf110ff290 100644 --- a/test/es-module/test-esm-forbidden-globals.mjs +++ b/test/es-module/test-esm-forbidden-globals.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; // eslint-disable-next-line no-undef if (typeof arguments !== 'undefined') { diff --git a/test/es-module/test-esm-import-meta.mjs b/test/es-module/test-esm-import-meta.mjs index c17e0e20d4..4c34b337fb 100644 --- a/test/es-module/test-esm-import-meta.mjs +++ b/test/es-module/test-esm-import-meta.mjs @@ -1,6 +1,7 @@ // Flags: --experimental-modules +/* eslint-disable node-core/required-modules */ -import '../common'; +import '../common/index.mjs'; import assert from 'assert'; assert.strictEqual(Object.getPrototypeOf(import.meta), null); diff --git a/test/es-module/test-esm-live-binding.mjs b/test/es-module/test-esm-live-binding.mjs index d151e004df..880a6c389b 100644 --- a/test/es-module/test-esm-live-binding.mjs +++ b/test/es-module/test-esm-live-binding.mjs @@ -1,6 +1,7 @@ // Flags: --experimental-modules +/* eslint-disable node-core/required-modules */ -import '../common'; +import '../common/index.mjs'; import assert from 'assert'; import fs, { readFile, readFileSync } from 'fs'; diff --git a/test/es-module/test-esm-loader-invalid-format.mjs b/test/es-module/test-esm-loader-invalid-format.mjs index f8714d4aa1..c3f3a87407 100644 --- a/test/es-module/test-esm-loader-invalid-format.mjs +++ b/test/es-module/test-esm-loader-invalid-format.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-format.mjs -import { expectsError, mustCall } from '../common'; +/* eslint-disable node-core/required-modules */ +import { expectsError, mustCall } from '../common/index.mjs'; import assert from 'assert'; import('../fixtures/es-modules/test-esm-ok.mjs') diff --git a/test/es-module/test-esm-loader-invalid-url.mjs b/test/es-module/test-esm-loader-invalid-url.mjs index 43971a2e6e..9cf17b2478 100644 --- a/test/es-module/test-esm-loader-invalid-url.mjs +++ b/test/es-module/test-esm-loader-invalid-url.mjs @@ -1,5 +1,7 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-url.mjs -import { expectsError, mustCall } from '../common'; +/* eslint-disable node-core/required-modules */ + +import { expectsError, mustCall } from '../common/index.mjs'; import assert from 'assert'; import('../fixtures/es-modules/test-esm-ok.mjs') diff --git a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs b/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs index f2b37f7e8a..ab2da7adce 100644 --- a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs +++ b/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs @@ -1,6 +1,7 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs +/* eslint-disable node-core/required-modules */ -import { expectsError } from '../common'; +import { expectsError } from '../common/index.mjs'; import('test').catch(expectsError({ code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', diff --git a/test/es-module/test-esm-main-lookup.mjs b/test/es-module/test-esm-main-lookup.mjs index ca313a1d26..76c6263853 100644 --- a/test/es-module/test-esm-main-lookup.mjs +++ b/test/es-module/test-esm-main-lookup.mjs @@ -1,6 +1,26 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; import assert from 'assert'; -import main from '../fixtures/es-modules/pjson-main'; -assert.strictEqual(main, 'main'); +async function main() { + let mod; + try { + mod = await import('../fixtures/es-modules/pjson-main'); + } catch (e) { + assert.strictEqual(e.code, 'MODULE_NOT_FOUND'); + } + + assert.strictEqual(mod, undefined); + + try { + mod = await import('../fixtures/es-modules/pjson-main/main.mjs'); + } catch (e) { + console.log(e); + assert.fail(); + } + + assert.strictEqual(mod.main, 'main'); +} + +main(); diff --git a/test/es-module/test-esm-named-exports.mjs b/test/es-module/test-esm-named-exports.mjs index 3aae9230de..e235f598cb 100644 --- a/test/es-module/test-esm-named-exports.mjs +++ b/test/es-module/test-esm-named-exports.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; import { readFile } from 'fs'; import assert from 'assert'; import ok from '../fixtures/es-modules/test-esm-ok.mjs'; diff --git a/test/es-module/test-esm-namespace.mjs b/test/es-module/test-esm-namespace.mjs index dcd159f6c8..6b58e074ff 100644 --- a/test/es-module/test-esm-namespace.mjs +++ b/test/es-module/test-esm-namespace.mjs @@ -1,5 +1,7 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ + +import '../common/index.mjs'; import * as fs from 'fs'; import assert from 'assert'; import Module from 'module'; diff --git a/test/es-module/test-esm-preserve-symlinks-not-found.mjs b/test/es-module/test-esm-preserve-symlinks-not-found.mjs index 5119957bae..b5be2d7e63 100644 --- a/test/es-module/test-esm-preserve-symlinks-not-found.mjs +++ b/test/es-module/test-esm-preserve-symlinks-not-found.mjs @@ -1,3 +1,3 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs /* eslint-disable node-core/required-modules */ -import './not-found'; +import './not-found.mjs'; diff --git a/test/es-module/test-esm-require-cache.mjs b/test/es-module/test-esm-require-cache.mjs index 1228013eee..09030e0578 100644 --- a/test/es-module/test-esm-require-cache.mjs +++ b/test/es-module/test-esm-require-cache.mjs @@ -1,5 +1,6 @@ // Flags: --experimental-modules -import { createRequire } from '../common'; +/* eslint-disable node-core/required-modules */ +import { createRequire } from '../common/index.mjs'; import assert from 'assert'; // const require = createRequire(import.meta.url); diff --git a/test/es-module/test-esm-shared-loader-dep.mjs b/test/es-module/test-esm-shared-loader-dep.mjs index 637fbe3893..20d156fb8b 100644 --- a/test/es-module/test-esm-shared-loader-dep.mjs +++ b/test/es-module/test-esm-shared-loader-dep.mjs @@ -1,5 +1,7 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-shared-dep.mjs -import { createRequire } from '../common'; +/* eslint-disable node-core/required-modules */ +import { createRequire } from '../common/index.mjs'; + import assert from 'assert'; import '../fixtures/es-modules/test-esm-ok.mjs'; diff --git a/test/es-module/test-esm-shebang.mjs b/test/es-module/test-esm-shebang.mjs index d5faace479..486e04dade 100644 --- a/test/es-module/test-esm-shebang.mjs +++ b/test/es-module/test-esm-shebang.mjs @@ -1,6 +1,7 @@ #! }]) // isn't js // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; const isJs = true; export default isJs; diff --git a/test/es-module/test-esm-symlink-main.js b/test/es-module/test-esm-symlink-main.js index f7631ef2e5..871180f5cc 100644 --- a/test/es-module/test-esm-symlink-main.js +++ b/test/es-module/test-esm-symlink-main.js @@ -9,7 +9,7 @@ const fs = require('fs'); tmpdir.refresh(); const realPath = path.resolve(__dirname, '../fixtures/es-modules/symlink.mjs'); -const symlinkPath = path.resolve(tmpdir.path, 'symlink.js'); +const symlinkPath = path.resolve(tmpdir.path, 'symlink.mjs'); try { fs.symlinkSync(realPath, symlinkPath); diff --git a/test/es-module/test-esm-symlink.js b/test/es-module/test-esm-symlink.js index 232925a52e..9b9eb98cd9 100644 --- a/test/es-module/test-esm-symlink.js +++ b/test/es-module/test-esm-symlink.js @@ -12,8 +12,8 @@ const tmpDir = tmpdir.path; const entry = path.join(tmpDir, 'entry.mjs'); const real = path.join(tmpDir, 'index.mjs'); -const link_absolute_path = path.join(tmpDir, 'absolute'); -const link_relative_path = path.join(tmpDir, 'relative'); +const link_absolute_path = path.join(tmpDir, 'absolute.mjs'); +const link_relative_path = path.join(tmpDir, 'relative.mjs'); const link_ignore_extension = path.join(tmpDir, 'ignore_extension.json'); const link_directory = path.join(tmpDir, 'directory'); @@ -22,15 +22,13 @@ fs.writeFileSync(real, 'export default [];'); fs.writeFileSync(entry, ` import assert from 'assert'; import real from './index.mjs'; -import absolute from './absolute'; -import relative from './relative'; +import absolute from './absolute.mjs'; +import relative from './relative.mjs'; import ignoreExtension from './ignore_extension.json'; -import directory from './directory'; assert.strictEqual(absolute, real); assert.strictEqual(relative, real); assert.strictEqual(ignoreExtension, real); -assert.strictEqual(directory, real); `); try { diff --git a/test/es-module/test-esm-throw-undefined.mjs b/test/es-module/test-esm-throw-undefined.mjs index 541127eee5..97e917da5e 100644 --- a/test/es-module/test-esm-throw-undefined.mjs +++ b/test/es-module/test-esm-throw-undefined.mjs @@ -1,11 +1,13 @@ // Flags: --experimental-modules -import '../common'; +/* eslint-disable node-core/required-modules */ + +import '../common/index.mjs'; import assert from 'assert'; async function doTest() { await assert.rejects( async () => { - await import('../fixtures/es-module-loaders/throw-undefined'); + await import('../fixtures/es-module-loaders/throw-undefined.mjs'); }, (e) => e === undefined ); diff --git a/test/fixtures/es-module-loaders/loader-invalid-url.mjs b/test/fixtures/es-module-loaders/loader-invalid-url.mjs index 12efbb5021..f653155899 100644 --- a/test/fixtures/es-module-loaders/loader-invalid-url.mjs +++ b/test/fixtures/es-module-loaders/loader-invalid-url.mjs @@ -1,3 +1,4 @@ +/* eslint-disable node-core/required-modules */ export async function resolve(specifier, parentModuleURL, defaultResolve) { if (parentModuleURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') { return { diff --git a/test/fixtures/es-module-loaders/loader-shared-dep.mjs b/test/fixtures/es-module-loaders/loader-shared-dep.mjs index f7e15baf47..419c75bdbf 100644 --- a/test/fixtures/es-module-loaders/loader-shared-dep.mjs +++ b/test/fixtures/es-module-loaders/loader-shared-dep.mjs @@ -1,6 +1,6 @@ import assert from 'assert'; -import {createRequire} from '../../common'; +import {createRequire} from '../../common/index.mjs'; const require = createRequire(import.meta.url); const dep = require('./loader-dep.js'); diff --git a/test/fixtures/es-module-loaders/loader-with-dep.mjs b/test/fixtures/es-module-loaders/loader-with-dep.mjs index 21798d446a..5afd3b2e21 100644 --- a/test/fixtures/es-module-loaders/loader-with-dep.mjs +++ b/test/fixtures/es-module-loaders/loader-with-dep.mjs @@ -1,4 +1,4 @@ -import {createRequire} from '../../common'; +import {createRequire} from '../../common/index.mjs'; const require = createRequire(import.meta.url); const dep = require('./loader-dep.js'); diff --git a/test/fixtures/es-module-loaders/syntax-error-import.mjs b/test/fixtures/es-module-loaders/syntax-error-import.mjs index 9cad68c7ce..3a6bc5effc 100644 --- a/test/fixtures/es-module-loaders/syntax-error-import.mjs +++ b/test/fixtures/es-module-loaders/syntax-error-import.mjs @@ -1 +1 @@ -import { foo, notfound } from './module-named-exports'; +import { foo, notfound } from './module-named-exports.mjs'; diff --git a/test/fixtures/es-module-loaders/throw-undefined.mjs b/test/fixtures/es-module-loaders/throw-undefined.mjs index f062276767..0349ae112d 100644 --- a/test/fixtures/es-module-loaders/throw-undefined.mjs +++ b/test/fixtures/es-module-loaders/throw-undefined.mjs @@ -1,3 +1,4 @@ 'use strict'; +/* eslint-disable node-core/required-modules */ throw undefined; diff --git a/test/fixtures/es-modules/loop.mjs b/test/fixtures/es-modules/loop.mjs index 1b5cab10ed..3d2ddd2eb7 100644 --- a/test/fixtures/es-modules/loop.mjs +++ b/test/fixtures/es-modules/loop.mjs @@ -1,4 +1,4 @@ -import { message } from './message'; +import { message } from './message.mjs'; var t = 1; var k = 1; diff --git a/test/fixtures/es-modules/pjson-main/main.mjs b/test/fixtures/es-modules/pjson-main/main.mjs index 3d1d2b15c9..9eb0aade18 100644 --- a/test/fixtures/es-modules/pjson-main/main.mjs +++ b/test/fixtures/es-modules/pjson-main/main.mjs @@ -1 +1 @@ -export default 'main'; +export const main = 'main' diff --git a/test/message/esm_display_syntax_error_import.mjs b/test/message/esm_display_syntax_error_import.mjs index 87cedf1d4e..12d10270e9 100644 --- a/test/message/esm_display_syntax_error_import.mjs +++ b/test/message/esm_display_syntax_error_import.mjs @@ -1,7 +1,7 @@ // Flags: --experimental-modules -/* eslint-disable no-unused-vars */ -import '../common'; +/* eslint-disable no-unused-vars, node-core/required-modules */ +import '../common/index.mjs'; import { foo, notfound -} from '../fixtures/es-module-loaders/module-named-exports'; +} from '../fixtures/es-module-loaders/module-named-exports.mjs'; diff --git a/test/message/esm_display_syntax_error_import.out b/test/message/esm_display_syntax_error_import.out index 31ee2b6f4b..48f2e2fb74 100644 --- a/test/message/esm_display_syntax_error_import.out +++ b/test/message/esm_display_syntax_error_import.out @@ -2,5 +2,5 @@ file:///*/test/message/esm_display_syntax_error_import.mjs:6 notfound ^^^^^^^^ -SyntaxError: The requested module '../fixtures/es-module-loaders/module-named-exports' does not provide an export named 'notfound' +SyntaxError: The requested module '../fixtures/es-module-loaders/module-named-exports.mjs' does not provide an export named 'notfound' at ModuleJob._instantiate (internal/modules/esm/module_job.js:*:*) diff --git a/test/message/esm_display_syntax_error_import_module.mjs b/test/message/esm_display_syntax_error_import_module.mjs index 32c0edb350..a53bbbcd19 100644 --- a/test/message/esm_display_syntax_error_import_module.mjs +++ b/test/message/esm_display_syntax_error_import_module.mjs @@ -1,3 +1,4 @@ // Flags: --experimental-modules -import '../common'; -import '../fixtures/es-module-loaders/syntax-error-import'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; +import '../fixtures/es-module-loaders/syntax-error-import.mjs'; diff --git a/test/message/esm_display_syntax_error_import_module.out b/test/message/esm_display_syntax_error_import_module.out index b067a77942..3e1024db8a 100644 --- a/test/message/esm_display_syntax_error_import_module.out +++ b/test/message/esm_display_syntax_error_import_module.out @@ -1,6 +1,6 @@ (node:*) ExperimentalWarning: The ESM module loader is experimental. file:///*/test/fixtures/es-module-loaders/syntax-error-import.mjs:1 -import { foo, notfound } from './module-named-exports'; +import { foo, notfound } from './module-named-exports.mjs'; ^^^^^^^^ -SyntaxError: The requested module './module-named-exports' does not provide an export named 'notfound' +SyntaxError: The requested module './module-named-exports.mjs' does not provide an export named 'notfound' at ModuleJob._instantiate (internal/modules/esm/module_job.js:*:*) diff --git a/test/message/esm_display_syntax_error_module.mjs b/test/message/esm_display_syntax_error_module.mjs index e74b70bec8..5905d2a954 100644 --- a/test/message/esm_display_syntax_error_module.mjs +++ b/test/message/esm_display_syntax_error_module.mjs @@ -1,3 +1,4 @@ // Flags: --experimental-modules -import '../common'; -import '../fixtures/es-module-loaders/syntax-error'; +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; +import '../fixtures/es-module-loaders/syntax-error.mjs'; diff --git a/test/parallel/test-module-main-extension-lookup.js b/test/parallel/test-module-main-extension-lookup.js index 3d20316647..9e7eab295e 100644 --- a/test/parallel/test-module-main-extension-lookup.js +++ b/test/parallel/test-module-main-extension-lookup.js @@ -6,6 +6,6 @@ const { execFileSync } = require('child_process'); const node = process.argv[0]; execFileSync(node, ['--experimental-modules', - fixtures.path('es-modules', 'test-esm-ok')]); + fixtures.path('es-modules', 'test-esm-ok.mjs')]); execFileSync(node, ['--experimental-modules', fixtures.path('es-modules', 'noext')]); From 8b7311adfeacc6c0ea4a54403f4b44fba8fa2e90 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 2 Oct 2018 01:53:35 -0400 Subject: [PATCH 5/7] doc: document minimal kernel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Guy Bedford Reviewed-By: John-David Dalton Reviewed-By: Michaël Zasso --- doc/api/esm.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index 988436f849..e58dfe80af 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -8,10 +8,11 @@ Node.js contains support for ES Modules based upon the -[Node.js EP for ES Modules][]. +[Node.js EP for ES Modules][] and the [ESM Minimal Kernel][]. -Not all features of the EP are complete and will be landing as both VM support -and implementation is ready. Error messages are still being polished. +The minimal feature set is designed to be compatible with all potential +future implementations. Expect major changes in the implementation including +interoperability support, specifier resolution, and default behavior. ## Enabling @@ -96,9 +97,9 @@ import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2" For now, only modules using the `file:` protocol can be loaded. -## CommonJS, JSON, and Native Modules +## CommonJS, JSON, and Native Modules -CommonJS, JSON, and Native modules can be used with [`module.createRequireFromPath()`][`module.createRequireFromPath()`]. +CommonJS, JSON, and Native modules can be used with [`module.createRequireFromPath()`][]. ```js // cjs.js @@ -266,3 +267,4 @@ in the import tree. [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md [dynamic instantiate hook]: #esm_dynamic_instantiate_hook [`module.createRequireFromPath()`]: modules.html#modules_module_createrequirefrompath_filename +[ESM Minimal Kernel]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md From 3605400578708c9df071ccac6c067a6a635accd3 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 2 Oct 2018 16:02:08 -0700 Subject: [PATCH 6/7] esm: Remove --loader. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Guy Bedford Reviewed-By: Myles Borins Reviewed-By: Michaël Zasso --- .eslintrc.js | 1 - doc/api/cli.md | 9 -- doc/api/esm.md | 119 ------------------ lib/internal/modules/cjs/loader.js | 3 +- lib/internal/process/esm_loader.js | 10 +- src/node_config.cc | 4 - src/node_options.cc | 9 -- src/node_options.h | 1 - test/es-module/test-esm-example-loader.js | 6 - test/es-module/test-esm-loader-dependency.mjs | 5 - .../test-esm-loader-invalid-format.mjs | 12 -- .../es-module/test-esm-loader-invalid-url.mjs | 14 --- ...oader-missing-dynamic-instantiate-hook.mjs | 10 -- test/es-module/test-esm-named-exports.mjs | 9 -- ...-esm-preserve-symlinks-not-found-plain.mjs | 3 - .../test-esm-preserve-symlinks-not-found.mjs | 3 - test/es-module/test-esm-resolve-hook.mjs | 8 -- test/es-module/test-esm-shared-loader-dep.mjs | 11 -- test/es-module/test-esm-throw-undefined.mjs | 16 --- .../builtin-named-exports-loader.mjs | 29 ----- .../es-module-loaders/example-loader.mjs | 37 ------ test/fixtures/es-module-loaders/js-as-esm.js | 1 - test/fixtures/es-module-loaders/js-loader.mjs | 24 ---- test/fixtures/es-module-loaders/loader-dep.js | 1 - .../loader-invalid-format.mjs | 8 -- .../es-module-loaders/loader-invalid-url.mjs | 10 -- .../es-module-loaders/loader-shared-dep.mjs | 11 -- .../es-module-loaders/loader-with-dep.mjs | 11 -- .../missing-dynamic-instantiate-hook.mjs | 6 - .../module-named-exports.mjs | 2 - .../not-found-assert-loader.mjs | 22 ---- .../es-module-loaders/syntax-error-import.mjs | 1 - .../es-module-loaders/syntax-error.mjs | 2 - .../es-module-loaders/throw-undefined.mjs | 4 - .../esm_display_syntax_error_import.mjs | 7 -- .../esm_display_syntax_error_import.out | 6 - ...esm_display_syntax_error_import_module.mjs | 4 - ...esm_display_syntax_error_import_module.out | 6 - .../esm_display_syntax_error_module.mjs | 4 - .../esm_display_syntax_error_module.out | 6 - 40 files changed, 2 insertions(+), 453 deletions(-) delete mode 100644 test/es-module/test-esm-example-loader.js delete mode 100644 test/es-module/test-esm-loader-dependency.mjs delete mode 100644 test/es-module/test-esm-loader-invalid-format.mjs delete mode 100644 test/es-module/test-esm-loader-invalid-url.mjs delete mode 100644 test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs delete mode 100644 test/es-module/test-esm-named-exports.mjs delete mode 100644 test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs delete mode 100644 test/es-module/test-esm-preserve-symlinks-not-found.mjs delete mode 100644 test/es-module/test-esm-resolve-hook.mjs delete mode 100644 test/es-module/test-esm-shared-loader-dep.mjs delete mode 100644 test/es-module/test-esm-throw-undefined.mjs delete mode 100644 test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs delete mode 100644 test/fixtures/es-module-loaders/example-loader.mjs delete mode 100644 test/fixtures/es-module-loaders/js-as-esm.js delete mode 100644 test/fixtures/es-module-loaders/js-loader.mjs delete mode 100644 test/fixtures/es-module-loaders/loader-dep.js delete mode 100644 test/fixtures/es-module-loaders/loader-invalid-format.mjs delete mode 100644 test/fixtures/es-module-loaders/loader-invalid-url.mjs delete mode 100644 test/fixtures/es-module-loaders/loader-shared-dep.mjs delete mode 100644 test/fixtures/es-module-loaders/loader-with-dep.mjs delete mode 100644 test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs delete mode 100644 test/fixtures/es-module-loaders/module-named-exports.mjs delete mode 100644 test/fixtures/es-module-loaders/not-found-assert-loader.mjs delete mode 100644 test/fixtures/es-module-loaders/syntax-error-import.mjs delete mode 100644 test/fixtures/es-module-loaders/syntax-error.mjs delete mode 100644 test/fixtures/es-module-loaders/throw-undefined.mjs delete mode 100644 test/message/esm_display_syntax_error_import.mjs delete mode 100644 test/message/esm_display_syntax_error_import.out delete mode 100644 test/message/esm_display_syntax_error_import_module.mjs delete mode 100644 test/message/esm_display_syntax_error_import_module.out delete mode 100644 test/message/esm_display_syntax_error_module.mjs delete mode 100644 test/message/esm_display_syntax_error_module.out diff --git a/.eslintrc.js b/.eslintrc.js index 6a8403d348..5d10357f58 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -36,7 +36,6 @@ module.exports = { files: [ 'doc/api/esm.md', '*.mjs', - 'test/es-module/test-esm-example-loader.js', ], parserOptions: { sourceType: 'module' }, }, diff --git a/doc/api/cli.md b/doc/api/cli.md index 9c6195aa4e..c9b8dc3b2b 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -155,13 +155,6 @@ V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug and profile Node.js instances. The tools attach to Node.js instances via a tcp port and communicate using the [Chrome DevTools Protocol][]. -### `--loader=file` - - -Specify the `file` of the custom [experimental ECMAScript Module][] loader. - ### `--napi-modules` - -To customize the default module resolution, loader hooks can optionally be -provided via a `--loader ./loader-name.mjs` argument to Node.js. - -When hooks are used they only apply to ES module loading and not to any -CommonJS modules loaded. - -### Resolve hook - -The resolve hook returns the resolved file URL and module format for a -given module specifier and parent file URL: - -```js -const baseURL = new URL('file://'); -baseURL.pathname = `${process.cwd()}/`; - -export async function resolve(specifier, - parentModuleURL = baseURL, - defaultResolver) { - return { - url: new URL(specifier, parentModuleURL).href, - format: 'esm' - }; -} -``` - -The `parentModuleURL` is provided as `undefined` when performing main Node.js -load itself. - -The default Node.js ES module resolution function is provided as a third -argument to the resolver for easy compatibility workflows. - -In addition to returning the resolved file URL value, the resolve hook also -returns a `format` property specifying the module format of the resolved -module. This can be one of the following: - -| `format` | Description | -| --- | --- | -| `'esm'` | Load a standard JavaScript module | -| `'builtin'` | Load a node builtin CommonJS module | -| `'dynamic'` | Use a [dynamic instantiate hook][] | - -For example, a dummy loader to load JavaScript restricted to browser resolution -rules with only JS file extension and Node.js builtin modules support could -be written: - -```js -import path from 'path'; -import process from 'process'; -import Module from 'module'; - -const builtins = Module.builtinModules; -const JS_EXTENSIONS = new Set(['.js', '.mjs']); - -const baseURL = new URL('file://'); -baseURL.pathname = `${process.cwd()}/`; - -export function resolve(specifier, parentModuleURL = baseURL, defaultResolve) { - if (builtins.includes(specifier)) { - return { - url: specifier, - format: 'builtin' - }; - } - if (/^\.{0,2}[/]/.test(specifier) !== true && !specifier.startsWith('file:')) { - // For node_modules support: - // return defaultResolve(specifier, parentModuleURL); - throw new Error( - `imports must begin with '/', './', or '../'; '${specifier}' does not`); - } - const resolved = new URL(specifier, parentModuleURL); - const ext = path.extname(resolved.pathname); - if (!JS_EXTENSIONS.has(ext)) { - throw new Error( - `Cannot load file with non-JavaScript file extension ${ext}.`); - } - return { - url: resolved.href, - format: 'esm' - }; -} -``` - -With this loader, running: - -```console -NODE_OPTIONS='--experimental-modules --loader ./custom-loader.mjs' node x.js -``` - -would load the module `x.js` as an ES module with relative resolution support -(with `node_modules` loading skipped in this example). - -### Dynamic instantiate hook - -To create a custom dynamic module that doesn't correspond to one of the -existing `format` interpretations, the `dynamicInstantiate` hook can be used. -This hook is called only for modules that return `format: 'dynamic'` from -the `resolve` hook. - -```js -export async function dynamicInstantiate(url) { - return { - exports: ['customExportName'], - execute: (exports) => { - // get and set functions provided for pre-allocated export names - exports.customExportName.set('value'); - } - }; -} -``` - -With the list of module exports provided upfront, the `execute` function will -then be called at the exact point of module evaluation order for that module -in the import tree. - [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md -[dynamic instantiate hook]: #esm_dynamic_instantiate_hook [`module.createRequireFromPath()`]: modules.html#modules_module_createrequirefrompath_filename [ESM Minimal Kernel]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index c91ba91d91..4148300810 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -43,7 +43,6 @@ const { const preserveSymlinks = !!process.binding('config').preserveSymlinks; const preserveSymlinksMain = !!process.binding('config').preserveSymlinksMain; const experimentalModules = !!process.binding('config').experimentalModules; -const hasLoader = !!process.binding('config').userLoader; const { ERR_INVALID_ARG_TYPE, @@ -747,7 +746,7 @@ Module.runMain = function() { const ext = path.extname(base); const isESM = ext === '.mjs'; - if (experimentalModules && (isESM || hasLoader)) { + if (experimentalModules && isESM) { if (asyncESM === undefined) lazyLoadESM(); asyncESM.loaderPromise.then((loader) => { return loader.import(pathToFileURL(process.argv[1]).pathname); diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index 6f3ac729f8..6ed9453521 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -47,16 +47,8 @@ exports.ESMLoader = undefined; exports.setup = function() { setInitializeImportMetaObjectCallback(initializeImportMetaObject); - let ESMLoader = new Loader(); + const ESMLoader = new Loader(); const loaderPromise = (async () => { - const userLoader = process.binding('config').userLoader; - if (userLoader) { - const hooks = await ESMLoader.import( - userLoader, pathToFileURL(`${process.cwd()}/`).href); - ESMLoader = new Loader(); - ESMLoader.hook(hooks); - exports.ESMLoader = ESMLoader; - } return ESMLoader; })(); loaderResolve(loaderPromise); diff --git a/src/node_config.cc b/src/node_config.cc index c3592dc3e8..2a1e3a2967 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -89,10 +89,6 @@ static void Initialize(Local target, if (env->options()->experimental_modules) { READONLY_BOOLEAN_PROPERTY("experimentalModules"); - const std::string& userland_loader = env->options()->userland_loader; - if (!userland_loader.empty()) { - READONLY_STRING_PROPERTY(target, "userLoader", userland_loader); - } } if (env->options()->experimental_vm_modules) diff --git a/src/node_options.cc b/src/node_options.cc index 4951679994..f4c19028cf 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -32,10 +32,6 @@ void PerIsolateOptions::CheckOptions(std::vector* errors) { } void EnvironmentOptions::CheckOptions(std::vector* errors) { - if (!userland_loader.empty() && !experimental_modules) { - errors->push_back("--loader requires --experimental-modules be enabled"); - } - if (syntax_check_only && has_eval_string) { errors->push_back("either --check or --eval can be used, not both"); } @@ -102,11 +98,6 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::experimental_worker, kAllowedInEnvironment); AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals); - AddOption("--loader", - "(with --experimental-modules) use the specified file as a " - "custom loader", - &EnvironmentOptions::userland_loader, - kAllowedInEnvironment); AddOption("--no-deprecation", "silence deprecation warnings", &EnvironmentOptions::no_deprecation, diff --git a/src/node_options.h b/src/node_options.h index 32189dacfb..2eb1714046 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -83,7 +83,6 @@ class EnvironmentOptions : public Options { bool trace_deprecation = false; bool trace_sync_io = false; bool trace_warnings = false; - std::string userland_loader; bool syntax_check_only = false; bool has_eval_string = false; diff --git a/test/es-module/test-esm-example-loader.js b/test/es-module/test-esm-example-loader.js deleted file mode 100644 index 0b0001acea..0000000000 --- a/test/es-module/test-esm-example-loader.js +++ /dev/null @@ -1,6 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/example-loader.mjs -/* eslint-disable node-core/required-modules */ -import assert from 'assert'; -import ok from '../fixtures/es-modules/test-esm-ok.mjs'; - -assert(ok); diff --git a/test/es-module/test-esm-loader-dependency.mjs b/test/es-module/test-esm-loader-dependency.mjs deleted file mode 100644 index 1ed8685a6f..0000000000 --- a/test/es-module/test-esm-loader-dependency.mjs +++ /dev/null @@ -1,5 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-with-dep.mjs -/* eslint-disable node-core/required-modules */ -import '../fixtures/es-modules/test-esm-ok.mjs'; - -// We just test that this module doesn't fail loading diff --git a/test/es-module/test-esm-loader-invalid-format.mjs b/test/es-module/test-esm-loader-invalid-format.mjs deleted file mode 100644 index c3f3a87407..0000000000 --- a/test/es-module/test-esm-loader-invalid-format.mjs +++ /dev/null @@ -1,12 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-format.mjs -/* eslint-disable node-core/required-modules */ -import { expectsError, mustCall } from '../common/index.mjs'; -import assert from 'assert'; - -import('../fixtures/es-modules/test-esm-ok.mjs') -.then(assert.fail, expectsError({ - code: 'ERR_INVALID_RETURN_PROPERTY_VALUE', - message: 'Expected string to be returned for the "format" from the ' + - '"loader resolve" function but got type undefined.' -})) -.then(mustCall()); diff --git a/test/es-module/test-esm-loader-invalid-url.mjs b/test/es-module/test-esm-loader-invalid-url.mjs deleted file mode 100644 index 9cf17b2478..0000000000 --- a/test/es-module/test-esm-loader-invalid-url.mjs +++ /dev/null @@ -1,14 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-invalid-url.mjs -/* eslint-disable node-core/required-modules */ - -import { expectsError, mustCall } from '../common/index.mjs'; -import assert from 'assert'; - -import('../fixtures/es-modules/test-esm-ok.mjs') -.then(assert.fail, expectsError({ - code: 'ERR_INVALID_RETURN_PROPERTY', - message: 'Expected a valid url to be returned for the "url" from the ' + - '"loader resolve" function but got ' + - '../fixtures/es-modules/test-esm-ok.mjs.' -})) -.then(mustCall()); diff --git a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs b/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs deleted file mode 100644 index ab2da7adce..0000000000 --- a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs +++ /dev/null @@ -1,10 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs -/* eslint-disable node-core/required-modules */ - -import { expectsError } from '../common/index.mjs'; - -import('test').catch(expectsError({ - code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', - message: 'The ES Module loader may not return a format of \'dynamic\' ' + - 'when no dynamicInstantiate function was provided' -})); diff --git a/test/es-module/test-esm-named-exports.mjs b/test/es-module/test-esm-named-exports.mjs deleted file mode 100644 index e235f598cb..0000000000 --- a/test/es-module/test-esm-named-exports.mjs +++ /dev/null @@ -1,9 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs -/* eslint-disable node-core/required-modules */ -import '../common/index.mjs'; -import { readFile } from 'fs'; -import assert from 'assert'; -import ok from '../fixtures/es-modules/test-esm-ok.mjs'; - -assert(ok); -assert(readFile); diff --git a/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs b/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs deleted file mode 100644 index 2ca0f56581..0000000000 --- a/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs +++ /dev/null @@ -1,3 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs -/* eslint-disable node-core/required-modules */ -import './not-found.js'; diff --git a/test/es-module/test-esm-preserve-symlinks-not-found.mjs b/test/es-module/test-esm-preserve-symlinks-not-found.mjs deleted file mode 100644 index b5be2d7e63..0000000000 --- a/test/es-module/test-esm-preserve-symlinks-not-found.mjs +++ /dev/null @@ -1,3 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs -/* eslint-disable node-core/required-modules */ -import './not-found.mjs'; diff --git a/test/es-module/test-esm-resolve-hook.mjs b/test/es-module/test-esm-resolve-hook.mjs deleted file mode 100644 index e326d20b6d..0000000000 --- a/test/es-module/test-esm-resolve-hook.mjs +++ /dev/null @@ -1,8 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/js-loader.mjs -/* eslint-disable node-core/required-modules */ -import { namedExport } from '../fixtures/es-module-loaders/js-as-esm.js'; -import assert from 'assert'; -import ok from '../fixtures/es-modules/test-esm-ok.mjs'; - -assert(ok); -assert(namedExport); diff --git a/test/es-module/test-esm-shared-loader-dep.mjs b/test/es-module/test-esm-shared-loader-dep.mjs deleted file mode 100644 index 20d156fb8b..0000000000 --- a/test/es-module/test-esm-shared-loader-dep.mjs +++ /dev/null @@ -1,11 +0,0 @@ -// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-shared-dep.mjs -/* eslint-disable node-core/required-modules */ -import { createRequire } from '../common/index.mjs'; - -import assert from 'assert'; -import '../fixtures/es-modules/test-esm-ok.mjs'; - -const require = createRequire(import.meta.url); -const dep = require('../fixtures/es-module-loaders/loader-dep.js'); - -assert.strictEqual(dep.format, 'esm'); diff --git a/test/es-module/test-esm-throw-undefined.mjs b/test/es-module/test-esm-throw-undefined.mjs deleted file mode 100644 index 97e917da5e..0000000000 --- a/test/es-module/test-esm-throw-undefined.mjs +++ /dev/null @@ -1,16 +0,0 @@ -// Flags: --experimental-modules -/* eslint-disable node-core/required-modules */ - -import '../common/index.mjs'; -import assert from 'assert'; - -async function doTest() { - await assert.rejects( - async () => { - await import('../fixtures/es-module-loaders/throw-undefined.mjs'); - }, - (e) => e === undefined - ); -} - -doTest(); diff --git a/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs b/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs deleted file mode 100644 index 28ccd6ecf2..0000000000 --- a/test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import module from 'module'; - -const builtins = new Set( - Object.keys(process.binding('natives')).filter(str => - /^(?!(?:internal|node|v8)\/)/.test(str)) -); - -export function dynamicInstantiate(url) { - const builtinInstance = module._load(url.substr(5)); - const builtinExports = ['default', ...Object.keys(builtinInstance)]; - return { - exports: builtinExports, - execute: exports => { - for (let name of builtinExports) - exports[name].set(builtinInstance[name]); - exports.default.set(builtinInstance); - } - }; -} - -export function resolve(specifier, base, defaultResolver) { - if (builtins.has(specifier)) { - return { - url: `node:${specifier}`, - format: 'dynamic' - }; - } - return defaultResolver(specifier, base); -} diff --git a/test/fixtures/es-module-loaders/example-loader.mjs b/test/fixtures/es-module-loaders/example-loader.mjs deleted file mode 100644 index acb4486edc..0000000000 --- a/test/fixtures/es-module-loaders/example-loader.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import url from 'url'; -import path from 'path'; -import process from 'process'; - -const builtins = new Set( - Object.keys(process.binding('natives')).filter((str) => - /^(?!(?:internal|node|v8)\/)/.test(str)) -); -const JS_EXTENSIONS = new Set(['.js', '.mjs']); - -const baseURL = new url.URL('file://'); -baseURL.pathname = process.cwd() + '/'; - -export function resolve(specifier, parentModuleURL = baseURL /*, defaultResolve */) { - if (builtins.has(specifier)) { - return { - url: specifier, - format: 'builtin' - }; - } - if (/^\.{0,2}[/]/.test(specifier) !== true && !specifier.startsWith('file:')) { - // For node_modules support: - // return defaultResolve(specifier, parentModuleURL); - throw new Error( - `imports must begin with '/', './', or '../'; '${specifier}' does not`); - } - const resolved = new url.URL(specifier, parentModuleURL); - const ext = path.extname(resolved.pathname); - if (!JS_EXTENSIONS.has(ext)) { - throw new Error( - `Cannot load file with non-JavaScript file extension ${ext}.`); - } - return { - url: resolved.href, - format: 'esm' - }; -} diff --git a/test/fixtures/es-module-loaders/js-as-esm.js b/test/fixtures/es-module-loaders/js-as-esm.js deleted file mode 100644 index b4d2741b2f..0000000000 --- a/test/fixtures/es-module-loaders/js-as-esm.js +++ /dev/null @@ -1 +0,0 @@ -export const namedExport = 'named-export'; diff --git a/test/fixtures/es-module-loaders/js-loader.mjs b/test/fixtures/es-module-loaders/js-loader.mjs deleted file mode 100644 index 9fa6b9eed4..0000000000 --- a/test/fixtures/es-module-loaders/js-loader.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import { URL } from 'url'; - -const builtins = new Set( - Object.keys(process.binding('natives')).filter(str => - /^(?!(?:internal|node|v8)\/)/.test(str)) -) - -const baseURL = new URL('file://'); -baseURL.pathname = process.cwd() + '/'; - -export function resolve (specifier, base = baseURL) { - if (builtins.has(specifier)) { - return { - url: specifier, - format: 'builtin' - }; - } - // load all dependencies as esm, regardless of file extension - const url = new URL(specifier, base).href; - return { - url, - format: 'esm' - }; -} diff --git a/test/fixtures/es-module-loaders/loader-dep.js b/test/fixtures/es-module-loaders/loader-dep.js deleted file mode 100644 index cf821afec1..0000000000 --- a/test/fixtures/es-module-loaders/loader-dep.js +++ /dev/null @@ -1 +0,0 @@ -exports.format = 'esm'; diff --git a/test/fixtures/es-module-loaders/loader-invalid-format.mjs b/test/fixtures/es-module-loaders/loader-invalid-format.mjs deleted file mode 100644 index 17a0dcd04d..0000000000 --- a/test/fixtures/es-module-loaders/loader-invalid-format.mjs +++ /dev/null @@ -1,8 +0,0 @@ -export async function resolve(specifier, parentModuleURL, defaultResolve) { - if (parentModuleURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') { - return { - url: 'file:///asdf' - }; - } - return defaultResolve(specifier, parentModuleURL); -} diff --git a/test/fixtures/es-module-loaders/loader-invalid-url.mjs b/test/fixtures/es-module-loaders/loader-invalid-url.mjs deleted file mode 100644 index f653155899..0000000000 --- a/test/fixtures/es-module-loaders/loader-invalid-url.mjs +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable node-core/required-modules */ -export async function resolve(specifier, parentModuleURL, defaultResolve) { - if (parentModuleURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') { - return { - url: specifier, - format: 'esm' - }; - } - return defaultResolve(specifier, parentModuleURL); -} diff --git a/test/fixtures/es-module-loaders/loader-shared-dep.mjs b/test/fixtures/es-module-loaders/loader-shared-dep.mjs deleted file mode 100644 index 419c75bdbf..0000000000 --- a/test/fixtures/es-module-loaders/loader-shared-dep.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import assert from 'assert'; - -import {createRequire} from '../../common/index.mjs'; - -const require = createRequire(import.meta.url); -const dep = require('./loader-dep.js'); - -export function resolve(specifier, base, defaultResolve) { - assert.strictEqual(dep.format, 'esm'); - return defaultResolve(specifier, base); -} diff --git a/test/fixtures/es-module-loaders/loader-with-dep.mjs b/test/fixtures/es-module-loaders/loader-with-dep.mjs deleted file mode 100644 index 5afd3b2e21..0000000000 --- a/test/fixtures/es-module-loaders/loader-with-dep.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import {createRequire} from '../../common/index.mjs'; - -const require = createRequire(import.meta.url); -const dep = require('./loader-dep.js'); - -export function resolve (specifier, base, defaultResolve) { - return { - url: defaultResolve(specifier, base).url, - format: dep.format - }; -} diff --git a/test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs b/test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs deleted file mode 100644 index 6993747fcc..0000000000 --- a/test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs +++ /dev/null @@ -1,6 +0,0 @@ -export function resolve(specifier, parentModule, defaultResolver) { - if (specifier !== 'test') { - return defaultResolver(specifier, parentModule); - } - return { url: 'file://', format: 'dynamic' }; -} diff --git a/test/fixtures/es-module-loaders/module-named-exports.mjs b/test/fixtures/es-module-loaders/module-named-exports.mjs deleted file mode 100644 index 04f7f43ebd..0000000000 --- a/test/fixtures/es-module-loaders/module-named-exports.mjs +++ /dev/null @@ -1,2 +0,0 @@ -export const foo = 'foo'; -export const bar = 'bar'; diff --git a/test/fixtures/es-module-loaders/not-found-assert-loader.mjs b/test/fixtures/es-module-loaders/not-found-assert-loader.mjs deleted file mode 100644 index d15f294fe6..0000000000 --- a/test/fixtures/es-module-loaders/not-found-assert-loader.mjs +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; - -// a loader that asserts that the defaultResolve will throw "not found" -// (skipping the top-level main of course) -let mainLoad = true; -export async function resolve (specifier, base, defaultResolve) { - if (mainLoad) { - mainLoad = false; - return defaultResolve(specifier, base); - } - try { - await defaultResolve(specifier, base); - } - catch (e) { - assert.strictEqual(e.code, 'MODULE_NOT_FOUND'); - return { - format: 'builtin', - url: 'fs' - }; - } - assert.fail(`Module resolution for ${specifier} should be throw MODULE_NOT_FOUND`); -} diff --git a/test/fixtures/es-module-loaders/syntax-error-import.mjs b/test/fixtures/es-module-loaders/syntax-error-import.mjs deleted file mode 100644 index 3a6bc5effc..0000000000 --- a/test/fixtures/es-module-loaders/syntax-error-import.mjs +++ /dev/null @@ -1 +0,0 @@ -import { foo, notfound } from './module-named-exports.mjs'; diff --git a/test/fixtures/es-module-loaders/syntax-error.mjs b/test/fixtures/es-module-loaders/syntax-error.mjs deleted file mode 100644 index bda4a7e6eb..0000000000 --- a/test/fixtures/es-module-loaders/syntax-error.mjs +++ /dev/null @@ -1,2 +0,0 @@ -'use strict'; -await async () => 0; diff --git a/test/fixtures/es-module-loaders/throw-undefined.mjs b/test/fixtures/es-module-loaders/throw-undefined.mjs deleted file mode 100644 index 0349ae112d..0000000000 --- a/test/fixtures/es-module-loaders/throw-undefined.mjs +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; -/* eslint-disable node-core/required-modules */ - -throw undefined; diff --git a/test/message/esm_display_syntax_error_import.mjs b/test/message/esm_display_syntax_error_import.mjs deleted file mode 100644 index 12d10270e9..0000000000 --- a/test/message/esm_display_syntax_error_import.mjs +++ /dev/null @@ -1,7 +0,0 @@ -// Flags: --experimental-modules -/* eslint-disable no-unused-vars, node-core/required-modules */ -import '../common/index.mjs'; -import { - foo, - notfound -} from '../fixtures/es-module-loaders/module-named-exports.mjs'; diff --git a/test/message/esm_display_syntax_error_import.out b/test/message/esm_display_syntax_error_import.out deleted file mode 100644 index 48f2e2fb74..0000000000 --- a/test/message/esm_display_syntax_error_import.out +++ /dev/null @@ -1,6 +0,0 @@ -(node:*) ExperimentalWarning: The ESM module loader is experimental. -file:///*/test/message/esm_display_syntax_error_import.mjs:6 - notfound - ^^^^^^^^ -SyntaxError: The requested module '../fixtures/es-module-loaders/module-named-exports.mjs' does not provide an export named 'notfound' - at ModuleJob._instantiate (internal/modules/esm/module_job.js:*:*) diff --git a/test/message/esm_display_syntax_error_import_module.mjs b/test/message/esm_display_syntax_error_import_module.mjs deleted file mode 100644 index a53bbbcd19..0000000000 --- a/test/message/esm_display_syntax_error_import_module.mjs +++ /dev/null @@ -1,4 +0,0 @@ -// Flags: --experimental-modules -/* eslint-disable node-core/required-modules */ -import '../common/index.mjs'; -import '../fixtures/es-module-loaders/syntax-error-import.mjs'; diff --git a/test/message/esm_display_syntax_error_import_module.out b/test/message/esm_display_syntax_error_import_module.out deleted file mode 100644 index 3e1024db8a..0000000000 --- a/test/message/esm_display_syntax_error_import_module.out +++ /dev/null @@ -1,6 +0,0 @@ -(node:*) ExperimentalWarning: The ESM module loader is experimental. -file:///*/test/fixtures/es-module-loaders/syntax-error-import.mjs:1 -import { foo, notfound } from './module-named-exports.mjs'; - ^^^^^^^^ -SyntaxError: The requested module './module-named-exports.mjs' does not provide an export named 'notfound' - at ModuleJob._instantiate (internal/modules/esm/module_job.js:*:*) diff --git a/test/message/esm_display_syntax_error_module.mjs b/test/message/esm_display_syntax_error_module.mjs deleted file mode 100644 index 5905d2a954..0000000000 --- a/test/message/esm_display_syntax_error_module.mjs +++ /dev/null @@ -1,4 +0,0 @@ -// Flags: --experimental-modules -/* eslint-disable node-core/required-modules */ -import '../common/index.mjs'; -import '../fixtures/es-module-loaders/syntax-error.mjs'; diff --git a/test/message/esm_display_syntax_error_module.out b/test/message/esm_display_syntax_error_module.out deleted file mode 100644 index e636abad9e..0000000000 --- a/test/message/esm_display_syntax_error_module.out +++ /dev/null @@ -1,6 +0,0 @@ -(node:*) ExperimentalWarning: The ESM module loader is experimental. -file:///*/test/fixtures/es-module-loaders/syntax-error.mjs:2 -await async () => 0; -^^^^^ -SyntaxError: Unexpected reserved word - at translators.set (internal/modules/esm/translators.js:*:*) From c9dafb9b7155f32cb1f12e638035c39bca53d454 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 2 Oct 2018 16:09:22 -0700 Subject: [PATCH 7/7] esm: Remove --experimental-vm-modules. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/ecmascript-modules/pull/6 Reviewed-By: Guy Bedford Reviewed-By: Myles Borins Reviewed-By: Michaël Zasso --- doc/api/cli.md | 8 - doc/api/vm.md | 358 ------------------ lib/internal/bootstrap/node.js | 3 +- lib/internal/process/esm_loader.js | 7 +- lib/internal/vm/source_text_module.js | 251 ------------ lib/vm.js | 5 - node.gyp | 1 - src/node_config.cc | 3 - src/node_options.cc | 4 - test/parallel/test-util-inspect-namespace.js | 20 - test/parallel/test-util-types.js | 145 ------- test/parallel/test-vm-module-basic.js | 52 --- .../parallel/test-vm-module-dynamic-import.js | 26 -- test/parallel/test-vm-module-errors.js | 263 ------------- test/parallel/test-vm-module-import-meta.js | 43 --- test/parallel/test-vm-module-link.js | 136 ------- test/parallel/test-vm-module-reevaluate.js | 48 --- 17 files changed, 4 insertions(+), 1369 deletions(-) delete mode 100644 lib/internal/vm/source_text_module.js delete mode 100644 test/parallel/test-util-inspect-namespace.js delete mode 100644 test/parallel/test-util-types.js delete mode 100644 test/parallel/test-vm-module-basic.js delete mode 100644 test/parallel/test-vm-module-dynamic-import.js delete mode 100644 test/parallel/test-vm-module-errors.js delete mode 100644 test/parallel/test-vm-module-import-meta.js delete mode 100644 test/parallel/test-vm-module-link.js delete mode 100644 test/parallel/test-vm-module-reevaluate.js diff --git a/doc/api/cli.md b/doc/api/cli.md index c9b8dc3b2b..8337a9aef5 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -97,13 +97,6 @@ added: v10.0.0 Enable experimental top-level `await` keyword support in REPL. -### `--experimental-vm-modules` - - -Enable experimental ES Module support in the `vm` module. - ### `--experimental-worker` - -> Stability: 1 - Experimental - -*This feature is only available with the `--experimental-vm-modules` command -flag enabled.* - -The `vm.SourceTextModule` class provides a low-level interface for using -ECMAScript modules in VM contexts. It is the counterpart of the `vm.Script` -class that closely mirrors [Source Text Module Record][]s as defined in the -ECMAScript specification. - -Unlike `vm.Script` however, every `vm.SourceTextModule` object is bound to a -context from its creation. Operations on `vm.SourceTextModule` objects are -intrinsically asynchronous, in contrast with the synchronous nature of -`vm.Script` objects. With the help of async functions, however, manipulating -`vm.SourceTextModule` objects is fairly straightforward. - -Using a `vm.SourceTextModule` object requires four distinct steps: -creation/parsing, linking, instantiation, and evaluation. These four steps are -illustrated in the following example. - -This implementation lies at a lower level than the [ECMAScript Module -loader][]. There is also currently no way to interact with the Loader, though -support is planned. - -```js -const vm = require('vm'); - -const contextifiedSandbox = vm.createContext({ secret: 42 }); - -(async () => { - // Step 1 - // - // Create a Module by constructing a new `vm.SourceTextModule` object. This - // parses the provided source text, throwing a `SyntaxError` if anything goes - // wrong. By default, a Module is created in the top context. But here, we - // specify `contextifiedSandbox` as the context this Module belongs to. - // - // Here, we attempt to obtain the default export from the module "foo", and - // put it into local binding "secret". - - const bar = new vm.SourceTextModule(` - import s from 'foo'; - s; - `, { context: contextifiedSandbox }); - - // Step 2 - // - // "Link" the imported dependencies of this Module to it. - // - // The provided linking callback (the "linker") accepts two arguments: the - // parent module (`bar` in this case) and the string that is the specifier of - // the imported module. The callback is expected to return a Module that - // corresponds to the provided specifier, with certain requirements documented - // in `module.link()`. - // - // If linking has not started for the returned Module, the same linker - // callback will be called on the returned Module. - // - // Even top-level Modules without dependencies must be explicitly linked. The - // callback provided would never be called, however. - // - // The link() method returns a Promise that will be resolved when all the - // Promises returned by the linker resolve. - // - // Note: This is a contrived example in that the linker function creates a new - // "foo" module every time it is called. In a full-fledged module system, a - // cache would probably be used to avoid duplicated modules. - - async function linker(specifier, referencingModule) { - if (specifier === 'foo') { - return new vm.SourceTextModule(` - // The "secret" variable refers to the global variable we added to - // "contextifiedSandbox" when creating the context. - export default secret; - `, { context: referencingModule.context }); - - // Using `contextifiedSandbox` instead of `referencingModule.context` - // here would work as well. - } - throw new Error(`Unable to resolve dependency: ${specifier}`); - } - await bar.link(linker); - - // Step 3 - // - // Instantiate the top-level Module. - // - // Only the top-level Module needs to be explicitly instantiated; its - // dependencies will be recursively instantiated by instantiate(). - - bar.instantiate(); - - // Step 4 - // - // Evaluate the Module. The evaluate() method returns a Promise with a single - // property "result" that contains the result of the very last statement - // executed in the Module. In the case of `bar`, it is `s;`, which refers to - // the default export of the `foo` module, the `secret` we set in the - // beginning to 42. - - const { result } = await bar.evaluate(); - - console.log(result); - // Prints 42. -})(); -``` - -### Constructor: new vm.SourceTextModule(code[, options]) - -* `code` {string} JavaScript Module code to parse -* `options` - * `url` {string} URL used in module resolution and stack traces. **Default:** - `'vm:module(i)'` where `i` is a context-specific ascending index. - * `context` {Object} The [contextified][] object as returned by the - `vm.createContext()` method, to compile and evaluate this `Module` in. - * `lineOffset` {integer} Specifies the line number offset that is displayed - in stack traces produced by this `Module`. - * `columnOffset` {integer} Specifies the column number offset that is - displayed in stack traces produced by this `Module`. - * `initalizeImportMeta` {Function} Called during evaluation of this `Module` - to initialize the `import.meta`. This function has the signature `(meta, - module)`, where `meta` is the `import.meta` object in the `Module`, and - `module` is this `vm.SourceTextModule` object. - -Creates a new ES `Module` object. - -Properties assigned to the `import.meta` object that are objects may -allow the `Module` to access information outside the specified `context`, if the -object is created in the top level context. Use `vm.runInContext()` to create -objects in a specific context. - -```js -const vm = require('vm'); - -const contextifiedSandbox = vm.createContext({ secret: 42 }); - -(async () => { - const module = new vm.SourceTextModule( - 'Object.getPrototypeOf(import.meta.prop).secret = secret;', - { - initializeImportMeta(meta) { - // Note: this object is created in the top context. As such, - // Object.getPrototypeOf(import.meta.prop) points to the - // Object.prototype in the top context rather than that in - // the sandbox. - meta.prop = {}; - } - }); - // Since module has no dependencies, the linker function will never be called. - await module.link(() => {}); - module.instantiate(); - await module.evaluate(); - - // Now, Object.prototype.secret will be equal to 42. - // - // To fix this problem, replace - // meta.prop = {}; - // above with - // meta.prop = vm.runInContext('{}', contextifiedSandbox); -})(); -``` - -### module.dependencySpecifiers - -* {string[]} - -The specifiers of all dependencies of this module. The returned array is frozen -to disallow any changes to it. - -Corresponds to the `[[RequestedModules]]` field of -[Source Text Module Record][]s in the ECMAScript specification. - -### module.error - -* {any} - -If the `module.status` is `'errored'`, this property contains the exception -thrown by the module during evaluation. If the status is anything else, -accessing this property will result in a thrown exception. - -The value `undefined` cannot be used for cases where there is not a thrown -exception due to possible ambiguity with `throw undefined;`. - -Corresponds to the `[[EvaluationError]]` field of [Source Text Module Record][]s -in the ECMAScript specification. - -### module.evaluate([options]) - -* `options` {Object} - * `timeout` {number} Specifies the number of milliseconds to evaluate - before terminating execution. If execution is interrupted, an [`Error`][] - will be thrown. - * `breakOnSigint` {boolean} If `true`, the execution will be terminated when - `SIGINT` (Ctrl+C) is received. Existing handlers for the event that have - been attached via `process.on('SIGINT')` will be disabled during script - execution, but will continue to work after that. If execution is - interrupted, an [`Error`][] will be thrown. -* Returns: {Promise} - -Evaluate the module. - -This must be called after the module has been instantiated; otherwise it will -throw an error. It could be called also when the module has already been -evaluated, in which case it will do one of the following two things: - -- return `undefined` if the initial evaluation ended in success (`module.status` - is `'evaluated'`) -- rethrow the same exception the initial evaluation threw if the initial - evaluation ended in an error (`module.status` is `'errored'`) - -This method cannot be called while the module is being evaluated -(`module.status` is `'evaluating'`) to prevent infinite recursion. - -Corresponds to the [Evaluate() concrete method][] field of [Source Text Module -Record][]s in the ECMAScript specification. - -### module.instantiate() - -Instantiate the module. This must be called after linking has completed -(`linkingStatus` is `'linked'`); otherwise it will throw an error. It may also -throw an exception if one of the dependencies does not provide an export the -parent module requires. - -However, if this function succeeded, further calls to this function after the -initial instantiation will be no-ops, to be consistent with the ECMAScript -specification. - -Unlike other methods operating on `Module`, this function completes -synchronously and returns nothing. - -Corresponds to the [Instantiate() concrete method][] field of [Source Text -Module Record][]s in the ECMAScript specification. - -### module.link(linker) - -* `linker` {Function} -* Returns: {Promise} - -Link module dependencies. This method must be called before instantiation, and -can only be called once per module. - -Two parameters will be passed to the `linker` function: - -- `specifier` The specifier of the requested module: - - ```js - import foo from 'foo'; - // ^^^^^ the module specifier - ``` -- `referencingModule` The `Module` object `link()` is called on. - -The function is expected to return a `Module` object or a `Promise` that -eventually resolves to a `Module` object. The returned `Module` must satisfy the -following two invariants: - -- It must belong to the same context as the parent `Module`. -- Its `linkingStatus` must not be `'errored'`. - -If the returned `Module`'s `linkingStatus` is `'unlinked'`, this method will be -recursively called on the returned `Module` with the same provided `linker` -function. - -`link()` returns a `Promise` that will either get resolved when all linking -instances resolve to a valid `Module`, or rejected if the linker function either -throws an exception or returns an invalid `Module`. - -The linker function roughly corresponds to the implementation-defined -[HostResolveImportedModule][] abstract operation in the ECMAScript -specification, with a few key differences: - -- The linker function is allowed to be asynchronous while - [HostResolveImportedModule][] is synchronous. -- The linker function is executed during linking, a Node.js-specific stage - before instantiation, while [HostResolveImportedModule][] is called during - instantiation. - -The actual [HostResolveImportedModule][] implementation used during module -instantiation is one that returns the modules linked during linking. Since at -that point all modules would have been fully linked already, the -[HostResolveImportedModule][] implementation is fully synchronous per -specification. - -### module.linkingStatus - -* {string} - -The current linking status of `module`. It will be one of the following values: - -- `'unlinked'`: `module.link()` has not yet been called. -- `'linking'`: `module.link()` has been called, but not all Promises returned by - the linker function have been resolved yet. -- `'linked'`: `module.link()` has been called, and all its dependencies have - been successfully linked. -- `'errored'`: `module.link()` has been called, but at least one of its - dependencies failed to link, either because the callback returned a `Promise` - that is rejected, or because the `Module` the callback returned is invalid. - -### module.namespace - -* {Object} - -The namespace object of the module. This is only available after instantiation -(`module.instantiate()`) has completed. - -Corresponds to the [GetModuleNamespace][] abstract operation in the ECMAScript -specification. - -### module.status - -* {string} - -The current status of the module. Will be one of: - -- `'uninstantiated'`: The module is not instantiated. It may because of any of - the following reasons: - - - The module was just created. - - `module.instantiate()` has been called on this module, but it failed for - some reason. - - This status does not convey any information regarding if `module.link()` has - been called. See `module.linkingStatus` for that. - -- `'instantiating'`: The module is currently being instantiated through a - `module.instantiate()` call on itself or a parent module. - -- `'instantiated'`: The module has been instantiated successfully, but - `module.evaluate()` has not yet been called. - -- `'evaluating'`: The module is being evaluated through a `module.evaluate()` on - itself or a parent module. - -- `'evaluated'`: The module has been successfully evaluated. - -- `'errored'`: The module has been evaluated, but an exception was thrown. - -Other than `'errored'`, this status string corresponds to the specification's -[Source Text Module Record][]'s `[[Status]]` field. `'errored'` corresponds to -`'evaluated'` in the specification, but with `[[EvaluationError]]` set to a -value that is not `undefined`. - -### module.url - -* {string} - -The URL of the current module, as set in the constructor. - ## Class: vm.Script