Skip to content

Commit d1ef6aa

Browse files
authored
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: #50140 Fixes: #50134 Refs: v8/v8@159c82c Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent f447a46 commit d1ef6aa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+472
-353
lines changed

.eslintignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ tools/github_reporter
99
benchmark/tmp
1010
benchmark/fixtures
1111
doc/**/*.js
12+
doc/changelogs/CHANGELOG_v1*.md
13+
!doc/changelogs/CHANGELOG_v18.md
1214
!doc/api_assets/*.js
1315
!.eslintrc.js

.eslintrc.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const hacks = [
1818
'eslint-plugin-jsdoc',
1919
'eslint-plugin-markdown',
2020
'@babel/eslint-parser',
21-
'@babel/plugin-syntax-import-assertions',
21+
'@babel/plugin-syntax-import-attributes',
2222
];
2323
Module._findPath = (request, paths, isMain) => {
2424
const r = ModuleFindPath(request, paths, isMain);
@@ -44,7 +44,7 @@ module.exports = {
4444
parserOptions: {
4545
babelOptions: {
4646
plugins: [
47-
Module._findPath('@babel/plugin-syntax-import-assertions'),
47+
Module._findPath('@babel/plugin-syntax-import-attributes'),
4848
],
4949
},
5050
requireConfigFile: false,

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

+26-13
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
@@ -203,7 +206,7 @@ added: v12.10.0
203206

204207
```js
205208
import 'data:text/javascript,console.log("hello!");';
206-
import _ from 'data:application/json,"world!"' assert { type: 'json' };
209+
import _ from 'data:application/json,"world!"' with { type: 'json' };
207210
```
208211

209212
`data:` URLs only resolve [bare specifiers][Terminology] for builtin modules
@@ -235,30 +238,40 @@ absolute URL strings.
235238
import fs from 'node:fs/promises';
236239
```
237240

238-
## Import assertions
241+
<a id="import-assertions"></a>
242+
243+
## Import attributes
239244

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

246-
> Stability: 1 - Experimental
255+
> Stability: 1.1 - Active development
256+
257+
> This feature was previously named "Import assertions", and using the `assert`
258+
> keyword instead of `with`. Any uses in code of the prior `assert` keyword
259+
> should be updated to use `with` instead.
247260
248-
The [Import Assertions proposal][] adds an inline syntax for module import
261+
The [Import Attributes proposal][] adds an inline syntax for module import
249262
statements to pass on more information alongside the module specifier.
250263

251264
```js
252-
import fooData from './foo.json' assert { type: 'json' };
265+
import fooData from './foo.json' with { type: 'json' };
253266

254267
const { default: barData } =
255-
await import('./bar.json', { assert: { type: 'json' } });
268+
await import('./bar.json', { with: { type: 'json' } });
256269
```
257270

258-
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
259272
mandatory:
260273

261-
| Assertion `type` | Needed for |
274+
| Attribute `type` | Needed for |
262275
| ---------------- | ---------------- |
263276
| `'json'` | [JSON modules][] |
264277

@@ -545,10 +558,10 @@ separate cache.
545558
JSON files can be referenced by `import`:
546559

547560
```js
548-
import packageConfig from './package.json' assert { type: 'json' };
561+
import packageConfig from './package.json' with { type: 'json' };
549562
```
550563
551-
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
564+
The `with { type: 'json' }` syntax is mandatory; see [Import Attributes][].
552565
553566
The imported JSON only exposes a `default` export. There is no support for named
554567
exports. A cache entry is created in the CommonJS cache to avoid duplication.
@@ -1055,8 +1068,8 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
10551068
[Determining module system]: packages.md#determining-module-system
10561069
[Dynamic `import()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
10571070
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
1058-
[Import Assertions]: #import-assertions
1059-
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
1071+
[Import Attributes]: #import-attributes
1072+
[Import Attributes proposal]: https://github.com/tc39/proposal-import-attributes
10601073
[JSON modules]: #json-modules
10611074
[Module customization hooks]: module.md#customization-hooks
10621075
[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
@@ -1280,12 +1280,17 @@ E('ERR_HTTP_SOCKET_ENCODING',
12801280
E('ERR_HTTP_TRAILER_INVALID',
12811281
'Trailers are invalid with this transfer encoding', Error);
12821282
E('ERR_ILLEGAL_CONSTRUCTOR', 'Illegal constructor', TypeError);
1283+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
12831284
E('ERR_IMPORT_ASSERTION_TYPE_FAILED',
12841285
'Module "%s" is not of type "%s"', TypeError);
1286+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
12851287
E('ERR_IMPORT_ASSERTION_TYPE_MISSING',
1286-
'Module "%s" needs an import assertion of type "%s"', TypeError);
1288+
'Module "%s" needs an import attribute of type "%s"', TypeError);
1289+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
12871290
E('ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
1288-
'Import assertion type "%s" is unsupported', TypeError);
1291+
'Import attribute type "%s" is unsupported', TypeError);
1292+
E('ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
1293+
'Import attribute "%s" with value "%s" is not supported', TypeError);
12891294
E('ERR_INCOMPATIBLE_OPTION_PAIR',
12901295
'Option "%s" cannot be used in combination with option "%s"', TypeError);
12911296
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
@@ -1258,10 +1258,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12581258
const script = new Script(wrapper, {
12591259
filename,
12601260
lineOffset: 0,
1261-
importModuleDynamically: async (specifier, _, importAssertions) => {
1261+
importModuleDynamically: async (specifier, _, importAttributes) => {
12621262
const cascadedLoader = getCascadedLoader();
12631263
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
1264-
importAssertions);
1264+
importAttributes);
12651265
},
12661266
});
12671267

@@ -1285,10 +1285,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12851285
], {
12861286
filename,
12871287
cachedData: codeCache,
1288-
importModuleDynamically(specifier, _, importAssertions) {
1288+
importModuleDynamically(specifier, _, importAttributes) {
12891289
const cascadedLoader = getCascadedLoader();
12901290
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
1291-
importAssertions);
1291+
importAttributes);
12921292
},
12931293
});
12941294

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)