Skip to content

Commit 3c8aa21

Browse files
GeoffreyBoothtargos
authored andcommitted
module: import assertions improvements
PR-URL: #40785 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Bradley Farias <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent b92416f commit 3c8aa21

File tree

6 files changed

+41
-31
lines changed

6 files changed

+41
-31
lines changed

doc/api/esm.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ import fs from 'node:fs/promises';
228228
added: v17.1.0
229229
-->
230230

231+
> Stability: 1 - Experimental
232+
231233
The [Import Assertions proposal][] adds an inline syntax for module import
232234
statements to pass on more information alongside the module specifier.
233235

@@ -238,11 +240,12 @@ const { default: barData } =
238240
await import('./bar.json', { assert: { type: 'json' } });
239241
```
240242

241-
Node.js supports the following `type` values:
243+
Node.js supports the following `type` values, for which the assertion is
244+
mandatory:
242245

243-
| `type` | Resolves to |
244-
| -------- | ---------------- |
245-
| `'json'` | [JSON modules][] |
246+
| Assertion `type` | Needed for |
247+
| ---------------- | ---------------- |
248+
| `'json'` | [JSON modules][] |
246249

247250
## Builtin modules
248251

@@ -553,6 +556,8 @@ node index.mjs # fails
553556
node --experimental-json-modules index.mjs # works
554557
```
555558
559+
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
560+
556561
<i id="esm_experimental_wasm_modules"></i>
557562
558563
## Wasm modules
@@ -1390,6 +1395,7 @@ success!
13901395
[Dynamic `import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports
13911396
[ECMAScript Top-Level `await` proposal]: https://github.com/tc39/proposal-top-level-await/
13921397
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
1398+
[Import Assertions]: #import-assertions
13931399
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
13941400
[JSON modules]: #json-modules
13951401
[Node.js Module Resolution Algorithm]: #resolver-algorithm-specification

lib/internal/modules/esm/assert.js

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict';
22

33
const {
4+
ArrayPrototypeFilter,
45
ArrayPrototypeIncludes,
56
ObjectCreate,
67
ObjectValues,
78
ObjectPrototypeHasOwnProperty,
8-
Symbol,
99
} = primordials;
1010
const { validateString } = require('internal/validators');
1111

@@ -15,24 +15,32 @@ const {
1515
ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED,
1616
} = require('internal/errors').codes;
1717

18-
const kImplicitAssertType = Symbol('implicit assert type');
18+
// The HTML spec has an implied default type of `'javascript'`.
19+
const kImplicitAssertType = 'javascript';
1920

2021
/**
21-
* Define a map of module formats to import assertion types (the value of `type`
22-
* in `assert { type: 'json' }`).
23-
* @type {Map<string, string | typeof kImplicitAssertType}
22+
* Define a map of module formats to import assertion types (the value of
23+
* `type` in `assert { type: 'json' }`).
24+
* @type {Map<string, string>}
2425
*/
2526
const formatTypeMap = {
2627
'__proto__': null,
2728
'builtin': kImplicitAssertType,
2829
'commonjs': kImplicitAssertType,
2930
'json': 'json',
3031
'module': kImplicitAssertType,
31-
'wasm': kImplicitAssertType, // Should probably be 'webassembly' per https://github.com/tc39/proposal-import-assertions
32+
'wasm': kImplicitAssertType, // It's unclear whether the HTML spec will require an assertion type or not for Wasm; see https://github.com/WebAssembly/esm-integration/issues/42
3233
};
3334

34-
/** @type {Array<string, string | typeof kImplicitAssertType} */
35-
const supportedAssertionTypes = ObjectValues(formatTypeMap);
35+
/**
36+
* The HTML spec disallows the default type to be explicitly specified
37+
* (for now); so `import './file.js'` is okay but
38+
* `import './file.js' assert { type: 'javascript' }` throws.
39+
* @type {Array<string, string>}
40+
*/
41+
const supportedAssertionTypes = ArrayPrototypeFilter(
42+
ObjectValues(formatTypeMap),
43+
(type) => type !== kImplicitAssertType);
3644

3745

3846
/**
@@ -50,14 +58,10 @@ function validateAssertions(url, format,
5058

5159
switch (validType) {
5260
case undefined:
53-
// Ignore assertions for module types we don't recognize, to allow new
61+
// Ignore assertions for module formats we don't recognize, to allow new
5462
// formats in the future.
5563
return true;
5664

57-
case importAssertions.type:
58-
// The asserted type is the valid type for this format.
59-
return true;
60-
6165
case kImplicitAssertType:
6266
// This format doesn't allow an import assertion type, so the property
6367
// must not be set on the import assertions object.
@@ -66,9 +70,13 @@ function validateAssertions(url, format,
6670
}
6771
return handleInvalidType(url, importAssertions.type);
6872

73+
case importAssertions.type:
74+
// The asserted type is the valid type for this format.
75+
return true;
76+
6977
default:
7078
// There is an expected type for this format, but the value of
71-
// `importAssertions.type` was not it.
79+
// `importAssertions.type` might not have been it.
7280
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
7381
// `type` wasn't specified at all.
7482
throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType);
@@ -86,7 +94,7 @@ function handleInvalidType(url, type) {
8694
// `type` might have not been a string.
8795
validateString(type, 'type');
8896

89-
// `type` was not one of the types we understand.
97+
// `type` might not have been one of the types we understand.
9098
if (!ArrayPrototypeIncludes(supportedAssertionTypes, type)) {
9199
throw new ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED(type);
92100
}

lib/internal/modules/esm/module_map.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,17 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
1212
const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes;
1313
const { validateString } = require('internal/validators');
1414

15-
const validateAssertType = (type) =>
16-
type === kImplicitAssertType || validateString(type, 'type');
17-
1815
// Tracks the state of the loader-level module cache
1916
class ModuleMap extends SafeMap {
2017
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
2118
get(url, type = kImplicitAssertType) {
2219
validateString(url, 'url');
23-
validateAssertType(type);
20+
validateString(type, 'type');
2421
return super.get(url)?.[type];
2522
}
2623
set(url, type = kImplicitAssertType, job) {
2724
validateString(url, 'url');
28-
validateAssertType(type);
25+
validateString(type, 'type');
2926
if (job instanceof ModuleJob !== true &&
3027
typeof job !== 'function') {
3128
throw new ERR_INVALID_ARG_TYPE('job', 'ModuleJob', job);
@@ -39,7 +36,7 @@ class ModuleMap extends SafeMap {
3936
}
4037
has(url, type = kImplicitAssertType) {
4138
validateString(url, 'url');
42-
validateAssertType(type);
39+
validateString(type, 'type');
4340
return super.get(url)?.[type] !== undefined;
4441
}
4542
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async function test() {
2525
);
2626

2727
await rejects(
28-
import('data:text/javascript,', { assert: { type: 'unsupported' } }),
28+
import(jsModuleDataUrl, { assert: { type: 'unsupported' } }),
2929
{ code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED' }
3030
);
3131

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ assert.throws(() => validateAssertions(url, 'module', { type: 'json' }), {
2222
code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED',
2323
});
2424

25-
// This should be allowed according to HTML spec. Let's keep it disabled
26-
// until WASM module import is sorted out.
25+
// The HTML spec specifically disallows this for now, while Wasm module import
26+
// and whether it will require a type assertion is still an open question.
2727
assert.throws(() => validateAssertions(url, 'module', { type: 'javascript' }), {
2828
code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
2929
});

test/es-module/test-esm-loader-modulemap.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const { strictEqual, throws } = require('assert');
77
const { ESMLoader } = require('internal/modules/esm/loader');
88
const ModuleMap = require('internal/modules/esm/module_map');
99
const ModuleJob = require('internal/modules/esm/module_job');
10-
const { kImplicitAssertType } = require('internal/modules/esm/assert');
1110
const createDynamicModule = require(
1211
'internal/modules/esm/create_dynamic_module');
1312

@@ -38,14 +37,14 @@ const jsonModuleJob = new ModuleJob(loader, stubJsonModule.module,
3837
strictEqual(moduleMap.get(jsonModuleDataUrl, 'json'), jsonModuleJob);
3938

4039
strictEqual(moduleMap.has(jsModuleDataUrl), true);
41-
strictEqual(moduleMap.has(jsModuleDataUrl, kImplicitAssertType), true);
40+
strictEqual(moduleMap.has(jsModuleDataUrl, 'javascript'), true);
4241
strictEqual(moduleMap.has(jsonModuleDataUrl, 'json'), true);
4342

4443
strictEqual(moduleMap.has('unknown'), false);
4544

4645
// The types must match
4746
strictEqual(moduleMap.has(jsModuleDataUrl, 'json'), false);
48-
strictEqual(moduleMap.has(jsonModuleDataUrl, kImplicitAssertType), false);
47+
strictEqual(moduleMap.has(jsonModuleDataUrl, 'javascript'), false);
4948
strictEqual(moduleMap.has(jsonModuleDataUrl), false);
5049
strictEqual(moduleMap.has(jsModuleDataUrl, 'unknown'), false);
5150
strictEqual(moduleMap.has(jsonModuleDataUrl, 'unknown'), false);

0 commit comments

Comments
 (0)