From 6deeec43af1eb6679a068cc4602ad25c1e1ba71e Mon Sep 17 00:00:00 2001 From: Geoffrey Booth <456802+GeoffreyBooth@users.noreply.github.com> Date: Mon, 31 Jan 2022 14:16:37 -0800 Subject: [PATCH 1/2] module: unflag esm json modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/41736 Reviewed-By: Antoine du Hamel Reviewed-By: Guy Bedford Reviewed-By: Mohammed Keyvanzadeh Reviewed-By: Michaƫl Zasso Reviewed-By: Bradley Farias --- doc/api/cli.md | 8 ---- doc/api/esm.md | 45 +++---------------- doc/api/packages.md | 3 +- doc/node.1 | 3 -- lib/internal/modules/esm/get_format.js | 11 ++--- src/node_options.cc | 10 +---- src/node_options.h | 2 - .../test-esm-assertionless-json-import.js | 2 +- test/es-module/test-esm-data-urls.js | 1 - .../test-esm-dynamic-import-assertion.js | 1 - .../test-esm-dynamic-import-assertion.mjs | 1 - .../es-module/test-esm-import-assertion-1.mjs | 1 - .../es-module/test-esm-import-assertion-2.mjs | 1 - .../es-module/test-esm-import-assertion-3.mjs | 1 - .../es-module/test-esm-import-assertion-4.mjs | 1 - .../test-esm-import-assertion-errors.js | 1 - .../test-esm-import-assertion-errors.mjs | 1 - .../test-esm-import-json-named-export.mjs | 1 - test/es-module/test-esm-invalid-data-urls.js | 14 ++---- test/es-module/test-esm-json-cache.mjs | 1 - test/es-module/test-esm-json.mjs | 2 - test/es-module/test-esm-non-js.js | 21 --------- test/es-module/test-esm-non-js.mjs | 23 ++++++++++ 23 files changed, 41 insertions(+), 114 deletions(-) delete mode 100644 test/es-module/test-esm-non-js.js create mode 100644 test/es-module/test-esm-non-js.mjs diff --git a/doc/api/cli.md b/doc/api/cli.md index 6c469ae656bf66..2d1e6c2d6963b7 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -282,14 +282,6 @@ added: Enable experimental `import.meta.resolve()` support. -### `--experimental-json-modules` - - - -Enable experimental JSON support for the ES Module loader. - ### `--experimental-loader=module` - -```js -import { readFile } from 'fs/promises'; -const json = JSON.parse(await readFile(new URL('./dat.json', import.meta.url))); -``` - -Alternatively `module.createRequire()` can be used. - #### No Native Module Loading Native modules are not currently supported with ES module imports. @@ -524,35 +508,19 @@ separate cache. > Stability: 1 - Experimental -Currently importing JSON modules are only supported in the `commonjs` mode -and are loaded using the CJS loader. [WHATWG JSON modules specification][] are -still being standardized, and are experimentally supported by including the -additional flag `--experimental-json-modules` when running Node.js. - -When the `--experimental-json-modules` flag is included, both the -`commonjs` and `module` mode use the new experimental JSON -loader. The imported JSON only exposes a `default`. There is no -support for named exports. A cache entry is created in the CommonJS -cache to avoid duplication. The same object is returned in -CommonJS if the JSON module has already been imported from the -same path. - -Assuming an `index.mjs` with +JSON files can be referenced by `import`: ```js import packageConfig from './package.json' assert { type: 'json' }; ``` -The `--experimental-json-modules` flag is needed for the module -to work. - -```bash -node index.mjs # fails -node --experimental-json-modules index.mjs # works -``` - The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][]. +The imported JSON only exposes a `default` export. There is no support for named +exports. A cache entry is created in the CommonJS cache to avoid duplication. +The same object is returned in CommonJS if the JSON module has already been +imported from the same path. + ## Wasm modules @@ -1467,7 +1435,6 @@ success! [Node.js Module Resolution Algorithm]: #resolver-algorithm-specification [Terminology]: #terminology [URL]: https://url.spec.whatwg.org/ -[WHATWG JSON modules specification]: https://html.spec.whatwg.org/#creating-a-json-module-script [`"exports"`]: packages.md#exports [`"type"`]: packages.md#type [`--input-type`]: cli.md#--input-typetype diff --git a/doc/api/packages.md b/doc/api/packages.md index 7a25699c55a1f9..1e93fb4de624a1 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -117,8 +117,7 @@ There is the ECMAScript module loader: `'./startup/index.js'`) must be fully specified. * It does no extension searching. A file extension must be provided when the specifier is a relative or absolute file URL. -* It can load JSON modules, but an import assertion is required (behind - `--experimental-json-modules` flag). +* It can load JSON modules, but an import assertion is required. * It accepts only `.js`, `.mjs`, and `.cjs` extensions for JavaScript text files. * It can be used to load JavaScript CommonJS modules. Such modules diff --git a/doc/node.1 b/doc/node.1 index d127bb84cc7f3c..7518fe1e6da570 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -142,9 +142,6 @@ Enable Source Map V3 support for stack traces. .It Fl -experimental-import-meta-resolve Enable experimental ES modules support for import.meta.resolve(). . -.It Fl -experimental-json-modules -Enable experimental JSON interop support for the ES Module loader. -. .It Fl -experimental-loader Ns = Ns Ar module Specify the .Ar module diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js index 9712890139596d..ca90cd6a5d1e97 100644 --- a/lib/internal/modules/esm/get_format.js +++ b/lib/internal/modules/esm/get_format.js @@ -8,7 +8,6 @@ const { const { extname } = require('path'); const { getOptionValue } = require('internal/options'); -const experimentalJsonModules = getOptionValue('--experimental-json-modules'); const experimentalSpecifierResolution = getOptionValue('--experimental-specifier-resolution'); const experimentalWasmModules = getOptionValue('--experimental-wasm-modules'); @@ -20,7 +19,8 @@ const extensionFormatMap = { '__proto__': null, '.cjs': 'commonjs', '.js': 'module', - '.mjs': 'module' + '.json': 'json', + '.mjs': 'module', }; const legacyExtensionFormatMap = { @@ -29,7 +29,7 @@ const legacyExtensionFormatMap = { '.js': 'commonjs', '.json': 'commonjs', '.mjs': 'module', - '.node': 'commonjs' + '.node': 'commonjs', }; let experimentalSpecifierResolutionWarned = false; @@ -37,9 +37,6 @@ let experimentalSpecifierResolutionWarned = false; if (experimentalWasmModules) extensionFormatMap['.wasm'] = legacyExtensionFormatMap['.wasm'] = 'wasm'; -if (experimentalJsonModules) - extensionFormatMap['.json'] = legacyExtensionFormatMap['.json'] = 'json'; - const protocolHandlers = ObjectAssign(ObjectCreate(null), { 'data:'(parsed) { const { 1: mime } = RegExpPrototypeExec( @@ -49,7 +46,7 @@ const protocolHandlers = ObjectAssign(ObjectCreate(null), { const format = ({ '__proto__': null, 'text/javascript': 'module', - 'application/json': experimentalJsonModules ? 'json' : null, + 'application/json': 'json', 'application/wasm': experimentalWasmModules ? 'wasm' : null })[mime] || null; diff --git a/src/node_options.cc b/src/node_options.cc index cd537ad684155e..00339ff340123f 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -312,19 +312,13 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { kAllowedInEnvironment); AddOption("--experimental-abortcontroller", "", NoOp{}, kAllowedInEnvironment); - AddOption("--experimental-json-modules", - "experimental JSON interop support for the ES Module loader", - &EnvironmentOptions::experimental_json_modules, - kAllowedInEnvironment); + AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvironment); AddOption("--experimental-loader", "use the specified module as a custom loader", &EnvironmentOptions::userland_loader, kAllowedInEnvironment); AddAlias("--loader", "--experimental-loader"); - AddOption("--experimental-modules", - "", - &EnvironmentOptions::experimental_modules, - kAllowedInEnvironment); + AddOption("--experimental-modules", "", NoOp{}, kAllowedInEnvironment); AddOption("--experimental-wasm-modules", "experimental ES Module support for webassembly modules", &EnvironmentOptions::experimental_wasm_modules, diff --git a/src/node_options.h b/src/node_options.h index 5cf2bb442fad40..1efa7202ed2f17 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -103,8 +103,6 @@ class EnvironmentOptions : public Options { std::vector conditions; std::string dns_result_order; bool enable_source_maps = false; - bool experimental_json_modules = false; - bool experimental_modules = false; std::string experimental_specifier_resolution; bool experimental_wasm_modules = false; bool experimental_import_meta_resolve = false; diff --git a/test/es-module/test-esm-assertionless-json-import.js b/test/es-module/test-esm-assertionless-json-import.js index 2f06508dd2e509..23c71a1ba105d2 100644 --- a/test/es-module/test-esm-assertionless-json-import.js +++ b/test/es-module/test-esm-assertionless-json-import.js @@ -1,4 +1,4 @@ -// Flags: --experimental-json-modules --experimental-loader ./test/fixtures/es-module-loaders/assertionless-json-import.mjs +// Flags: --experimental-loader ./test/fixtures/es-module-loaders/assertionless-json-import.mjs 'use strict'; const common = require('../common'); const { strictEqual } = require('assert'); diff --git a/test/es-module/test-esm-data-urls.js b/test/es-module/test-esm-data-urls.js index 85a693b54221a7..9d0deb70a1568c 100644 --- a/test/es-module/test-esm-data-urls.js +++ b/test/es-module/test-esm-data-urls.js @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules 'use strict'; const common = require('../common'); const assert = require('assert'); diff --git a/test/es-module/test-esm-dynamic-import-assertion.js b/test/es-module/test-esm-dynamic-import-assertion.js index c6ff97d790a44c..71ef9cd1d1d30b 100644 --- a/test/es-module/test-esm-dynamic-import-assertion.js +++ b/test/es-module/test-esm-dynamic-import-assertion.js @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules 'use strict'; const common = require('../common'); const { strictEqual } = require('assert'); diff --git a/test/es-module/test-esm-dynamic-import-assertion.mjs b/test/es-module/test-esm-dynamic-import-assertion.mjs index a53ea145479eb5..4010259b743cbd 100644 --- a/test/es-module/test-esm-dynamic-import-assertion.mjs +++ b/test/es-module/test-esm-dynamic-import-assertion.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { strictEqual } from 'assert'; diff --git a/test/es-module/test-esm-import-assertion-1.mjs b/test/es-module/test-esm-import-assertion-1.mjs index f011c948d8edea..72b3426bdbb601 100644 --- a/test/es-module/test-esm-import-assertion-1.mjs +++ b/test/es-module/test-esm-import-assertion-1.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { strictEqual } from 'assert'; diff --git a/test/es-module/test-esm-import-assertion-2.mjs b/test/es-module/test-esm-import-assertion-2.mjs index 70947fcf212d61..8001c29772b1f0 100644 --- a/test/es-module/test-esm-import-assertion-2.mjs +++ b/test/es-module/test-esm-import-assertion-2.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { strictEqual } from 'assert'; diff --git a/test/es-module/test-esm-import-assertion-3.mjs b/test/es-module/test-esm-import-assertion-3.mjs index 0409095aec5d97..b9de9232cfff4d 100644 --- a/test/es-module/test-esm-import-assertion-3.mjs +++ b/test/es-module/test-esm-import-assertion-3.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { strictEqual } from 'assert'; diff --git a/test/es-module/test-esm-import-assertion-4.mjs b/test/es-module/test-esm-import-assertion-4.mjs index 4f3e33a6eefe2d..547983e51f449a 100644 --- a/test/es-module/test-esm-import-assertion-4.mjs +++ b/test/es-module/test-esm-import-assertion-4.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { strictEqual } from 'assert'; diff --git a/test/es-module/test-esm-import-assertion-errors.js b/test/es-module/test-esm-import-assertion-errors.js index c7d5abee693979..147781b45ccca3 100644 --- a/test/es-module/test-esm-import-assertion-errors.js +++ b/test/es-module/test-esm-import-assertion-errors.js @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules 'use strict'; const common = require('../common'); const { rejects } = require('assert'); diff --git a/test/es-module/test-esm-import-assertion-errors.mjs b/test/es-module/test-esm-import-assertion-errors.mjs index c96e8f3dd046b7..d3f958ced0eb93 100644 --- a/test/es-module/test-esm-import-assertion-errors.mjs +++ b/test/es-module/test-esm-import-assertion-errors.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { rejects } from 'assert'; diff --git a/test/es-module/test-esm-import-json-named-export.mjs b/test/es-module/test-esm-import-json-named-export.mjs index f70b927329b6a6..3c0f3af662c7cc 100644 --- a/test/es-module/test-esm-import-json-named-export.mjs +++ b/test/es-module/test-esm-import-json-named-export.mjs @@ -5,7 +5,6 @@ import { spawn } from 'child_process'; import { execPath } from 'process'; const child = spawn(execPath, [ - '--experimental-json-modules', path('es-modules', 'import-json-named-export.mjs'), ]); diff --git a/test/es-module/test-esm-invalid-data-urls.js b/test/es-module/test-esm-invalid-data-urls.js index 67f0bfe4e25588..c349aa309cee65 100644 --- a/test/es-module/test-esm-invalid-data-urls.js +++ b/test/es-module/test-esm-invalid-data-urls.js @@ -5,20 +5,14 @@ const assert = require('assert'); (async () => { await assert.rejects(import('data:text/plain,export default0'), { code: 'ERR_INVALID_MODULE_SPECIFIER', - message: - 'Invalid module "data:text/plain,export default0" has an unsupported ' + - 'MIME type "text/plain"', + message: 'Invalid module "data:text/plain,export default0" has an unsupported MIME type "text/plain"', }); await assert.rejects(import('data:text/plain;base64,'), { code: 'ERR_INVALID_MODULE_SPECIFIER', - message: - 'Invalid module "data:text/plain;base64," has an unsupported ' + - 'MIME type "text/plain"', + message: 'Invalid module "data:text/plain;base64," has an unsupported MIME type "text/plain"', }); - await assert.rejects(import('data:application/json,[]'), { + await assert.rejects(import('data:text/css,.error { color: red; }'), { code: 'ERR_INVALID_MODULE_SPECIFIER', - message: - 'Invalid module "data:application/json,[]" has an unsupported ' + - 'MIME type "application/json"', + message: 'Invalid module "data:text/css,.error { color: red; }" has an unsupported MIME type "text/css"', }); })().then(common.mustCall()); diff --git a/test/es-module/test-esm-json-cache.mjs b/test/es-module/test-esm-json-cache.mjs index 90694748c39e5f..b766519d663f9a 100644 --- a/test/es-module/test-esm-json-cache.mjs +++ b/test/es-module/test-esm-json-cache.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { strictEqual, deepStrictEqual } from 'assert'; diff --git a/test/es-module/test-esm-json.mjs b/test/es-module/test-esm-json.mjs index f33b4f9937ddb1..6d55419eedc857 100644 --- a/test/es-module/test-esm-json.mjs +++ b/test/es-module/test-esm-json.mjs @@ -1,4 +1,3 @@ -// Flags: --experimental-json-modules import '../common/index.mjs'; import { path } from '../common/fixtures.mjs'; import { strictEqual, ok } from 'assert'; @@ -10,7 +9,6 @@ strictEqual(secret.ofLife, 42); // Test warning message const child = spawn(process.execPath, [ - '--experimental-json-modules', path('/es-modules/json-modules.mjs'), ]); diff --git a/test/es-module/test-esm-non-js.js b/test/es-module/test-esm-non-js.js deleted file mode 100644 index 3e572809bbdf35..00000000000000 --- a/test/es-module/test-esm-non-js.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -const common = require('../common'); -const { spawn } = require('child_process'); -const assert = require('assert'); - -const entry = require.resolve('./test-esm-json.mjs'); - -// Verify non-js extensions fail for ESM -const child = spawn(process.execPath, [entry]); - -let stderr = ''; -child.stderr.setEncoding('utf8'); -child.stderr.on('data', (data) => { - stderr += data; -}); -child.on('close', common.mustCall((code, signal) => { - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - assert.ok(stderr.indexOf('ERR_UNKNOWN_FILE_EXTENSION') !== -1); -})); diff --git a/test/es-module/test-esm-non-js.mjs b/test/es-module/test-esm-non-js.mjs new file mode 100644 index 00000000000000..749cd0b6132086 --- /dev/null +++ b/test/es-module/test-esm-non-js.mjs @@ -0,0 +1,23 @@ +import { mustCall } from '../common/index.mjs'; +import { fileURL } from '../common/fixtures.mjs'; +import { match, strictEqual } from 'assert'; +import { spawn } from 'child_process'; +import { execPath } from 'process'; + +// Verify non-js extensions fail for ESM +const child = spawn(execPath, [ + '--input-type=module', + '--eval', + `import ${JSON.stringify(fileURL('es-modules', 'file.unknown'))}`, +]); + +let stderr = ''; +child.stderr.setEncoding('utf8'); +child.stderr.on('data', (data) => { + stderr += data; +}); +child.on('close', mustCall((code, signal) => { + strictEqual(code, 1); + strictEqual(signal, null); + match(stderr, /ERR_UNKNOWN_FILE_EXTENSION/); +})); From d58d588ba2813c4b2a10c439ae34bfb63525af29 Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 30 Nov 2020 10:03:30 -0600 Subject: [PATCH 2/2] esm: support https remotely and http locally under flag Co-authored-by: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com> Co-authored-by: James M Snell Co-authored-by: Jordan Harband Co-authored-by: James Sumners PR-URL: https://github.com/nodejs/node/pull/36328 Reviewed-By: Matteo Collina Reviewed-By: Geoffrey Booth --- doc/api/cli.md | 11 + doc/api/errors.md | 17 ++ doc/api/esm.md | 65 ++++ doc/node.1 | 3 + lib/internal/errors.js | 11 +- lib/internal/main/check_syntax.js | 6 +- lib/internal/modules/esm/fetch_module.js | 286 ++++++++++++++++++ lib/internal/modules/esm/formats.js | 65 ++++ lib/internal/modules/esm/get_format.js | 93 +++--- lib/internal/modules/esm/get_source.js | 25 +- .../modules/esm/initialize_import_meta.js | 25 +- lib/internal/modules/esm/load.js | 6 +- lib/internal/modules/esm/loader.js | 27 +- lib/internal/modules/esm/resolve.js | 168 +++++++++- lib/internal/policy/manifest.js | 36 +++ lib/repl.js | 2 +- src/node_options.cc | 4 + src/node_options.h | 1 + test/common/index.mjs | 2 + test/es-module/test-esm-dynamic-import.js | 4 +- .../test-esm-import-assertion-errors.js | 4 +- .../test-esm-import-assertion-errors.mjs | 2 +- test/es-module/test-esm-invalid-data-urls.js | 15 +- .../test-esm-loader-resolve-type.mjs | 3 +- test/es-module/test-esm-loader-search.js | 4 +- test/es-module/test-esm-resolve-type.js | 17 +- test/es-module/test-http-imports.mjs | 170 +++++++++++ .../builtin-named-exports-loader.mjs | 4 +- .../es-module-loaders/hook-resolve-type.mjs | 8 +- .../es-module-loaders/loader-with-dep.mjs | 4 +- .../es-module-loaders/mock-loader.mjs | 14 +- test/parallel/test-bootstrap-modules.js | 36 ++- test/pummel/test-policy-integrity-dep.js | 1 + 33 files changed, 995 insertions(+), 144 deletions(-) create mode 100644 lib/internal/modules/esm/fetch_module.js create mode 100644 lib/internal/modules/esm/formats.js create mode 100644 test/es-module/test-http-imports.mjs diff --git a/doc/api/cli.md b/doc/api/cli.md index 2d1e6c2d6963b7..ae0dde8630099a 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -291,6 +291,16 @@ added: v9.0.0 Specify the `module` of a custom experimental [ECMAScript module loader][]. `module` may be any string accepted as an [`import` specifier][]. +### `--experimental-network-imports` + + + +> Stability: 1 - Experimental + +Enable experimental support for the `https:` protocol in `import` specifiers. + ### `--experimental-policy`