Modules: ECMAScript modules | Node.js v25.9.0 Documentation
Skip to content
Node.js
About this documentation
Usage and example
Assertion testing
Asynchronous context tracking
Async hooks
Buffer
C++ addons
C/C++ addons with Node-API
C++ embedder API
Child processes
Cluster
Command-line options
Console
Crypto
Debugger
Deprecated APIs
Diagnostics Channel
DNS
Domain
Environment Variables
Errors
Events
File system
Globals
HTTP
HTTP/2
HTTPS
Inspector
Internationalization
Modules: CommonJS modules
Modules: ECMAScript modules
Modules:
node:module
API
Modules: Packages
Modules: TypeScript
Net
Iterable Streams API
OS
Path
Performance hooks
Permissions
Process
Punycode
Query strings
Readline
REPL
Report
Single executable applications
SQLite
Stream
String decoder
Test runner
Timers
TLS/SSL
Trace events
TTY
UDP/datagram
URL
Utilities
V8
VM
WASI
Web Crypto API
Web Streams API
Worker threads
Zlib
Zlib Iterable Compression
Code repository and issue tracker
Table of contents
Modules: ECMAScript modules
Introduction
Enabling
Packages
import
Specifiers
Terminology
Mandatory file extensions
URLs
file:
URLs
data:
imports
node:
imports
Import attributes
Built-in modules
import()
expressions
import.meta
import.meta.dirname
import.meta.filename
import.meta.url
import.meta.main
import.meta.resolve(specifier)
Interoperability with CommonJS
import
statements
require
CommonJS Namespaces
Differences between ES modules and CommonJS
No
require
exports
, or
module.exports
No
__filename
or
__dirname
No Addon Loading
No
require.main
No
require.resolve
No
NODE_PATH
No
require.extensions
No
require.cache
JSON modules
Wasm modules
Wasm Source Phase Imports
JavaScript String Builtins
Wasm Instance Phase Imports
Reserved Wasm Namespaces
Top-level
await
Loaders
Resolution and loading algorithm
Features
Resolution algorithm
Resolution Algorithm Specification
Customizing ESM specifier resolution algorithm
Modules: ECMAScript modules
Added in: v8.5.0
History
Version
Changes
v23.1.0, v22.12.0, v20.18.3, v18.20.5
Import attributes are no longer experimental.
v22.0.0
Drop support for import assertions.
v21.0.0, v20.10.0, v18.20.0
Add experimental support for import attributes.
v20.0.0, v18.19.0
Module customization hooks are executed off the main thread.
v18.6.0, v16.17.0
Add support for chaining module customization hooks.
v17.1.0, v16.14.0
Add experimental support for import assertions.
v17.0.0, v16.12.0
Consolidate customization hooks, removed
getFormat
getSource
transformSource
, and
getGlobalPreloadCode
hooks added
load
and
globalPreload
hooks allowed returning
format
from either
resolve
or
load
hooks.
v15.3.0, v14.17.0, v12.22.0
Stabilize modules implementation.
v14.13.0, v12.20.0
Support for detection of CommonJS named exports.
v14.8.0
Unflag Top-Level Await.
v14.0.0, v13.14.0, v12.20.0
Remove experimental modules warning.
v13.2.0, v12.17.0
Loading ECMAScript modules no longer requires a command-line flag.
v12.0.0
Add support for ES modules using
.js
file extension via
package.json
"type"
field.
Stability: 2
- Stable
Introduction
ECMAScript modules are
the official standard format
to package JavaScript
code for reuse. Modules are defined using a variety of
import
and
export
statements.
The following example of an ES module exports a function:
// addTwo.mjs
function
addTwo
num
return
num
export
addTwo
};
The following example of an ES module imports the function from
addTwo.mjs
// app.mjs
import
addTwo
from
'./addTwo.mjs'
// Prints: 6
console
log
addTwo
))
Node.js fully supports ECMAScript modules as they are currently specified and
provides interoperability between them and its original module format,
CommonJS
Enabling
Node.js has two module systems:
CommonJS
modules and ECMAScript modules.
Authors can tell Node.js to interpret JavaScript as an ES module via the
.mjs
file extension, the
package.json
"type"
field with a value
"module"
or the
--input-type
flag with a value of
"module"
. These are explicit
markers of code being intended to run as an ES module.
Inversely, authors can explicitly tell Node.js to interpret JavaScript as
CommonJS via the
.cjs
file extension, the
package.json
"type"
field
with a value
"commonjs"
, or the
--input-type
flag with a value of
"commonjs"
When code lacks explicit markers for either module system, Node.js will inspect
the source code of a module to look for ES module syntax. If such syntax is
found, Node.js will run the code as an ES module; otherwise it will run the
module as CommonJS. See
Determining module system
for more details.
Packages
This section was moved to
Modules: Packages
import
Specifiers
Terminology
The
specifier
of an
import
statement is the string after the
from
keyword,
e.g.
'node:path'
in
import { sep } from 'node:path'
. Specifiers are also
used in
export from
statements, and as the argument to an
import()
expression.
There are three types of specifiers:
Relative specifiers
like
'./startup.js'
or
'../config.mjs'
. They refer
to a path relative to the location of the importing file.
The file extension
is always necessary for these.
Bare specifiers
like
'some-package'
or
'some-package/shuffle'
. They can
refer to the main entry point of a package by the package name, or a
specific feature module within a package prefixed by the package name as per
the examples respectively.
Including the file extension is only necessary
for packages without an
"exports"
field.
Absolute specifiers
like
'file:///opt/nodejs/config.js'
. They refer
directly and explicitly to a full path.
Bare specifier resolutions are handled by the
Node.js module
resolution and loading algorithm
All other specifier resolutions are always only resolved with
the standard relative
URL
resolution semantics.
Like in CommonJS, module files within packages can be accessed by appending a
path to the package name unless the package's
package.json
contains an
"exports"
field, in which case files within packages can only be accessed
via the paths defined in
"exports"
For details on these package resolution rules that apply to bare specifiers in
the Node.js module resolution, see the
packages documentation
Mandatory file extensions
A file extension must be provided when using the
import
keyword to resolve
relative or absolute specifiers. Directory indexes (e.g.
'./startup/index.js'
must also be fully specified.
This behavior matches how
import
behaves in browser environments, assuming a
typically configured server.
URLs
ES modules are resolved and cached as URLs. This means that special characters
must be
percent-encoded
, such as
with
%23
and
with
%3F
file:
node:
, and
data:
URL schemes are supported. A specifier like
'https://example.com/app.js'
is not supported natively in Node.js unless using
custom HTTPS loader
file:
URLs
Modules are loaded multiple times if the
import
specifier used to resolve
them has a different query or fragment.
import
'./foo.mjs?query=1'
// loads ./foo.mjs with query of "?query=1"
import
'./foo.mjs?query=2'
// loads ./foo.mjs with query of "?query=2"
The volume root may be referenced via
//
, or
file:///
. Given the
differences between
URL
and path resolution (such as percent encoding
details), it is recommended to use
url.pathToFileURL
when importing a path.
data:
imports
Added in: v12.10.0
data:
URLs
are supported for importing with the following MIME types:
text/javascript
for ES modules
application/json
for JSON
application/wasm
for Wasm
import
'data:text/javascript,console.log("hello!");'
import
from
'data:application/json,"world!"'
with
type
'json'
};
data:
URLs only resolve
bare specifiers
for builtin modules
and
absolute specifiers
. Resolving
relative specifiers
does not work because
data:
is not a
special scheme
. For example, attempting to load
./foo
from
data:text/javascript,import "./foo";
fails to resolve because there
is no concept of relative resolution for
data:
URLs.
node:
imports
Added in: v14.13.1, v12.20.0
History
Version
Changes
v16.0.0, v14.18.0
Added
node:
import support to
require(...)
node:
URLs are supported as an alternative means to load Node.js builtin
modules. This URL scheme allows for builtin modules to be referenced by valid
absolute URL strings.
import
fs
from
'node:fs/promises'
Import attributes
Added in: v17.1.0, v16.14.0
History
Version
Changes
v21.0.0, v20.10.0, v18.20.0
Switch from Import Assertions to Import Attributes.
Import attributes
are an inline syntax for module import
statements to pass on more information alongside the module specifier.
import
fooData
from
'./foo.json'
with
type
'json'
};
const
default
barData
await
import
'./bar.json'
with
type
'json'
Node.js only supports the
type
attribute, for which it supports the following values:
Attribute
type
Needed for
'json'
JSON modules
The
type: 'json'
attribute is mandatory when importing JSON modules.
Built-in modules
Built-in modules
provide named exports of their public API. A
default export is also provided which is the value of the CommonJS exports.
The default export can be used for, among other things, modifying the named
exports. Named exports of built-in modules are updated only by calling
module.syncBuiltinESMExports()
import
EventEmitter
from
'node:events'
const
new
EventEmitter
()
import
readFile
from
'node:fs'
readFile
'./foo.txt'
err
source
=>
if
(err)
console
error
(err)
else
console
log
(source)
import
fs
readFileSync
from
'node:fs'
import
syncBuiltinESMExports
from
'node:module'
import
Buffer
from
'node:buffer'
fs
readFileSync
()
=>
Buffer
from
'Hello, ESM'
syncBuiltinESMExports
()
fs
readFileSync
===
readFileSync
When importing built-in modules, all the named exports (i.e. properties of the module exports object)
are populated even if they are not individually accessed.
This can make initial imports of built-in modules slightly slower compared to loading them with
require()
or
process.getBuiltinModule()
, where the module exports object is evaluated immediately,
but some of its properties may only be initialized when first accessed individually.
import()
expressions
Dynamic
import()
provides an asynchronous way to import modules. It is
supported in both CommonJS and ES modules, and can be used to load both CommonJS
and ES modules.
import.meta
Type:

