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: https://github.com/nodejs/node/pull/50140
Fixes: https://github.com/nodejs/node/issues/50134
Refs: 159c82c5e6
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Antoine du Hamel 2023-10-14 05:52:38 +02:00 committed by GitHub
parent f447a4611a
commit d1ef6aa2db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 470 additions and 351 deletions

View File

@ -9,5 +9,7 @@ tools/github_reporter
benchmark/tmp
benchmark/fixtures
doc/**/*.js
doc/changelogs/CHANGELOG_v1*.md
!doc/changelogs/CHANGELOG_v18.md
!doc/api_assets/*.js
!.eslintrc.js

View File

@ -18,7 +18,7 @@ const hacks = [
'eslint-plugin-jsdoc',
'eslint-plugin-markdown',
'@babel/eslint-parser',
'@babel/plugin-syntax-import-assertions',
'@babel/plugin-syntax-import-attributes',
];
Module._findPath = (request, paths, isMain) => {
const r = ModuleFindPath(request, paths, isMain);
@ -44,7 +44,7 @@ module.exports = {
parserOptions: {
babelOptions: {
plugins: [
Module._findPath('@babel/plugin-syntax-import-assertions'),
Module._findPath('@babel/plugin-syntax-import-attributes'),
],
},
requireConfigFile: false,

View File

@ -1759,7 +1759,8 @@ added:
- v16.14.0
-->
An import assertion has failed, preventing the specified module to be imported.
An import `type` attribute was provided, but the specified module is of a
different type.
<a id="ERR_IMPORT_ASSERTION_TYPE_MISSING"></a>
@ -1771,7 +1772,7 @@ added:
- v16.14.0
-->
An import assertion is missing, preventing the specified module to be imported.
An import attribute is missing, preventing the specified module to be imported.
<a id="ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED"></a>
@ -1783,7 +1784,17 @@ added:
- v16.14.0
-->
An import assertion is not supported by this version of Node.js.
An import attribute is not supported by this version of Node.js.
<a id="ERR_IMPORT_ATTRIBUTE_UNSUPPORTED"></a>
### `ERR_IMPORT_ATTRIBUTE_UNSUPPORTED`
<!-- YAML
added: REPLACEME
-->
An import attribute is not supported by this version of Node.js.
<a id="ERR_INCOMPATIBLE_OPTION_PAIR"></a>

View File

@ -7,6 +7,9 @@
<!-- YAML
added: v8.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50140
description: Add experimental support for import attributes.
- version: v20.0.0
pr-url: https://github.com/nodejs/node/pull/44710
description: Module customization hooks are executed off the main thread.
@ -19,7 +22,7 @@ changes:
- v17.1.0
- v16.14.0
pr-url: https://github.com/nodejs/node/pull/40250
description: Add support for import assertions.
description: Add experimental support for import assertions.
- version:
- v17.0.0
- v16.12.0
@ -203,7 +206,7 @@ added: v12.10.0
```js
import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' assert { type: 'json' };
import _ from 'data:application/json,"world!"' with { type: 'json' };
```
`data:` URLs only resolve [bare specifiers][Terminology] for builtin modules
@ -235,30 +238,40 @@ absolute URL strings.
import fs from 'node:fs/promises';
```
## Import assertions
<a id="import-assertions"></a>
## Import attributes
<!-- YAML
added:
- v17.1.0
- v16.14.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50140
description: Switch from Import Assertions to Import Attributes.
-->
> Stability: 1 - Experimental
> Stability: 1.1 - Active development
The [Import Assertions proposal][] adds an inline syntax for module import
> This feature was previously named "Import assertions", and using the `assert`
> keyword instead of `with`. Any uses in code of the prior `assert` keyword
> should be updated to use `with` instead.
The [Import Attributes proposal][] adds an inline syntax for module import
statements to pass on more information alongside the module specifier.
```js
import fooData from './foo.json' assert { type: 'json' };
import fooData from './foo.json' with { type: 'json' };
const { default: barData } =
await import('./bar.json', { assert: { type: 'json' } });
await import('./bar.json', { with: { type: 'json' } });
```
Node.js supports the following `type` values, for which the assertion is
Node.js supports the following `type` values, for which the attribute is
mandatory:
| Assertion `type` | Needed for |
| Attribute `type` | Needed for |
| ---------------- | ---------------- |
| `'json'` | [JSON modules][] |
@ -545,10 +558,10 @@ separate cache.
JSON files can be referenced by `import`:
```js
import packageConfig from './package.json' assert { type: 'json' };
import packageConfig from './package.json' with { type: 'json' };
```
The `assert { type: 'json' }` syntax is mandatory; see [Import Assertions][].
The `with { type: 'json' }` syntax is mandatory; see [Import Attributes][].
The imported JSON only exposes a `default` export. There is no support for named
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][].
[Determining module system]: packages.md#determining-module-system
[Dynamic `import()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
[ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration
[Import Assertions]: #import-assertions
[Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions
[Import Attributes]: #import-attributes
[Import Attributes proposal]: https://github.com/tc39/proposal-import-attributes
[JSON modules]: #json-modules
[Module customization hooks]: module.md#customization-hooks
[Node.js Module Resolution And Loading Algorithm]: #resolution-algorithm-specification

View File

@ -458,6 +458,11 @@ register('./path-to-my-hooks.js', {
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/50140
description: The property `context.importAssertions` is replaced with
`context.importAttributes`. Using the old name is still
supported and will emit an experimental warning.
- version:
- v18.6.0
- v16.17.0
@ -477,8 +482,8 @@ changes:
* `specifier` {string}
* `context` {Object}
* `conditions` {string\[]} Export conditions of the relevant `package.json`
* `importAssertions` {Object} An object whose key-value pairs represent the
assertions for the module to import
* `importAttributes` {Object} An object whose key-value pairs represent the
attributes for the module to import
* `parentURL` {string|undefined} The module importing this one, or undefined
if this is the Node.js entry point
* `nextResolve` {Function} The subsequent `resolve` hook in the chain, or the
@ -489,7 +494,7 @@ changes:
* `format` {string|null|undefined} A hint to the load hook (it might be
ignored)
`'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'`
* `importAssertions` {Object|undefined} The import assertions to use when
* `importAttributes` {Object|undefined} The import attributes to use when
caching the module (optional; if excluded the input will be used)
* `shortCircuit` {undefined|boolean} A signal that this hook intends to
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
`resolve`); if `resolve` provides a `format`, a custom `load` hook is required
even if only to pass the value to the Node.js default `load` hook.
Import type assertions are part of the cache key for saving loaded modules into
Import type attributes are part of the cache key for saving loaded modules into
the internal module cache. The `resolve` hook is responsible for returning an
`importAssertions` object if the module should be cached with different
assertions than were present in the source code.
`importAttributes` object if the module should be cached with different
attributes than were present in the source code.
The `conditions` property in `context` is an array of conditions for
[package exports conditions][Conditional exports] that apply to this resolution
@ -575,7 +580,7 @@ changes:
* `conditions` {string\[]} Export conditions of the relevant `package.json`
* `format` {string|null|undefined} The format optionally supplied by the
`resolve` hook chain
* `importAssertions` {Object}
* `importAttributes` {Object}
* `nextLoad` {Function} The subsequent `load` hook in the chain, or the
Node.js default `load` hook after the last user-supplied `load` hook
* `specifier` {string}

View File

@ -1280,12 +1280,17 @@ E('ERR_HTTP_SOCKET_ENCODING',
E('ERR_HTTP_TRAILER_INVALID',
'Trailers are invalid with this transfer encoding', Error);
E('ERR_ILLEGAL_CONSTRUCTOR', 'Illegal constructor', TypeError);
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
E('ERR_IMPORT_ASSERTION_TYPE_FAILED',
'Module "%s" is not of type "%s"', TypeError);
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
E('ERR_IMPORT_ASSERTION_TYPE_MISSING',
'Module "%s" needs an import assertion of type "%s"', TypeError);
'Module "%s" needs an import attribute of type "%s"', TypeError);
// TODO(aduh95): change the error to mention import attributes instead of import assertions.
E('ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
'Import assertion type "%s" is unsupported', TypeError);
'Import attribute type "%s" is unsupported', TypeError);
E('ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
'Import attribute "%s" with value "%s" is not supported', TypeError);
E('ERR_INCOMPATIBLE_OPTION_PAIR',
'Option "%s" cannot be used in combination with option "%s"', TypeError);
E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' +

View File

@ -1258,10 +1258,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
const script = new Script(wrapper, {
filename,
lineOffset: 0,
importModuleDynamically: async (specifier, _, importAssertions) => {
importModuleDynamically: async (specifier, _, importAttributes) => {
const cascadedLoader = getCascadedLoader();
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
importAssertions);
importAttributes);
},
});
@ -1285,10 +1285,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
], {
filename,
cachedData: codeCache,
importModuleDynamically(specifier, _, importAssertions) {
importModuleDynamically(specifier, _, importAttributes) {
const cascadedLoader = getCascadedLoader();
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
importAssertions);
importAttributes);
},
});

View File

@ -13,16 +13,15 @@ const {
ERR_IMPORT_ASSERTION_TYPE_FAILED,
ERR_IMPORT_ASSERTION_TYPE_MISSING,
ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED,
ERR_IMPORT_ATTRIBUTE_UNSUPPORTED,
} = require('internal/errors').codes;
// The HTML spec has an implied default type of `'javascript'`.
const kImplicitAssertType = 'javascript';
let alreadyWarned = false;
/**
* Define a map of module formats to import assertion types (the value of
* `type` in `assert { type: 'json' }`).
* Define a map of module formats to import attributes types (the value of
* `type` in `with { type: 'json' }`).
* @type {Map<string, string>}
*/
const formatTypeMap = {
@ -31,13 +30,13 @@ const formatTypeMap = {
'commonjs': kImplicitAssertType,
'json': 'json',
'module': kImplicitAssertType,
'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
'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
};
/**
* The HTML spec disallows the default type to be explicitly specified
* (for now); so `import './file.js'` is okay but
* `import './file.js' assert { type: 'javascript' }` throws.
* `import './file.js' with { type: 'javascript' }` throws.
* @type {Array<string, string>}
*/
const supportedAssertionTypes = ArrayPrototypeFilter(
@ -46,54 +45,50 @@ const supportedAssertionTypes = ArrayPrototypeFilter(
/**
* Test a module's import assertions.
* Test a module's import attributes.
* @param {string} url The URL of the imported module, for error reporting.
* @param {string} format One of Node's supported translators
* @param {Record<string, string>} importAssertions Validations for the
* @param {Record<string, string>} importAttributes Validations for the
* module import.
* @returns {true}
* @throws {TypeError} If the format and assertion type are incompatible.
*/
function validateAssertions(url, format,
importAssertions = { __proto__: null }) {
const validType = formatTypeMap[format];
if (!alreadyWarned && ObjectKeys(importAssertions).length !== 0) {
alreadyWarned = true;
process.emitWarning(
'Import assertions are not a stable feature of the JavaScript language. ' +
'Avoid relying on their current behavior and syntax as those might change ' +
'in a future version of Node.js.',
'ExperimentalWarning',
);
function validateAttributes(url, format,
importAttributes = { __proto__: null }) {
const keys = ObjectKeys(importAttributes);
for (let i = 0; i < keys.length; i++) {
if (keys[i] !== 'type') {
throw new ERR_IMPORT_ATTRIBUTE_UNSUPPORTED(keys[i], importAttributes[keys[i]]);
}
}
const validType = formatTypeMap[format];
switch (validType) {
case undefined:
// Ignore assertions for module formats we don't recognize, to allow new
// Ignore attributes for module formats we don't recognize, to allow new
// formats in the future.
return true;
case kImplicitAssertType:
// This format doesn't allow an import assertion type, so the property
// must not be set on the import assertions object.
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
// must not be set on the import attributes object.
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
return true;
}
return handleInvalidType(url, importAssertions.type);
return handleInvalidType(url, importAttributes.type);
case importAssertions.type:
case importAttributes.type:
// The asserted type is the valid type for this format.
return true;
default:
// There is an expected type for this format, but the value of
// `importAssertions.type` might not have been it.
if (!ObjectPrototypeHasOwnProperty(importAssertions, 'type')) {
// `importAttributes.type` might not have been it.
if (!ObjectPrototypeHasOwnProperty(importAttributes, 'type')) {
// `type` wasn't specified at all.
throw new ERR_IMPORT_ASSERTION_TYPE_MISSING(url, validType);
}
return handleInvalidType(url, importAssertions.type);
return handleInvalidType(url, importAttributes.type);
}
}
@ -118,5 +113,5 @@ function handleInvalidType(url, type) {
module.exports = {
kImplicitAssertType,
validateAssertions,
validateAttributes,
};

View File

@ -11,6 +11,7 @@ const {
ObjectDefineProperty,
ObjectSetPrototypeOf,
Promise,
ReflectSet,
SafeSet,
StringPrototypeSlice,
StringPrototypeToUpperCase,
@ -65,6 +66,30 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
});
let importMetaInitializer;
let importAssertionAlreadyWarned = false;
function emitImportAssertionWarning() {
if (!importAssertionAlreadyWarned) {
importAssertionAlreadyWarned = true;
process.emitWarning('Use `importAttributes` instead of `importAssertions`', 'ExperimentalWarning');
}
}
function defineImportAssertionAlias(context) {
return ObjectDefineProperty(context, 'importAssertions', {
__proto__: null,
configurable: true,
get() {
emitImportAssertionWarning();
return this.importAttributes;
},
set(value) {
emitImportAssertionWarning();
return ReflectSet(this, 'importAttributes', value);
},
});
}
/**
* @typedef {object} ExportedHooks
* @property {Function} resolve Resolve hook.
@ -164,21 +189,21 @@ class Hooks {
* @param {string} originalSpecifier The specified URL path of the module to
* be resolved.
* @param {string} [parentURL] The URL path of the module's parent.
* @param {ImportAssertions} [importAssertions] Assertions from the import
* @param {ImportAttributes} [importAttributes] Attributes from the import
* statement or expression.
* @returns {Promise<{ format: string, url: URL['href'] }>}
*/
async resolve(
originalSpecifier,
parentURL,
importAssertions = { __proto__: null },
importAttributes = { __proto__: null },
) {
throwIfInvalidParentURL(parentURL);
const chain = this.#chains.resolve;
const context = {
conditions: getDefaultConditions(),
importAssertions,
importAttributes,
parentURL,
};
const meta = {
@ -220,9 +245,9 @@ class Hooks {
throw new ERR_LOADER_CHAIN_INCOMPLETE(hookErrIdentifier);
}
let resolvedImportAttributes;
const {
format,
importAssertions: resolvedImportAssertions,
url,
} = resolution;
@ -252,15 +277,22 @@ class Hooks {
this.#validatedUrls.add(url);
}
if (!('importAttributes' in resolution) && ('importAssertions' in resolution)) {
emitImportAssertionWarning();
resolvedImportAttributes = resolution.importAssertions;
} else {
resolvedImportAttributes = resolution.importAttributes;
}
if (
resolvedImportAssertions != null &&
typeof resolvedImportAssertions !== 'object'
resolvedImportAttributes != null &&
typeof resolvedImportAttributes !== 'object'
) {
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
'an object',
hookErrIdentifier,
'importAssertions',
resolvedImportAssertions,
'importAttributes',
resolvedImportAttributes,
);
}
@ -279,12 +311,12 @@ class Hooks {
return {
__proto__: null,
format,
importAssertions: resolvedImportAssertions,
importAttributes: resolvedImportAttributes,
url,
};
}
resolveSync(_originalSpecifier, _parentURL, _importAssertions) {
resolveSync(_originalSpecifier, _parentURL, _importAttributes) {
throw new ERR_METHOD_NOT_IMPLEMENTED('resolveSync()');
}
@ -347,7 +379,7 @@ class Hooks {
const nextLoad = nextHookFactory(chain[chain.length - 1], meta, { validateArgs, validateOutput });
const loaded = await nextLoad(url, context);
const loaded = await nextLoad(url, defineImportAssertionAlias(context));
const { hookErrIdentifier } = meta; // Retrieve the value after all settled
validateOutput(hookErrIdentifier, loaded);

View File

@ -8,7 +8,7 @@ const {
const { kEmptyObject } = require('internal/util');
const { defaultGetFormat } = require('internal/modules/esm/get_format');
const { validateAssertions } = require('internal/modules/esm/assert');
const { validateAttributes, emitImportAssertionWarning } = require('internal/modules/esm/assert');
const { getOptionValue } = require('internal/options');
const { readFileSync } = require('fs');
@ -107,19 +107,29 @@ function getSourceSync(url, context) {
*/
async function defaultLoad(url, context = kEmptyObject) {
let responseURL = url;
const { importAssertions } = context;
let {
importAttributes,
format,
source,
} = context;
if (importAttributes == null && !('importAttributes' in context) && 'importAssertions' in context) {
emitImportAssertionWarning();
importAttributes = context.importAssertions;
// Alias `importAssertions` to `importAttributes`
context = {
...context,
importAttributes,
};
}
const urlInstance = new URL(url);
throwIfUnsupportedURLScheme(urlInstance, experimentalNetworkImports);
format ??= await defaultGetFormat(urlInstance, context);
validateAssertions(url, format, importAssertions);
validateAttributes(url, format, importAttributes);
if (
format === 'builtin' ||
@ -141,7 +151,7 @@ async function defaultLoad(url, context = kEmptyObject) {
* @typedef LoadContext
* @property {string} [format] A hint (possibly returned from `resolve`)
* @property {string | Buffer | ArrayBuffer} [source] source
* @property {Record<string, string>} [importAssertions] import attributes
* @property {Record<string, string>} [importAttributes] import attributes
*/
/**
@ -158,7 +168,7 @@ async function defaultLoad(url, context = kEmptyObject) {
*/
function defaultLoadSync(url, context = kEmptyObject) {
let responseURL = url;
const { importAssertions } = context;
const { importAttributes } = context;
let {
format,
source,
@ -170,7 +180,7 @@ function defaultLoadSync(url, context = kEmptyObject) {
format ??= defaultGetFormat(urlInstance, context);
validateAssertions(url, format, importAssertions);
validateAttributes(url, format, importAttributes);
if (format === 'builtin') {
source = null;

View File

@ -165,12 +165,12 @@ class ModuleLoader {
* resolve(
* originalSpecifier:
* string, parentURL: string,
* importAssertions: Record<string, string>
* importAttributes: Record<string, string>
* ): Promise<ResolveResult>
* resolveSync(
* originalSpecifier:
* string, parentURL: string,
* importAssertions: Record<string, string>
* importAttributes: Record<string, string>
* ) ResolveResult;
* register(specifier: string, parentURL: string): any;
* forceLoadHooks(): void;
@ -204,8 +204,8 @@ class ModuleLoader {
registerModule(module, {
__proto__: null,
initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }),
importModuleDynamically: (specifier, { url }, importAssertions) => {
return this.import(specifier, url, importAssertions);
importModuleDynamically: (specifier, { url }, importAttributes) => {
return this.import(specifier, url, importAttributes);
},
});
@ -232,24 +232,24 @@ class ModuleLoader {
* @param {string | undefined} parentURL The URL of the module importing this
* one, unless this is the Node.js entry
* point.
* @param {Record<string, string>} importAssertions Validations for the
* @param {Record<string, string>} importAttributes Validations for the
* module import.
* @returns {Promise<ModuleJob>} The (possibly pending) module job
*/
async getModuleJob(specifier, parentURL, importAssertions) {
const resolveResult = await this.resolve(specifier, parentURL, importAssertions);
return this.getJobFromResolveResult(resolveResult, parentURL, importAssertions);
async getModuleJob(specifier, parentURL, importAttributes) {
const resolveResult = await this.resolve(specifier, parentURL, importAttributes);
return this.getJobFromResolveResult(resolveResult, parentURL, importAttributes);
}
getModuleJobSync(specifier, parentURL, importAssertions) {
const resolveResult = this.resolveSync(specifier, parentURL, importAssertions);
return this.getJobFromResolveResult(resolveResult, parentURL, importAssertions, true);
getModuleJobSync(specifier, parentURL, importAttributes) {
const resolveResult = this.resolveSync(specifier, parentURL, importAttributes);
return this.getJobFromResolveResult(resolveResult, parentURL, importAttributes, true);
}
getJobFromResolveResult(resolveResult, parentURL, importAssertions, sync) {
getJobFromResolveResult(resolveResult, parentURL, importAttributes, sync) {
const { url, format } = resolveResult;
const resolvedImportAssertions = resolveResult.importAssertions ?? importAssertions;
let job = this.loadCache.get(url, resolvedImportAssertions.type);
const resolvedImportAttributes = resolveResult.importAttributes ?? importAttributes;
let job = this.loadCache.get(url, resolvedImportAttributes.type);
// CommonJS will set functions for lazy job evaluation.
if (typeof job === 'function') {
@ -257,7 +257,7 @@ class ModuleLoader {
}
if (job === undefined) {
job = this.#createModuleJob(url, resolvedImportAssertions, parentURL, format, sync);
job = this.#createModuleJob(url, resolvedImportAttributes, parentURL, format, sync);
}
return job;
@ -266,7 +266,7 @@ class ModuleLoader {
/**
* Create and cache an object representing a loaded module.
* @param {string} url The absolute URL that was resolved for this module
* @param {Record<string, string>} importAssertions Validations for the
* @param {Record<string, string>} importAttributes Validations for the
* module import.
* @param {string} [parentURL] The absolute URL of the module importing this
* one, unless this is the Node.js entry point
@ -274,7 +274,7 @@ class ModuleLoader {
* `resolve` hook
* @returns {Promise<ModuleJob>} The (possibly pending) module job
*/
#createModuleJob(url, importAssertions, parentURL, format, sync) {
#createModuleJob(url, importAttributes, parentURL, format, sync) {
const callTranslator = ({ format: finalFormat, responseURL, source }, isMain) => {
const translator = getTranslators().get(finalFormat);
@ -284,7 +284,8 @@ class ModuleLoader {
return FunctionPrototypeCall(translator, this, responseURL, source, isMain);
};
const context = { format, importAssertions };
const context = { format, importAttributes };
const moduleProvider = sync ?
(url, isMain) => callTranslator(this.loadSync(url, context), isMain) :
async (url, isMain) => callTranslator(await this.load(url, context), isMain);
@ -302,14 +303,14 @@ class ModuleLoader {
const job = new ModuleJob(
this,
url,
importAssertions,
importAttributes,
moduleProvider,
parentURL === undefined,
inspectBrk,
sync,
);
this.loadCache.set(url, importAssertions.type, job);
this.loadCache.set(url, importAttributes.type, job);
return job;
}
@ -319,12 +320,12 @@ class ModuleLoader {
* Use directly with caution.
* @param {string} specifier The first parameter of an `import()` expression.
* @param {string} parentURL Path of the parent importing the module.
* @param {Record<string, string>} importAssertions Validations for the
* @param {Record<string, string>} importAttributes Validations for the
* module import.
* @returns {Promise<ModuleExports>}
*/
async import(specifier, parentURL, importAssertions) {
const moduleJob = await this.getModuleJob(specifier, parentURL, importAssertions);
async import(specifier, parentURL, importAttributes) {
const moduleJob = await this.getModuleJob(specifier, parentURL, importAttributes);
const { module } = await moduleJob.run();
return module.getNamespace();
}
@ -348,20 +349,20 @@ class ModuleLoader {
* @param {string} originalSpecifier The specified URL path of the module to
* be resolved.
* @param {string} [parentURL] The URL path of the module's parent.
* @param {ImportAssertions} importAssertions Assertions from the import
* @param {ImportAttributes} importAttributes Attributes from the import
* statement or expression.
* @returns {{ format: string, url: URL['href'] }}
*/
resolve(originalSpecifier, parentURL, importAssertions) {
resolve(originalSpecifier, parentURL, importAttributes) {
if (this.#customizations) {
return this.#customizations.resolve(originalSpecifier, parentURL, importAssertions);
return this.#customizations.resolve(originalSpecifier, parentURL, importAttributes);
}
const requestKey = this.#resolveCache.serializeKey(originalSpecifier, importAssertions);
const requestKey = this.#resolveCache.serializeKey(originalSpecifier, importAttributes);
const cachedResult = this.#resolveCache.get(requestKey, parentURL);
if (cachedResult != null) {
return cachedResult;
}
const result = this.defaultResolve(originalSpecifier, parentURL, importAssertions);
const result = this.defaultResolve(originalSpecifier, parentURL, importAttributes);
this.#resolveCache.set(requestKey, parentURL, result);
return result;
}
@ -370,11 +371,11 @@ class ModuleLoader {
* Just like `resolve` except synchronous. This is here specifically to support
* `import.meta.resolve` which must happen synchronously.
*/
resolveSync(originalSpecifier, parentURL, importAssertions) {
resolveSync(originalSpecifier, parentURL, importAttributes) {
if (this.#customizations) {
return this.#customizations.resolveSync(originalSpecifier, parentURL, importAssertions);
return this.#customizations.resolveSync(originalSpecifier, parentURL, importAttributes);
}
return this.defaultResolve(originalSpecifier, parentURL, importAssertions);
return this.defaultResolve(originalSpecifier, parentURL, importAttributes);
}
/**
@ -382,13 +383,13 @@ class ModuleLoader {
* `resolve` and `resolveSync`. This function is here just to avoid
* repeating the same code block twice in those functions.
*/
defaultResolve(originalSpecifier, parentURL, importAssertions) {
defaultResolve(originalSpecifier, parentURL, importAttributes) {
defaultResolve ??= require('internal/modules/esm/resolve').defaultResolve;
const context = {
__proto__: null,
conditions: this.#defaultConditions,
importAssertions,
importAttributes,
parentURL,
};
@ -483,17 +484,17 @@ class CustomizedModuleLoader {
* @param {string} originalSpecifier The specified URL path of the module to
* be resolved.
* @param {string} [parentURL] The URL path of the module's parent.
* @param {ImportAssertions} importAssertions Assertions from the import
* @param {ImportAttributes} importAttributes Attributes from the import
* statement or expression.
* @returns {{ format: string, url: URL['href'] }}
*/
resolve(originalSpecifier, parentURL, importAssertions) {
return hooksProxy.makeAsyncRequest('resolve', undefined, originalSpecifier, parentURL, importAssertions);
resolve(originalSpecifier, parentURL, importAttributes) {
return hooksProxy.makeAsyncRequest('resolve', undefined, originalSpecifier, parentURL, importAttributes);
}
resolveSync(originalSpecifier, parentURL, importAssertions) {
resolveSync(originalSpecifier, parentURL, importAttributes) {
// This happens only as a result of `import.meta.resolve` calls, which must be sync per spec.
return hooksProxy.makeSyncRequest('resolve', undefined, originalSpecifier, parentURL, importAssertions);
return hooksProxy.makeSyncRequest('resolve', undefined, originalSpecifier, parentURL, importAttributes);
}
/**

View File

@ -50,10 +50,10 @@ const isCommonJSGlobalLikeNotDefinedError = (errorMessage) =>
class ModuleJob {
// `loader` is the Loader instance used for loading dependencies.
// `moduleProvider` is a function
constructor(loader, url, importAssertions = { __proto__: null },
constructor(loader, url, importAttributes = { __proto__: null },
moduleProvider, isMain, inspectBrk, sync = false) {
this.loader = loader;
this.importAssertions = importAssertions;
this.importAttributes = importAttributes;
this.isMain = isMain;
this.inspectBrk = inspectBrk;
@ -81,8 +81,8 @@ class ModuleJob {
// so that circular dependencies can't cause a deadlock by two of
// these `link` callbacks depending on each other.
const dependencyJobs = [];
const promises = this.module.link(async (specifier, assertions) => {
const job = await this.loader.getModuleJob(specifier, url, assertions);
const promises = this.module.link(async (specifier, attributes) => {
const job = await this.loader.getModuleJob(specifier, url, attributes);
ArrayPrototypePush(dependencyJobs, job);
return job.modulePromise;
});
@ -157,7 +157,7 @@ class ModuleJob {
let format;
try {
// This might throw for non-CommonJS modules because we aren't passing
// in the import assertions and some formats require them; but we only
// in the import attributes and some formats require them; but we only
// care about CommonJS for the purposes of this error message.
({ format } =
await this.loader.load(childFileURL));

View File

@ -17,7 +17,7 @@ const { validateString } = require('internal/validators');
/**
* Cache the results of the `resolve` step of the module resolution and loading process.
* Future resolutions of the same input (specifier, parent URL and import assertions)
* Future resolutions of the same input (specifier, parent URL and import attributes)
* must return the same result if the first attempt was successful, per
* https://tc39.es/ecma262/#sec-HostLoadImportedModule.
* This cache is *not* used when custom loaders are registered.
@ -30,15 +30,15 @@ class ResolveCache extends SafeMap {
*
* It is exposed to allow more efficient read and overwrite a cache entry.
* @param {string} specifier
* @param {Record<string,string>} importAssertions
* @param {Record<string,string>} importAttributes
* @returns {string}
*/
serializeKey(specifier, importAssertions) {
// To serialize the ModuleRequest (specifier + list of import assertions),
// we need to sort the assertions by key, then stringifying,
// so that different import statements with the same assertions are always treated
serializeKey(specifier, importAttributes) {
// To serialize the ModuleRequest (specifier + list of import attributes),
// we need to sort the attributes by key, then stringifying,
// so that different import statements with the same attributes are always treated
// as identical.
const keys = ObjectKeys(importAssertions);
const keys = ObjectKeys(importAttributes);
if (keys.length === 0) {
return specifier + '::';
@ -47,7 +47,7 @@ class ResolveCache extends SafeMap {
return specifier + '::' + ArrayPrototypeJoin(
ArrayPrototypeMap(
ArrayPrototypeSort(keys),
(key) => JSONStringify(key) + JSONStringify(importAssertions[key])),
(key) => JSONStringify(key) + JSONStringify(importAttributes[key])),
',');
}

View File

@ -138,11 +138,11 @@ function errPath(url) {
* @param {string} specifier - The module specifier to import.
* @param {object} options - An object containing options for the import.
* @param {string} options.url - The URL of the module requesting the import.
* @param {Record<string, string>} [assertions] - An object containing assertions for the import.
* @param {Record<string, string>} [attributes] - An object containing attributes for the import.
* @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} The imported module.
*/
async function importModuleDynamically(specifier, { url }, assertions) {
return asyncESM.esmLoader.import(specifier, url, assertions);
async function importModuleDynamically(specifier, { url }, attributes) {
return asyncESM.esmLoader.import(specifier, url, attributes);
}
// Strategy for loading a standard JavaScript module.
@ -201,8 +201,8 @@ function loadCJSModule(module, source, url, filename) {
'__dirname',
], {
filename,
importModuleDynamically(specifier, _, importAssertions) {
return asyncESM.esmLoader.import(specifier, url, importAssertions);
importModuleDynamically(specifier, _, importAttributes) {
return asyncESM.esmLoader.import(specifier, url, importAttributes);
},
}).function;
} catch (err) {
@ -213,14 +213,14 @@ function loadCJSModule(module, source, url, filename) {
const __dirname = dirname(filename);
// eslint-disable-next-line func-name-matching,func-style
const requireFn = function require(specifier) {
let importAssertions = kEmptyObject;
let importAttributes = kEmptyObject;
if (!StringPrototypeStartsWith(specifier, 'node:')) {
// TODO: do not depend on the monkey-patchable CJS loader here.
const path = CJSModule._resolveFilename(specifier, module);
if (specifier !== path) {
switch (extname(path)) {
case '.json':
importAssertions = { __proto__: null, type: 'json' };
importAttributes = { __proto__: null, type: 'json' };
break;
case '.node':
return CJSModule._load(specifier, module);
@ -230,7 +230,7 @@ function loadCJSModule(module, source, url, filename) {
specifier = `${pathToFileURL(path)}`;
}
}
const job = asyncESM.esmLoader.getModuleJobSync(specifier, url, importAssertions);
const job = asyncESM.esmLoader.getModuleJobSync(specifier, url, importAttributes);
job.runSync();
return cjsCache.get(job.url).exports;
};

View File

@ -88,7 +88,7 @@ function getConditionsSet(conditions) {
* @callback ImportModuleDynamicallyCallback
* @param {string} specifier
* @param {ModuleWrap|ContextifyScript|Function|vm.Module} callbackReferrer
* @param {object} assertions
* @param {Record<string, string>} attributes
* @returns { Promise<void> }
*/
@ -162,15 +162,15 @@ function initializeImportMetaObject(symbol, meta) {
* Asynchronously imports a module dynamically using a callback function. The native callback.
* @param {symbol} symbol - Reference to the module.
* @param {string} specifier - The module specifier string.
* @param {Record<string, string>} assertions - The import assertions object.
* @param {Record<string, string>} attributes - The import attributes object.
* @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} - The imported module object.
* @throws {ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING} - If the callback function is missing.
*/
async function importModuleDynamicallyCallback(symbol, specifier, assertions) {
async function importModuleDynamicallyCallback(symbol, specifier, attributes) {
if (moduleRegistries.has(symbol)) {
const { importModuleDynamically, callbackReferrer } = moduleRegistries.get(symbol);
if (importModuleDynamically !== undefined) {
return importModuleDynamically(specifier, callbackReferrer, assertions);
return importModuleDynamically(specifier, callbackReferrer, attributes);
}
}
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();

View File

@ -84,9 +84,9 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) {
filename: name,
displayErrors: true,
[kVmBreakFirstLineSymbol]: !!breakFirstLine,
importModuleDynamically(specifier, _, importAssertions) {
importModuleDynamically(specifier, _, importAttributes) {
const loader = asyncESM.esmLoader;
return loader.import(specifier, baseUrl, importAssertions);
return loader.import(specifier, baseUrl, importAttributes);
},
}));
if (print) {

View File

@ -483,9 +483,9 @@ function REPLServer(prompt,
vm.createScript(fallbackCode, {
filename: file,
displayErrors: true,
importModuleDynamically: (specifier, _, importAssertions) => {
importModuleDynamically: (specifier, _, importAttributes) => {
return asyncESM.esmLoader.import(specifier, parentURL,
importAssertions);
importAttributes);
},
});
} catch (fallbackError) {
@ -527,9 +527,9 @@ function REPLServer(prompt,
script = vm.createScript(code, {
filename: file,
displayErrors: true,
importModuleDynamically: (specifier, _, importAssertions) => {
importModuleDynamically: (specifier, _, importAttributes) => {
return asyncESM.esmLoader.import(specifier, parentURL,
importAssertions);
importAttributes);
},
});
} catch (e) {

View File

@ -249,19 +249,19 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(that);
}
static Local<Object> createImportAssertionContainer(Environment* env,
Isolate* isolate, Local<FixedArray> raw_assertions) {
Local<Object> assertions =
Object::New(isolate, v8::Null(env->isolate()), nullptr, nullptr, 0);
for (int i = 0; i < raw_assertions->Length(); i += 3) {
assertions
->Set(env->context(),
raw_assertions->Get(env->context(), i).As<String>(),
raw_assertions->Get(env->context(), i + 1).As<Value>())
.ToChecked();
static Local<Object> createImportAttributesContainer(
Environment* env, Isolate* isolate, Local<FixedArray> raw_attributes) {
Local<Object> attributes =
Object::New(isolate, v8::Null(env->isolate()), nullptr, nullptr, 0);
for (int i = 0; i < raw_attributes->Length(); i += 3) {
attributes
->Set(env->context(),
raw_attributes->Get(env->context(), i).As<String>(),
raw_attributes->Get(env->context(), i + 1).As<Value>())
.ToChecked();
}
return assertions;
return attributes;
}
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
@ -297,13 +297,13 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
Utf8Value specifier_utf8(env->isolate(), specifier);
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
Local<FixedArray> raw_assertions = module_request->GetImportAssertions();
Local<Object> assertions =
createImportAssertionContainer(env, isolate, raw_assertions);
Local<FixedArray> raw_attributes = module_request->GetImportAssertions();
Local<Object> attributes =
createImportAttributesContainer(env, isolate, raw_attributes);
Local<Value> argv[] = {
specifier,
assertions,
attributes,
};
MaybeLocal<Value> maybe_resolve_return_value =
@ -499,7 +499,7 @@ void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
Local<Context> context,
Local<String> specifier,
Local<FixedArray> import_assertions,
Local<FixedArray> import_attributes,
Local<Module> referrer) {
Environment* env = Environment::GetCurrent(context);
if (env == nullptr) {
@ -552,7 +552,7 @@ static MaybeLocal<Promise> ImportModuleDynamically(
Local<v8::Data> host_defined_options,
Local<Value> resource_name,
Local<String> specifier,
Local<FixedArray> import_assertions) {
Local<FixedArray> import_attributes) {
Isolate* isolate = context->GetIsolate();
Environment* env = Environment::GetCurrent(context);
if (env == nullptr) {
@ -580,13 +580,13 @@ static MaybeLocal<Promise> ImportModuleDynamically(
Local<Symbol> id =
options->Get(context, HostDefinedOptions::kID).As<Symbol>();
Local<Object> assertions =
createImportAssertionContainer(env, isolate, import_assertions);
Local<Object> attributes =
createImportAttributesContainer(env, isolate, import_attributes);
Local<Value> import_args[] = {
id,
Local<Value>(specifier),
assertions,
attributes,
};
Local<Value> result;

View File

@ -97,7 +97,7 @@ class ModuleWrap : public BaseObject {
static v8::MaybeLocal<v8::Module> ResolveModuleCallback(
v8::Local<v8::Context> context,
v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_assertions,
v8::Local<v8::FixedArray> import_attributes,
v8::Local<v8::Module> referrer);
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);

View File

@ -753,6 +753,13 @@ static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
"--no-harmony-import-assertions") == v8_args.end()) {
v8_args.emplace_back("--harmony-import-assertions");
}
// TODO(aduh95): remove this when the harmony-import-attributes flag
// is removed in V8.
if (std::find(v8_args.begin(),
v8_args.end(),
"--no-harmony-import-attributes") == v8_args.end()) {
v8_args.emplace_back("--harmony-import-attributes");
}
auto env_opts = per_process::cli_options->per_isolate->per_env;
if (std::find(v8_args.begin(), v8_args.end(),

View File

@ -9,7 +9,7 @@ async function test() {
import('../fixtures/experimental.json'),
import(
'../fixtures/experimental.json',
{ assert: { type: 'json' } }
{ with: { type: 'json' } }
),
]);
@ -24,7 +24,7 @@ async function test() {
import('../fixtures/experimental.json?test'),
import(
'../fixtures/experimental.json?test',
{ assert: { type: 'json' } }
{ with: { type: 'json' } }
),
]);
@ -39,7 +39,7 @@ async function test() {
import('../fixtures/experimental.json#test'),
import(
'../fixtures/experimental.json#test',
{ assert: { type: 'json' } }
{ with: { type: 'json' } }
),
]);
@ -54,7 +54,7 @@ async function test() {
import('../fixtures/experimental.json?test2#test'),
import(
'../fixtures/experimental.json?test2#test',
{ assert: { type: 'json' } }
{ with: { type: 'json' } }
),
]);
@ -69,7 +69,7 @@ async function test() {
import('data:application/json,{"ofLife":42}'),
import(
'data:application/json,{"ofLife":42}',
{ assert: { type: 'json' } }
{ with: { type: 'json' } }
),
]);

View File

@ -60,21 +60,21 @@ function createBase64URL(mime, body) {
}
{
const ns = await import('data:application/json;foo="test,"this"',
{ assert: { type: 'json' } });
{ with: { type: 'json' } });
assert.deepStrictEqual(Object.keys(ns), ['default']);
assert.strictEqual(ns.default, 'this');
}
{
const ns = await import(`data:application/json;foo=${
encodeURIComponent('test,')
},0`, { assert: { type: 'json' } });
},0`, { with: { type: 'json' } });
assert.deepStrictEqual(Object.keys(ns), ['default']);
assert.strictEqual(ns.default, 0);
}
{
await assert.rejects(async () =>
import('data:application/json;foo="test,",0',
{ assert: { type: 'json' } }), {
{ with: { type: 'json' } }), {
name: 'SyntaxError',
message: /Unterminated string in JSON at position 3/
});
@ -82,14 +82,14 @@ function createBase64URL(mime, body) {
{
const body = '{"x": 1}';
const plainESMURL = createURL('application/json', body);
const ns = await import(plainESMURL, { assert: { type: 'json' } });
const ns = await import(plainESMURL, { with: { type: 'json' } });
assert.deepStrictEqual(Object.keys(ns), ['default']);
assert.strictEqual(ns.default.x, 1);
}
{
const body = '{"default": 2}';
const plainESMURL = createURL('application/json', body);
const ns = await import(plainESMURL, { assert: { type: 'json' } });
const ns = await import(plainESMURL, { with: { type: 'json' } });
assert.deepStrictEqual(Object.keys(ns), ['default']);
assert.strictEqual(ns.default.default, 2);
}

View File

@ -5,7 +5,7 @@ const { strictEqual } = require('assert');
async function test() {
{
const results = await Promise.allSettled([
import('../fixtures/empty.js', { assert: { type: 'json' } }),
import('../fixtures/empty.js', { with: { type: 'json' } }),
import('../fixtures/empty.js'),
]);
@ -16,7 +16,7 @@ async function test() {
{
const results = await Promise.allSettled([
import('../fixtures/empty.js'),
import('../fixtures/empty.js', { assert: { type: 'json' } }),
import('../fixtures/empty.js', { with: { type: 'json' } }),
]);
strictEqual(results[0].status, 'fulfilled');
@ -25,7 +25,7 @@ async function test() {
{
const results = await Promise.allSettled([
import('../fixtures/empty.json', { assert: { type: 'json' } }),
import('../fixtures/empty.json', { with: { type: 'json' } }),
import('../fixtures/empty.json'),
]);
@ -36,7 +36,7 @@ async function test() {
{
const results = await Promise.allSettled([
import('../fixtures/empty.json'),
import('../fixtures/empty.json', { assert: { type: 'json' } }),
import('../fixtures/empty.json', { with: { type: 'json' } }),
]);
strictEqual(results[0].status, 'rejected');

View File

@ -3,7 +3,7 @@ import { strictEqual } from 'assert';
{
const results = await Promise.allSettled([
import('../fixtures/empty.js', { assert: { type: 'json' } }),
import('../fixtures/empty.js', { with: { type: 'json' } }),
import('../fixtures/empty.js'),
]);
@ -14,7 +14,7 @@ import { strictEqual } from 'assert';
{
const results = await Promise.allSettled([
import('../fixtures/empty.js'),
import('../fixtures/empty.js', { assert: { type: 'json' } }),
import('../fixtures/empty.js', { with: { type: 'json' } }),
]);
strictEqual(results[0].status, 'fulfilled');
@ -23,7 +23,7 @@ import { strictEqual } from 'assert';
{
const results = await Promise.allSettled([
import('../fixtures/empty.json', { assert: { type: 'json' } }),
import('../fixtures/empty.json', { with: { type: 'json' } }),
import('../fixtures/empty.json'),
]);
@ -34,7 +34,7 @@ import { strictEqual } from 'assert';
{
const results = await Promise.allSettled([
import('../fixtures/empty.json'),
import('../fixtures/empty.json', { assert: { type: 'json' } }),
import('../fixtures/empty.json', { with: { type: 'json' } }),
]);
strictEqual(results[0].status, 'rejected');

View File

@ -1,6 +0,0 @@
import '../common/index.mjs';
import { strictEqual } from 'assert';
import secret from '../fixtures/experimental.json' assert { type: 'json', unsupportedAssertion: 'should ignore' };
strictEqual(secret.ofLife, 42);

View File

@ -1,45 +0,0 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
const assert = require('assert');
const { validateAssertions } = require('internal/modules/esm/assert');
common.expectWarning(
'ExperimentalWarning',
'Import assertions are not a stable feature of the JavaScript language. ' +
'Avoid relying on their current behavior and syntax as those might change ' +
'in a future version of Node.js.'
);
const url = 'test://';
assert.ok(validateAssertions(url, 'builtin', {}));
assert.ok(validateAssertions(url, 'commonjs', {}));
assert.ok(validateAssertions(url, 'json', { type: 'json' }));
assert.ok(validateAssertions(url, 'module', {}));
assert.ok(validateAssertions(url, 'wasm', {}));
assert.throws(() => validateAssertions(url, 'json', {}), {
code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING',
});
assert.throws(() => validateAssertions(url, 'module', { type: 'json' }), {
code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED',
});
// The HTML spec specifically disallows this for now, while Wasm module import
// and whether it will require a type assertion is still an open question.
assert.throws(() => validateAssertions(url, 'module', { type: 'javascript' }), {
code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
});
assert.throws(() => validateAssertions(url, 'module', { type: 'css' }), {
code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
});
assert.throws(() => validateAssertions(url, 'module', { type: false }), {
code: 'ERR_INVALID_ARG_TYPE',
});

View File

@ -1,10 +1,37 @@
import { expectWarning } from '../common/index.mjs';
import { spawnPromisified } from '../common/index.mjs';
import assert from 'node:assert';
import { execPath } from 'node:process';
expectWarning(
'ExperimentalWarning',
'Import assertions are not a stable feature of the JavaScript language. ' +
'Avoid relying on their current behavior and syntax as those might change ' +
'in a future version of Node.js.'
);
await Promise.all([
// Using importAssertions in the resolve hook should warn but still work.
`data:text/javascript,export ${encodeURIComponent(function resolve() {
return { shortCircuit: true, url: 'data:application/json,1', importAssertions: { type: 'json' } };
})}`,
// Setting importAssertions on the context object of the load hook should warn but still work.
`data:text/javascript,export ${encodeURIComponent(function load(u, c, n) {
c.importAssertions = { type: 'json' };
return n('data:application/json,1', c);
})}`,
// Creating a new context object with importAssertions in the load hook should warn but still work.
`data:text/javascript,export ${encodeURIComponent(function load(u, c, n) {
return n('data:application/json,1', { importAssertions: { type: 'json' } });
})}`,
].map(async (loaderURL) => {
const { stdout, stderr, code } = await spawnPromisified(execPath, [
'--input-type=module',
'--eval', `
import assert from 'node:assert';
import { register } from 'node:module';
register(${JSON.stringify(loaderURL)});
assert.deepStrictEqual(
{ ...await import('data:') },
{ default: 1 }
);`,
]);
await import('data:text/javascript,', { assert: { someUnsupportedKey: 'value' } });
assert.match(stderr, /Use `importAttributes` instead of `importAssertions`/);
assert.strictEqual(stdout, '');
assert.strictEqual(code, 0);
}));

View File

@ -1,6 +1,6 @@
import '../common/index.mjs';
import { strictEqual } from 'assert';
import secret from '../fixtures/experimental.json' assert { type: 'json' };
import secret from '../fixtures/experimental.json' with { type: 'json' };
strictEqual(secret.ofLife, 42);

View File

@ -1,9 +1,9 @@
import '../common/index.mjs';
import { strictEqual } from 'assert';
import secret0 from '../fixtures/experimental.json' assert { type: 'json' };
import secret0 from '../fixtures/experimental.json' with { type: 'json' };
const secret1 = await import('../fixtures/experimental.json', {
assert: { type: 'json' },
with: { type: 'json' },
});
strictEqual(secret0.ofLife, 42);

View File

@ -1,9 +1,9 @@
import '../common/index.mjs';
import { strictEqual } from 'assert';
import secret0 from '../fixtures/experimental.json' assert { type: 'json' };
import secret0 from '../fixtures/experimental.json' with { type: 'json' };
const secret1 = await import('../fixtures/experimental.json',
{ assert: { type: 'json' } });
{ with: { type: 'json' } });
strictEqual(secret0.ofLife, 42);
strictEqual(secret1.default.ofLife, 42);

View File

@ -5,31 +5,29 @@ const { rejects } = require('assert');
const jsModuleDataUrl = 'data:text/javascript,export{}';
const jsonModuleDataUrl = 'data:application/json,""';
common.expectWarning(
'ExperimentalWarning',
'Import assertions are not a stable feature of the JavaScript language. ' +
'Avoid relying on their current behavior and syntax as those might change ' +
'in a future version of Node.js.'
);
async function test() {
await rejects(
import('data:text/css,', { assert: { type: 'css' } }),
import('data:text/css,', { with: { type: 'css' } }),
{ code: 'ERR_UNKNOWN_MODULE_FORMAT' }
);
await rejects(
import(`data:text/javascript,import${JSON.stringify(jsModuleDataUrl)}assert{type:"json"}`),
import('data:text/css,', { with: { unsupportedAttribute: 'value' } }),
{ code: 'ERR_IMPORT_ATTRIBUTE_UNSUPPORTED' }
);
await rejects(
import(`data:text/javascript,import${JSON.stringify(jsModuleDataUrl)}with{type:"json"}`),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED' }
);
await rejects(
import(jsModuleDataUrl, { assert: { type: 'json' } }),
import(jsModuleDataUrl, { with: { type: 'json' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED' }
);
await rejects(
import(jsModuleDataUrl, { assert: { type: 'unsupported' } }),
import(jsModuleDataUrl, { with: { type: 'unsupported' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED' }
);
@ -39,17 +37,17 @@ async function test() {
);
await rejects(
import(jsonModuleDataUrl, { assert: {} }),
import(jsonModuleDataUrl, { with: {} }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING' }
);
await rejects(
import(jsonModuleDataUrl, { assert: { foo: 'bar' } }),
import(jsonModuleDataUrl, { with: { foo: 'bar' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING' }
);
await rejects(
import(jsonModuleDataUrl, { assert: { type: 'unsupported' } }),
import(jsonModuleDataUrl, { with: { type: 'unsupported' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED' }
);
}

View File

@ -1,36 +1,28 @@
import { expectWarning } from '../common/index.mjs';
import '../common/index.mjs';
import { rejects } from 'assert';
const jsModuleDataUrl = 'data:text/javascript,export{}';
const jsonModuleDataUrl = 'data:application/json,""';
expectWarning(
'ExperimentalWarning',
'Import assertions are not a stable feature of the JavaScript language. ' +
'Avoid relying on their current behavior and syntax as those might change ' +
'in a future version of Node.js.'
);
await rejects(
// This rejects because of the unsupported MIME type, not because of the
// unsupported assertion.
import('data:text/css,', { assert: { type: 'css' } }),
import('data:text/css,', { with: { type: 'css' } }),
{ code: 'ERR_UNKNOWN_MODULE_FORMAT' }
);
await rejects(
import(`data:text/javascript,import${JSON.stringify(jsModuleDataUrl)}assert{type:"json"}`),
import(`data:text/javascript,import${JSON.stringify(jsModuleDataUrl)}with{type:"json"}`),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED' }
);
await rejects(
import(jsModuleDataUrl, { assert: { type: 'json' } }),
import(jsModuleDataUrl, { with: { type: 'json' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED' }
);
await rejects(
import(import.meta.url, { assert: { type: 'unsupported' } }),
import(import.meta.url, { with: { type: 'unsupported' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED' }
);
@ -40,16 +32,16 @@ await rejects(
);
await rejects(
import(jsonModuleDataUrl, { assert: {} }),
import(jsonModuleDataUrl, { with: {} }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING' }
);
await rejects(
import(jsonModuleDataUrl, { assert: { foo: 'bar' } }),
import(jsonModuleDataUrl, { with: { foo: 'bar' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING' }
);
await rejects(
import(jsonModuleDataUrl, { assert: { type: 'unsupported' } }),
import(jsonModuleDataUrl, { with: { type: 'unsupported' } }),
{ code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED' }
);

View File

@ -0,0 +1,45 @@
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const { validateAttributes } = require('internal/modules/esm/assert');
const url = 'test://';
assert.ok(validateAttributes(url, 'builtin', {}));
assert.ok(validateAttributes(url, 'commonjs', {}));
assert.ok(validateAttributes(url, 'json', { type: 'json' }));
assert.ok(validateAttributes(url, 'module', {}));
assert.ok(validateAttributes(url, 'wasm', {}));
assert.throws(() => validateAttributes(url, 'json', {}), {
code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING',
});
assert.throws(() => validateAttributes(url, 'json', { type: 'json', unsupportedAttribute: 'value' }), {
code: 'ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
});
assert.throws(() => validateAttributes(url, 'module', { unsupportedAttribute: 'value' }), {
code: 'ERR_IMPORT_ATTRIBUTE_UNSUPPORTED',
});
assert.throws(() => validateAttributes(url, 'module', { type: 'json' }), {
code: 'ERR_IMPORT_ASSERTION_TYPE_FAILED',
});
// The HTML spec specifically disallows this for now, while Wasm module import
// and whether it will require a type assertion is still an open question.
assert.throws(() => validateAttributes(url, 'module', { type: 'javascript' }), {
code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
});
assert.throws(() => validateAttributes(url, 'module', { type: 'css' }), {
code: 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED',
});
assert.throws(() => validateAttributes(url, 'module', { type: false }), {
code: 'ERR_INVALID_ARG_TYPE',
});

View File

@ -6,7 +6,7 @@ import { createRequire } from 'module';
import mod from '../fixtures/es-modules/json-cache/mod.cjs';
import another from '../fixtures/es-modules/json-cache/another.cjs';
import test from '../fixtures/es-modules/json-cache/test.json' assert
import test from '../fixtures/es-modules/json-cache/test.json' with
{ type: 'json' };
const require = createRequire(import.meta.url);

View File

@ -7,7 +7,7 @@ import { describe, it, test } from 'node:test';
import { mkdir, rm, writeFile } from 'node:fs/promises';
import * as tmpdir from '../common/tmpdir.js';
import secret from '../fixtures/experimental.json' assert { type: 'json' };
import secret from '../fixtures/experimental.json' with { type: 'json' };
describe('ESM: importing JSON', () => {
it('should load JSON', () => {
@ -20,7 +20,6 @@ describe('ESM: importing JSON', () => {
]);
assert.match(stderr, /ExperimentalWarning: Importing JSON modules/);
assert.match(stderr, /ExperimentalWarning: Import assertions/);
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
});
@ -35,19 +34,19 @@ describe('ESM: importing JSON', () => {
const url = new URL('./foo.json', root);
await writeFile(url, JSON.stringify({ id: i++ }));
const absoluteURL = await import(`${url}`, {
assert: { type: 'json' },
with: { type: 'json' },
});
await writeFile(url, JSON.stringify({ id: i++ }));
const queryString = await import(`${url}?a=2`, {
assert: { type: 'json' },
with: { type: 'json' },
});
await writeFile(url, JSON.stringify({ id: i++ }));
const hash = await import(`${url}#a=2`, {
assert: { type: 'json' },
with: { type: 'json' },
});
await writeFile(url, JSON.stringify({ id: i++ }));
const queryStringAndHash = await import(`${url}?a=2#a=2`, {
assert: { type: 'json' },
with: { type: 'json' },
});
assert.notDeepStrictEqual(absoluteURL, queryString);

View File

@ -12,7 +12,7 @@ async function resolve(referrer, context, next) {
}
function load(url, context, next) {
if (context.importAssertions.type === 'json') {
if (context.importAttributes.type === 'json') {
return {
shortCircuit: true,
format: 'json',
@ -25,6 +25,6 @@ function load(url, context, next) {
register(`data:text/javascript,export ${encodeURIComponent(resolve)};export ${encodeURIComponent(load)}`);
assert.notDeepStrictEqual(
await import(fixtures.fileURL('empty.json'), { assert: { type: 'json' } }),
await import(fixtures.fileURL('empty.json'), { assert: { type: 'json' } }),
await import(fixtures.fileURL('empty.json'), { with: { type: 'json' } }),
await import(fixtures.fileURL('empty.json'), { with: { type: 'json' } }),
);

View File

@ -2,20 +2,20 @@ const DATA_URL_PATTERN = /^data:application\/json(?:[^,]*?)(;base64)?,([\s\S]*)$
const JSON_URL_PATTERN = /^[^?]+\.json(\?[^#]*)?(#.*)?$/;
export async function resolve(specifier, context, next) {
const noAssertionSpecified = context.importAssertions.type == null;
const noAttributesSpecified = context.importAttributes.type == null;
// Mutation from resolve hook should be discarded.
context.importAssertions.type = 'whatever';
context.importAttributes.type = 'whatever';
// This fixture assumes that no other resolve hooks in the chain will error on invalid import assertions
// This fixture assumes that no other resolve hooks in the chain will error on invalid import attributes
// (as defaultResolve doesn't).
const result = await next(specifier, context);
if (noAssertionSpecified &&
if (noAttributesSpecified &&
(DATA_URL_PATTERN.test(result.url) || JSON_URL_PATTERN.test(result.url))) {
// Clean new import assertions object to ensure that this test isn't passing due to mutation.
result.importAssertions = {
...(result.importAssertions ?? context.importAssertions),
// Clean new import attributes object to ensure that this test isn't passing due to mutation.
result.importAttributes = {
...(result.importAttributes ?? context.importAttributes),
type: 'json',
};
}

View File

@ -14,7 +14,7 @@ export async function resolve(specifier, context, next) {
return {
shortCircuit: true,
url: `custom-${def.url}`,
importAssertions: context.importAssertions,
importAttributes: context.importAttributes,
};
}
return def;

View File

@ -6,7 +6,7 @@ import count from '../es-modules/stateful.mjs';
// used to assert node-land and user-land have different contexts
count();
export function resolve(specifier, { importAssertions }, next) {
export function resolve(specifier, { importAttributes }, next) {
let format = '';
if (specifier === 'esmHook/format.false') {
@ -24,7 +24,7 @@ export function resolve(specifier, { importAssertions }, next) {
format,
shortCircuit: true,
url: pathToFileURL(specifier).href,
importAssertions,
importAttributes,
};
}

View File

@ -22,12 +22,12 @@ export async function resolve(specifier, context, next) {
assert.strictEqual(context.parentURL, undefined);
}
assert.deepStrictEqual(context.importAssertions, {});
assert.deepStrictEqual(context.importAttributes, {});
} else if (resolveCalls === 2) {
url = new URL(specifier, context.parentURL).href;
assert.match(specifier, /experimental\.json$/);
assert.match(context.parentURL, /json-modules\.mjs$/);
assert.deepStrictEqual(context.importAssertions, {
assert.deepStrictEqual(context.importAttributes, {
type: 'json',
});
}
@ -35,7 +35,7 @@ export async function resolve(specifier, context, next) {
// Ensure `context` has all and only the properties it's supposed to
assert.deepStrictEqual(Reflect.ownKeys(context), [
'conditions',
'importAssertions',
'importAttributes',
'parentURL',
]);
assert.ok(Array.isArray(context.conditions));
@ -59,11 +59,11 @@ export async function load(url, context, next) {
if (loadCalls === 1) {
assert.match(url, /json-modules\.mjs$/);
assert.deepStrictEqual(context.importAssertions, {});
assert.deepStrictEqual(context.importAttributes, {});
format = 'module';
} else if (loadCalls === 2) {
assert.match(url, /experimental\.json$/);
assert.deepStrictEqual(context.importAssertions, {
assert.deepStrictEqual(context.importAttributes, {
type: 'json',
});
format = 'json';
@ -73,7 +73,7 @@ export async function load(url, context, next) {
// Ensure `context` has all and only the properties it's supposed to
assert.deepStrictEqual(Object.keys(context), [
'format',
'importAssertions',
'importAttributes',
]);
assert.strictEqual(context.format, 'test');
assert.strictEqual(typeof next, 'function');

View File

@ -1,4 +1,4 @@
export async function resolve(specifier, { parentURL, importAssertions }, next) {
export async function resolve(specifier, { parentURL, importAttributes }, next) {
if (parentURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') {
return {
shortCircuit: true,

View File

@ -1,9 +1,9 @@
export async function resolve(specifier, { parentURL, importAssertions }, next) {
export async function resolve(specifier, { parentURL, importAttributes }, next) {
if (parentURL && specifier === '../fixtures/es-modules/test-esm-ok.mjs') {
return {
shortCircuit: true,
url: specifier,
importAssertions,
importAttributes,
};
}
return next(specifier);

View File

@ -3,9 +3,9 @@ import {createRequire} from '../../common/index.mjs';
const require = createRequire(import.meta.url);
const dep = require('./loader-dep.js');
export async function resolve(specifier, { parentURL, importAssertions }, defaultResolve) {
export async function resolve(specifier, { parentURL, importAttributes }, defaultResolve) {
return {
url: (await defaultResolve(specifier, { parentURL, importAssertions }, defaultResolve)).url,
url: (await defaultResolve(specifier, { parentURL, importAttributes }, defaultResolve)).url,
format: dep.format
};
}

View File

@ -3,7 +3,7 @@ import assert from 'node:assert';
// A loader that asserts that the defaultResolve will throw "not found"
// (skipping the top-level main of course, and the built-in ones needed for run-worker).
let mainLoad = true;
export async function resolve(specifier, { importAssertions }, next) {
export async function resolve(specifier, { importAttributes }, next) {
if (mainLoad || specifier === 'path' || specifier === 'worker_threads') {
mainLoad = false;
return next(specifier);
@ -11,6 +11,6 @@ export async function resolve(specifier, { importAssertions }, next) {
await assert.rejects(next(specifier), { code: 'ERR_MODULE_NOT_FOUND' });
return {
url: 'node:fs',
importAssertions,
importAttributes,
};
}

View File

@ -23,7 +23,7 @@ const SOURCES = {
export function resolve(specifier, context, next) {
if (specifier.startsWith('test:')) {
return {
importAssertions: context.importAssertions,
importAttributes: context.importAttributes,
shortCircuit: true,
url: specifier,
};

View File

@ -1 +1 @@
import { ofLife } from '../experimental.json' assert { type: 'json' };
import { ofLife } from '../experimental.json' with { type: 'json' };

View File

@ -1 +1 @@
import secret from '../experimental.json' assert { type: 'json' };
import secret from '../experimental.json' with { type: 'json' };

View File

@ -58,11 +58,11 @@ async function test() {
}
{
const s = new Script('import("foo", { assert: { key: "value" } })', {
importModuleDynamically: common.mustCall((specifier, wrap, assertion) => {
const s = new Script('import("foo", { with: { key: "value" } })', {
importModuleDynamically: common.mustCall((specifier, wrap, attributes) => {
assert.strictEqual(specifier, 'foo');
assert.strictEqual(wrap, s);
assert.deepStrictEqual(assertion, { __proto__: null, key: 'value' });
assert.deepStrictEqual(attributes, { __proto__: null, key: 'value' });
return foo;
}),
});

View File

@ -47,7 +47,7 @@ rm -rf ../node_modules/eslint
eslint-plugin-markdown \
@babel/core \
@babel/eslint-parser \
@babel/plugin-syntax-import-assertions
@babel/plugin-syntax-import-attributes
)
(
cd node_modules/eslint
@ -62,7 +62,7 @@ rm -rf ../node_modules/eslint
eslint-plugin-markdown \
@babel/core \
@babel/eslint-parser \
@babel/plugin-syntax-import-assertions
@babel/plugin-syntax-import-attributes
)
# Use dmn to remove some unneeded files.
"$NODE" "$NPM" exec --package=dmn@2.2.2 --yes -- dmn -f clean

View File

@ -1,19 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _helperPluginUtils = require("@babel/helper-plugin-utils");
var _default = (0, _helperPluginUtils.declare)(api => {
api.assertVersion(7);
return {
name: "syntax-import-assertions",
manipulateOptions(opts, parserOpts) {
parserOpts.plugins.push("importAssertions");
}
};
});
exports.default = _default;
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _helperPluginUtils = require("@babel/helper-plugin-utils");
var _default = (0, _helperPluginUtils.declare)((api, {
deprecatedAssertSyntax
}) => {
api.assertVersion("^7.22.0");
if (deprecatedAssertSyntax != null && typeof deprecatedAssertSyntax !== "boolean") {
throw new Error("'deprecatedAssertSyntax' must be a boolean, if specified.");
}
return {
name: "syntax-import-attributes",
manipulateOptions({
parserOpts,
generatorOpts
}) {
var _generatorOpts$import;
(_generatorOpts$import = generatorOpts.importAttributesKeyword) != null ? _generatorOpts$import : generatorOpts.importAttributesKeyword = "with";
parserOpts.plugins.push(["importAttributes", {
deprecatedAssertSyntax: Boolean(deprecatedAssertSyntax)
}]);
}
};
});
exports.default = _default;
//# sourceMappingURL=index.js.map

View File

@ -1,11 +1,11 @@
{
"name": "@babel/plugin-syntax-import-assertions",
"name": "@babel/plugin-syntax-import-attributes",
"version": "7.22.5",
"description": "Allow parsing of the module assertion attributes in the import statement",
"description": "Allow parsing of the module attributes in the import statement",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-plugin-syntax-import-assertions"
"directory": "packages/babel-plugin-syntax-import-attributes"
},
"license": "MIT",
"publishConfig": {
@ -28,5 +28,9 @@
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)",
"exports": {
".": "./lib/index.js",
"./package.json": "./package.json"
},
"type": "commonjs"
}

View File

@ -1 +1 @@
module.exports={A:{A:{"2":"J D E F A B NC"},B:{"2":"C K L G M N O","132":"P Q R S T U V W X","260":"0 1 2 3 Y Z a b c d e f g h i j k l m r s t u v w x y z H"},C:{"2":"4 5 6 7 8 9 OC 3B I J D E F A B C K L G M N O n o p AB BB CB DB EB FB GB HB IB JB QC RC","132":"KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB 4B gB 5B","260":"0 1 2 3 hB iB jB kB lB mB nB oB pB qB rB q sB tB uB vB wB P Q R 6B S T U V W X Y Z a b c d e f g h i j k l m r s t u v w x y z H xB 7B 8B PC"},D:{"2":"4 5 6 7 8 9 I J D E F A B C K L G M N O n o p AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB","132":"kB lB mB nB oB pB qB rB q sB tB uB vB wB P Q R S T U V W X","194":"fB 4B gB 5B hB iB jB","260":"0 1 2 3 Y Z a b c d e f g h i j k l m r s t u v w x y z H xB 7B 8B"},E:{"1":"2B KC aC","2":"4 I J D E F A B SC 9B TC UC VC WC AC","132":"C K L G yB zB BC XC YC CC DC 0B ZC","516":"EC FC GC HC IC JC","772":"1B"},F:{"2":"5 6 7 8 9 F B C G M N O n o p AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB bC cC dC eC yB LC fC zB","132":"ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB q sB tB","260":"uB vB wB P Q R 6B S T U V W X Y Z a b c d e f g h i j k l m"},G:{"1":"2B KC","2":"E 9B gC MC hC iC jC kC lC mC nC oC pC","132":"qC rC sC tC uC vC","260":"wC xC yC zC CC DC 0B 0C","516":"EC FC GC HC IC JC","772":"1B"},H:{"2":"1C"},I:{"2":"3B I 2C 3C 4C 5C MC 6C 7C","260":"H"},J:{"2":"D A"},K:{"2":"A B C yB LC zB","260":"q"},L:{"260":"H"},M:{"260":"xB"},N:{"2":"A B"},O:{"132":"0B"},P:{"2":"I 8C 9C AD BD","132":"CD AC DD ED FD GD","260":"n o p HD 1B 2B ID JD"},Q:{"132":"BC"},R:{"260":"KD"},S:{"132":"LD","260":"MD"}},B:4,C:"CSS display: contents",D:true};
module.exports={A:{A:{"2":"J D E F A B NC"},B:{"2":"C K L G M N O","132":"P Q R S T U V W X","260":"0 1 2 3 Y Z a b c d e f g h i j k l m r s t u v w x y z H"},C:{"2":"4 5 6 7 8 9 OC 3B I J D E F A B C K L G M N O n o p AB BB CB DB EB FB GB HB IB JB QC RC","132":"KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB 4B gB 5B","260":"0 1 2 3 hB iB jB kB lB mB nB oB pB qB rB q sB tB uB vB wB P Q R 6B S T U V W X Y Z a b c d e f g h i j k l m r s t u v w x y z H xB 7B 8B PC"},D:{"2":"4 5 6 7 8 9 I J D E F A B C K L G M N O n o p AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB","132":"kB lB mB nB oB pB qB rB q sB tB uB vB wB P Q R S T U V W X","194":"fB 4B gB 5B hB iB jB","260":"0 1 2 3 Y Z a b c d e f g h i j k l m r s t u v w x y z H xB 7B 8B"},E:{"2":"4 I J D E F A B SC 9B TC UC VC WC AC","132":"C K L G yB zB BC XC YC CC DC 0B ZC","260":"2B KC aC","772":"1B EC FC GC HC IC JC"},F:{"2":"5 6 7 8 9 F B C G M N O n o p AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB PB QB RB SB TB UB VB WB XB YB bC cC dC eC yB LC fC zB","132":"ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB q sB tB","260":"uB vB wB P Q R 6B S T U V W X Y Z a b c d e f g h i j k l m"},G:{"1":"2B KC","2":"E 9B gC MC hC iC jC kC lC mC nC oC pC","132":"qC rC sC tC uC vC","260":"wC xC yC zC CC DC 0B 0C","516":"EC FC GC HC IC JC","772":"1B"},H:{"2":"1C"},I:{"2":"3B I 2C 3C 4C 5C MC 6C 7C","260":"H"},J:{"2":"D A"},K:{"2":"A B C yB LC zB","260":"q"},L:{"260":"H"},M:{"260":"xB"},N:{"2":"A B"},O:{"132":"0B"},P:{"2":"I 8C 9C AD BD","132":"CD AC DD ED FD GD","260":"n o p HD 1B 2B ID JD"},Q:{"132":"BC"},R:{"260":"KD"},S:{"132":"LD","260":"MD"}},B:4,C:"CSS display: contents",D:true};

View File

@ -1,6 +1,6 @@
{
"name": "caniuse-lite",
"version": "1.0.30001546",
"version": "1.0.30001547",
"description": "A smaller version of caniuse-db, with only the essentials!",
"main": "dist/unpacker/index.js",
"files": [

View File

@ -2306,7 +2306,8 @@ module.exports = {
"22.3.23",
"22.3.24",
"22.3.25",
"22.3.26"
"22.3.26",
"22.3.27"
],
"110.0.5415.0": [
"23.0.0-alpha.1",
@ -2512,7 +2513,8 @@ module.exports = {
"24.8.3",
"24.8.4",
"24.8.5",
"24.8.6"
"24.8.6",
"24.8.7"
],
"114.0.5694.0": [
"25.0.0-alpha.1",
@ -2812,6 +2814,9 @@ module.exports = {
"27.0.0-nightly.20230814",
"27.0.0-nightly.20230815"
],
"118.0.5993.54": [
"27.0.0"
],
"118.0.5991.0": [
"28.0.0-nightly.20230906"
],
@ -2847,6 +2852,8 @@ module.exports = {
"28.0.0-nightly.20231005"
],
"119.0.6045.0": [
"28.0.0-nightly.20231006"
"28.0.0-nightly.20231006",
"28.0.0-nightly.20231009",
"28.0.0-nightly.20231010"
]
};

File diff suppressed because one or more lines are too long

View File

@ -1691,6 +1691,7 @@ module.exports = {
"22.3.24": "108.0.5359.215",
"22.3.25": "108.0.5359.215",
"22.3.26": "108.0.5359.215",
"22.3.27": "108.0.5359.215",
"23.0.0-alpha.1": "110.0.5415.0",
"23.0.0-alpha.2": "110.0.5451.0",
"23.0.0-alpha.3": "110.0.5451.0",
@ -1844,6 +1845,7 @@ module.exports = {
"24.8.4": "112.0.5615.204",
"24.8.5": "112.0.5615.204",
"24.8.6": "112.0.5615.204",
"24.8.7": "112.0.5615.204",
"25.0.0-alpha.1": "114.0.5694.0",
"25.0.0-alpha.2": "114.0.5694.0",
"25.0.0-alpha.3": "114.0.5710.0",
@ -2045,6 +2047,7 @@ module.exports = {
"27.0.0-nightly.20230814": "117.0.5921.0",
"27.0.0-nightly.20230815": "117.0.5921.0",
"27.0.0-nightly.20230816": "118.0.5949.0",
"27.0.0": "118.0.5993.54",
"28.0.0-nightly.20230817": "118.0.5949.0",
"28.0.0-nightly.20230818": "118.0.5949.0",
"28.0.0-nightly.20230821": "118.0.5949.0",
@ -2076,5 +2079,7 @@ module.exports = {
"28.0.0-nightly.20231003": "119.0.6043.0",
"28.0.0-nightly.20231004": "119.0.6043.0",
"28.0.0-nightly.20231005": "119.0.6043.0",
"28.0.0-nightly.20231006": "119.0.6045.0"
"28.0.0-nightly.20231006": "119.0.6045.0",
"28.0.0-nightly.20231009": "119.0.6045.0",
"28.0.0-nightly.20231010": "119.0.6045.0"
};

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "electron-to-chromium",
"version": "1.4.544",
"version": "1.4.549",
"description": "Provides a list of electron-to-chromium version mappings",
"main": "index.js",
"files": [