Skip to content

Commit d9602c7

Browse files
committed
esm: use import attributes instead of import assertions
The old import assertions proposal has been renamed to "import attributes" with the follwing major changes: 1. The keyword is now `with` instead of `assert`. 2. Unknown assertions cause an error rather than being ignored, This commit updates the documentation to encourage folks to use the new syntax, and add aliases for module customization hooks. PR-URL: nodejs#50140 Fixes: nodejs#50134 Refs: v8/v8@159c82c Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Jacob Smith <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent 9577a00 commit d9602c7

39 files changed

+345
-262
lines changed

doc/api/errors.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -1759,7 +1759,8 @@ added:
17591759
- v16.14.0
17601760
-->
17611761

1762-
An import assertion has failed, preventing the specified module to be imported.
1762+
An import `type` attribute was provided, but the specified module is of a
1763+
different type.
17631764

17641765
<a id="ERR_IMPORT_ASSERTION_TYPE_MISSING"></a>
17651766

@@ -1771,7 +1772,7 @@ added:
17711772
- v16.14.0
17721773
-->
17731774

1774-
An import assertion is missing, preventing the specified module to be imported.
1775+
An import attribute is missing, preventing the specified module to be imported.
17751776

17761777
<a id="ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED"></a>
17771778

@@ -1783,7 +1784,17 @@ added:
17831784
- v16.14.0
17841785
-->
17851786

1786-
An import assertion is not supported by this version of Node.js.
1787+
An import attribute is not supported by this version of Node.js.
1788+
1789+
<a id="ERR_IMPORT_ATTRIBUTE_UNSUPPORTED"></a>
1790+
1791+
### `ERR_IMPORT_ATTRIBUTE_UNSUPPORTED`
1792+
1793+
<!-- YAML
1794+
added: REPLACEME
1795+
-->
1796+
1797+
An import attribute is not supported by this version of Node.js.
17871798

17881799
<a id="ERR_INCOMPATIBLE_OPTION_PAIR"></a>
17891800

doc/api/esm.md

