This is the primary specification of the jspm 2.0 resolver algorithm.
Module specifiers are considered relative URLs, so obey URL encoding and normalization rules.
This specification handles resolution in path space, including handling of .
and ..
internal segments, converting \\
into /
for the resolution process, before outputting a valid pathname for the environment using the "/" separator at the end of resolution. The reason for this is that we can avoid URL encoding / decoding this way until the end.
When resolving paths in Windows, we ensure that resolution uses the /
separator for greater consistency between platforms, replacing any instances of the \\
separator on resolution.
To match URL resolution behaviours, /c:/path
and //\\c:\\path
will resolve absolutely into file:///c:/path
, while c:\\path
will resolve to an invalid c:
protocol and //c:/path
will fail resolution.
Automatic file extensions and directory indices are only provided for packages that are in the CommonJS mode.
Packages in the ESM mode get no automatic extensions. If no package.json main is present, importing the package without an explicit subpath will throw a resolution error.
Instead, map configuration is designed to allow customizing internal package resolutions.
In addition, jspm does not implement the real path lookup for modules loaded. This allows globally and locally linked packages to be scoped to the project they are loaded in, so the entire resolution for the project is managed through a single jspm configuration file despite many packages possibly being linked together.
plain names
or bare names
as they are referred to in the WHATWG loader specification are module names
that do not start with /
or ./
or ../
and do not parse as valid URLs.
Plain names are the module names that run through local map, jspm project resolution and exports resolution phases.
jspm projects have two levels of package scopes - the base-level project scope containing the package.json and jspm.json file, and the dependency package scopes, denoted by their jspm_packages/package@version paths.
When resolving into a package, the local package.json's "main"
and other entries are used along with "exports"
, "map"
and "type"
configuration.
When resolving a new package, the jspm.json
the local package.json "map"
is checked followed by the jspm file of the project base for the package resolution.
The detection of the jspm project for a given path is based on taking the base folder containing the "jspm_packages"
if any, or alternatively checking for a jspm.json
configuration file. Both operations stop on the first "jspm_packages"
or "node_modules"
segment hit.
If a jspm_packages
match is made without there being a corresponding jspm.json file, an Invalid Configuration error is thrown.
If no project is found, special treatment is given to resolutions made "without a jspm project", by falling back to node_modules
resolution for compatibility.
Each package scope is interpreted based on its "type" being either "commonjs" or "module".
By default jspm treats all packages within a jspm project as "type": "commonjs"
unless they explicitly contain "type": "module"
, just like Node.js does.
Custom assets and paths (via a trailing slash) can also be resolved through the jspm resolver, which will return "format": "unknown"
.
The default entry point for a package is the "main"
.
Custom entry points can be defined based on environment conditions.
Condition names can take values "browser" | "node" | "dev" | "production" | "react-native" | "electron" | "main"
, with matching done in priority order.
Custom environment conditions can also be defined and passed to the resolver.
Only if the field is enabled for the environment, and the corresponding value is a string, is it correctly matched, otherwise the next priority environment name is checked.
Package "exports"
are supported as in Node.js, with the same resolution and validation rules, with the following structure:
{
"exports": {
[RelName]: ExportsTarget | ExportsTarget[]
}
}
where:
RelName
is a specifier starting with"./"
.ExportsTarget
is aRelName
orConditionalExportsTarget
.ConditionalExportsTarget
is an object mapping condition value strings to map values ({ [ConditionName: string]: ExportsTarget }
).
Invalid types are ignored in fallbacks or mappings.
In addition to supporting string targets, object targets are also supported in exports fallbacks:
{
"exports": {
"./asdf": [{
"browser": {
"dev": "./asdf-browser-dev.js",
"production": "./asdf-browser-production.js"
},
"node": "./asdf-node.js"
}, "./asdf.js"]
}
}
Targets within the conditional object are themselves valid object or string targets. Nested fallback arrays are not currently supported.
If no object match is found, the matching moves on to the next fallback path.
The names are checked on the object based on the condition priority order, exactly as for the entry points.
Trailing slashes can be used to map folders, but require the target to also use a trailing slash.
The package.json
file "map"
property can be used to configure mappings for both installed dependencies and the local project, with the following structure:
package.json
:
{
"map": {
[name: PlainName]: MapTarget | MapTarget[]
}
}
where:
RelOrPlain
is a/
-separated name that either starts with./
or is a plain name.MapTarget
isRelOrPlain
orConditionalMapTarget
.ConditionalMapTarget
is an object mapping condition value strings to map values ({ [ConditionName: string]: MapTarget }
).
The mapping rules are identical to "exports"
except that the mapping is applied for imports made within the package boundary of the package itself.
When mapping into a plain name, the plain name must at least be a valid package name (as defined here).
For example:
{
"main": "dist/index.js",
"map": {
"subpath/": "./dist/subpath/",
"polyfill": {
"browser": "dep/polyfill-browser.js",
"node": "dep/polyfill-node.js"
}
}
}
If the map target is defined but is not valid or does not match any conditions, then a Module Not Found error is thrown for a missing map. The lookup does not fallback to a package lookup.
The package.json "browser"
field used as an object is supported internally as being desugared to map and exports:
{
"main": "./x",
"browser": {
"./x.js": "./x-browser.js",
"x": "y"
}
}
is treated as:
{
"main": "./x",
"browser": "./x-browser.js",
"exports": {
"./x.js": {
"browser": "./x-browser.js"
}
},
"map": {
"x": {
"browser": "y"
}
}
}
The "jspm"
field in the package.json file can be used to define configuration that is unique to jspm.
This configuration is treated as an override of the base-level package.json configuration. Extension only overrides the direct value properties, and does not iterate arrays or objects further.
jspm resolution information is provided through a jspm.json
file which must be in the same folder as the package.json file forming the package scope.
The jspm.json
jspm configuration file stores jspm configuration and version lock information for jspm projects.
The following properties are the only ones which affect the jspm resolution of a module:
jspm.json
:
{
// Top-level dependency versions
"resolve": {
[name: PlainName]: ExactPackageName
},
// Installed dependency version ranges
"dependencies": {
[exactName: ExactPackageName]: {
"resolve": {
[name: PlainName]: ExactPackageName
}
}
}
}
Where the above types are defined by:
PlainName
: A strings
satisfyingisPlainName(s)
.ExactPackageName
: A strings
satisfying the package name canonical validation.
When these configuration type assertions are broken, the configuration is considered invalid, and the entire resolution will abort on the assertion error.
Any error in any operation, including assertion errors, should be fully propagated as a top-level resolve error abrupt completion.
Plain or bare specifier detection is exactly as in the WHATWG module resolution detection:
IS_PLAIN(name: String): boolean
- If name parses as a valid URL then return false.
- Otherwise if name begins with "/", "./" or "../" then return false.
- Otherwise return true.
Package names are of the form name
or @scope/name
.
Canonical package names are of the form registry:name@version
.
Valid package names satisfy the JS regular expression /^((@[^/\\%]+\/)?[^./\\%][^/\\%]*)$/
.
The registry in the canonical form must satisfy the regular expression /^[a-z]+:$/
.
The version in the canonical form must satisfy the regular expression /^@[^/\]+$
.
Because versions permit percent-encoding special care must be taken when resolving them, as they should not be URI decoded.
A package called registry:package@version
in jspm is stored in /path/to/jspm_packages/registry/package@version/
.
To convert a package between these forms, the following methods are defined:
URI_DECODE(path: String)**
- Replace in path any percent-encoded values with their URI-decodings throwing a Invalid Module Name error for any "%2E", "%2F" or "%5C" encodings.
- Replace in path any "\" with "/".
- Return path.
PARSE_PACKAGE(specifier: String)
- Let packageName be undefined.
- Let packageSubpath be undefined.
- If specifier is an empty string then,
- Throw an Invalid Specifier error.
- If _specifier is equal to "@" or starts with "@/" then,
- Set packageName to "@".
- Otherwise, if specifier does not start with "@" then,
- Set packageName to the substring of specifier until before the first "/" separator or the end of the string.
- Otherwise,
- If specifier does not contain a "/" separator then,
- Throw an Invalid Specifier error.
- Set packageName to the substring of specifier until before the second "/" separator or the end of the string.
- If packageName starts with "." or contains "\" or "%" then,
- Throw an Invalid Specifier error.
- Let packageSubpath be undefined.
- If the length of specifier is greater than the length of packageName then,
- Set packageSubpath to "." concatenated with the substring of specifier from the position at the length of packageName.
- Return the object with values { packageName, packageSubpath }.
PARSE_PACKAGE_PATH(path: String, jspmProjectPath: String): { packageName: String, packageSubpath: String }
- Let jspmPackagesPath be the path "jspm_packages/" resolved to directory jspmProjectPath, including a trailing separator.
- If path does not start with the string jspmPackagesPath then,
- Return undefined.
- Let relPackagePath be the substring of path starting at the index of the length of jspmPackagesPath.
- Let registrySep be the index of "/" in relPackagePath.
- _If registrySep is not defined then,
- Return undefined.
- Let registry be the substring of relPackagePath of the length of registrySep.
- Let namePath be the substring of relPackagePath starting at the index after registrySep.
- Let packageName be the destructured result of PARSE_PACKAGE(namePath), returning undefined on abrupt completion.
- Return the concatenation of registry, ":" and packageName.
PACKAGE_TO_PATH(name: String, jspmProjectPath: String): String
- Let jspmPackagesPath be the path "jspm_packages/" resolved to directory jspmProjectPath, including a trailing separator.
- Replace in name the first ":" character with "/", throwing an Invalid Configuration Error if none is found.
- Return the result of the path resolution of name within parent jspmPackagesPath.
Given any file path, we can determine the base jspm project folder with the following algorithm:
GET_JSPM_PROJECT_PATH(modulePath: String)
- Let basePackagePath be undefined.
- Let jspmPackagesIndex be the index of the last "jspm_packages" segment in modulePath if any.
- If there is not a "node_modules" path segment after jspmPackagesIndex and jspmPackagesIndex it not undefined then,
- Let baseProjectPath be the substring of modulePath of the length of jspmPackagesIndex.
- Let packageName be the result of PARSE_PACKAGE_PATH(modulePath, baseProjectPath).
- If packageName is not undefined then,
- Set basePackagePath to PACKAGE_TO_PATH(packageName, baseProjectPath).
- For each parent path projectPath of modulePath,
- If projectPath is equal to basePackagePath, continue.
- If the last segment of projectPath is a "node_modules" segment, return.
- If the file "jspm.json" exists in the folder projectPath then,
- Return projectPath.
- Return undefined.
The above algorithm is based on a jspm.json lookup, while ensuring that jspm projects linked into other projects use the base project for resolution.
The process of reading the package.json configuration for a given package path is based on the following algorithms:
READ_PACKAGE_JSON(packagePath: String)
- If the file at packagePath + "/package.json" does not exist then,
- Return undefined.
- Let source be the contents of the file packagePath + "/package.json"
- Let pcfg be set to the cached output of the JSON parser applied to source, throwing a Configuration Error on invalid JSON.
- If pjson.jspm is an Object, then,
- Let overrides be the keys of pjson.jspm.
- For each override of overrides, do
- Set pjson[override] to pjson.jspm[override].
- Let type be set to undefined.
- If pjson.type is equal to "commonjs" or "module" then,
- Set type to pjson.type.
- Let entries be an empty Object.
- Let exports be undefined.
- If pjson.exports is not null or undefined then,
- Set exports to an empty Object.
- If pjson.exports is a String or Array then,
- Set entries.default to pjson.exports.
- If pjson.exports is an Object then,
- Set exports to pjson.exports.
- If exports has a "." property then,
- If exports["."] is a String or Array then,
- Set entries to { default: exports["."] }.
- Otherwise, if exports["."] is an Object then,
- Set entries to exports["."].
- If pjson.main is a String then and entries.default does not exist,
- Let target be pjson.main.
- If target does not start with "./" then,
- Set target to "./" concatenated with target.
- Remove any trailing "/" from target.
- Set entries.default to target.
- If pjson.browser is a String then and entries.browser does not exist,
- Let target be pjson.main.
- If target does not start with "./" then,
- Set target to "./" concatenated with target.
- Remove any trailing "/" from target.
- Set entries.browser to target.
- Let map be set to the value of pjson.map if an Object or an empty object otherwise.
- If pjson.browser is an Object then,
- For each key name in pjson.browser do,
- Let target be pjson.browser[name].
- If target is equal to false then,
- Set target to "@empty".
- Ff target is not a String, continue the loop.
- If name starts with "./" then,
- If target does not start with "./" then,
- Set target to "./" concatenated with target.
- If entries.default starts with name then,
- Let extra be the substring of name starting at the length of entries.default.
- If extra is equal to "", ".js", ".json", ".node", "/index.js", "/index.json", "/index.node" then,
- Set entries.browser to target.
- If _exports[name] is undefined then,
- Set exports[name] to the object { browser: target, default: name }.
- Otherwise, if map[name] is not defined then,
- Set _map[name] to the object { browser: target, default: name }.
- Return the object with properties { type, entries, exports, map }.
GET_PACKAGE_SCOPE(modulePath: String)
- Let scope be modulePath.
- While scope is not the file system root,
- If the last path segment of scope is "node_modules" or "jspm_packages", return undefined.
- If the file at scope + "/package.json" exists then,
- Return scope.
- Set scope to the parent path of scope.
- Return undefined.
RESOLVE_EXPORTS_TARGET(packagePath: String, target: Any, subpath: String)
- If target is a String then,
- If subpath has non-zero length and target does not end with "/", throw a Module Not Found error.
- If target does not start with "./" then,
- Let possibleBuiltin be the parsed URL of target concatenated with subpath.
- If possibleBuiltin is a valid builtin module for the environment then,
- Return possibleBuiltin.
- Otherwise, throw a Module Not Found error.
- Set target to URI_DECODE(target).
- Set subpath to URI_DECODE(subpath).
- Let resolvedTarget be the resolution of packagePath and target.
- If resolvedTarget is contained in packagePath then,
- Let resolved be the resolution of subpath and resolvedTarget.
- If resolved is contained in resolvedTarget, return resolved.
- Otherwise, if target is an Array then,
- For each item targetValue of target,
- If targetValue is not a String or Object, continue the loop.
- Return the result of RESOLVE_EXPORTS_TARGET(packagePath, targetValue, subpath), continuing the loop on a Module Not Found error.
- Otherwise, if target is Null then,
- Throw a Module Not Found error.
- Otherwise, if target is an Object then,
- For each environment condition condition in priority order,
- If _condition is a valid key of target then,
- Let targetValue be target[condition].
- Return the result of RESOLVE_EXPORTS_TARGET(packagePath, targetValue, subpath), continuing the loop on validation errors.
- Throw a Module Not Found error.
RESOLVE_MAP(name: String, packagePath: String, pcfg: Object)
- If pcfg.map is undefined then,
- Return undefined.
- If name is a key of pcfg.map then,
- Return the result of RESOLVE_MAP_TARGET(packagePath, pcfg.map[name], ""), propagating any error on abrupt completion.
- For each entry map of the keys of pcfg.map sorted by length descending,
- If map does not end in "/" and the character of map at the index of the length of name is not "/", continue the loop.
- If name begins with map then,
- Let target be pcfg.map[map].
- Let mapSubpath be the substring of name starting at the length of map.
- Return the result of RESOLVE_MAP_TARGET(packagePath, target, mapSubpath), propagating any error on abrupt completion.
- Return undefined.
RESOLVE_MAP_TARGET(packagePath: string, target: Any, subpath: String)
- Note: If subpath starts with "/" that indicates a possible direct package subpath resolution. which will invalidate any matched target that is not a direct package name without a subpath.
- If target is a String then,
- If target is a valid package name then (as determined by PARSE_PACKAGE below),
- Let packageSubpath be the destructured property of PARSE_PACKAGE(target).
- If subpath starts with "/" then,
- If packageSubpath has non-zero length, throw a Module Not Found error.
- Return the concatenation of target, "/" and subpath.
- If subpath has non-zero length and target does not end in "/", throw a Module Not Found error.
- Return the concatenation of target and subpath.
- Otherwise,
- If subpath starts with "/", throw a Module Not Found error.
- If target does not start with "./", throw a Module Not Found error.
- If subpath has non-zero length and target does not end with "/", throw a Module Not Found error.
- Set target to URI_DECODE(target).
- Set subpath to URI_DECODE(subpath).
- Let resolvedTarget be the resolution of packagePath and target.
- If resolvedTarget is contained in packagePath then,
- Let resolved be the resolution of subpath and resolvedTarget.
- If resolved is contained in resolvedTarget, return "./" concatenated with the substring of resolved from the length of packagePath.
- Otherwise, if target is an Array then,
- For each item targetValue of target,
- If targetValue is not a String or Object, continue the loop.
- Return the result of RESOLVE_MAP_TARGET(packagePath, targetValue, subpath), continuing the loop on a Module Not Found error.
- Otherwise, if target is an Object then,
- For each environment condition condition in priority order,
- Let targetValue be target[condition].
- If targetValue is not a String or Object, continue the loop.
- Return the result of RESOLVE_MAP_TARGET(packagePath, targetValue, subpath), propagating any error on abrupt completion.
- Throw a Module Not Found error.
Module resolution is always based on resolving resolve(name, parentPath)
where name
is the optional unresolved name to resolve and parentPath
is an absolute file path to resolve relative to.
When handling conditional resolution, the environment conditional state is required to be known, an array of matched conditions in the following order:
[
"browser",
"node",
"production",
"dev",
"react-native",
"electron",
"main"
]
where the first match in order will be picked from conditional branches in resolution configuration.
All NodeJS builtins are accepted as plain name resolutions, corresponding to { resolved: builtinName, format: "builtin" }
from the resolver.
@empty
is a jspm-specific builtin providing an empty module object with an empty object as its default export, and is treated as another builtin module in all builtin module checks.
The resolver will either return a resolved path string, or throw a Module Not Found, Invalid Module Name or Invalid Configuration error.
The resolution algorithm breaks down into the following high-level process to get the fully resolved path:
JSPM_RESOLVE(name: String, parentPath: String, cjsResolve: Boolean, isMain: Boolean)
- Assert parentPath is a valid absolute file system path.
- Let jspmProjectPath be the result of GET_JSPM_PROJECT_PATH(parentPath).
- If IS_PLAIN(name) is false then,
- Return the result of RELATIVE_RESOLVE(name, parentPath, jspmProjectPath, cjsResolve, isMain).
- Let parentScope be the result of GET_PACKAGE_SCOPE(parentPath).
- Let parentConfig be the result of READ_PACKAGE_JSON(parentScope), if _parentScope is not undefined.
- Let mapped be the value of RESOLVE_MAP(name, parentScope, parentConfig)
- If mapped is not undefined then,
- If mapped does not start with parentScope then,
- Set name to mapped.
- Otherwise,
- If cjsResolve is equal to true then,
- Return CJS_FINALIZE_RESOLVE(CJS_FILE_RESOLVE(mapped), jspmProjectPath).
- Otherwise
- Return FINALIZE_RESOLVE(mapped, jspmProjectPath, isMain).
- If jspmProjectPath is not undefined then,
- Return the result of JSPM_PROJECT_RESOLVE(name, parentPath, jspmProjectPath, cjsResolve, isMain).
- Otherwise,
- Return the result of NODE_MODULES_RESOLVE(name, parentPath, cjsResolve, isMain).
RELATIVE_RESOLVE(name: String, parentPath: String, jspmProjectPath: String, cjsResolve: Boolean, isMain)
- Let resolved be undefined.
- Set name to URI_DECODE(name).
- If name starts with "//" and name does not start with "///" then,
- Throw an Invalid Module Name error.
- Otherwise if name starts with "/" or name starts with "/" then,
- Set resolved to the resolved file path of name.
- Otherwise if name starts with "." then,
- Set resolved to the path resolution of name relative to parentPath.
- Otherwise if running in Windows, and name starts with a letter (uppercase or lowercase) in the a-z range followed by ":" then,
- Set resolved to the value of name.
- Otherwise,
- If name is not a valid file URL then,
- Throw an Invalid Module Name error.
- Set resolved to the absolute file system path of the file URL name.
- If cjsResolve is equal to true then,
- Return CJS_FINALIZE_RESOLVE(CJS_FILE_RESOLVE(resolved), jspmProjectPath).
- Otherwise,
- Return FINALIZE_RESOLVE(resolved, jspmProjectPath, isMain).
JSPM_PROJECT_RESOLVE(name: String, parentPath: String, jspmProjectPath: String, cjsResolve: Boolean, isMain: Boolean)
- Let jspmConfig be the parsed contents of "jspm.json" in jspmProjectPath, throwing a Configuration Error for not found or invalid JSON.
- Let parentPackage be PARSE_PACKAGE_PATH(parentPath, jspmProjectPath) if parentPath is not undefined.
- Let packageName and packageSubpath be the destructured properties of PARSE_PACKAGE(name), throwing on abrupt completion.
- Let packagePath be undefined.
- If packageName is equal to "@" then,
- If parentPackage is not undefined then,
- Let scope be the result of GET_PACKAGE_SCOPE(parentPath) throwing a Module Not Found error if undefined.
- Set packagePath to scope.
- Otherwise,
- Set packagePath to PACKAGE_TO_PATH(parentPackage, jspmProjectPath).
- Otherwise,
- Let packageResolution be undefined.
- If parentPackage is not undefined then,
- Set packageResolution to jspmConfig.dependencies[parentPackage]?.resolve[packageName].
- Otherwise,
- Set packageResolution to jspmConfig.resolve[packageName].
- If packageResolution is undefined then,
- Set packageResolution to jspmConfig.resolvePeer?[packageName].
- If packageResolution is undefined then,
- If packageName corresponds to the name part of parentPackage, excluding the registry and version then,
- Set packagePath to PACKAGE_TO_PATH(parentPackage, jspmProjectPath).
- Otherwise, if name is a builtin module, return { resolved: name, format: "builtin" }.
- Otherwise, throw a Module Not Found error.
- Let packagePath be the result of PACKAGE_TO_PATH(packageResolution, jspmProjectPath).
- Let packageConfig be the result of READ_PACKAGE_JSON(packagePath).
- Let resolved be the result of RESOLVE_PACKAGE(packagePath, packageSubpath, packageConfig, cjsResolve).
- If cjsResolve then,
- Return CJS_FINALIZE_RESOLVE(resolved, jspmProjectPath).
- Otherwise
- Return FINALIZE_RESOLVE(resolved, jspmProjectPath, isMain).
NODE_MODULES_RESOLVE(name: String, parentPath: String, cjsResolve: Boolean, isMain: Boolean)
- If name is a builtin module, return { resolved: name, format: "builtin" }.
- Let packageName and packageSubpath be the destructured values of PARSE_PACKAGE(specifier), throwing on abrupt completion.
- If packageName is equal to "@" then,
- Let packagePath be the result of GET_PACKAGE_SCOPE(parentPath).
- If packagePath is undefined, throw a Module Not Found error.
- Let packageConfig be the result of READ_PACKAGE_JSON(packagePath).
- Return the result of RESOLVE_PACKAGE(packagePath, packageSubpath, packageConfig, cjsResolve).
- While parentPath is not the file system root,
- Let packagePath be the resolution of "node_modules/" concatenated with name, relative to parentPath.
- Set parentPath to the parent folder of parentPath.
- If the folder at packagePath does not exist, then
- Set parentPath to the parent path of parentPath.
- Continue the next loop iteration.
- Let packageConfig be the result of READ_PACKAGE_JSON(packagePath).
- Return the result of RESOLVE_PACKAGE(packagePath, packageSubpath, packageConfig, cjsResolve).
- Throw a Module Not Found error.
RESOLVE_PACKAGE(packagePath: String, subpath: String, pcfg: Object, cjsResolve: Boolean)
- Assert: subpath is the empty String or starts with "./".
- If subpath is the empty String then,
- Let match be the first matching environment condition in pcfg.entries in condition priority order.
- Let resolvedEntry be undefined.
- If match is not undefined then,
- Set resolvedEntry to the result of RESOLVE_EXPORTS_TARGET(packagePath, pcfg.entries[match], "").
- If the file at resolvedEntry exists, return resolvedEntry.
- If pcfg.type is equal to "module" and cjsResolve is false, throw a Module Not Found error.
- Let entryDir be the substring of resolvedEntry of the length of packagePath plus one.
- Set resolved to LEGACY_DIR_RESOLVE(packagePath + "/", entryDir).
- If resolved is not undefined, return resolved.
- Throw a Module Not Found error.
- If subpath is equal to "./" return packagePath + "/".
- If pcfg.exports is undefined or null then,
- Set subpath to URI_DECODE(subpath).
- Let resolved be the resolution of subpath in packagePath.
- If cjsResolve then,
- Return CJS_FILE_RESOLVE(resolved).
- Otherwise,
- Return resolved_.
- If pcfg.exports is not an Object then,
- Throw a Module Not Found error.
- If subpath is a key of pcfg.exports then,
- Return the result of RESOLVE_EXPORTS_TARGET(packagePath, pcfg.exports[subpath], "").
- For each entry export of the keys of pcfg.exports sorted by length descending,
- If export does not end in "/", continue the loop.
- If subpath begins with export then,
- Let target be pcfg.exports[export].
- Let exportSubpath be the substring of subpath starting at the length of export.
- Return RESOLVE_EXPORTS_TARGET(packagePath, target, exportSubpath).
- Throw a Module Not Found error.
FINALIZE_RESOLVE(path: String, jspmProjectPath: String | undefined, isMain: Boolean)
- Let realpathBase be the value of PACKAGE_TO_PATH(path) or jspmProjectPath if jspmProjectPath is defined, and undefined otherwise.
- Let resolved be the real path of path within realpathBase.
- Let scope be the result of GET_PACKAGE_SCOPE(resolved).
- Let scopeConfig be the result of READ_PACKAGE_JSON(scope + "/package.json"), if scope is defined.
- If resolved ends with the character "/" then,
- If resolved does not point to an existing directory, throw a Module Not Found error.
- Return { resolved, format: "unknown" }.
- If resolved does not point to an existing file, throw a Module Not Found error.
- If resolved ends in ".mjs" then,
- Return { resolved, format: "module" }.
- If resolved ends in ".node" then,
- Return { resolved, format: "addon" }.
- If resolved ends in ".json" then,
- Return { resolved, format: "json" }.
- If isMain is false and resolved does not end with ".js" then,
- Return { resolved, format: "unknown" }.
- If scopeConfig?.type is "module" then,
- Return { resolved, format: "module" }.
- Return { resolved, format: "commonjs" }.
CJS_FINALIZE_RESOLVE(path: String, jspmProjectPath: String | undefined)
- Let realpathBase be the value of PACKAGE_TO_PATH(path) or jspmProjectPath if jspmProjectPath is defined, and undefined otherwise.
- Let resolved be the real path of path within realpathBase.
- Let scope be the result of GET_PACKAGE_SCOPE(resolved).
- Let scopeConfig be the result of READ_PACKAGE_JSON(scope + "/package.json"), if scope is defined.
- If resolved ends with ".mjs" or resolved ends with ".js" and scopeConfig?.type is equal to "module" then,
- Throw a Invalid Module Name error.
- If resolved ends in ".node" then,
- Return { resolved, format: "addon" }.
- If resolved ends with ".json" then,
- Return { resolved, format: "json" }.
- Return { resolved, format: "commonjs" }.
CJS_FILE_RESOLVE(path: String)
- Let resolved be LEGACY_FILE_RESOLVE(path).
- If resolved is undefined then,
- Let pjson be the value of READ_PACKAGE_JSON(path).
- Set resolved to LEGACY_DIR_RESOLVE(path, pjson?.main).
- If resolved is undefined then,
- Throw a Module Not Found error.
- Return resolved.
LEGACY_FILE_RESOLVE(path: String)
- Assert path is a valid file path.
- If the file at path exists,
- Return path.
- Return LEGACY_EXTENSION_RESOLVE(path).
LEGACY_EXTENSION_RESOLVE(path: String)
- If the file at path + ".js" exists,
- Return path + ".js".
- Otherwise if the file at path + ".json" exists,
- Return path + ".json".
- Otherwise if the file at path + ".node" exists,
- Return path + ".node".
- Return undefined.
LEGACY_DIR_RESOLVE(path: String, main: String | Undefined)
- If main is a String then,
- Let resolved be LEGACY_FILE_RESOLVE(path + "/" + main).
- If resolved is undefined then,
- Set resolved to LEGACY_EXTENSION_RESOLVE(path + "/" + main + "/index").
- If resolved is not undefined, return resolved.
- Return LEGACY_EXTENSION_RESOLVE(path + "/index").