Skip to content

Commit

Permalink
esm: add --experimental-import-non-javascript-without-assertion flag
Browse files Browse the repository at this point in the history
Restricts import of JSON modules to the assertion form only, unless the
`--experimental-import-non-javascript-without-assertion` CLI flag is
provided.
  • Loading branch information
aduh95 committed Sep 24, 2021
1 parent d19ecc4 commit 1fffc54
Show file tree
Hide file tree
Showing 15 changed files with 61 additions and 7 deletions.
8 changes: 8 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ added:

Enable experimental `import.meta.resolve()` support.

### `--experimental-import-non-javascript-without-assertion`
<!-- YAML
added: REPLACEME
-->

Enable experimental support importing non-JS modules without using an import
assertion.

### `--experimental-json-modules`
<!-- YAML
added: v12.9.0
Expand Down
9 changes: 9 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1924,6 +1924,15 @@ strict compliance with the API specification (which in some cases may accept
`func(undefined)` and `func()` are treated identically, and the
[`ERR_INVALID_ARG_TYPE`][] error code may be used instead.

<a id="ERR_MISSING_IMPORT_ASSERTION"></a>
### `ERR_MISSING_IMPORT_ASSERTION`
<!-- YAML
added: REPLACEME
-->

An attempt was made to impor without an assertion a module that requires a
specific import assertion to be loaded.

<a id="ERR_MISSING_OPTION"></a>
### `ERR_MISSING_OPTION`

Expand Down
3 changes: 3 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,9 @@ E('ERR_MISSING_ARGS',
}
return `${msg} must be specified`;
}, TypeError);
E('ERR_MISSING_IMPORT_ASSERTION',
'Failed to load %s: Node.js requires modules of format "%s" to be loaded ' +
'using an assertion "%s" with value "%s"', TypeError);
E('ERR_MISSING_OPTION', '%s is required', TypeError);
E('ERR_MODULE_NOT_FOUND', (path, base, type = 'package') => {
return `Cannot find ${type} '${path}' imported from ${base}`;
Expand Down
13 changes: 13 additions & 0 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const {
ERR_INVALID_MODULE_SPECIFIER,
ERR_INVALID_RETURN_PROPERTY_VALUE,
ERR_INVALID_RETURN_VALUE,
ERR_MISSING_IMPORT_ASSERTION,
ERR_UNKNOWN_MODULE_FORMAT
} = require('internal/errors').codes;
const { pathToFileURL, isURLInstance } = require('internal/url');
Expand All @@ -48,6 +49,9 @@ const { translators } = require(
'internal/modules/esm/translators');
const { getOptionValue } = require('internal/options');

const experimentalAssertionlessNonJsImports =
getOptionValue('--experimental-import-non-javascript-without-assertion');

const importAssertionTypeCache = new SafeWeakMap();
const finalFormatCache = new SafeWeakMap();
const supportedTypes = ObjectFreeze([undefined, 'json']);
Expand Down Expand Up @@ -252,6 +256,11 @@ class ESMLoader {

if (job !== undefined) {
const finalFormat = finalFormatCache.get(job);
if (!experimentalAssertionlessNonJsImports &&
import_assertions.type == null && finalFormat === 'json') {
throw new ERR_MISSING_IMPORT_ASSERTION(url, finalFormat,
'type', 'json');
}
if (
import_assertions.type == null ||
(import_assertions.type === 'json' && finalFormat === 'json')
Expand All @@ -268,6 +277,10 @@ class ESMLoader {
throw new ERR_FAILED_IMPORT_ASSERTION(
url, 'type', import_assertions.type, finalFormat);
}
if (!experimentalAssertionlessNonJsImports && finalFormat === 'json') {
throw new ERR_MISSING_IMPORT_ASSERTION(url, finalFormat,
'type', 'json');
}
finalFormatCache.set(job, finalFormat);

const translator = translators.get(finalFormat);
Expand Down
5 changes: 5 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"experimental JSON interop support for the ES Module loader",
&EnvironmentOptions::experimental_json_modules,
kAllowedInEnvironment);
AddOption("--experimental-import-non-javascript-without-assertion",
"experimental support for importing non-JS modules without using "
"an import assertion",
&EnvironmentOptions::experimental_assertionless_non_js_imports,
kAllowedInEnvironment);
AddOption("--experimental-loader",
"use the specified module as a custom loader",
&EnvironmentOptions::userland_loader,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class EnvironmentOptions : public Options {
std::string dns_result_order;
bool enable_source_maps = false;
bool experimental_json_modules = false;
bool experimental_assertionless_non_js_imports = false;
bool experimental_modules = false;
std::string experimental_specifier_resolution;
bool experimental_wasm_modules = false;
Expand Down
2 changes: 1 addition & 1 deletion test/es-module/test-esm-data-urls.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
'use strict';
const common = require('../common');
const assert = require('assert');
Expand Down
2 changes: 1 addition & 1 deletion test/es-module/test-esm-dynamic-import-assertion.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
'use strict';
const common = require('../common');
const { rejects, strictEqual } = require('assert');
Expand Down
2 changes: 1 addition & 1 deletion test/es-module/test-esm-dynamic-import-assertion.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
import '../common/index.mjs';
import { rejects, strictEqual } from 'assert';

Expand Down
2 changes: 1 addition & 1 deletion test/es-module/test-esm-import-assertion-3.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
import '../common/index.mjs';
import { strictEqual } from 'assert';

Expand Down
2 changes: 1 addition & 1 deletion test/es-module/test-esm-import-assertion-4.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
import '../common/index.mjs';
import { strictEqual } from 'assert';

Expand Down
2 changes: 1 addition & 1 deletion test/es-module/test-esm-json-cache.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
import '../common/index.mjs';

import { strictEqual, deepStrictEqual } from 'assert';
Expand Down
3 changes: 2 additions & 1 deletion test/es-module/test-esm-json.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-json-modules
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
import '../common/index.mjs';
import { path } from '../common/fixtures.mjs';
import { strictEqual, ok } from 'assert';
Expand All @@ -11,6 +11,7 @@ strictEqual(secret.ofLife, 42);
// Test warning message
const child = spawn(process.execPath, [
'--experimental-json-modules',
'--experimental-import-non-javascript-without-assertion',
path('/es-modules/json-modules.mjs'),
]);

Expand Down
3 changes: 3 additions & 0 deletions test/message/esm_import_assertion_missing.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Flags: --experimental-json-modules
import '../common/index.mjs';
import 'data:application/json,{}';
11 changes: 11 additions & 0 deletions test/message/esm_import_assertion_missing.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
node:internal/errors:*
ErrorCaptureStackTrace(err);
^

TypeError [ERR_MISSING_IMPORT_ASSERTION]: Failed to load data:application/json,{}: Node.js requires modules of format "json" to be loaded using an assertion "type" with value "json"
at new NodeError (node:internal/errors:*:*)
at ESMLoader.moduleProvider (node:internal/modules/esm/loader:*:*) {
code: 'ERR_MISSING_IMPORT_ASSERTION'
}

Node.js *

0 comments on commit 1fffc54

Please sign in to comment.