bare-addon-resolve
Low-level addon 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-addon-resolve
Usage
For synchronous resolution:
const resolve = require('bare-addon-resolve')
function readPackage(url) {
// Read and parse `url` if it exists, otherwise `null`
}
for (const resolution of resolve('./addon', new URL('file:///directory/'), readPackage)) {
console.log(resolution)
}
For asynchronous resolution:
const resolve = require('bare-addon-resolve')
async function readPackage(url) {
// Read and parse `url` if it exists, otherwise `null`
}
for await (const resolution of resolve('./addon', new URL('file:///directory/'), readPackage)) {
console.log(resolution)
}
API
See the bare-addon-resolve reference.
Algorithm
The following generator functions implement the resolution algorithm. 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 addon 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.addon(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.
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.
The preresolved, builtinTarget, and lookupPackageScope generators referenced below are provided by bare-module-resolve and behave as documented there.
const generator = resolve.addon(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 a resolution that isn't deferred, return.
- If
- If
url(specifier, parentURL, options)yields, return. - Let
versionbenull. - Let
ibe the index of the last@inspecifier. - If
iis greater than0:- Set
versionto the substring ofspecifierfollowingi. - If
versionis a valid semantic version:- Set
specifierto the substring ofspecifieruntili.
- Set
- Otherwise, set
versiontonull.
- Set
- If
specifierequals.or.., or ifspecifierstarts with/,\,./,.\,../, or..\:- If
file(specifier, parentURL, options)resolves, return. - Return
directory(specifier, version, parentURL, options).
- If
- Return
package(specifier, version, parentURL, options).
const generator = resolve.url(url, parentURL[, options])
- If
urlis not a valid URL, return. - Yield
url.
const generator = resolve.package(packageSpecifier, packageVersion, 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
- 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. - Let
packageSubpathbe.concatenated with the substring ofpackageSpecifierfrom the position at the length ofpackageName. - If
parentURLhas an opaque path, return. - If
packageSelf(packageName, packageSubpath, packageVersion, parentURL, options)yields, return. - Repeat:
- Let
packageURLbe the resolution ofnode_modules/concatenated withpackageNameand/relative toparentURL. - Set
parentURLto its parent directory. - Let
infobe the result of yielding the resolution ofpackage.jsonrelative topackageURL. - If
infois notnull:- Return
directory(packageSubpath, packageVersion, packageURL, options).
- Return
- If the path of
parentURLis empty or/, return.
- Let
const generator = resolve.packageSelf(packageName, packageSubpath, packageVersion, parentURL[, options])
- For each value
packageURLoflookupPackageScope(parentURL, options):- Let
infobe the result of yieldingpackageURL. - If
infois notnull:- If
info.namedoes not equalpackageName, return. - Return
directory(packageSubpath, packageVersion, packageURL, options).
- If
- Let
const generator = resolve.file(filename, parentURL[, options])
- If
filenameequals.or.., or iffilenameends with/or\, return. - If
parentURLhas an opaque path, return. - If
parentURLis afile:URL andfilenameincludes encoded/or\, throw. - For each value
extofoptions.extensions:- If
filenameends withext, setextto the empty string. - Yield the resolution of
filenameconcatenated withextrelative toparentURL; if it resolves, return.
- If
const generator = resolve.directory(dirname, version, parentURL[, options])
- If
parentURLhas an opaque path, return. - If
dirnameends with/or\:- Let
directoryURLbe the resolution ofdirnamerelative toparentURL.
- Let
- Otherwise:
- Let
directoryURLbe the resolution ofdirnameconcatenated with/relative toparentURL.
- Let
- Let
unversionedbetrueifversionisnull, otherwisefalse. - Let
infobe the result of yielding the resolution ofpackage.jsonrelative todirectoryURL. - If
infoisnull, return. - If
info.nameis a non-empty string:- If
info.nameincludes__, throw. - Let
namebeinfo.namewith every/replaced by__and any leading@removed.
- If
- Otherwise, return.
- If
info.versionis a non-empty string:- If
versionis notnullandinfo.versiondoes not equalversion, return. - Set
versiontoinfo.version.
- If
- If
builtinTarget(name, version, options.builtins, options)yields, return. - Let
nativebe the values ofoptions.hoststhat do not start withwasi-, andwasmthe values that do. - If
prebuilds(name, version, unversioned, directoryURL, native, options)resolves, return. - If
linked(name, version, options)resolves, return. - Return
prebuilds(name, version, unversioned, directoryURL, wasm, options)withoptions.extensionsset to['.wasm'].
const generator = prebuilds(name, version, unversioned, directoryURL, hosts, options)
- For each value
prebuildsURLoflookupPrebuildsScope(directoryURL, options):- Let
resolvedbefalse. - For each value
hostofhosts:- Let
conditionsbe the result of splittinghoston-. - If
hostis one ofdarwin-arm64,darwin-x64,ios-arm64-simulator, orios-x64-simulator, letuniversalbehostwith its second component replaced byuniversal; otherwise letuniversalbenull. - Append the values of
conditionstooptions.matchedConditions. - If
versionis notnull:- If
file(host + '/' + name + '@' + version, prebuildsURL, options)resolves, setresolvedtotrue. - If
universalis notnullandfile(universal + '/' + name + '@' + version, prebuildsURL, options)resolves, setresolvedtotrue.
- If
- If
unversionedistrue:- If
file(host + '/' + name, prebuildsURL, options)resolves, setresolvedtotrue. - If
universalis notnullandfile(universal + '/' + name, prebuildsURL, options)resolves, setresolvedtotrue.
- If
- Remove the values of
conditionsfromoptions.matchedConditions.
- Let
- If
resolvedistrue, return.
- Let
const generator = resolve.linked(name, version[, options])
- If
options.linkedisfalseoroptions.hostsis empty, return. - For each value
hostofoptions.hosts:- Let
platformbe the substring ofhostuntil the first-. - If
platformis the empty string, continue. - Append
platformtooptions.matchedConditions. - Let
candidatesbe the following artefact names, in order, where each entry containing<version>is only included whenversionis notnull:- If
platformisdarwinorios:<name>.<version>.framework/<name>.<version>lib<name>.<version>.dylib, only ifplatformisdarwin<name>.framework/<name>lib<name>.dylib, only ifplatformisdarwin
- Otherwise, if
platformislinuxorandroid:lib<name>.<version>.solib<name>.so
- Otherwise, if
platformiswin32:<name>-<version>.dll<name>.dll
- If
- For each value
candidateofcandidates:- Yield
options.linkedProtocolconcatenated withcandidate; if it resolves, stop iteratingcandidates.
- Yield
- Remove
platformfromoptions.matchedConditions.
- Let
License
Apache-2.0