+23-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
<!-- YAML
88
added: v8.5.0
99
changes:
10+
- version: REPLACEME
11+
pr-url: https://github.com/nodejs/node/pull/50140
12+
description: Add experimental support for import attributes.
1013
- version: v20.0.0
1114
pr-url: https://github.com/nodejs/node/pull/44710
1215
description: Module customization hooks are executed off the main thread.
@@ -19,7 +22,7 @@ changes:
1922
- v17.1.0
2023
- v16.14.0
2124
pr-url: https://github.com/nodejs/node/pull/40250
22-
description: Add support for import assertions.
25+
description: Add experimental support for import assertions.
2326
- version:
2427
- v17.0.0
2528
- v16.12.0
@@ -234,17 +237,28 @@ absolute URL strings.
234237
import fs from 'node:fs/promises';
235238
```
236239

237-
## Import assertions
240+
<a id="import-assertions"></a>
241+
242+
## Import attributes
238243

239244
<!-- YAML
240245
added:
241246
- v17.1.0
242247
- v16.14.0
248+
changes:
249+
- version: REPLACEME
250+
pr-url: https://github.com/nodejs/node/pull/50140
251+
description: Switch from Import Assertions to Import Attributes.
243252
-->
244253

245-
> Stability: 1 - Experimental
254+
> Stability: 1.1 - Active development
255+
256+
> This feature was previously named "Import assertions", and using the `assert`
257+
> keyword instead of `with`. Because the version of V8 on this release line does
258+
> not support the `with` keyword, you need to keep using `assert` to support
259+
> this version of Node.js.
246260
247-
The [Import Assertions proposal][] adds an inline syntax for module import
261+
The [Import Attributes proposal][] adds an inline syntax for module import
248262
statements to pass on more information alongside the module specifier.
249263

250264
```js
@@ -254,10 +268,10 @@ const { default: barData } =
254268
await import('./bar.json', { assert: { type: 'json' } });
255269
```
256270

257-
Node.js supports the following `type` values, for which the assertion is
271+
Node.js supports the following `type` values, for which the attribute is
258272
mandatory:
259273

260-
| Assertion `type` | Needed for |
274+
| Attribute `type` | Needed for |
261275
| ---------------- | ---------------- |
262276
| `'json'` | [JSON modules][] |
263277

@@ -547,7 +561,7 @@ JSON files can be referenced by `import`:
547561
import packageConfig from './package.json' assert { type: 'json' };
548562
```
549563
550-
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
564+
The `assert { type: 'json' }` syntax is mandatory; see [Import Attributes][].
551565
552566
The imported JSON only exposes a `default` export. There is no support for named
553567
exports. A cache entry is created in the CommonJS cache to avoid duplication.
@@ -1050,8 +1064,8 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
10501064
[Determining module system]: packages.md#determining-module-system
10511065
[Dynamic `import()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
10521066
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
1053-
[Import Assertions]: #import-assertions
1054-
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
1067+
[Import Attributes]: #import-attributes
1068+
[Import Attributes proposal]: https://github.com/tc39/proposal-import-attributes
10551069
[JSON modules]: #json-modules
10561070
[Module customization hooks]: module.md#customization-hooks
10571071
[Node.js Module Resolution And Loading Algorithm]: #resolution-algorithm-specification

doc/api/module.md

+12-7
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,11 @@ register('./path-to-my-hooks.js', {
458458
459459
<!-- YAML
460460
changes:
461+
- version: REPLACEME
462+
pr-url: https://github.com/nodejs/node/pull/50140
463+
description: The property `context.importAssertions` is replaced with
464+
`context.importAttributes`. Using the old name is still
465+
supported and will emit an experimental warning.
461466
- version:
462467
- v18.6.0
463468
- v16.17.0
@@ -477,8 +482,8 @@ changes:
477482
* `specifier` {string}
478483
* `context` {Object}
479484
* `conditions` {string\[]} Export conditions of the relevant `package.json`
480-
* `importAssertions` {Object} An object whose key-value pairs represent the
481-
assertions for the module to import
485+
* `importAttributes` {Object} An object whose key-value pairs represent the
486+
attributes for the module to import
482487
* `parentURL` {string|undefined} The module importing this one, or undefined
483488
if this is the Node.js entry point
484489
* `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the
@@ -489,7 +494,7 @@ changes:
489494
* `format` {string|null|undefined} A hint to the load hook (it might be
490495
ignored)
491496
`'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'`
492-
* `importAssertions` {Object|undefined} The import assertions to use when
497+
* `importAttributes` {Object|undefined} The import attributes to use when
493498
caching the module (optional; if excluded the input will be used)
494499
* `shortCircuit` {undefined|boolean} A signal that this hook intends to
495500
terminate the chain of `resolve` hooks. **Default:** `false`
@@ -506,10 +511,10 @@ the final `format` value (and it is free to ignore the hint provided by
506511
`resolve`); if `resolve` provides a `format`, a custom `load` hook is required
507512
even if only to pass the value to the Node.js default `load` hook.
508513
509-
Import type assertions are part of the cache key for saving loaded modules into
514+
Import type attributes are part of the cache key for saving loaded modules into
510515
the internal module cache. The `resolve` hook is responsible for returning an
511-
`importAssertions` object if the module should be cached with different
512-
assertions than were present in the source code.
516+
`importAttributes` object if the module should be cached with different
517+
attributes than were present in the source code.
513518
514519
The `conditions` property in `context` is an array of conditions for
515520
[package exports conditions][Conditional exports] that apply to this resolution
@@ -575,7 +580,7 @@ changes:
575580
* `conditions` {string\[]} Export conditions of the relevant `package.json`
576581
* `format` {string|null|undefined} The format optionally supplied by the
577582
`resolve` hook chain
578-
* `importAssertions` {Object}
583+
* `importAttributes` {Object}
579584
* `nextLoad` {Function} The subsequent `load` hook in the chain, or the
580585
Node.js default `load` hook after the last user-supplied `load` hook
581586
* `specifier` {string}

lib/internal/errors.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1178,12 +1178,17 @@ E('ERR_HTTP_SOCKET_ENCODING',
11781178
E('ERR_HTTP_TRAILER_INVALID',
11791179
'Trailers are invalid with this transfer encoding', Error);
11801180
E('ERR_ILLEGAL_CONSTRUCTOR', 'Illegal constructor', TypeError);
1181+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
11811182
E('ERR_IMPORT_ASSERTION_TYPE_FAILED',
11821183
'Module "%s" is not of type "%s"', TypeError);
1184+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
11831185
E('ERR_IMPORT_ASSERTION_TYPE_MISSING',
1184-
'Module "%s" needs an import assertion of type "%s"', TypeError);
1186+
'Module "%s" needs an import attribute of type "%s"', TypeError);
1187+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
11851188
E('ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
1186-
'Import assertion type "%s" is unsupported', TypeError);
1189+
'Import attribute type "%s" is unsupported', TypeError);
1190+
E('ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
1191+
'Import attribute "%s" with value "%s" is not supported', TypeError);
11871192
E('ERR_INCOMPATIBLE_OPTION_PAIR',
11881193
'Option "%s" cannot be used in combination with option "%s"', TypeError);
11891194
E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' +

lib/internal/modules/cjs/loader.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1295,10 +1295,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12951295
const script = new Script(wrapper, {
12961296
filename,
12971297
lineOffset: 0,
1298-
importModuleDynamically: async (specifier, _, importAssertions) => {
1298+
importModuleDynamically: async (specifier, _, importAttributes) => {
12991299
const cascadedLoader = getCascadedLoader();
13001300
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
1301-
importAssertions);
1301+
importAttributes);
13021302
},
13031303
});
13041304

