797 lines
20 KiB
JavaScript
797 lines
20 KiB
JavaScript
const { satisfies } = require('bare-semver')
|
|
const errors = require('./lib/errors')
|
|
|
|
module.exports = exports = function resolve(specifier, parentURL, opts, readPackage) {
|
|
if (typeof opts === 'function') {
|
|
readPackage = opts
|
|
opts = {}
|
|
} else if (typeof readPackage !== 'function') {
|
|
readPackage = defaultReadPackage
|
|
}
|
|
|
|
return {
|
|
*[Symbol.iterator]() {
|
|
const generator = exports.module(specifier, parentURL, opts)
|
|
|
|
let next = generator.next()
|
|
|
|
while (next.done !== true) {
|
|
const value = next.value
|
|
|
|
if (value.package) {
|
|
next = generator.next(readPackage(value.package))
|
|
} else {
|
|
next = generator.next(yield value.resolution)
|
|
}
|
|
}
|
|
|
|
return next.value
|
|
},
|
|
|
|
async *[Symbol.asyncIterator]() {
|
|
const generator = exports.module(specifier, parentURL, opts)
|
|
|
|
let next = generator.next()
|
|
|
|
while (next.done !== true) {
|
|
const value = next.value
|
|
|
|
if (value.package) {
|
|
next = generator.next(await readPackage(value.package))
|
|
} else {
|
|
next = generator.next(yield value.resolution)
|
|
}
|
|
}
|
|
|
|
return next.value
|
|
}
|
|
}
|
|
}
|
|
|
|
function defaultReadPackage() {
|
|
return null
|
|
}
|
|
|
|
// No resolution candidate was yielded
|
|
const UNRESOLVED = 0x0
|
|
// At least 1 resolution candidate was yielded
|
|
const YIELDED = 0x1
|
|
// At least 1 resolution candidate was yielded and resolved
|
|
const RESOLVED = YIELDED | 0x2
|
|
|
|
exports.constants = {
|
|
UNRESOLVED,
|
|
YIELDED,
|
|
RESOLVED
|
|
}
|
|
|
|
exports.module = function* (specifier, parentURL, opts = {}) {
|
|
const { resolutions = null, imports = null } = opts
|
|
|
|
if (exports.startsWithWindowsDriveLetter(specifier)) {
|
|
specifier = '/' + specifier
|
|
}
|
|
|
|
let status
|
|
|
|
if (resolutions) {
|
|
status = yield* exports.preresolved(specifier, resolutions, parentURL, opts)
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
status = yield* exports.url(specifier, parentURL, opts)
|
|
|
|
if (status) return status
|
|
|
|
status = yield* exports.packageImports(specifier, parentURL, opts)
|
|
|
|
if (status) return status
|
|
|
|
if (
|
|
specifier === '.' ||
|
|
specifier === '..' ||
|
|
specifier[0] === '/' ||
|
|
specifier[0] === '\\' ||
|
|
specifier.startsWith('./') ||
|
|
specifier.startsWith('.\\') ||
|
|
specifier.startsWith('../') ||
|
|
specifier.startsWith('..\\')
|
|
) {
|
|
if (imports) {
|
|
status = yield* exports.packageImportsExports(specifier, imports, parentURL, true, opts)
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
status = yield* exports.deferred(specifier, opts)
|
|
|
|
if (status) return status
|
|
|
|
status = yield* exports.file(specifier, parentURL, false, opts)
|
|
|
|
if (status === RESOLVED) return status
|
|
|
|
return yield* exports.directory(specifier, parentURL, opts)
|
|
}
|
|
|
|
return yield* exports.package(specifier, parentURL, opts)
|
|
}
|
|
|
|
exports.url = function* (url, parentURL, opts = {}) {
|
|
const { imports = null, deferredProtocol = 'deferred:', resolutions = null } = opts
|
|
|
|
let resolution
|
|
try {
|
|
resolution = new URL(url)
|
|
} catch {
|
|
return UNRESOLVED
|
|
}
|
|
|
|
if (imports) {
|
|
const status = yield* exports.packageImportsExports(
|
|
resolution.href,
|
|
imports,
|
|
parentURL,
|
|
true,
|
|
opts
|
|
)
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
if (resolution.protocol === deferredProtocol) {
|
|
const specifier = resolution.pathname
|
|
|
|
if (resolutions) {
|
|
const imports = resolutions[parentURL.href]
|
|
|
|
if (typeof imports === 'object' && imports !== null) {
|
|
opts = {
|
|
...opts,
|
|
resolutions: { ...resolutions, [parentURL.href]: { ...imports, [specifier]: null } }
|
|
}
|
|
}
|
|
}
|
|
|
|
return yield* exports.module(specifier, parentURL, opts)
|
|
}
|
|
|
|
if (resolution.protocol === 'node:') {
|
|
const specifier = resolution.pathname
|
|
|
|
if (
|
|
specifier === '.' ||
|
|
specifier === '..' ||
|
|
specifier[0] === '/' ||
|
|
specifier.startsWith('./') ||
|
|
specifier.startsWith('../')
|
|
) {
|
|
throw errors.INVALID_MODULE_SPECIFIER(`Module specifier '${url}' is not a valid package name`)
|
|
}
|
|
|
|
return yield* exports.package(specifier, parentURL, opts)
|
|
}
|
|
|
|
const resolved = yield { resolution }
|
|
|
|
return resolved ? RESOLVED : YIELDED
|
|
}
|
|
|
|
exports.preresolved = function* (specifier, resolutions, parentURL, opts = {}) {
|
|
const imports = resolutions[parentURL.href]
|
|
|
|
if (typeof imports === 'object' && imports !== null) {
|
|
return yield* exports.packageImportsExports(specifier, imports, parentURL, true, opts)
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.deferred = function* (specifier, opts = {}) {
|
|
const { deferredProtocol = 'deferred:', defer = [] } = opts
|
|
|
|
if (defer.includes(specifier)) {
|
|
const resolved = yield { resolution: new URL(deferredProtocol + specifier) }
|
|
|
|
return resolved ? RESOLVED : YIELDED
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.package = function* (packageSpecifier, parentURL, opts = {}) {
|
|
const { builtins = [] } = opts
|
|
|
|
if (packageSpecifier === '') {
|
|
throw errors.INVALID_MODULE_SPECIFIER(
|
|
`Module specifier '${packageSpecifier}' is not a valid package name`
|
|
)
|
|
}
|
|
|
|
let packageName
|
|
|
|
if (packageSpecifier[0] !== '@') {
|
|
packageName = packageSpecifier.split('/', 1).join()
|
|
} else {
|
|
if (!packageSpecifier.includes('/')) {
|
|
throw errors.INVALID_MODULE_SPECIFIER(
|
|
`Module specifier '${packageSpecifier}' is not a valid package name`
|
|
)
|
|
}
|
|
|
|
packageName = packageSpecifier.split('/', 2).join('/')
|
|
}
|
|
|
|
if (packageName[0] === '.' || packageName.includes('\\') || packageName.includes('%')) {
|
|
throw errors.INVALID_MODULE_SPECIFIER(
|
|
`Module specifier '${packageSpecifier}' is not a valid package name`
|
|
)
|
|
}
|
|
|
|
let status
|
|
|
|
status = yield* exports.builtinTarget(packageSpecifier, null, builtins, opts)
|
|
|
|
if (status) return status
|
|
|
|
status = yield* exports.deferred(packageSpecifier, opts)
|
|
|
|
if (status) return status
|
|
|
|
let packageSubpath = '.' + packageSpecifier.substring(packageName.length)
|
|
|
|
status = yield* exports.packageSelf(packageName, packageSubpath, parentURL, opts)
|
|
|
|
if (status) return status
|
|
|
|
parentURL = new URL(parentURL.href)
|
|
|
|
for (const packageURL of exports.lookupPackageRoot(packageName, parentURL)) {
|
|
const info = yield { package: packageURL }
|
|
|
|
if (info) {
|
|
if (info.engines) exports.validateEngines(packageURL, info.engines, opts)
|
|
|
|
if (info.exports) {
|
|
return yield* exports.packageExports(packageURL, packageSubpath, info.exports, opts)
|
|
}
|
|
|
|
if (packageSubpath === '.') {
|
|
if (typeof info.main === 'string' && info.main !== '') {
|
|
packageSubpath = info.main
|
|
} else {
|
|
return yield* exports.file('index', packageURL, true, opts)
|
|
}
|
|
}
|
|
|
|
status = yield* exports.file(packageSubpath, packageURL, false, opts)
|
|
|
|
if (status === RESOLVED) return status
|
|
|
|
return yield* exports.directory(packageSubpath, packageURL, opts)
|
|
}
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.packageSelf = function* (packageName, packageSubpath, parentURL, opts = {}) {
|
|
for (const packageURL of exports.lookupPackageScope(parentURL, opts)) {
|
|
const info = yield { package: packageURL }
|
|
|
|
if (info) {
|
|
if (info.name !== packageName) return false
|
|
|
|
if (info.exports) {
|
|
return yield* exports.packageExports(packageURL, packageSubpath, info.exports, opts)
|
|
}
|
|
|
|
if (packageSubpath === '.') {
|
|
if (typeof info.main === 'string' && info.main !== '') {
|
|
packageSubpath = info.main
|
|
} else {
|
|
return yield* exports.file('index', packageURL, true, opts)
|
|
}
|
|
}
|
|
|
|
const status = yield* exports.file(packageSubpath, packageURL, false, opts)
|
|
|
|
if (status === RESOLVED) return status
|
|
|
|
return yield* exports.directory(packageSubpath, packageURL, opts)
|
|
}
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.packageExports = function* (packageURL, subpath, packageExports, opts = {}) {
|
|
if (subpath === '.') {
|
|
let mainExport
|
|
|
|
if (typeof packageExports === 'string' || Array.isArray(packageExports)) {
|
|
mainExport = packageExports
|
|
} else if (typeof packageExports === 'object' && packageExports !== null) {
|
|
const keys = Object.keys(packageExports)
|
|
|
|
if (keys.some((key) => key.startsWith('.'))) {
|
|
if ('.' in packageExports) mainExport = packageExports['.']
|
|
} else {
|
|
mainExport = packageExports
|
|
}
|
|
}
|
|
|
|
if (mainExport) {
|
|
const status = yield* exports.packageTarget(packageURL, mainExport, null, false, opts)
|
|
|
|
if (status) return status
|
|
}
|
|
} else if (typeof packageExports === 'object' && packageExports !== null) {
|
|
const keys = Object.keys(packageExports)
|
|
|
|
if (keys.every((key) => key.startsWith('.'))) {
|
|
const status = yield* exports.packageImportsExports(
|
|
subpath,
|
|
packageExports,
|
|
packageURL,
|
|
false,
|
|
opts
|
|
)
|
|
|
|
if (status) return status
|
|
}
|
|
}
|
|
|
|
throw errors.PACKAGE_PATH_NOT_EXPORTED(
|
|
`Package subpath '${subpath}' is not defined by "exports" in '${packageURL}'`
|
|
)
|
|
}
|
|
|
|
exports.packageImports = function* (specifier, parentURL, opts = {}) {
|
|
const { imports = null } = opts
|
|
|
|
if (specifier === '#' || specifier.startsWith('#/')) {
|
|
throw errors.INVALID_MODULE_SPECIFIER(
|
|
`Module specifier '${specifier}' is not a valid internal imports specifier`
|
|
)
|
|
}
|
|
|
|
for (const packageURL of exports.lookupPackageScope(parentURL, opts)) {
|
|
const info = yield { package: packageURL }
|
|
|
|
if (info) {
|
|
if (info.imports) {
|
|
const status = yield* exports.packageImportsExports(
|
|
specifier,
|
|
info.imports,
|
|
packageURL,
|
|
true,
|
|
opts
|
|
)
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
if (specifier.startsWith('#')) {
|
|
throw errors.PACKAGE_IMPORT_NOT_DEFINED(
|
|
`Package import specifier '${specifier}' is not defined by "imports" in '${packageURL}'`
|
|
)
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if (imports) {
|
|
const status = yield* exports.packageImportsExports(specifier, imports, parentURL, true, opts)
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.packageImportsExports = function* (
|
|
matchKey,
|
|
matchObject,
|
|
packageURL,
|
|
isImports,
|
|
opts = {}
|
|
) {
|
|
if (matchKey in matchObject && !matchKey.includes('*')) {
|
|
const target = matchObject[matchKey]
|
|
|
|
return yield* exports.packageTarget(packageURL, target, null, isImports, opts)
|
|
}
|
|
|
|
const expansionKeys = Object.keys(matchObject)
|
|
.filter((key) => key.includes('*'))
|
|
.sort(exports.patternKeyCompare)
|
|
|
|
for (const expansionKey of expansionKeys) {
|
|
const patternIndex = expansionKey.indexOf('*')
|
|
const patternBase = expansionKey.substring(0, patternIndex)
|
|
|
|
if (matchKey.startsWith(patternBase) && matchKey !== patternBase) {
|
|
const patternTrailer = expansionKey.substring(patternIndex + 1)
|
|
|
|
if (
|
|
patternTrailer === '' ||
|
|
(matchKey.endsWith(patternTrailer) && matchKey.length >= expansionKey.length)
|
|
) {
|
|
const target = matchObject[expansionKey]
|
|
|
|
const patternMatch = matchKey.substring(
|
|
patternBase.length,
|
|
matchKey.length - patternTrailer.length
|
|
)
|
|
|
|
return yield* exports.packageTarget(packageURL, target, patternMatch, isImports, opts)
|
|
}
|
|
}
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.validateEngines = function validateEngines(packageURL, packageEngines, opts = {}) {
|
|
const { engines = {} } = opts
|
|
|
|
for (const [engine, range] of Object.entries(packageEngines)) {
|
|
if (engine in engines) {
|
|
const version = engines[engine]
|
|
|
|
if (!satisfies(version, range)) {
|
|
throw errors.UNSUPPORTED_ENGINE(
|
|
`Package not compatible with engine '${engine}' ${version}, requires range '${range}' defined by "engines" in '${packageURL}'`
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.patternKeyCompare = function patternKeyCompare(keyA, keyB) {
|
|
const patternIndexA = keyA.indexOf('*')
|
|
const patternIndexB = keyB.indexOf('*')
|
|
const baseLengthA = patternIndexA === -1 ? keyA.length : patternIndexA + 1
|
|
const baseLengthB = patternIndexB === -1 ? keyB.length : patternIndexB + 1
|
|
if (baseLengthA > baseLengthB) return -1
|
|
if (baseLengthB > baseLengthA) return 1
|
|
if (patternIndexA === -1) return 1
|
|
if (patternIndexB === -1) return -1
|
|
if (keyA.length > keyB.length) return -1
|
|
if (keyB.length > keyA.length) return 1
|
|
return 0
|
|
}
|
|
|
|
exports.packageTarget = function* (packageURL, target, patternMatch, isImports, opts = {}) {
|
|
const { conditions = [], matchedConditions = [] } = opts
|
|
|
|
if (typeof target === 'string') {
|
|
if (!target.startsWith('./') && !isImports) {
|
|
throw errors.INVALID_PACKAGE_TARGET(
|
|
`Invalid target '${target}' defined by "exports" in '${packageURL}'`
|
|
)
|
|
}
|
|
|
|
if (patternMatch !== null) {
|
|
target = target.replaceAll('*', patternMatch)
|
|
}
|
|
|
|
const status = yield* exports.url(target, packageURL, opts)
|
|
|
|
if (status) return status
|
|
|
|
if (
|
|
target === '.' ||
|
|
target === '..' ||
|
|
target[0] === '/' ||
|
|
target.startsWith('./') ||
|
|
target.startsWith('../')
|
|
) {
|
|
const resolved = yield { resolution: new URL(target, packageURL) }
|
|
|
|
return resolved ? RESOLVED : YIELDED
|
|
}
|
|
|
|
return yield* exports.package(target, packageURL, opts)
|
|
}
|
|
|
|
if (Array.isArray(target)) {
|
|
for (const targetValue of target) {
|
|
const status = yield* exports.packageTarget(
|
|
packageURL,
|
|
targetValue,
|
|
patternMatch,
|
|
isImports,
|
|
opts
|
|
)
|
|
|
|
if (status) return status
|
|
}
|
|
} else if (typeof target === 'object' && target !== null) {
|
|
let status = UNRESOLVED
|
|
|
|
for (const [condition, targetValue, subset] of exports.conditionMatches(
|
|
target,
|
|
conditions,
|
|
opts
|
|
)) {
|
|
matchedConditions.push(condition)
|
|
|
|
status |= yield* exports.packageTarget(packageURL, targetValue, patternMatch, isImports, {
|
|
...opts,
|
|
conditions: subset
|
|
})
|
|
|
|
matchedConditions.pop()
|
|
}
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.builtinTarget = function* (packageSpecifier, packageVersion, target, opts = {}) {
|
|
const { builtinProtocol = 'builtin:', conditions = [], matchedConditions = [] } = opts
|
|
|
|
if (typeof target === 'string') {
|
|
const targetParts = target.split('@')
|
|
|
|
let targetName
|
|
let targetVersion
|
|
|
|
if (target[0] !== '@') {
|
|
targetName = targetParts[0]
|
|
targetVersion = targetParts[1] || null
|
|
} else {
|
|
targetName = targetParts.slice(0, 2).join('@')
|
|
targetVersion = targetParts[2] || null
|
|
}
|
|
|
|
if (packageSpecifier === targetName) {
|
|
if (packageVersion === null && targetVersion === null) {
|
|
const resolved = yield {
|
|
resolution: new URL(builtinProtocol + packageSpecifier)
|
|
}
|
|
|
|
return resolved ? RESOLVED : YIELDED
|
|
}
|
|
|
|
let version = null
|
|
|
|
if (packageVersion === null) {
|
|
version = targetVersion
|
|
} else if (targetVersion === null || packageVersion === targetVersion) {
|
|
version = packageVersion
|
|
}
|
|
|
|
if (version !== null) {
|
|
const resolved = yield {
|
|
resolution: new URL(builtinProtocol + packageSpecifier + '@' + version)
|
|
}
|
|
|
|
return resolved ? RESOLVED : YIELDED
|
|
}
|
|
}
|
|
} else if (Array.isArray(target)) {
|
|
for (const targetValue of target) {
|
|
const status = yield* exports.builtinTarget(
|
|
packageSpecifier,
|
|
packageVersion,
|
|
targetValue,
|
|
opts
|
|
)
|
|
|
|
if (status) return status
|
|
}
|
|
} else if (typeof target === 'object' && target !== null) {
|
|
let status = UNRESOLVED
|
|
|
|
for (const [condition, targetValue, subset] of exports.conditionMatches(
|
|
target,
|
|
conditions,
|
|
opts
|
|
)) {
|
|
matchedConditions.push(condition)
|
|
|
|
status |= yield* exports.builtinTarget(packageSpecifier, packageVersion, targetValue, {
|
|
...opts,
|
|
conditions: subset
|
|
})
|
|
|
|
matchedConditions.pop()
|
|
}
|
|
|
|
if (status) return status
|
|
}
|
|
|
|
return UNRESOLVED
|
|
}
|
|
|
|
exports.conditionMatches = function* conditionMatches(target, conditions, opts = {}) {
|
|
if (conditions.every((condition) => typeof condition === 'string')) {
|
|
const keys = Object.keys(target)
|
|
|
|
for (const condition of keys) {
|
|
if (condition === 'default' || conditions.includes(condition)) {
|
|
yield [condition, target[condition], conditions]
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
let yielded = false
|
|
|
|
for (const subset of conditions) {
|
|
if (yield* conditionMatches(target, subset, opts)) {
|
|
yielded = true
|
|
}
|
|
}
|
|
|
|
return yielded
|
|
}
|
|
|
|
exports.lookupPackageRoot = function* (packageName, parentURL) {
|
|
parentURL = new URL(parentURL.href)
|
|
|
|
do {
|
|
const packageURL = new URL('node_modules/' + packageName + '/', parentURL)
|
|
|
|
const info = yield new URL('package.json', packageURL)
|
|
|
|
if (info) return info
|
|
|
|
parentURL.pathname = parentURL.pathname.substring(0, parentURL.pathname.lastIndexOf('/'))
|
|
|
|
if (
|
|
parentURL.pathname.length === 3 &&
|
|
exports.isWindowsDriveLetter(parentURL.pathname.substring(1))
|
|
) {
|
|
break
|
|
}
|
|
} while (parentURL.pathname !== '' && parentURL.pathname !== '/')
|
|
|
|
return null
|
|
}
|
|
|
|
exports.lookupPackageScope = function* lookupPackageScope(scopeURL, opts = {}) {
|
|
const { resolutions = null } = opts
|
|
|
|
if (resolutions) {
|
|
for (const { resolution } of exports.preresolved('#package', resolutions, scopeURL, opts)) {
|
|
if (resolution) return yield resolution
|
|
}
|
|
}
|
|
|
|
scopeURL = new URL(scopeURL.href)
|
|
|
|
do {
|
|
if (scopeURL.pathname.endsWith('/node_modules')) break
|
|
|
|
const info = yield new URL('package.json', scopeURL)
|
|
|
|
if (info) return info
|
|
|
|
scopeURL.pathname = scopeURL.pathname.substring(0, scopeURL.pathname.lastIndexOf('/'))
|
|
|
|
if (
|
|
scopeURL.pathname.length === 3 &&
|
|
exports.isWindowsDriveLetter(scopeURL.pathname.substring(1))
|
|
) {
|
|
break
|
|
}
|
|
} while (scopeURL.pathname !== '' && scopeURL.pathname !== '/')
|
|
|
|
return null
|
|
}
|
|
|
|
exports.file = function* (filename, parentURL, isIndex, opts = {}) {
|
|
if (
|
|
filename === '.' ||
|
|
filename === '..' ||
|
|
filename[filename.length - 1] === '/' ||
|
|
filename[filename.length - 1] === '\\'
|
|
) {
|
|
return UNRESOLVED
|
|
}
|
|
|
|
if (parentURL.protocol === 'file:' && /%2f|%5c/i.test(filename)) {
|
|
throw errors.INVALID_MODULE_SPECIFIER(`Module specifier '${filename}' is invalid`)
|
|
}
|
|
|
|
const { extensions = [] } = opts
|
|
|
|
let status = UNRESOLVED
|
|
|
|
if (!isIndex) {
|
|
if (yield { resolution: new URL(filename, parentURL) }) {
|
|
return RESOLVED
|
|
}
|
|
|
|
status = YIELDED
|
|
}
|
|
|
|
for (const ext of extensions) {
|
|
if (filename.endsWith(ext)) continue
|
|
|
|
if (yield { resolution: new URL(filename + ext, parentURL) }) {
|
|
return RESOLVED
|
|
}
|
|
|
|
status = YIELDED
|
|
}
|
|
|
|
return status
|
|
}
|
|
|
|
exports.directory = function* (dirname, parentURL, opts = {}) {
|
|
let directoryURL
|
|
|
|
if (dirname[dirname.length - 1] === '/' || dirname[dirname.length - 1] === '\\') {
|
|
directoryURL = new URL(dirname, parentURL)
|
|
} else {
|
|
directoryURL = new URL(dirname + '/', parentURL)
|
|
}
|
|
|
|
const info = yield { package: new URL('package.json', directoryURL) }
|
|
|
|
if (info) {
|
|
if (info.exports) {
|
|
return yield* exports.packageExports(directoryURL, '.', info.exports, opts)
|
|
}
|
|
|
|
if (typeof info.main === 'string' && info.main !== '') {
|
|
const status = yield* exports.file(info.main, directoryURL, false, opts)
|
|
|
|
if (status === RESOLVED) return status
|
|
|
|
return yield* exports.directory(info.main, directoryURL, opts)
|
|
}
|
|
}
|
|
|
|
return yield* exports.file('index', directoryURL, true, opts)
|
|
}
|
|
|
|
// https://infra.spec.whatwg.org/#ascii-upper-alpha
|
|
function isASCIIUpperAlpha(c) {
|
|
return c >= 0x41 && c <= 0x5a
|
|
}
|
|
|
|
// https://infra.spec.whatwg.org/#ascii-lower-alpha
|
|
function isASCIILowerAlpha(c) {
|
|
return c >= 0x61 && c <= 0x7a
|
|
}
|
|
|
|
// https://infra.spec.whatwg.org/#ascii-alpha
|
|
function isASCIIAlpha(c) {
|
|
return isASCIIUpperAlpha(c) || isASCIILowerAlpha(c)
|
|
}
|
|
|
|
// https://url.spec.whatwg.org/#windows-drive-letter
|
|
exports.isWindowsDriveLetter = function isWindowsDriveLetter(input) {
|
|
return (
|
|
input.length >= 2 &&
|
|
isASCIIAlpha(input.charCodeAt(0)) &&
|
|
(input.charCodeAt(1) === 0x3a || input.charCodeAt(1) === 0x7c)
|
|
)
|
|
}
|
|
|
|
// https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
|
|
exports.startsWithWindowsDriveLetter = function startsWithWindowsDriveLetter(input) {
|
|
return (
|
|
input.length >= 2 &&
|
|
exports.isWindowsDriveLetter(input) &&
|
|
(input.length === 2 ||
|
|
input.charCodeAt(2) === 0x2f ||
|
|
input.charCodeAt(2) === 0x5c ||
|
|
input.charCodeAt(2) === 0x3f ||
|
|
input.charCodeAt(2) === 0x23)
|
|
)
|
|
}
|