The
import.meta
meta property is an
Object
that contains the following
properties. It is only supported in ES modules.
import.meta.dirname
Added in: v21.2.0, v20.11.0
History
Version
Changes
v24.0.0, v22.16.0
This property is no longer experimental.
Type:

The directory name of the current module.
This is the same as the
path.dirname()
of the
import.meta.filename
Caveat
: only present on
file:
modules.
import.meta.filename
Added in: v21.2.0, v20.11.0
History
Version
Changes
v24.0.0, v22.16.0
This property is no longer experimental.
Type:

The full absolute path and filename of the current module, with
symlinks resolved.
This is the same as the
url.fileURLToPath()
of the
import.meta.url
Caveat
only local modules support this property. Modules not using the
file:
protocol will not provide it.
import.meta.url
Type:

The absolute
file:
URL of the module.
This is defined exactly the same as it is in browsers providing the URL of the
current module file.
This enables useful patterns such as relative file loading:
import
readFileSync
from
'node:fs'
const
buffer
readFileSync
new
URL
'./data.proto'
import
meta
url))
import.meta.main
Added in: v24.2.0, v22.18.0
Stability: 1.0 - Early development
Type:

true
when the current module is the entry point of the current process;
false
otherwise.
Equivalent to
require.main === module
in CommonJS.
Analogous to Python's
__name__ == "__main__"
export
function
foo
()
return
'Hello, world'
function
main
()
const
message
foo
()
console
log
(message)
if
import
meta
main)
main
()
// `foo` can be imported from another module without possible side-effects from `main`
import.meta.resolve(specifier)
Added in: v13.9.0, v12.16.2
History
Version
Changes
v20.6.0, v18.19.0
No longer behind
--experimental-import-meta-resolve
CLI flag, except for the non-standard
parentURL
parameter.
v20.6.0, v18.19.0
This API no longer throws when targeting
file:
URLs that do not map to an existing file on the local FS.
v20.0.0, v18.19.0
This API now returns a string synchronously instead of a Promise.
v16.2.0, v14.18.0
Add support for WHATWG
URL
object to
parentURL
parameter.
Stability: 1.2 - Release candidate
specifier

