Skip to content

Commit 2c1745e

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 <webadmin@geoffreybooth.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent a0e60c1 commit 2c1745e

35 files changed

+279
-213
lines changed

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

+24-12
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ changes:
1515
- v17.1.0
1616
- v16.14.0
1717
pr-url: https://github.com/nodejs/node/pull/40250
18-
description: Add support for import assertions.
18+
description: Add experimental support for import assertions.
1919
- version:
2020
- v17.0.0
2121
- v16.12.0
@@ -230,17 +230,24 @@ absolute URL strings.
230230
import fs from 'node:fs/promises';
231231
```
232232

233-
## Import assertions
233+
<a id="import-assertions"></a>
234+
235+
## Import attributes
234236

235237
<!-- YAML
236238
added:
237239
- v17.1.0
238240
- v16.14.0
239241
-->
240242

241-
> Stability: 1 - Experimental
243+
> Stability: 1.1 - Active development
244+
245+
> This feature was previously named "Import assertions", and using the `assert`
246+
> keyword instead of `with`. Because the version of V8 on this release line does
247+
> not support the `with` keyword, you need to keep using `assert` to support
248+
> this version of Node.js.
242249
243-
The [Import Assertions proposal][] adds an inline syntax for module import
250+
The [Import Attributes proposal][] adds an inline syntax for module import
244251
statements to pass on more information alongside the module specifier.
245252

246253
```js
@@ -250,10 +257,10 @@ const { default: barData } =
250257
await import('./bar.json', { assert: { type: 'json' } });
251258
```
252259

253-
Node.js supports the following `type` values, for which the assertion is
260+
Node.js supports the following `type` values, for which the attribute is
254261
mandatory:
255262

256-
| Assertion `type` | Needed for |
263+
| Attribute `type` | Needed for |
257264
| ---------------- | ---------------- |
258265
| `'json'` | [JSON modules][] |
259266

@@ -529,7 +536,7 @@ JSON files can be referenced by `import`:
529536
import packageConfig from './package.json' assert { type: 'json' };
530537
```
531538
532-
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
539+
The `assert { type: 'json' }` syntax is mandatory; see [Import Attributes][].
533540
534541
The imported JSON only exposes a `default` export. There is no support for named
535542
exports. A cache entry is created in the CommonJS cache to avoid duplication.
@@ -732,6 +739,11 @@ prevent unintentional breaks in the chain.
732739
733740
<!-- YAML
734741
changes:
742+
- version: REPLACEME
743+
pr-url: https://github.com/nodejs/node/pull/50140
744+
description: The property `context.importAssertions` is replaced with
745+
`context.importAttributes`. Using the old name is still
746+
supported and will emit an experimental warning.
735747
- version: v18.6.0
736748
pr-url: https://github.com/nodejs/node/pull/42623
737749
description: Add support for chaining resolve hooks. Each hook must either
@@ -750,7 +762,7 @@ changes:
750762
* `specifier` {string}
751763
* `context` {Object}
752764
* `conditions` {string\[]} Export conditions of the relevant `package.json`
753-
* `importAssertions` {Object}
765+
* `importAttributes` {Object}
754766
* `parentURL` {string|undefined} The module importing this one, or undefined
755767
if this is the Node.js entry point
756768
* `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the
@@ -842,7 +854,7 @@ changes:
842854
* `conditions` {string\[]} Export conditions of the relevant `package.json`
843855
* `format` {string|null|undefined} The format optionally supplied by the
844856
`resolve` hook chain
845-
* `importAssertions` {Object}
857+
* `importAttributes` {Object}
846858
* `nextLoad` {Function} The subsequent `load` hook in the chain, or the
847859
Node.js default `load` hook after the last user-supplied `load` hook
848860
* `specifier` {string}
@@ -855,7 +867,7 @@ changes:
855867
856868
The `load` hook provides a way to define a custom method of determining how
857869
a URL should be interpreted, retrieved, and parsed. It is also in charge of
858-
validating the import assertion.
870+
validating the import attributes.
859871
860872
The final value of `format` must be one of the following:
861873
@@ -1578,8 +1590,8 @@ success!
15781590
[Dynamic `import()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
15791591
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
15801592
[HTTPS and HTTP imports]: #https-and-http-imports
1581-
[Import Assertions]: #import-assertions
1582-
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
1593+
[Import Attributes]: #import-attributes
1594+
[Import Attributes proposal]: https://github.com/tc39/proposal-import-attributes
15831595
[JSON modules]: #json-modules
15841596
[Loaders API]: #loaders
15851597
[Node.js Module Resolution And Loading Algorithm]: #resolution-algorithm-specification

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
@@ -1157,10 +1157,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
11571157
const script = new Script(wrapper, {
11581158
filename,
11591159
lineOffset: 0,
1160-
importModuleDynamically: async (specifier, _, importAssertions) => {
1160+
importModuleDynamically: async (specifier, _, importAttributes) => {
11611161
const loader = asyncESM.esmLoader;
11621162
return loader.import(specifier, normalizeReferrerURL(filename),
1163-
importAssertions);
1163+
importAttributes);
11641164
},
11651165
});
11661166

