bare-module-resolve v1.10.2
bare-module-resolve
Low-level module resolution algorithm for Bare. The algorithm is implemented as a generator function that yields either package manifests to be read or resolution candidates to be tested by the caller. As a convenience, the main export is a synchronous and asynchronous iterable that relies on package manifests being read by a callback. For asynchronous iteration, the callback may return promises which will be awaited before being passed to the generator.
npm i bare-module-resolveUsage
For synchronous resolution:
const resolve = require('bare-module-resolve')
function readPackage(url) {
// Read and parse `url` if it exists, otherwise `null`
}
for (const resolution of resolve(
'./file.js',
new URL('file:///directory/'),
readPackage
)) {
console.log(resolution)
}For asynchronous resolution:
const resolve = require('bare-module-resolve')
async function readPackage(url) {
// Read and parse `url` if it exists, otherwise `null`
}
for await (const resolution of resolve(
'./file.js',
new URL('file:///directory/'),
readPackage
)) {
console.log(resolution)
}API
const resolver = resolve(specifier, parentURL[, options][, readPackage])
Resolve specifier relative to parentURL, which must be a WHATWG URL instance. readPackage is called with a URL instance for every package manifest to be read and must either return the parsed JSON package manifest, if it exists, or null. If readPackage returns a promise, synchronous iteration is not supported.
Options include:
options = {
// A default "imports" map to apply to all specifiers. Follows the same
// syntax and rules as the "imports" property defined in `package.json`.
imports,
// A list of builtin module specifiers. If matched, the protocol of the
// resolved URL will be `builtinProtocol`.
builtins: [],
// The protocol to use for resolved builtin module specifiers.
builtinProtocol: 'builtin:',
// The supported import conditions. "default" is always recognized.
conditions: [],
// An array reference which will contain the matched conditions when yielding
// resolutions.
matchedConditions: [],
// The supported engine versions.
engines: {},
// The file extensions to look for. Must be provided to support extensionless
// specifier resolution and directory support, such as resolving './foo' to
// './foo.js' or './foo/index.js'.
extensions: [],
// A map of preresolved imports with keys being serialized parent URLs and
// values being "imports" maps.
resolutions
}for (const resolution of resolver)
Synchronously iterate the module resolution candidates. The resolved module is the first candidate that exists, either as a file on a file system, a resource at a URL, or something else entirely.
for await (const resolution of resolver)
Asynchronously iterate the module resolution candidates. If readPackage returns promises, these will be awaited. The same comments as for (const resolution of resolver) apply.
Algorithm
The following generator functions implement the resolution algorithm, which has been adapted from the Node.js resolution algorithms for CommonJS and ES modules. Unlike Node.js, Bare uses the same resolution algorithm for both module formats. The yielded values have the following shape:
Package manifest
next.value = {
package: URL
}If the package manifest identified by next.value.package exists, generator.next() must be passed the parsed JSON value of the manifest. If it does not exist, pass null instead.
Resolution candidate
next.value = {
resolution: URL
}If the module identified by next.value.resolution exists, generator.next() may be passed true to signal that the resolution for the current set of conditions has been identified. If it does not exist, pass false instead.
To drive the generator functions, a loop like the following can be used:
const generator = resolve.module(specifier, parentURL)
let next = generator.next()
while (next.done !== true) {
const value = next.value
if (value.package) {
// Read and parse `value.package` if it exists, otherwise `null`
let info
next = generator.next(info)
} else {
const resolution = value.resolution
// `true` if `resolution` was the correct candidate, otherwise `false`
let resolved
next = generator.next(resolved)
}
}Options are the same as resolve() for all functions.
!WARNING These functions are currently subject to change between minor releases. If using them directly, make sure to specify a tilde range (
~1.2.3) when declaring the module dependency.
const generator = resolve.module(specifier, parentURL[, options])
- If
specifierstarts with a Windows drive letter:- Prepend
/tospecifier.
- Prepend
- If
options.resolutionsis set:- If
preresolved(specifier, options.resolutions, parentURL, options)yields, return.
- If
- If
url(specifier, parentURL, options)yields, return. - If
packageImports(specifier, parentURL, options)yields, return. - If
specifierequals.or.., or ifspecifierstarts with/,\,./,.\,../, or..\:- If
options.importsis set:- If
packageImportsExports(specifier, options.imports, parentURL, true, options)yields, return.
- If
- If
file(specifier, parentURL, false, options)resolves, return. - Return
directory(specifier, parentURL, options).
- If
- Return
package(specifier, parentURL, options).
const generator = resolve.url(url, parentURL[, options])
- If
urlis not a valid URL, return. - If
options.importsis set:- If
packageImportsExports(url.href, options.imports, parentURL, true, options)yields, return.
- If
- If
url.protocolequalsnode::- Let
specifierbeurl.pathname. - If
specifierequals.or.., or ifspecifierstarts with/,\,./,.\,../, or..\, throw. - Return
package(specifier, parentURL, options).
- Let
- Yield
url.
const generator = resolve.preresolved(specifier, resolutions, parentURL[, options])
- Let
importsberesolutions[parentURL]. - If
importsis a non-nullobject:- Return
packageImportsExports(specifier, imports, parentURL, true, options).
- Return
const generator = resolve.package(packageSpecifier, parentURL[, options])
- If
packageSpecifieris the empty string, throw. - If
packageSpecifierdoes not start with@:- Set
packageNameto the substring ofpackageSpecifieruntil the first/or the end of the string.
- Set
- Let
packageNamebeundefined. - Otherwise:
- If
packageSpecifierdoes not include/, throw. - Set
packageNameto the substring ofpackageSpecifieruntil the second/or the end of the string.
- If
- If
packageNamestarts with.or includes\or%, throw. - If
builtinTarget(packageSpecifier, null, options.builtins, options)yields, return. - Let
packageSubpathbe.concatenated with the substring ofpackageSpecifierfrom the position at the length ofpackageName. - If
packageSelf(packageName, packageSubpath, parentURL, options)yields, return. - Repeat:
- Let
packageURLbe the resolution ofnode_modules/concatenated withpackageNameand/relative toparentURL. - Set
parentURLto the substring ofparentURLuntil the last/. - Let
infobe the result of yielding the resolution ofpackage.jsonrelative topackageURL. - If
infois notnull:- If
info.enginesis set:- Call
validateEngines(packageURL, info.engines, options).
- Call
- If
info.exportsis set:- Return
packageExports(packageURL, packageSubpath, info.exports, options).
- Return
- If
packageSubpathis.:- If
info.mainis a non-empty string:- Set
packageSubpathtoinfo.main.
- Set
- Otherwise:
- Return
file('index', packageURL, true, options).
- Return
- If
- If
file(packageSubpath, packageURL, false, options)resolves, return. - Return
directory(packageSubpath, packageURL, options).
- If
- If
parentURLis the file system root, return.
- Let
const generator = resolve.packageSelf(packageName, packageSubpath, parentURL[, options])
- For each value
packageURLoflookupPackageScope(parentURL, options):- Let
infobe the result of yieldingpackageURL. - If
infois notnull:- If
info.namedoes not equalpackageName, return. - If
info.exportsis set:- Return
packageExports(packageURL, packageSubpath, info.exports, options).
- Return
- If
packageSubpathis.:- If
info.mainis a non-empty string:- Set
packageSubpathtoinfo.main.
- Set
- Otherwise:
- Return
file('index', packageURL, true, options).
- Return
- If
- If
file(packageSubpath, packageURL, false, options)resolves, return. - Return
directory(packageSubpath, packageURL, options).
- If
- Let
const generator = resolve.packageExports(packageURL, subpath, exports[, options])
- If
subpathis.:- Let
mainExportbeundefined. - If
exportsis a string or an array:- Set
mainExporttoexports.
- Set
- If
exportsis a non-nullobject:- If some keys of
exportsstart with.:- If
.is a key ofexports:- Set
mainExporttoexports['.'].
- Set
- If
- Otherwise:
- Set
mainExporttoexports.
- Set
- If some keys of
- If
mainExportis notundefined:- If
packageTarget(packageURL, mainExport, null, false, options)yields, return.
- If
- Let
- Otherwise, if
exportsis a non-nullobject:- If every key of
exportsstarts with.:- If
packageImportsExports(subpath, exports, packageURL, false, options)yields, return.
- If
- If every key of
- Throw.
const generator = resolve.packageImports(specifier, parentURL[, options])
- If
specifieris#or starts with#/, throw. - For each value
packageURLoflookupPackageScope(parentURL, opions):- Let
infobe the result of yieldingpackageURL. - If
infois notnull:- If
info.importsis set:- If
packageImportsExports(specifier, info.imports, packageURL, true, options)yields, return.
- If
- If specifier starts with
#, throw. - Return.
- If
- Let
- If
options.importsis set:- If
packageImportsExports(url.href, options.imports, parentURL, true, options)yields, return.
- If
const generator = resolve.packageImportsExports(matchKey, matchObject, packageURL, isImports[, options])
- If
matchKeyis a key ofmatchObjectandmatchKeydoes not include*:- Let
targetbematchObject[matchKey]. - Return
packageTarget(packageURL, target, null, isImports, options).
- Let
- Let
expansionKeysbe the keys ofmatchObjectthat include*sorted bypatternKeyCompare. - For each value
expansionKeyofexpansionKeys:- Let
patternBasebe the substring ofexpansionKeyuntil the first*. - If
matchKeystarts with but isn't equal topatternBase:- Let
patternTrailerbe the substring ofexpansionKeyfrom the position at the index after the first*. - If
patternTraileris the empty string, or ifmatchKeyends withpatternTrailerand the length ofmatchKeyis greater than or equal to the length ofexpansionKey:- Let
targetbematchObject[expansionKey]. - Let
patternMatchbe the substring ofmatchKeyfrom the position at the length ofpatternBaseuntil the length ofmatchKeyminus the length ofpatternTrailer. - Return
packageTarget(packageURL, target, patternMatch, isImports, options).
- Let
- Let
- Let
const generator = resolve.packageTarget(packageURL, target, patternMatch, isImports[, options])
- If
targetis a string:- If
targetdoes not start with./andisImportsisfalse, throw. - If
patternMatchis notnull:- Replace every instance of
*intargetwithpatternMatch.
- Replace every instance of
- If
url(target, packageURL, options)yields, return. - If
targetequals.or.., or iftargetstarts with/,./, or../:- Yield the resolution of
targetrelative topackageURLand return.
- Yield the resolution of
- Return
package(target, packageURL, options).
- If
- If
targetis an array:- For each value
targetValueoftarget:- If
packageTarget(packageURL, targetValue, patternMatch, isImports, options)yields, return.
- If
- For each value
- If
targetis a non-nullobject:- For each key
conditionoftarget:- If
conditionequalsdefaultor ifoptions.conditionsincludescondition:- Let
targetValuebetarget[condition]. - Return
packageTarget(packageURL, targetValue, patternMatch, isImports, options).
- Let
- If
- For each key
const generator = resolve.builtinTarget(packageSpecifier, packageVersion, target[, options])
- If
targetis a string:- If
targetdoes not start with@:- Let
targetNamebe the substring oftargetuntil the first@or the end of the string. - Let
targetVersionbe the substring oftargetfrom the character following the first@and to the end of string, ornullif no such substring exists.
- Let
- Otherwise:
- Let
targetNamebe the substring oftargetuntil the second@or the end of the string. - Let
targetVersionbe the substring oftargetfrom the character following the second@and to the end of string, ornullif no such substring exists.
- Let
- If
packageSpecifierequalstargetName:- If
packageVersionisnullandtargetVersionisnull:- Yield
options.builtinProtocolconcatenated withpackageSpecifierand return.
- Yield
- Let
versionbenull. - If
packageVersionisnull, letversionbetargetVersion. - Otherwise, if
targetVersionis eithernullor equalspackageVersion, letversionbepackageVersion - If
versionis notnull:- Yield
options.builtinProtocolconcatenated withpackageSpecifier,@, andversionand return.
- Yield
- If
- If
- If
targetis an array:- For each value
targetValueoftarget:- If
builtinTarget(packageSpecifier, packageVersion, targetValue, options)yields, return.
- If
- For each value
- If
targetis a non-nullobject:- For each key
conditionoftarget:- If
conditionequalsdefaultor ifoptions.conditionsincludescondition:- Let
targetValuebetarget[condition]. - Return
builtinTarget(packageSpecifier, packageVersion, targetValue, options).
- Let
- If
- For each key
const generator = resolve.file(filename, parentURL, isIndex[, options])
- If
filenameequals.or.., or iffilenameends with/or\, return. - If
parentURLis afile:URL andfilenameincludes encoded/or\, throw. - If
isIndexisfalse:- Yield the resolution of
filenamerelative toparentURL.
- Yield the resolution of
- For each value
extofoptions.extensions:- Yield the resolution of
filenameconcatenated withextrelative toparentURL.
- Yield the resolution of
const generator = resolve.directory(dirname, parentURL[, options])
- Let
directoryURLbeundefined. - If
dirnameends with/or\:- Set
directoryURLto the resolution ofdirnamerelative toparentURL.
- Set
- Otherwise:
- Set
directoryURLto the resolution ofdirnameconcatenated with/relative toparentURL.
- Set
- Let
infobe the result of yielding the resolution ofpackage.jsonrelative todirectoryURL. - If
infois notnull:- If
info.exportsis set:- Return
packageExports(directoryURL, '.', info.exports, options).
- Return
- If
info.mainis a non-empty string:- If
file(info.main, directoryURL, false, options)resolves, return. - Return
directory(info.main, directoryURL, options).
- If
- If
- Return
file('index', directoryURL, true, options).
License
Apache-2.0
9 months ago
9 months ago
11 months ago
11 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago