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 parent URL.
The resolution algorithm determines the full resolved URL for a module load, along with its suggested module format.
It does not determine whether the resolved URL protocol can be loaded, or whether file extensions are permitted, instead
these validations are applied during a 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 extension (ESM_FILE_FORMAT).
If it does not recognize the file extension, then a format of undefined is returned, which will result in an error
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 a legacy CommonJS loader.
In the algorithms below, all subroutine errors are propagated as errors of top-level routines unless stated otherwise.
The resolver can throw the following errors:
ERR_INVALID_MODULE_SPECIFIER— module specifier is an invalid URL, package name or package subpath specifierERR_INVALID_PACKAGE_CONFIG— apackage.jsonconfiguration is invalid or contains an invalid configurationERR_INVALID_PACKAGE_TARGET— package exports or imports define a target module for the package that is an invalid type or string targetERR_MODULE_NOT_FOUND— the package or module requested does not existERR_PACKAGE_IMPORT_NOT_DEFINED— package imports do not define the specifierERR_PACKAGE_PATH_NOT_EXPORTED— package exports do not define or permit a target subpath in the package for the given moduleERR_UNSUPPORTED_DIR_IMPORT— the resolved specifier corresponds to a directory, which is not a supported target for module imports
DETECT_MODULE_SYNTAXESM_FILE_FORMATESM_RESOLVELOOKUP_PACKAGE_SCOPEPACKAGE_EXPORTS_RESOLVEPACKAGE_IMPORTS_EXPORTS_RESOLVEPACKAGE_IMPORTS_RESOLVEPACKAGE_RESOLVEPACKAGE_SELF_RESOLVEPACKAGE_TARGET_RESOLVEPATTERN_KEY_COMPAREREAD_PACKAGE_JSON
- Parse
sourceas an ECMAScript module - If the parse is successful, then
- If
sourcecontains top-levelawait, staticimportorexportstatements, orimport.meta, returntrue - If
sourcecontains a top-level lexical declaration (class,const, orlet) of any CommonJS wrapper variables (__dirname,__filename,exports,module, orrequire) then returntrue
- If
- Else return
false
- ☝️ Assert:
urlcorresponds to an existing file - Let ext be the result the file extension of
url - If
extensionFormatMaphas the key ext,- Let format be the result of
extensionFormatMap.get(ext) - If
--experimental-wasm-modulesis enabled and ext is'.wasm',- Return format
- Return format
- Let format be the result of
- 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
urlends in'.js', then- If packageType is not
null, then- Return packageType.
- If the result of
DETECT_MODULE_SYNTAX(source)istrue, then- Return
'module'
- Return
- Return
'commonjs'
- If packageType is not
- If
urldoes not have any extension, then- If packageType is
'module'and--experimental-wasm-modulesis enabled and the file aturlcontains the header for a WebAssembly module, then- Return
'wasm'
- Return
- If packageType is not
null, then- Return packageType
- If the result of
DETECT_MODULE_SYNTAX(source)istrue, then- Return
'module'
- Return
- Return
'commonjs'
- If packageType is
- Return
undefined(will throw during load phase)
- Let resolved be
undefined - If
specifieris a valid URL, then- Set resolved to the result of parsing and re-serializing
specifieras a URL
- Set resolved to the result of parsing and re-serializing
- Otherwise, if
specifierstarts with'/','./', or'../', then- Set resolved to the URL resolution of
specifierrelative toparent
- Set resolved to the URL resolution of
- Otherwise, if
specifierstarts with'#', then- Set resolved to the result of
PACKAGE_IMPORTS_RESOLVE(specifier, parent, conditions, mainFields
- Set resolved to the result of
- Otherwise,
- 👉 Note:
specifieris now a bare specifier - Set resolved the result of
PACKAGE_RESOLVE(specifier, parent, conditions, mainFields)
- 👉 Note:
- Let format be
undefined - If resolved is a
file:URL, then- If resolved contains any percent encodings of
'/'or'\\'('%2F'and'%5C'respectively), then - If the file at resolved is a directory, then
- If the file at resolved does not exist, then
- Throw
ERR_MODULE_NOT_FOUND
- Throw
- 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, extensionFormatMap)
- If resolved contains any percent encodings of
- Otherwise,
- Set format the module format of the content type associated with the URL resolved
- Return format and resolved to the loading phase
- For each mainField in mainFields, do
- Let tries be the list of possible entry point URL inputs
- Let mainFieldValue be the result of
manifest[mainField] - If mainFieldValue is a string, then
- Push mainFieldValue,
./${value}.js,./${value}.json,./${value}.node,./${value}/index.js,./${value}/index.json, and./${value}/index.nodeinto tries
- Push mainFieldValue,
- Push
'./index.js','./index.json', and'./index.node'into tries - For each item input in tries, do
- Let mainUrl be the URL resolution of input relative to packageUrl
- If the file at mainUrl exists,
- Return mainUrl
- Throw
ERR_MODULE_NOT_FOUND
- Let scopeUrl be
url - While scopeUrl is not
end,- Set scopeUrl to the parent URL of scopeUrl
- If scopeUrl ends in a
'node_modules'path segment, returnnull - Let pjsonUrl be the resolution of
'package.json'within scopeUrl - If the file at pjsonUrl exists, then
- Return scopeUrl
- Return
null
- If
exportsis anObjectwith both a key starting with'.'and a key not starting with'.', - If
subpathis equal to'.', then- Let mainExport be
undefined - If exports is a
Array,Stringor anObjectcontaining no keys starting with'.', then- Set mainExport to
exports
- Set mainExport to
- Otherwise if
exportsis anObjectcontaining a'.'property, then- Set mainExport to
exports['.']
- Set mainExport to
- If mainExport is not
undefined, then- Let resolved be the result of
PACKAGE_TARGET_RESOLVE(packageUrl, mainExport, null, false, conditions) - If resolved is not
nullorundefined,- Return resolved
- Let resolved be the result of
- Let mainExport be
- Otherwise, if
exportsis anObjectand all keys ofexportsstart with'.', then- ☝️ Assert:
subpathbegins with'./' - Let resolved be the result of
PACKAGE_IMPORTS_EXPORTS_RESOLVE(subpath, exports, packageUrl, false) - If resolved is not
nullorundefined,- Return resolved
- ☝️ Assert:
- Throw
ERR_PACKAGE_PATH_NOT_EXPORTED
PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObject, packageUrl, isImports, conditions, mainFields)
- If
matchKeyis a key ofmatchObjand does not contain'*', then- Let target be the value of
matchObj[matchKey] - Return the result of
PACKAGE_TARGET_RESOLVE(packageUrl, target, null, isImports, conditions, mainFields)
- Let target be the value of
- Let expansionKeys be the list of keys of matchObj containing only a single
'*', sorted by the sorting functionPATTERN_KEY_COMPAREwhich 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 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, mainFields)
- Let target the value of
- Let patternTrailer be the substring of expansionKey from the index after the first
- Let patternBase be the substring of expansionKey up to but excluding the first
- Return
null
- ☝️ Assert:
specifierbegins with'#' - If
specifieris exactly equal to'#'or starts with'#/'*, then - Let packageUrl be the result of
LOOKUP_PACKAGE_SCOPE(parent) - 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, mainFields) - If resolved is not
nullorundefined,- Return resolved
- Let resolved be the result of
- Let pjson be the result of
- Throw
ERR_PACKAGE_IMPORT_NOT_DEFINED
- Let packageName be
undefined - If
specifieris an empty string, then - If
specifieris a Node.js builtin module name, then- Return
specifieras anode:URL
- Return
- If
specifierdoes not start with'@', then- Set packageName to the substring of
specifieruntil the first'/'separator or the end of the string
- Set packageName to the substring of
- Otherwise,
- If
specifierdoes not contain a'/'separator, then - Set packageName to the substring of
specifieruntil the second'/'separator or the end of the string
- If
- If packageName starts with
'.'or contains'\\'or'%', then - Let packageSubpath be
'.'concatenated with the substring ofspecifierfrom the position at the length of packageName - If packageSubpath ends in
'/', then - Let selfUrl be the result of
PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parent, conditions) - 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 withspecifier, 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
nulland pjson.exports is notnullorundefined, then- Return the result of
PACKAGE_EXPORTS_RESOLVE(packageUrl, packageSubpath, pjson.exports, conditions)
- Return the result of
- Otherwise, if packageSubpath is equal to
'.', then- Return the result of
LEGACY_MAIN_RESOLVE(packageUrl, pjson, mainFields)
- Return the result of
- Otherwise,
- Return the URL resolution of packageSubpath in packageUrl
- Let packageUrl be the URL resolution of
- Throw
ERR_MODULE_NOT_FOUND
- Let packageUrl be the result of
LOOKUP_PACKAGE_SCOPE(parent) - If packageUrl is
null, then- Return
undefined
- Return
- Let pjson be the result of
READ_PACKAGE_JSON(packageUrl) - If pjson is
nullor if pjson.exports isnullorundefined, then- Return
undefined
- Return
- If pjson.name is equal to
name, then- Return the result of
PACKAGE_EXPORTS_RESOLVE(packageUrl, subpath, pjson.exports, conditions)
- Return the result of
- Otherwise, return
undefined
PACKAGE_TARGET_RESOLVE(packageUrl, target, subpath, patternMatch, isImports, conditions, mainFields)
- If
targetis aString, then- If
targetdoes not start with'./', then- If
isImportsisfalse, or iftargetstarts with'../'or'/', or iftargetis a valid URL, then - If
patternMatchis aString, then- Let matchedTarget be
targetwith every instance of'*'replaced bypatternMatch - Return
PACKAGE_RESOLVE(matchedTarget, packageUrl + '/', conditions, mainFields)
- Let matchedTarget be
- Return
PACKAGE_RESOLVE(target, packageUrl + '/', conditions, mainFields)
- If
- If
targetsplit on'/'or'\\'contains any'','.','..', or'node_modules'segments after the first'.'segment, case insensitive and including percent encoded variants, - Let resolvedTarget be the URL resolution of the concatenation of
packageUrlandtarget - ☝️ Assert:
packageUrlis 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, - Return the URL resolution of resolvedTarget with every instance of
'*'replaced with patternMatch
- If
- Otherwise, if
targetis a non-nullObject, then- If
targetcontains any index property keys, as defined in ECMA-262 6.1.7 Array Index, - For each property p of
target, in object insertion order as,- If p equals
'default'orconditionscontains an entry for p, then- Let targetValue be the value of the p property in
target - Let resolved be the result of
PACKAGE_TARGET_RESOLVE(packageUrl, targetValue, patternMatch, isImports, mainFields) - If resolved is not
undefined, then- Return resolved
- Otherwise, continue the loop
- Let targetValue be the value of the p property in
- If p equals
- Return
undefined
- If
- Otherwise, if
targetis anArray, then- If
target.lengthis zero (0), returnnull - For each item targetValue in
target, do- Let resolved be the result of
PACKAGE_TARGET_RESOLVE(packageUrl, targetValue, patternMatch, isImports, mainFields), continuing the loop on anyERR_INVALID_PACKAGE_TARGET - If resolved is not
undefined, then- Return resolved
- Otherwise, continue the loop
- Let resolved be the result of
- Throw the last fallback resolution error or return
null.
- If
- Otherwise, if
targetisnull, then- Return
target
- Return
- Otherwise,
- ☝️ Assert:
acontains only a single'*' - ☝️ Assert:
bcontains only a single'*' - Let baseLengthA be the index of
'*'ina - Let baseLengthB be the index of
'*'inb - If baseLengthA is greater than baseLengthB
- Return
-1
- Return
- If baseLengthB is greater than baseLengthA
- Return
1
- Return
- If the length of
ais greater than the length ofb- Return
-1
- Return
- If the length of
bis greater than the length ofa- Return
1
- Return
- Return
0
- Let pjsonUrl be the resolution of
'package.json'withinid - If the file at pjsonUrl does not exist, then
- Return
null
- Return
- If the file at pjsonUrl does not parse as valid JSON, then
- If the file at pjsonUrl does not parse to a JSON object, then
- Return the parsed JSON source of the file at pjsonUrl