@@ -1183,10 +1183,10 @@ function wrapSafe(filename, content, cjsModuleInstance) {
11831183
'__dirname',
11841184
], {
11851185
filename,
1186-
importModuleDynamically(specifier, _, importAssertions) {
1186+
importModuleDynamically(specifier, _, importAttributes) {
11871187
const loader = asyncESM.esmLoader;
11881188
return loader.import(specifier, normalizeReferrerURL(filename),
1189-
importAssertions);
1189+
importAttributes);
11901190
},
11911191
});
11921192

lib/internal/modules/esm/assert.js

+25-30
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,15 @@ const {
1414
ERR_IMPORT_ASSERTION_TYPE_FAILED,
1515
ERR_IMPORT_ASSERTION_TYPE_MISSING,
1616
ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED,
17+
ERR_IMPORT_ATTRIBUTE_UNSUPPORTED,
1718
} = require('internal/errors').codes;
1819

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

22-
let alreadyWarned = false;
23-
2423
/**
25-
* Define a map of module formats to import assertion types (the value of
26-
* `type` in `assert { type: 'json' }`).
24+
* Define a map of module formats to import attributes types (the value of
25+
* `type` in `with { type: 'json' }`).
2726
* @type {Map<string, string>}
2827
*/
2928
const formatTypeMap = {
@@ -32,7 +31,7 @@ const formatTypeMap = {
3231
'commonjs': kImplicitAssertType,
3332
'json': 'json',
3433
'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
34+
'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
3635
};
3736

3837
/**
@@ -41,60 +40,56 @@ const formatTypeMap = {
4140
* `import './file.js' assert { type: 'javascript' }` throws.
4241
* @type {Array<string, string>}
4342
*/
44-
const supportedAssertionTypes = ArrayPrototypeFilter(
43+
const supportedAttributesTypes = ArrayPrototypeFilter(
4544
ObjectValues(formatTypeMap),
4645
(type) => type !== kImplicitAssertType);
4746

4847

4948
/**
50-
* Test a module's import assertions.
49+
* Test a module's import attributes.
5150
* @param {string} url The URL of the imported module, for error reporting.
5251
* @param {string} format One of Node's supported translators
53-
* @param {Record<string, string>} importAssertions Validations for the
52+
* @param {Record<string, string>} importAttributes Validations for the
5453
* module import.
5554
* @returns {true}
5655
* @throws {TypeError} If the format and assertion type are incompatible.
5756
*/
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-
);
57+
function validateAttributes(url, format,
58+
importAttributes = { __proto__: null }) {
59+
const keys = ObjectKeys(importAttributes);
60+
for (let i = 0; i < keys.length; i++) {
61+
if (keys[i] !== 'type') {
62+
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]]);
63+
}
7064
}
65+
const validType = formatTypeMap[format];
7166

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

7873
case kImplicitAssertType:
7974
// 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')) {
75+
// must not be set on the import attributes object.
76+
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
8277
return true;
8378
}
84-
return handleInvalidType(url, importAssertions.type);
79+
return handleInvalidType(url, importAttributes.type);
8580

86-
case importAssertions.type:
81+
case importAttributes.type:
8782
// The asserted type is the valid type for this format.
8883
return true;
8984

9085
default:
9186
// 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')) {
87+
// `importAttributes.type` might not have been it.
88+
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
9489
// `type` wasn't specified at all.
9590
throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType);
9691
}
97-
return handleInvalidType(url, importAssertions.type);
92+
return handleInvalidType(url, importAttributes.type);
9893
}
9994
}
10095

@@ -108,7 +103,7 @@ function handleInvalidType(url, type) {
108103
validateString(type, 'type');
109104

110105
// `type` might not have been one of the types we understand.
111-
if (!ArrayPrototypeIncludes(supportedAssertionTypes, type)) {
106+
if (!ArrayPrototypeIncludes(supportedAttributesTypes, type)) {
112107
throw new ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED(type);
113108
}
114109

@@ -119,5 +114,5 @@ function handleInvalidType(url, type) {
119114

120115
module.exports = {
121116
kImplicitAssertType,
122-
validateAssertions,
117+
validateAttributes,
123118
};

lib/internal/modules/esm/load.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const {
77
} = primordials;
88

99
const { defaultGetFormat } = require('internal/modules/esm/get_format');
10-
const { validateAssertions } = require('internal/modules/esm/assert');
10+
const { validateAttributes, emitImportAssertionWarning } = require('internal/modules/esm/assert');
1111
const { getOptionValue } = require('internal/options');
1212
const { fetchModule } = require('internal/modules/esm/fetch_module');
1313

@@ -76,19 +76,29 @@ async function getSource(url, context) {
7676
*/
7777
async function defaultLoad(url, context) {
7878
let responseURL = url;
79-
const { importAssertions } = context;
8079
let {
80+
importAttributes,
8181
format,
8282
source,
8383
} = context;
8484

85+
if (importAttributes == null && !('importAttributes' in context) && 'importAssertions' in context) {
86+
emitImportAssertionWarning();
87+
importAttributes = context.importAssertions;
88+
// Alias `importAssertions` to `importAttributes`
89+
context = {
90+
...context,
91+
importAttributes,
92+
};
93+
}
94+
8595
const urlInstance = new URL(url);
8696

8797
throwIfUnsupportedURLScheme(urlInstance, experimentalNetworkImports);
8898

8999
format ??= await defaultGetFormat(urlInstance, context);
90100

91-
validateAssertions(url, format, importAssertions);
101+
validateAttributes(url, format, importAttributes);
92102

93103
if (
94104
format === 'builtin' ||

0 commit comments

Comments
 (0)