The module specifier to resolve relative to the
current module.
Returns:

The absolute URL string that the specifier would resolve to.
import.meta.resolve
is a module-relative resolution function scoped to
each module, returning the URL string.
const
dependencyAsset
import
meta
resolve
'component-lib/asset.css'
// file:///app/node_modules/component-lib/asset.css
import
meta
resolve
'./dep.js'
// file:///app/dep.js
All features of the Node.js module resolution are supported. Dependency
resolutions are subject to the permitted exports resolutions within the package.
Caveats
This can result in synchronous file-system operations, which
can impact performance similarly to
require.resolve
This feature is not available within custom loaders (it would
create a deadlock).
Non-standard API
When using the
--experimental-import-meta-resolve
flag, that function accepts
a second argument:
parent


An optional absolute parent module URL to resolve from.
Default:
import.meta.url
Interoperability with CommonJS
import
statements
An
import
statement can reference an ES module or a CommonJS module.
import
statements are permitted only in ES modules, but dynamic
import()
expressions are supported in CommonJS for loading ES modules.
When importing
CommonJS modules
, the
module.exports
object is provided as the default export. Named exports may be
available, provided by static analysis as a convenience for better ecosystem
compatibility.
require
The CommonJS module
require
currently only supports loading synchronous ES
modules (that is, ES modules that do not use top-level
await
).
See
Loading ECMAScript modules using
require()
for details.
CommonJS Namespaces
Added in: v14.13.0
History
Version
Changes
v23.0.0
Added
'module.exports'
export marker to CJS namespaces.
CommonJS modules consist of a
module.exports
object which can be of any type.
To support this, when importing CommonJS from an ECMAScript module, a namespace
wrapper for the CommonJS module is constructed, which always provides a
default
export key pointing to the CommonJS
module.exports
value.
In addition, a heuristic static analysis is performed against the source text of
the CommonJS module to get a best-effort static list of exports to provide on
the namespace from values on
module.exports
. This is necessary since these
namespaces must be constructed prior to the evaluation of the CJS module.
These CommonJS namespace objects also provide the
default
export as a
'module.exports'
named export, in order to unambiguously indicate that their
representation in CommonJS uses this value, and not the namespace value. This
mirrors the semantics of the handling of the
'module.exports'
export name in
require(esm)
interop support.
When importing a CommonJS module, it can be reliably imported using the ES
module default import or its corresponding sugar syntax:
import
default
as
cjs
from
'cjs'
// Identical to the above
import
cjsSugar
from
'cjs'
console
log
(cjs)
console
log
(cjs
===
cjsSugar)
// Prints:
//
// true
This Module Namespace Exotic Object can be directly observed either when using
import * as m from 'cjs'
or a dynamic import:
import
as
from
'cjs'
console
log
(m)
console
log
(m
===
await
import
'cjs'
))
// Prints:
// [Module] { default: , 'module.exports': }
// true
For better compatibility with existing usage in the JS ecosystem, Node.js
in addition attempts to determine the CommonJS named exports of every imported
CommonJS module to provide them as separate ES module exports using a static
analysis process.
For example, consider a CommonJS module written:
// cjs.cjs
exports
name
'exported'
The preceding module supports named imports in ES modules:
import
name
from
'./cjs.cjs'
console
log
(name)
// Prints: 'exported'
import
cjs
from
'./cjs.cjs'
console
log
(cjs)
// Prints: { name: 'exported' }
import
as
from
'./cjs.cjs'
console
log
(m)
// Prints:
// [Module] {
// default: { name: 'exported' },
// 'module.exports': { name: 'exported' },
// name: 'exported'
// }
As can be seen from the last example of the Module Namespace Exotic Object being
logged, the
name
export is copied off of the
module.exports
object and set
directly on the ES module namespace when the module is imported.
Live binding updates or new exports added to
module.exports
are not detected
for these named exports.
The detection of named exports is based on common syntax patterns but does not
always correctly detect named exports. In these cases, using the default
import form described above can be a better option.
Named exports detection covers many common export patterns, reexport patterns
and build tool and transpiler outputs. See
merve
for the exact
semantics implemented.
Differences between ES modules and CommonJS
No
require
exports
, or
module.exports
In most cases, the ES module
import
can be used to load CommonJS modules.
If needed, a
require
function can be constructed within an ES module using
module.createRequire()
No
__filename
or
__dirname
These CommonJS variables are not available in ES modules.
__filename
and
__dirname
use cases can be replicated via
import.meta.filename
and
import.meta.dirname
No Addon Loading
Addons
are not currently supported with ES module imports.
They can instead be loaded with
module.createRequire()
or
process.dlopen
No
require.main
To replace
require.main === module
, there is the
import.meta.main
API.
No
require.resolve
Relative resolution can be handled via
new URL('./local', import.meta.url)
For a complete
require.resolve
replacement, there is the
import.meta.resolve
API.
Alternatively
module.createRequire()
can be used.
No
NODE_PATH
NODE_PATH
is not part of resolving
import
specifiers. Please use symlinks
if this behavior is desired.
No
require.extensions
require.extensions
is not used by
import
. Module customization hooks can
provide a replacement.
No
require.cache
require.cache
is not used by
import
as the ES module loader has its own
separate cache.
JSON modules
History
Version
Changes
v23.1.0, v22.12.0, v20.18.3, v18.20.5
JSON modules are no longer experimental.
JSON files can be referenced by
import
import
packageConfig
from
'./package.json'
with
type
'json'
};
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.
The same object is returned in CommonJS if the JSON module has already been
imported from the same path.
Wasm modules
History
Version
Changes
v24.5.0, v22.19.0
Wasm modules no longer require the
--experimental-wasm-modules
flag.
Importing both WebAssembly module instances and WebAssembly source phase
imports is supported.
Both of these integrations are in line with the
ES Module Integration Proposal for WebAssembly
Wasm Source Phase Imports
Added in: v24.0.0
Stability: 1.2 - Release candidate
The
Source Phase Imports
proposal allows the
import source
keyword
combination to import a
WebAssembly.Module
object directly, instead of getting
a module instance already instantiated with its dependencies.
This is useful when needing custom instantiations for Wasm, while still
resolving and loading it through the ES module integration.
For example, to create multiple instances of a module, or to pass custom imports
into a new instance of
library.wasm
import
source libraryModule
from
'./library.wasm'
const
instance1
await
WebAssembly
instantiate
(libraryModule
importObject1)
const
instance2
await
WebAssembly
instantiate
(libraryModule
importObject2)
In addition to the static source phase, there is also a dynamic variant of the
source phase via the
import.source
dynamic phase import syntax:
const
dynamicLibrary
await
import
source
'./library.wasm'
const
instance
await
WebAssembly
instantiate
(dynamicLibrary
importObject)
JavaScript String Builtins
Added in: v24.5.0, v22.19.0
Stability: 1.2 - Release candidate
When importing WebAssembly modules, the
WebAssembly JS String Builtins Proposal
is automatically enabled through the
ESM Integration. This allows WebAssembly modules to directly use efficient
compile-time string builtins from the
wasm:js-string
namespace.
For example, the following Wasm module exports a string
getLength
function using
the
wasm:js-string
length
builtin:
(module
;; Compile-time import of the string length builtin.
(import "wasm:js-string" "length" (func $string_length (param externref) (result i32)))
;; Define getLength, taking a JS value parameter assumed to be a string,
;; calling string length on it and returning the result.
(func $getLength (param $str externref) (result i32)
local.get $str
call $string_length
;; Export the getLength function.
(export "getLength" (func $get_length))
import
getLength
from
'./string-len.wasm'
getLength
'foo'
// Returns 3.
Wasm builtins are compile-time imports that are linked during module compilation
rather than during instantiation. They do not behave like normal module graph
imports and they cannot be inspected via
WebAssembly.Module.imports(mod)
or virtualized unless recompiling the module using the direct
WebAssembly.compile
API with string builtins disabled.
String constants may also be imported from the
wasm:js/string-constants
builtin
import URL, allowing static JS string globals to be defined:
(module
(import "wasm:js/string-constants" "hello" (global $hello externref))
Importing a module in the source phase before it has been instantiated will also
use the compile-time builtins automatically:
import
source mod
from
'./string-len.wasm'
const
exports
getLength
await
WebAssembly
instantiate
(mod
{}
getLength
'foo'
// Also returns 3.
Wasm Instance Phase Imports
Stability: 1.1 - Active development
Instance imports allow any
.wasm
files to be imported as normal modules,
supporting their module imports in turn.
For example, an
index.js
containing:
import
as
from
'./library.wasm'
console
log
(M)
executed under:
node
index.mjs
would provide the exports interface for the instantiation of
library.wasm
Reserved Wasm Namespaces
Added in: v24.5.0, v22.19.0
When importing WebAssembly module instances, they cannot use import module
names or import/export names that start with reserved prefixes:
wasm-js:
- reserved in all module import names, module names and export
names.
wasm:
- reserved in module import names and export names (imported module
names are allowed in order to support future builtin polyfills).
Importing a module using the above reserved names will throw a
WebAssembly.LinkError
Top-level
await
Added in: v14.8.0
The
await
keyword may be used in the top level body of an ECMAScript module.
Assuming an
a.mjs
with
export
const
five
await
Promise
resolve
And a
b.mjs
with
import
five
from
'./a.mjs'
console
log
(five)
// Logs `5`
node
b.mjs
# works
If a top level
await
expression never resolves, the
node
process will exit
with a
13
status code
import
spawn
from
'node:child_process'
import
execPath
from
'node:process'
spawn
(execPath
'--input-type=module'
'--eval'
// Never-resolving Promise:
'await new Promise(() => {})'
])
once
'exit'
code
=>
console
log
(code)
// Logs `13`
Loaders
The former Loaders documentation is now at
Modules: Customization hooks
Resolution and loading algorithm
Features
The default resolver has the following properties:
FileURL-based resolution as is used by ES modules
Relative and absolute URL resolution
No default extensions
No folder mains
Bare specifier package resolution lookup through node_modules
Does not fail on unknown extensions or protocols
Can optionally provide a hint of the format to the loading phase
The default loader has the following properties
Support for builtin module loading via
node:
URLs
Support for "inline" module loading via
data:
URLs
Support for
file:
module loading
Fails on any other URL protocol
Fails on unknown extensions for
file:
loading
(supports only
.cjs
.js
, and
.mjs
Resolution algorithm
The algorithm to load an ES module specifier is given through the
ESM_RESOLVE
method below. It returns the resolved URL for a
module specifier relative to a parentURL.
The resolution algorithm determines the full resolved URL for a module
load, along with its suggested module format. The resolution algorithm
does not determine whether the resolved URL protocol can be loaded,
or whether the file extensions are permitted, instead these validations
are applied by Node.js during the load phase
(for example, if it was asked to load a URL that has a protocol that is
not
file:
data:
or
node:
The algorithm also tries to determine the format of the file based
on the extension (see
ESM_FILE_FORMAT
algorithm below). If it does
not recognize the file extension (eg if it is not
.mjs
.cjs
, or
.json
), then a format of
undefined
is returned,
which will throw during the load phase.
The algorithm to determine the module format of a resolved URL is
provided by
ESM_FILE_FORMAT
, which returns the unique module
format for any file. The
"module"
format is returned for an ECMAScript
Module, while the
"commonjs"
format is used to indicate loading through the
legacy CommonJS loader. Additional formats such as
"addon"
can be extended in
future updates.
In the following algorithms, all subroutine errors are propagated as errors
of these top-level routines unless stated otherwise.
defaultConditions
is the conditional environment name array,
["node", "import"]
The resolver can throw the following errors:
Invalid Module Specifier
: Module specifier is an invalid URL, package name
or package subpath specifier.
Invalid Package Configuration
: package.json configuration is invalid or
contains an invalid configuration.
Invalid Package Target
: Package exports or imports define a target module
for the package that is an invalid type or string target.
Package Path Not Exported
: Package exports do not define or permit a target
subpath in the package for the given module.
Package Import Not Defined
: Package imports do not define the specifier.
Module Not Found
: The package or module requested does not exist.
Unsupported Directory Import
: The resolved path corresponds to a directory,
which is not a supported target for module imports.
Resolution Algorithm Specification
ESM_RESOLVE
specifier
parentURL
Let
resolved
be
undefined
If
specifier
is a valid URL, then
Set
resolved
to the result of parsing and reserializing
specifier
as a URL.
Otherwise, if
specifier
starts with
"/"
"./"
, or
"../"
, then
Set
resolved
to the URL resolution of
specifier
relative to
parentURL
Otherwise, if
specifier
starts with
"#"
, then
Set
resolved
to the result of
PACKAGE_IMPORTS_RESOLVE
specifier
parentURL
defaultConditions
).
Otherwise,
Note:
specifier
is now a bare specifier.
Set
resolved
the result of
PACKAGE_RESOLVE
specifier
parentURL
).
Let
format
be
undefined
If
resolved
is a
"file:"
URL, then
If
resolved
contains any percent encodings of
"/"
or
"\"
"%2F"
and
"%5C"
respectively), then
Throw an
Invalid Module Specifier
error.
If the file at
resolved
is a directory, then
Throw an
Unsupported Directory Import
error.
If the file at
resolved
does not exist, then
Throw a
Module Not Found
error.
Set
resolved
to the real path of
resolved
, maintaining the
same URL querystring and fragment components.
Set
format
to the result of
ESM_FILE_FORMAT
resolved
).
Otherwise,
Set
format
the module format of the content type associated with the
URL
resolved
Return
format
and
resolved
to the loading phase
PACKAGE_RESOLVE
packageSpecifier
parentURL
Let
packageName
be
undefined
If
packageSpecifier
is an empty string, then
Throw an
Invalid Module Specifier
error.
If
packageSpecifier
is a Node.js builtin module name, then
Return the string
"node:"
concatenated with
packageSpecifier
If
packageSpecifier
does not start with
"@"
, then
Set
packageName
to the substring of
packageSpecifier
until the first
"/"
separator or the end of the string.
Otherwise,
If
packageSpecifier
does not contain a
"/"
separator, then
Throw an
Invalid Module Specifier
error.
Set
packageName
to the substring of
packageSpecifier
until the second
"/"
separator or the end of the string.
If
packageName
starts with
"."
or contains
"\"
or
"%"
, then
Throw an
Invalid Module Specifier
error.
Let
packageSubpath
be
"."
concatenated with the substring of
packageSpecifier
from the position at the length of
packageName
Let
selfUrl
be the result of
PACKAGE_SELF_RESOLVE
packageName
packageSubpath
parentURL
).
If
selfUrl
is not
undefined
, return
selfUrl
While
parentURL
is not the file system root,
Let
packageURL
be the URL resolution of
"node_modules/"
concatenated with
packageName
, relative to
parentURL
Set
parentURL
to the parent folder URL of
parentURL
If the folder at
packageURL
does not exist, then
Continue the next loop iteration.
Let
pjson
be the result of
READ_PACKAGE_JSON
packageURL
).
If
pjson
is not
null
and
pjson
exports
is not
null
or
undefined
, then
Return the result of
PACKAGE_EXPORTS_RESOLVE
packageURL
packageSubpath
pjson.exports
defaultConditions
).
Otherwise, if
packageSubpath
is equal to
"."
, then
If
pjson.main
is a string, then
Return the URL resolution of
main
in
packageURL
Otherwise,
Return the URL resolution of
packageSubpath
in
packageURL
Throw a
Module Not Found
error.
PACKAGE_SELF_RESOLVE
packageName
packageSubpath
parentURL
Let
packageURL
be the result of
LOOKUP_PACKAGE_SCOPE
parentURL
).
If
packageURL
is
null
, then
Return
undefined
Let
pjson
be the result of
READ_PACKAGE_JSON
packageURL
).
If
pjson
is
null
or if
pjson
exports
is
null
or
undefined
, then
Return
undefined
If
pjson.name
is equal to
packageName
, then
Return the result of
PACKAGE_EXPORTS_RESOLVE
packageURL
packageSubpath
pjson.exports
defaultConditions
).
Otherwise, return
undefined
PACKAGE_EXPORTS_RESOLVE
packageURL
subpath
exports
conditions
Note: This function is directly invoked by the CommonJS resolution algorithm.
If
exports
is an Object with both a key starting with
"."
and a key not
starting with
"."
, throw an
Invalid Package Configuration
error.
If
subpath
is equal to
"."
, then
Let
mainExport
be
undefined
If
exports
is a String or Array, or an Object containing no keys
starting with
"."
, then
Set
mainExport
to
exports
Otherwise if
exports
is an Object containing a
"."
property, then
Set
mainExport
to
exports
"."
].
If
mainExport
is not
undefined
, then
Let
resolved
be the result of
PACKAGE_TARGET_RESOLVE
packageURL
mainExport
null
false
conditions
).
If
resolved
is not
null
or
undefined
, return
resolved
Otherwise, if
exports
is an Object and all keys of
exports
start with
"."
, then
Assert:
subpath
begins with
"./"
Let
resolved
be the result of
PACKAGE_IMPORTS_EXPORTS_RESOLVE
subpath
exports
packageURL
false
conditions
).
If
resolved
is not
null
or
undefined
, return
resolved
Throw a
Package Path Not Exported
error.
PACKAGE_IMPORTS_RESOLVE
specifier
parentURL
conditions
Note: This function is directly invoked by the CommonJS resolution algorithm.
Assert:
specifier
begins with
"#"
If
specifier
is exactly equal to
"#"
, then
Throw an
Invalid Module Specifier
error.
Let
packageURL
be the result of
LOOKUP_PACKAGE_SCOPE
parentURL
).
If
packageURL
is not
null
, then
Let
pjson
be the result of
READ_PACKAGE_JSON
packageURL
).
If
pjson.imports
is a non-null Object, then
Let
resolved
be the result of
PACKAGE_IMPORTS_EXPORTS_RESOLVE
specifier
pjson.imports
packageURL
true
conditions
).
If
resolved
is not
null
or
undefined
, return
resolved
Throw a
Package Import Not Defined
error.
PACKAGE_IMPORTS_EXPORTS_RESOLVE
matchKey
matchObj
packageURL
isImports
conditions
If
matchKey
ends in
"/"
, then
Throw an
Invalid Module Specifier
error.
If
matchKey
is a key of
matchObj
and does not contain
"*"
, then
Let
target
be the value of
matchObj
matchKey
].
Return the result of
PACKAGE_TARGET_RESOLVE
packageURL
target
null
isImports
conditions
).
Let
expansionKeys
be the list of keys of
matchObj
containing only a
single
"*"
, sorted by the sorting function
PATTERN_KEY_COMPARE
which orders in descending order of specificity.
For each key
expansionKey
in
expansionKeys
, do
Let
patternBase
be the substring of
expansionKey
up to but excluding
the first
"*"
character.
If
matchKey
starts with but is not equal to
patternBase
, then
Let
patternTrailer
be the substring of
expansionKey
from the
index after the first
"*"
character.
If
patternTrailer
has zero length, or if
matchKey
ends with
patternTrailer
and the length of
matchKey
is greater than or
equal to the length of
expansionKey
, then
Let
target
be the value of
matchObj
expansionKey
].
Let
patternMatch
be the substring of
matchKey
starting at the
index of the length of
patternBase
up to the length of
matchKey
minus the length of
patternTrailer
Return the result of
PACKAGE_TARGET_RESOLVE
packageURL
target
patternMatch
isImports
conditions
).
Return
null
PATTERN_KEY_COMPARE
keyA
keyB
Assert:
keyA
contains only a single
"*"
Assert:
keyB
contains only a single
"*"
Let
baseLengthA
be the index of
"*"
in
keyA
Let
baseLengthB
be the index of
"*"
in
keyB
If
baseLengthA
is greater than
baseLengthB
, return -1.
If
baseLengthB
is greater than
baseLengthA
, return 1.
If the length of
keyA
is greater than the length of
keyB
, return -1.
If the length of
keyB
is greater than the length of
keyA
, return 1.
Return 0.
PACKAGE_TARGET_RESOLVE
packageURL
target
patternMatch
isImports
conditions
If
target
is a String, then
If
target
does not start with
"./"
, then
If
isImports
is
false
, or if
target
starts with
"../"
or
"/"
, or if
target
is a valid URL, then
Throw an
Invalid Package Target
error.
If
patternMatch
is a String, then
Return
PACKAGE_RESOLVE
target
with every instance of
"*"
replaced by
patternMatch
packageURL
"/"
).
Return
PACKAGE_RESOLVE
target
packageURL
"/"
).
If
target
split on
"/"
or
"\"
contains any
""
"."
".."
or
"node_modules"
segments after the first
"."
segment, case
insensitive and including percent encoded variants, throw an
Invalid
Package Target
error.
Let
resolvedTarget
be the URL resolution of the concatenation of
packageURL
and
target
Assert:
packageURL
is contained in
resolvedTarget
If
patternMatch
is
null
, then
Return
resolvedTarget
If
patternMatch
split on
"/"
or
"\"
contains any
""
"."
".."
, or
"node_modules"
segments, case insensitive and including
percent encoded variants, throw an
Invalid Module Specifier
error.
Return the URL resolution of
resolvedTarget
with every instance of
"*"
replaced with
patternMatch
Otherwise, if
target
is a non-null Object, then
If
target
contains any index property keys, as defined in ECMA-262
6.1.7 Array Index
, throw an
Invalid Package Configuration
error.
For each property
of
target
, in object insertion order as,
If
equals
"default"
or
conditions
contains an entry for
then
Let
targetValue
be the value of the
property in
target
Let
resolved
be the result of
PACKAGE_TARGET_RESOLVE
packageURL
targetValue
patternMatch
isImports
conditions
).
If
resolved
is equal to
undefined
, continue the loop.
Return
resolved
Return
undefined
Otherwise, if
target
is an Array, then
If _target.length is zero, return
null
For each item
targetValue
in
target
, do
Let
resolved
be the result of
PACKAGE_TARGET_RESOLVE
packageURL
targetValue
patternMatch
isImports
conditions
), continuing the loop on any
Invalid Package Target
error.
If
resolved
is
undefined
, continue the loop.
Return
resolved
Return or throw the last fallback resolution
null
return or error.
Otherwise, if
target
is
null
, return
null
Otherwise throw an
Invalid Package Target
error.
ESM_FILE_FORMAT
url
Assert:
url
corresponds to an existing file.
If
url
ends in
".mjs"
, then
Return
"module"
If
url
ends in
".cjs"
, then
Return
"commonjs"
If
url
ends in
".json"
, then
Return
"json"
If
url
ends in
".wasm"
, then
Return
"wasm"
If
--experimental-addon-modules
is enabled and
url
ends in
".node"
, then
Return
"addon"
Let
packageURL
be the result of
LOOKUP_PACKAGE_SCOPE
url
).
Let
pjson
be the result of
READ_PACKAGE_JSON
packageURL
).
Let
packageType
be
null
If
pjson?.type
is
"module"
or
"commonjs"
, then
Set
packageType
to
pjson.type
If
url
ends in
".js"
, then
If
packageType
is not
null
, then
Return
packageType
If the result of
DETECT_MODULE_SYNTAX
source
) is true, then
Return
"module"
Return
"commonjs"
If
url
does not have any extension, then
If
packageType
is
"module"
and the file at
url
contains the
"application/wasm" content type header for a WebAssembly module, then
Return
"wasm"
If
packageType
is not
null
, then
Return
packageType
If the result of
DETECT_MODULE_SYNTAX
source
) is true, then
Return
"module"
Return
"commonjs"
Return
undefined
(will throw during load phase).
LOOKUP_PACKAGE_SCOPE
url
Let
scopeURL
be
url
While
scopeURL
is not the file system root,
Set
scopeURL
to the parent URL of
scopeURL
If
scopeURL
ends in a
"node_modules"
path segment, return
null
Let
pjsonURL
be the resolution of
"package.json"
within
scopeURL
if the file at
pjsonURL
exists, then
Return
scopeURL
Return
null
READ_PACKAGE_JSON
packageURL
Let
pjsonURL
be the resolution of
"package.json"
within
packageURL
If the file at
pjsonURL
does not exist, then
Return
null
If the file at
packageURL
does not parse as valid JSON, then
Throw an
Invalid Package Configuration
error.
Return the parsed JSON source of the file at
pjsonURL
DETECT_MODULE_SYNTAX
source
Parse
source
as an ECMAScript module.
If the parse is successful, then
If
source
contains top-level
await
, static
import
or
export
statements, or
import.meta
, return
true
If
source
contains a top-level lexical declaration (
const
let
or
class
) of any of the CommonJS wrapper variables (
require
exports
module
__filename
, or
__dirname
) then return
true
Return
false
Customizing ESM specifier resolution algorithm
Module customization hooks
provide a mechanism for customizing the ESM
specifier resolution algorithm. An example that provides CommonJS-style
resolution for ESM specifiers is
commonjs-extension-resolution-loader

C U Cyber History — Public Interest Web Archive