Skip to content

Commit 5699370

Browse files
aduh95targos
authored andcommitted
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 83f680f commit 5699370

Some content is hidden

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

54 files changed

+401
-291
lines changed

.eslintignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ tools/lint-md/lint-md.mjs
88
benchmark/tmp
99
benchmark/fixtures
1010
doc/**/*.js
11+
doc/changelogs/CHANGELOG_v1*.md
12+
!doc/changelogs/CHANGELOG_v18.md
1113
!doc/api_assets/*.js
1214
!.eslintrc.js

.eslintrc.js

+5-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,10 @@ module.exports = {
4444
parserOptions: {
4545
babelOptions: {
4646
plugins: [
47-
Module._findPath('@babel/plugin-syntax-import-assertions'),
47+
[
48+
Module._findPath('@babel/plugin-syntax-import-attributes'),
49+
{ deprecatedAssertSyntax: true },
50+
],
4851
],
4952
},
5053
requireConfigFile: false,

doc/api/errors.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -1762,7 +1762,8 @@ added:
17621762
- v16.14.0
17631763
-->
17641764

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

17671768
<a id="ERR_IMPORT_ASSERTION_TYPE_MISSING"></a>
17681769

@@ -1774,7 +1775,7 @@ added:
17741775
- v16.14.0
17751776
-->
17761777

1777-
An import assertion is missing, preventing the specified module to be imported.
1778+
An import attribute is missing, preventing the specified module to be imported.
17781779

17791780
<a id="ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED"></a>
17801781

@@ -1786,7 +1787,17 @@ added:
17861787
- v16.14.0
17871788
-->
17881789

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

17911802
<a id="ERR_INCOMPATIBLE_OPTION_PAIR"></a>
17921803

doc/api/esm.md

+20-9
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ changes:
1818
- v17.1.0
1919
- v16.14.0
2020
pr-url: https://github.com/nodejs/node/pull/40250
21-
description: Add support for import assertions.
21+
description: Add experimental support for import assertions.
2222
- version:
2323
- v17.0.0
2424
- v16.12.0
@@ -234,17 +234,28 @@ absolute URL strings.
234234
import fs from 'node:fs/promises';
235235
```
236236

237-
## Import assertions
237+
<a id="import-assertions"></a>
238+
239+
## Import attributes
238240

239241
<!-- YAML
240242
added:
241243
- v17.1.0
242244
- v16.14.0
245+
changes:
246+
- version: REPLACEME
247+
pr-url: https://github.com/nodejs/node/pull/50140
248+
description: Switch from Import Assertions to Import Attributes.
243249
-->
244250

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

250261
```js
@@ -254,10 +265,10 @@ const { default: barData } =
254265
await import('./bar.json', { assert: { type: 'json' } });
255266
```
256267

257-
Node.js supports the following `type` values, for which the assertion is
268+
Node.js supports the following `type` values, for which the attribute is
258269
mandatory:
259270

260-
| Assertion `type` | Needed for |
271+
| Attribute `type` | Needed for |
261272
| ---------------- | ---------------- |
262273
| `'json'` | [JSON modules][] |
263274

@@ -547,7 +558,7 @@ JSON files can be referenced by `import`:
547558
import packageConfig from './package.json' assert { type: 'json' };
548559
```
549560
550-
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
561+
The `assert { type: 'json' }` syntax is mandatory; see [Import Attributes][].
551562
552563
The imported JSON only exposes a `default` export. There is no support for named
553564
exports. A cache entry is created in the CommonJS cache to avoid duplication.
@@ -1076,8 +1087,8 @@ success!
10761087
[Determining module system]: packages.md#determining-module-system
10771088
[Dynamic `import()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
10781089
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
1079-
[Import Assertions]: #import-assertions
1080-
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
1090+
[Import Attributes]: #import-attributes
1091+
[Import Attributes proposal]: https://github.com/tc39/proposal-import-attributes
10811092
[JSON modules]: #json-modules
10821093
[Module customization hooks]: module.md#customization-hooks
10831094
[Node.js Module Resolution And Loading Algorithm]: #resolution-algorithm-specification

doc/api/module.md

+12-7
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,11 @@ register('./path-to-my-hooks.js', {
456456
457457
<!-- YAML
458458
changes:
459+
- version: REPLACEME
460+
pr-url: https://github.com/nodejs/node/pull/50140
461+
description: The property `context.importAssertions` is replaced with
462+
`context.importAttributes`. Using the old name is still
463+
supported and will emit an experimental warning.
459464
- version:
460465
- v18.6.0
461466
- v16.17.0
@@ -475,8 +480,8 @@ changes:
475480
* `specifier` {string}
476481
* `context` {Object}
477482
* `conditions` {string\[]} Export conditions of the relevant `package.json`
478-
* `importAssertions` {Object} An object whose key-value pairs represent the
479-
assertions for the module to import
483+
* `importAttributes` {Object} An object whose key-value pairs represent the
484+
attributes for the module to import
480485
* `parentURL` {string|undefined} The module importing this one, or undefined
481486
if this is the Node.js entry point
482487
* `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the
@@ -487,7 +492,7 @@ changes:
487492
* `format` {string|null|undefined} A hint to the load hook (it might be
488493
ignored)
489494
`'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'`
490-
* `importAssertions` {Object|undefined} The import assertions to use when
495+
* `importAttributes` {Object|undefined} The import attributes to use when
491496
caching the module (optional; if excluded the input will be used)
492497
* `shortCircuit` {undefined|boolean} A signal that this hook intends to
493498
terminate the chain of `resolve` hooks. **Default:** `false`
@@ -504,10 +509,10 @@ the final `format` value (and it is free to ignore the hint provided by
504509
`resolve`); if `resolve` provides a `format`, a custom `load` hook is required
505510
even if only to pass the value to the Node.js default `load` hook.
506511
507-
Import type assertions are part of the cache key for saving loaded modules into
512+
Import type attributes are part of the cache key for saving loaded modules into
508513
the internal module cache. The `resolve` hook is responsible for returning an
509-
`importAssertions` object if the module should be cached with different
510-
assertions than were present in the source code.
514+
`importAttributes` object if the module should be cached with different
515+
attributes than were present in the source code.
511516
512517
The `conditions` property in `context` is an array of conditions for
513518
[package exports conditions][Conditional exports] that apply to this resolution
@@ -570,7 +575,7 @@ changes:
570575
* `conditions` {string\[]} Export conditions of the relevant `package.json`
571576
* `format` {string|null|undefined} The format optionally supplied by the
572577
`resolve` hook chain
573-
* `importAssertions` {Object}
578+
* `importAttributes` {Object}
574579
* `nextLoad` {Function} The subsequent `load` hook in the chain, or the
575580
Node.js default `load` hook after the last user-supplied `load` hook
576581
* `specifier` {string}

lib/internal/errors.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1177,12 +1177,17 @@ E('ERR_HTTP_SOCKET_ENCODING',
11771177
E('ERR_HTTP_TRAILER_INVALID',
11781178
'Trailers are invalid with this transfer encoding', Error);
11791179
E('ERR_ILLEGAL_CONSTRUCTOR', 'Illegal constructor', TypeError);
1180+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
11801181
E('ERR_IMPORT_ASSERTION_TYPE_FAILED',
11811182
'Module "%s" is not of type "%s"', TypeError);
1183+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
11821184
E('ERR_IMPORT_ASSERTION_TYPE_MISSING',
1183-
'Module "%s" needs an import assertion of type "%s"', TypeError);
1185+
'Module "%s" needs an import attribute of type "%s"', TypeError);
1186+
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
11841187
E('ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
1185-
'Import assertion type "%s" is unsupported', TypeError);
1188+
'Import attribute type "%s" is unsupported', TypeError);
1189+
E('ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
1190+
'Import attribute "%s" with value "%s" is not supported', TypeError);
11861191
E('ERR_INCOMPATIBLE_OPTION_PAIR',
11871192
'Option "%s" cannot be used in combination with option "%s"', TypeError);
11881193
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
@@ -1253,10 +1253,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
12531253
const script = new Script(wrapper, {
12541254
filename,
12551255
lineOffset: 0,
1256-
importModuleDynamically: async (specifier, _, importAssertions) => {
1256+
importModuleDynamically: async (specifier, _, importAttributes) => {
12571257
const cascadedLoader = getCascadedLoader();
12581258
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
1259-
importAssertions);
1259+
importAttributes);
12601260
},
12611261
});
12621262

@@ -1279,10 +1279,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
12791279
'__dirname',
12801280
], {
12811281
filename,
1282-
importModuleDynamically(specifier, _, importAssertions) {
1282+
importModuleDynamically(specifier, _, importAttributes) {
12831283
const cascadedLoader = getCascadedLoader();
12841284
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
1285-
importAssertions);
1285+
importAttributes);
12861286
},
12871287
});
12881288

lib/internal/modules/esm/assert.js

+24-30
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const {
44
ArrayPrototypeFilter,
55
ArrayPrototypeIncludes,
6-
ObjectCreate,
76
ObjectKeys,
87
ObjectValues,
98
ObjectPrototypeHasOwnProperty,
@@ -14,16 +13,15 @@ const {
1413
ERR_IMPORT_ASSERTION_TYPE_FAILED,
1514
ERR_IMPORT_ASSERTION_TYPE_MISSING,
1615
ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED,
16+
ERR_IMPORT_ATTRIBUTE_UNSUPPORTED,
1717
} = require('internal/errors').codes;
1818

1919
// The HTML spec has an implied default type of `'javascript'`.
2020
const kImplicitAssertType = 'javascript';
2121

22-
let alreadyWarned = false;
23-
2422
/**
25-
* Define a map of module formats to import assertion types (the value of
26-
* `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' }`).
2725
* @type {Map<string, string>}
2826
*/
2927
const formatTypeMap = {
@@ -32,13 +30,13 @@ const formatTypeMap = {
3230
'commonjs': kImplicitAssertType,
3331
'json': 'json',
3432
'module': kImplicitAssertType,
35-
'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
3634
};
3735

3836
/**
3937
* The HTML spec disallows the default type to be explicitly specified
4038
* (for now); so `import './file.js'` is okay but
41-
* `import './file.js' assert { type: 'javascript' }` throws.
39+
* `import './file.js' with { type: 'javascript' }` throws.
4240
* @type {Array<string, string>}
4341
*/
4442
const supportedAssertionTypes = ArrayPrototypeFilter(
@@ -47,54 +45,50 @@ const supportedAssertionTypes = ArrayPrototypeFilter(
4745

4846

4947
/**
50-
* Test a module's import assertions.
48+
* Test a module's import attributes.
5149
* @param {string} url The URL of the imported module, for error reporting.
5250
* @param {string} format One of Node's supported translators
53-
* @param {Record<string, string>} importAssertions Validations for the
51+
* @param {Record<string, string>} importAttributes Validations for the
5452
* module import.
5553
* @returns {true}
5654
* @throws {TypeError} If the format and assertion type are incompatible.
5755
*/
58-
function validateAssertions(url, format,
59-
importAssertions = ObjectCreate(null)) {
60-
const validType = formatTypeMap[format];
61-
62-
if (!alreadyWarned && ObjectKeys(importAssertions).length !== 0) {
63-
alreadyWarned = true;
64-
process.emitWarning(
65-
'Import assertions are not a stable feature of the JavaScript language. ' +
66-
'Avoid relying on their current behavior and syntax as those might change ' +
67-
'in a future version of Node.js.',
68-
'ExperimentalWarning',
69-
);
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+
}
7063
}
64+
const validType = formatTypeMap[format];
7165

7266
switch (validType) {
7367
case undefined:
74-
// 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
7569
// formats in the future.
7670
return true;
7771

7872
case kImplicitAssertType:
7973
// This format doesn't allow an import assertion type, so the property
80-
// must not be set on the import assertions object.
81-
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
74+
// must not be set on the import attributes object.
75+
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
8276
return true;
8377
}
84-
return handleInvalidType(url, importAssertions.type);
78+
return handleInvalidType(url, importAttributes.type);
8579

86-
case importAssertions.type:
80+
case importAttributes.type:
8781
// The asserted type is the valid type for this format.
8882
return true;
8983

9084
default:
9185
// There is an expected type for this format, but the value of
92-
// `importAssertions.type` might not have been it.
93-
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
86+
// `importAttributes.type` might not have been it.
87+
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
9488
// `type` wasn't specified at all.
9589
throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType);
9690
}
97-
return handleInvalidType(url, importAssertions.type);
91+
return handleInvalidType(url, importAttributes.type);
9892
}
9993
}
10094

@@ -119,5 +113,5 @@ function handleInvalidType(url, type) {
119113

120114
module.exports = {
121115
kImplicitAssertType,
122-
validateAssertions,
116+
validateAttributes,
123117
};

0 commit comments

Comments
 (0)