Skip to content

Commit 7da6afc

Browse files
committed
esm: add --experimental-import-non-javascript-without-assertion flag
Restricts import of JSON modules to the assertion form only, unless the `--experimental-import-non-javascript-without-assertion` CLI flag is provided.
1 parent d9f3bf1 commit 7da6afc

15 files changed

+61
-7
lines changed

doc/api/cli.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,14 @@ added:
251251

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

254+
### `--experimental-import-non-javascript-without-assertion`
255+
<!-- YAML
256+
added: REPLACEME
257+
-->
258+
259+
Enable experimental support importing non-JS modules without using an import
260+
assertion.
261+
254262
### `--experimental-json-modules`
255263
<!-- YAML
256264
added: v12.9.0

doc/api/errors.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,6 +1925,15 @@ strict compliance with the API specification (which in some cases may accept
19251925
`func(undefined)` and `func()` are treated identically, and the
19261926
[`ERR_INVALID_ARG_TYPE`][] error code may be used instead.
19271927

1928+
<a id="ERR_MISSING_IMPORT_ASSERTION"></a>
1929+
### `ERR_MISSING_IMPORT_ASSERTION`
1930+
<!-- YAML
1931+
added: REPLACEME
1932+
-->
1933+
1934+
An attempt was made to impor without an assertion a module that requires a
1935+
specific import assertion to be loaded.
1936+
19281937
<a id="ERR_MISSING_OPTION"></a>
19291938
### `ERR_MISSING_OPTION`
19301939

lib/internal/errors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,9 @@ E('ERR_MISSING_ARGS',
14001400
}
14011401
return `${msg} must be specified`;
14021402
}, TypeError);
1403+
E('ERR_MISSING_IMPORT_ASSERTION',
1404+
'Failed to load %s: Node.js requires modules of format "%s" to be loaded ' +
1405+
'using an assertion "%s" with value "%s"', TypeError);
14031406
E('ERR_MISSING_OPTION', '%s is required', TypeError);
14041407
E('ERR_MODULE_NOT_FOUND', (path, base, type = 'package') => {
14051408
return `Cannot find ${type} '${path}' imported from ${base}`;

lib/internal/modules/esm/loader.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const {
2929
ERR_INVALID_MODULE_SPECIFIER,
3030
ERR_INVALID_RETURN_PROPERTY_VALUE,
3131
ERR_INVALID_RETURN_VALUE,
32+
ERR_MISSING_IMPORT_ASSERTION,
3233
ERR_UNKNOWN_MODULE_FORMAT
3334
} = require('internal/errors').codes;
3435
const { pathToFileURL, isURLInstance } = require('internal/url');
@@ -48,6 +49,9 @@ const { translators } = require(
4849
'internal/modules/esm/translators');
4950
const { getOptionValue } = require('internal/options');
5051

52+
const experimentalAssertionlessNonJsImports =
53+
getOptionValue('--experimental-import-non-javascript-without-assertion');
54+
5155
const importAssertionTypeCache = new SafeWeakMap();
5256
const finalFormatCache = new SafeWeakMap();
5357
const supportedTypes = ObjectFreeze([undefined, 'json']);
@@ -252,6 +256,11 @@ class ESMLoader {
252256

253257
if (job !== undefined) {
254258
const finalFormat = finalFormatCache.get(job);
259+
if (!experimentalAssertionlessNonJsImports &&
260+
import_assertions.type == null && finalFormat === 'json') {
261+
throw new ERR_MISSING_IMPORT_ASSERTION(url, finalFormat,
262+
'type', 'json');
263+
}
255264
if (
256265
import_assertions.type == null ||
257266
(import_assertions.type === 'json' && finalFormat === 'json')
@@ -268,6 +277,10 @@ class ESMLoader {
268277
throw new ERR_FAILED_IMPORT_ASSERTION(
269278
url, 'type', import_assertions.type, finalFormat);
270279
}
280+
if (!experimentalAssertionlessNonJsImports && finalFormat === 'json') {
281+
throw new ERR_MISSING_IMPORT_ASSERTION(url, finalFormat,
282+
'type', 'json');
283+
}
271284
finalFormatCache.set(job, finalFormat);
272285

273286
const translator = translators.get(finalFormat);

src/node_options.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
315315
"experimental JSON interop support for the ES Module loader",
316316
&EnvironmentOptions::experimental_json_modules,
317317
kAllowedInEnvironment);
318+
AddOption("--experimental-import-non-javascript-without-assertion",
319+
"experimental support for importing non-JS modules without using "
320+
"an import assertion",
321+
&EnvironmentOptions::experimental_assertionless_non_js_imports,
322+
kAllowedInEnvironment);
318323
AddOption("--experimental-loader",
319324
"use the specified module as a custom loader",
320325
&EnvironmentOptions::userland_loader,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class EnvironmentOptions : public Options {
104104
std::string dns_result_order;
105105
bool enable_source_maps = false;
106106
bool experimental_json_modules = false;
107+
bool experimental_assertionless_non_js_imports = false;
107108
bool experimental_modules = false;
108109
std::string experimental_specifier_resolution;
109110
bool experimental_wasm_modules = false;

test/es-module/test-esm-data-urls.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
'use strict';
33
const common = require('../common');
44
const assert = require('assert');

test/es-module/test-esm-dynamic-import-assertion.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
'use strict';
33
const common = require('../common');
44
const { rejects, strictEqual } = require('assert');

test/es-module/test-esm-dynamic-import-assertion.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
import '../common/index.mjs';
33
import { rejects, strictEqual } from 'assert';
44

test/es-module/test-esm-import-assertion-3.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
import '../common/index.mjs';
33
import { strictEqual } from 'assert';
44

test/es-module/test-esm-import-assertion-4.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
import '../common/index.mjs';
33
import { strictEqual } from 'assert';
44

test/es-module/test-esm-json-cache.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
import '../common/index.mjs';
33

44
import { strictEqual, deepStrictEqual } from 'assert';

test/es-module/test-esm-json.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-json-modules
1+
// Flags: --experimental-json-modules --experimental-import-non-javascript-without-assertion
22
import '../common/index.mjs';
33
import { path } from '../common/fixtures.mjs';
44
import { strictEqual, ok } from 'assert';
@@ -11,6 +11,7 @@ strictEqual(secret.ofLife, 42);
1111
// Test warning message
1212
const child = spawn(process.execPath, [
1313
'--experimental-json-modules',
14+
'--experimental-import-non-javascript-without-assertion',
1415
path('/es-modules/json-modules.mjs'),
1516
]);
1617

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Flags: --experimental-json-modules
2+
import '../common/index.mjs';
3+
import 'data:application/json,{}';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
node:internal/errors:*
2+
ErrorCaptureStackTrace(err);
3+
^
4+
5+
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"
6+
at new NodeError (node:internal/errors:*:*)
7+
at ESMLoader.moduleProvider (node:internal/modules/esm/loader:*:*) {
8+
code: 'ERR_MISSING_IMPORT_ASSERTION'
9+
}
10+
11+
Node.js *

0 commit comments

Comments
 (0)