@@ -1322,10 +1322,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
13221322
], {
13231323
filename,
13241324
cachedData: codeCache,
1325-
importModuleDynamically(specifier, _, importAssertions) {
1325+
importModuleDynamically(specifier, _, importAttributes) {
13261326
const cascadedLoader = getCascadedLoader();
13271327
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
1328-
importAssertions);
1328+
importAttributes);
13291329
},
13301330
});
13311331

lib/internal/modules/esm/assert.js

+24-29
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@ const {
1313
ERR_IMPORT_ASSERTION_TYPE_FAILED,
1414
ERR_IMPORT_ASSERTION_TYPE_MISSING,
1515
ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED,
16+
ERR_IMPORT_ATTRIBUTE_UNSUPPORTED,
1617
} = require('internal/errors').codes;
1718

1819
// The HTML spec has an implied default type of `'javascript'`.
1920
const kImplicitAssertType = 'javascript';
2021

21-
let alreadyWarned = false;
22-
2322
/**
24-
* Define a map of module formats to import assertion types (the value of
25-
* `type` in `assert { type: 'json' }`).
23+
* Define a map of module formats to import attributes types (the value of
24+
* `type` in `with { type: 'json' }`).
2625
* @type {Map<string, string>}
2726
*/
2827
const formatTypeMap = {
@@ -31,13 +30,13 @@ const formatTypeMap = {
3130
'commonjs': kImplicitAssertType,
3231
'json': 'json',
3332
'module': kImplicitAssertType,
34-
'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
33+
'wasm': kImplicitAssertType, // It's unclear whether the HTML spec will require an attribute type or not for Wasm; see https://github.com/WebAssembly/esm-integration/issues/42
3534
};
3635

3736
/**
3837
* The HTML spec disallows the default type to be explicitly specified
3938
* (for now); so `import './file.js'` is okay but
40-
* `import './file.js' assert { type: 'javascript' }` throws.
39+
* `import './file.js' with { type: 'javascript' }` throws.
4140
* @type {Array<string, string>}
4241
*/
4342
const supportedAssertionTypes = ArrayPrototypeFilter(
@@ -46,54 +45,50 @@ const supportedAssertionTypes = ArrayPrototypeFilter(
4645

4746

4847
/**
49-
* Test a module's import assertions.
48+
* Test a module's import attributes.
5049
* @param {string} url The URL of the imported module, for error reporting.
5150
* @param {string} format One of Node's supported translators
52-
* @param {Record<string, string>} importAssertions Validations for the
51+
* @param {Record<string, string>} importAttributes Validations for the
5352
* module import.
5453
* @returns {true}
5554
* @throws {TypeError} If the format and assertion type are incompatible.
5655
*/
57-
function validateAssertions(url, format,
58-
importAssertions = { __proto__: null }) {
59-
const validType = formatTypeMap[format];
60-
61-
if (!alreadyWarned && ObjectKeys(importAssertions).length !== 0) {
62-
alreadyWarned = true;
63-
process.emitWarning(
64-
'Import assertions are not a stable feature of the JavaScript language. ' +
65-
'Avoid relying on their current behavior and syntax as those might change ' +
66-
'in a future version of Node.js.',
67-
'ExperimentalWarning',
68-
);
56+
function validateAttributes(url, format,
57+
importAttributes = { __proto__: null }) {
58+
const keys = ObjectKeys(importAttributes);
59+
for (let i = 0; i < keys.length; i++) {
60+
if (keys[i] !== 'type') {
61+
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]]);
62+
}
6963
}
64+
const validType = formatTypeMap[format];
7065

7166
switch (validType) {
7267
case undefined:
73-
// Ignore assertions for module formats we don't recognize, to allow new
68+
// Ignore attributes for module formats we don't recognize, to allow new
7469
// formats in the future.
7570
return true;
7671

7772
case kImplicitAssertType:
7873
// This format doesn't allow an import assertion type, so the property
79-
// must not be set on the import assertions object.
80-
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
74+
// must not be set on the import attributes object.
75+
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
8176
return true;
8277
}
83-
return handleInvalidType(url, importAssertions.type);
78+
return handleInvalidType(url, importAttributes.type);
8479

85-
case importAssertions.type:
80+
case importAttributes.type:
8681
// The asserted type is the valid type for this format.
8782
return true;
8883

8984
default:
9085
// There is an expected type for this format, but the value of
91-
// `importAssertions.type` might not have been it.
92-
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
86+
// `importAttributes.type` might not have been it.
87+
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
9388
// `type` wasn't specified at all.
9489
throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType);
9590
}
96-
return handleInvalidType(url, importAssertions.type);
91+
return handleInvalidType(url, importAttributes.type);
9792
}
9893
}
9994

@@ -118,5 +113,5 @@ function handleInvalidType(url, type) {
118113

119114
module.exports = {
120115
kImplicitAssertType,
121-
validateAssertions,
116+
validateAttributes,
122117
};

0 commit comments

Comments
 (0)