udes, StringPrototypeIndexOf, StringPrototypeLastIndexOf, StringPrototypeReplace, StringPrototypeSlice, StringPrototypeSplit, StringPrototypeStartsWith, } = primordials; const internalFS = require('internal/fs/utils'); const { NativeModule } = require('internal/bootstrap/loaders'); const { realpathSync, statSync, Stats, } = require('fs'); const { getOptionValue } = require('internal/options'); // Do not eagerly grab .manifest, it may be in TDZ const policy = getOptionValue('--experimental-policy') ? require('internal/process/policy') : null; const { sep, relative, resolve } = require('path'); const preserveSymlinks = getOptionValue('--preserve-symlinks'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); const experimentalNetworkImports = getOptionValue('--experimental-network-imports'); const typeFlag = getOptionValue('--input-type'); const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); const { ERR_INPUT_TYPE_NOT_ALLOWED, ERR_INVALID_ARG_VALUE, ERR_INVALID_MODULE_SPECIFIER, ERR_INVALID_PACKAGE_CONFIG, ERR_INVALID_PACKAGE_TARGET, ERR_MANIFEST_DEPENDENCY_MISSING, ERR_MODULE_NOT_FOUND, ERR_PACKAGE_IMPORT_NOT_DEFINED, ERR_PACKAGE_PATH_NOT_EXPORTED, ERR_UNSUPPORTED_DIR_IMPORT, ERR_NETWORK_IMPORT_DISALLOWED, ERR_UNSUPPORTED_ESM_URL_SCHEME, } = require('internal/errors').codes; const { Module: CJSModule } = require('internal/modules/cjs/loader'); const packageJsonReader = require('internal/modules/package_json_reader'); const userConditions = getOptionValue('--conditions'); const noAddons = getOptionValue('--no-addons'); const addonConditions = noAddons ? [] : ['node-addons']; const DEFAULT_CONDITIONS = ObjectFreeze([ 'node', 'import', ...addonConditions, ...userConditions, ]); const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); /** * @typedef {string | string[] | Record} Exports * @typedef {'module' | 'commonjs'} PackageType * @typedef {{ * pjsonPath: string, * exports?: ExportConfig, * name?: string, * main?: string, * type?: PackageType, * }} PackageConfig */ const emittedPackageWarnings = new SafeSet(); function emitTrailingSlashPatternDeprecation(match, pjsonUrl, base) { const pjsonPath = fileURLToPath(pjsonUrl); if (emittedPackageWarnings.has(pjsonPath + '|' + match)) return; emittedPackageWarnings.add(pjsonPath + '|' + match); process.emitWarning( `Use of deprecated trailing slash pattern mapping "${match}" in the ` + `"exports" field module resolution of the package at ${pjsonPath}${ base ? ` imported from ${fileURLToPath(base)}` : ''}. Mapping specifiers ending in "/" is no longer supported.`, 'DeprecationWarning', 'DEP0155' ); } /** * @param {URL} url * @param {URL} packageJSONUrl * @param {string | URL | undefined} base * @param {string} main * @returns {void} */ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) { const format = defaultGetFormatWithoutErrors(url); if (format !== 'module') return; const path = fileURLToPath(url); const pkgPath = fileURLToPath(new URL('.', packageJSONUrl)); const basePath = fileURLToPath(base); if (main) process.emitWarning( `Package ${pkgPath} has a "main" field set to ${JSONStringify(main)}, ` + `excluding the full filename and extension to the resolved file at "${ StringPrototypeSlice(path, pkgPath.length)}", imported from ${ basePath}.\n Automatic extension resolution of the "main" field is ` + 'deprecated for ES modules.', 'DeprecationWarning', 'DEP0151' ); else process.emitWarning( `No "main" or "exports" field defined in the package.json for ${pkgPath } resolving the main entry point "${ StringPrototypeSlice(path, pkgPath.length)}", imported from ${basePath }.\nDefault "index" lookups for the main are deprecated for ES modules.`, 'DeprecationWarning', 'DEP0151' ); } /** * @param {string[]} [conditions] * @returns {Set} */ function getConditionsSet(conditions) { if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) { if (!ArrayIsArray(conditions)) { throw new ERR_INVALID_ARG_VALUE('conditions', conditions, 'expected an array'); } return new SafeSet(conditions); } return DEFAULT_CONDITIONS_SET; } const realpathCache = new SafeMap(); const packageJSONCache = new SafeMap(); /* string -> PackageConfig */ /** * @param {string | URL} path * @returns {import('fs').Stats} */ const tryStatSync = (path) => statSync(path, { throwIfNoEntry: false }) ?? new Stats(); /** * @param {string} path * @param {string} specifier * @param {string | URL | undefined} base * @returns {PackageConfig} */ function getPackageConfig(path, specifier, base) { const existing = packageJSONCache.get(path); if (existing !== undefined) { return existing; } const source = packageJsonReader.read(path).string; if (source === undefined) { const packageConfig = { pjsonPath: path, exists: false, main: undefined, name: undefined, type: 'none', exports: undefined, imports: undefined, }; packageJSONCache.set(path, packageConfig); return packageConfig; } let packageJSON; try { packageJSON = JSONParse(source); } catch (error) { throw new ERR_INVALID_PACKAGE_CONFIG( path, (base ? `"${specifier}" from ` : '') + fileURLToPath(base || specifier), error.message ); } let { imports, main, name, type } = packageJSON; const { exports } = packageJSON; if (typeof imports !== 'object' || imports === null) imports = undefined; if (typeof main !== 'string') main = undefined; if (typeof name !== 'string') name = undefined; // Ignore unknown types for forwards compatibility if (type !== 'module' && type !== 'commonjs') type = 'none'; const packageConfig = { pjsonPath: path, exists: true, main, name, type, exports, imports, }; packageJSONCache.set(path, packageConfig); return packageConfig; } /** * @param {URL | string} resolved * @returns {PackageConfig} */ function getPackageScopeConfig(resolved) { let packageJSONUrl = new URL('./package.json', resolved); while (true) { const packageJSONPath = packageJSONUrl.pathname; if (StringPrototypeEndsWith(packageJSONPath, 'node_modules/package.json')) break; const packageConfig = getPackageConfig(fileURLToPath(packageJSONUrl), resolved); if (packageConfig.exists) return packageConfig; const lastPackageJSONUrl = packageJSONUrl; packageJSONUrl = new URL('../package.json', packageJSONUrl); // Terminates at root where ../package.json equals ../../package.json // (can't just check "/package.json" for Windows support). if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) break; } const packageJSONPath = fileURLToPath(packageJSONUrl); const packageConfig = { pjsonPath: packageJSONPath, exists: false, main: undefined, name: undefined, type: 'none', exports: undefined, imports: undefined, }; packageJSONCache.set(packageJSONPath, packageConfig); return packageConfig; } /** * @param {string | URL} url * @returns {boolean} */ function fileExists(url) { return statSync(url, { throwIfNoEntry: false })?.isFile() ?? false; } /** * Legacy CommonJS main resolution: * 1. let M = pkg_url + (json main field) * 2. TRY(M, M.js, M.json, M.node) * 3. TRY(M/index.js, M/index.json, M/index.node) * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node) * 5. NOT_FOUND * @param {URL} packageJSONUrl * @param {PackageConfig} packageConfig * @param {string | URL | undefined} base * @returns {URL} */ function legacyMainResolve(packageJSONUrl, packageConfig, base) { let guess; if (packageConfig.main !== undefined) { // Note: fs check redundances will be handled by Descriptor cache here. if (fileExists(guess = new URL(`./${packageConfig.main}`, packageJSONUrl))) { return guess; } else if (fileExists(guess = new URL(`./${packageConfig.main}.js`, packageJSONUrl))); else if (fileExists(guess = new URL(`./${packageConfig.main}.json`, packageJSONUrl))); else if (fileExists(guess = new URL(`./${packageConfig.main}.node`, packageJSONUrl))); else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`, packageJSONUrl))); else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`, packageJSONUrl))); else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`, packageJSONUrl))); else guess = undefined; if (guess) { emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main); return guess; } // Fallthrough. } if (fileExists(guess = new URL('./index.js', packageJSONUrl))); // So fs. else if (fileExists(guess = new URL('./index.json', packageJSONUrl))); else if (fileExists(guess = new URL('./index.node', packageJSONUrl))); else guess = undefined; if (guess) { emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main); return guess; } // Not found. throw new ERR_MODULE_NOT_FOUND( fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); } /** * @param {URL} search * @returns {URL | undefined} */ function resolveExtensionsWithTryExactName(search) { if (fileExists(search)) return search; return resolveExtensions(search); } const extensions = ['.js', '.json', '.node', '.mjs']; /** * @param {URL} search * @returns {URL | undefined} */ function resolveExtensions(search) { for (let i = 0; i < extensions.length; i++) { const extension = extensions[i]; const guess = new URL(`${search.pathname}${extension}`, search); if (fileExists(guess)) return guess; } return undefined; } /** * @param {URL} search * @returns {URL | undefined} */ function resolveDirectoryEntry(search) { const dirPath = fileURLToPath(search); const pkgJsonPath = resolve(dirPath, 'package.json'); if (fileExists(pkgJsonPath)) { const pkgJson = packageJsonReader.read(pkgJsonPath); if (pkgJson.containsKeys) { const { main } = JSONParse(pkgJson.string); if (main != null) { const mainUrl = pathToFileURL(resolve(dirPath, main)); return resolveExtensionsWithTryExactName(mainUrl); } } } return resolveExtensions(new URL('index', search)); } const encodedSepRegEx = /%2F|%5C/i; /** * @param {URL} resolved * @param {string | URL | undefined} base * @param {boolean} preserveSymlinks * @returns {URL | undefined} */ function finalizeResolution(resolved, base, preserveSymlinks) { if (RegExpPrototypeTest(encodedSepRegEx, resolved.pathname)) throw new ERR_INVALID_MODULE_SPECIFIER( resolved.pathname, 'must not include encoded "/" or "\\" characters', fileURLToPath(base)); let path = fileURLToPath(resolved); if (getOptionValue('--experimental-specifier-resolution') === 'node') { let file = resolveExtensionsWithTryExactName(resolved); // Directory if (file === undefined) { file = StringPrototypeEndsWith(path, '/') ? (resolveDirectoryEntry(resolved) || resolved) : resolveDirectoryEntry(new URL(`${resolved}/`)); if (file === resolved) return file; if (file === undefined) { throw new ERR_MODULE_NOT_FOUND( resolved.pathname, fileURLToPath(base), 'module'); } } path = file; } const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? StringPrototypeSlice(path, -1) : path); if (stats.isDirectory()) { const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base)); err.url = String(resolved); throw err; } else if (!stats.isFile()) { throw new ERR_MODULE_NOT_FOUND( path || resolved.pathname, base && fileURLToPath(base), 'module'); } if (!preserveSymlinks) { const real = realpathSync(path, { [internalFS.realpathCacheKey]: realpathCache }); const { search, hash } = resolved; resolved = pathToFileURL(real + (StringPrototypeEndsWith(path, sep) ? '/' : '')); resolved.search = search; resolved.hash = hash; } return resolved; } /** * @param {string} specifier * @param {URL} packageJSONUrl * @param {string | URL | undefined} base */ function throwImportNotDefined(specifier, packageJSONUrl, base) { throw new ERR_PACKAGE_IMPORT_NOT_DEFINED( specifier, packageJSONUrl && fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); } /** * @param {string} subpath * @param {URL} packageJSONUrl * @param {string | URL | undefined} base */ function throwExportsNotFound(subpath, packageJSONUrl, base) { throw new ERR_PACKAGE_PATH_NOT_EXPORTED( fileURLToPath(new URL('.', packageJSONUrl)), subpath, base && fileURLToPath(base)); } /** * * @param {string | URL} subpath * @param {URL} packageJSONUrl * @param {boolean} internal * @param {string | URL | undefined} base */ function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) { const reason = `request is not a valid subpath for the "${internal ? 'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}`; throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason, base && fileURLToPath(base)); } function throwInvalidPackageTarget( subpath, target, packageJSONUrl, internal, base) { if (typeof target === 'object' && target !== null) { target = JSONStringify(target, null, ''); } else { target = `${target}`; } throw new ERR_INVALID_PACKAGE_TARGET( fileURLToPath(new URL('.', packageJSONUrl)), subpath, target, internal, base && fileURLToPath(base)); } const invalidSegmentRegEx = /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i; const invalidPackageNameRegEx = /^\.|%|\\/; const patternRegEx = /\*/g; function resolvePackageTargetString( target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) { if (subpath !== '' && !pattern && target[target.length - 1] !== '/') throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); if (!StringPrototypeStartsWith(target, './')) { if (internal && !StringPrototypeStartsWith(target, '../') && !StringPrototypeStartsWith(target, '/')) { let isURL = false; try { new URL(target); isURL = true; } catch { // Continue regardless of error. } if (!isURL) { const exportTarget = pattern ? RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : target + subpath; return packageResolve( exportTarget, packageJSONUrl, conditions); } } throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); } if (RegExpPrototypeTest(invalidSegmentRegEx, StringPrototypeSlice(target, 2))) throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); const resolved = new URL(target, packageJSONUrl); const resolvedPath = resolved.pathname; const packagePath = new URL('.', packageJSONUrl).pathname; if (!StringPrototypeStartsWith(resolvedPath, packagePath)) throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); if (subpath === '') return resolved; if (RegExpPrototypeTest(invalidSegmentRegEx, subpath)) { const request = pattern ? StringPrototypeReplace(match, '*', () => subpath) : match + subpath; throwInvalidSubpath(request, packageJSONUrl, internal, base); } if (pattern) { return new URL( RegExpPrototypeSymbolReplace( patternRegEx, resolved.href, () => subpath ) ); } return new URL(subpath, resolved); } /** * @param {string} key * @returns {boolean} */ function isArrayIndex(key) { const keyNum = +key; if (`${keyNum}` !== key) return false; return keyNum >= 0 && keyNum < 0xFFFF_FFFF; } function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, base, pattern, internal, conditions) { if (typeof target === 'string') { return resolvePackageTargetString( target, subpath, packageSubpath, packageJSONUrl, base, pattern, internal, conditions); } else if (ArrayIsArray(target)) { if (target.length === 0) { return null; } let lastException; for (let i = 0; i < target.length; i++) { const targetItem = target[i]; let resolveResult; try { resolveResult = resolvePackageTarget( packageJSONUrl, targetItem, subpath, packageSubpath, base, pattern, internal, conditions); } catch (e) { lastException = e; if (e.code === 'ERR_INVALID_PACKAGE_TARGET') { continue; } throw e; } if (resolveResult === undefined) { continue; } if (resolveResult === null) { lastException = null; continue; } return resolveResult; } if (lastException === undefined || lastException === null) return lastException; throw lastException; } else if (typeof target === 'object' && target !== null) { const keys = ObjectGetOwnPropertyNames(target); for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (isArrayIndex(key)) { throw new ERR_INVALID_PACKAGE_CONFIG( fileURLToPath(packageJSONUrl), base, '"exports" cannot contain numeric property keys.'); } } for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (key === 'default' || conditions.has(key)) { const conditionalTarget = target[key]; const resolveResult = resolvePackageTarget( packageJSONUrl, conditionalTarget, subpath, packageSubpath, base, pattern, internal, conditions); if (resolveResult === undefined) continue; return resolveResult; } } return undefined; } else if (target === null) { return null; } throwInvalidPackageTarget(packageSubpath, target, packageJSONUrl, internal, base); } /** * * @param {Exports} exports * @param {URL} packageJSONUrl * @param {string | URL | undefined} base * @returns {boolean} */ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { if (typeof exports === 'string' || ArrayIsArray(exports)) return true; if (typeof exports !== 'object' || exports === null) return false; const keys = ObjectGetOwnPropertyNames(exports); let isConditionalSugar = false; let i = 0; for (let j = 0; j < keys.length; j++) { const key = keys[j]; const curIsConditionalSugar = key === '' || key[0] !== '.'; if (i++ === 0) { isConditionalSugar = curIsConditionalSugar; } else if (isConditionalSugar !== curIsConditionalSugar) { throw new ERR_INVALID_PACKAGE_CONFIG( fileURLToPath(packageJSONUrl), base, '"exports" cannot contain some keys starting with \'.\' and some not.' + ' The exports object must either be an object of package subpath keys' + ' or an object of main entry condition name keys only.'); } } return isConditionalSugar; } /** * @param {URL} packageJSONUrl * @param {string} packageSubpath * @param {PackageConfig} packageConfig * @param {string | URL | undefined} base * @param {Set} conditions * @returns {URL} */ function packageExportsResolve( packageJSONUrl, packageSubpath, packageConfig, base, conditions) { let exports = packageConfig.exports; if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) exports = { '.': exports }; if (ObjectPrototypeHasOwnProperty(exports, packageSubpath) && !StringPrototypeIncludes(packageSubpath, '*') && !StringPrototypeEndsWith(packageSubpath, '/')) { const target = exports[packageSubpath]; const resolveResult = resolvePackageTarget( packageJSONUrl, target, '', packageSubpath, base, false, false, conditions ); if (resolveResult == null) { throwExportsNotFound(packageSubpath, packageJSONUrl, base); } return resolveResult; } let bestMatch = ''; let bestMatchSubpath; const keys = ObjectGetOwnPropertyNames(exports); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const patternIndex = StringPrototypeIndexOf(key, '*'); if (patternIndex !== -1 && StringPrototypeStartsWith(packageSubpath, StringPrototypeSlice(key, 0, patternIndex))) { // When this reaches EOL, this can throw at the top of the whole function: // // if (StringPrototypeEndsWith(packageSubpath, '/')) // throwInvalidSubpath(packageSubpath) // // To match "imports" and the spec. if (StringPrototypeEndsWith(packageSubpath, '/')) emitTrailingSlashPatternDeprecation(packageSubpath, packageJSONUrl, base); const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); if (packageSubpath.length >= key.length && StringPrototypeEndsWith(packageSubpath, patternTrailer) && patternKeyCompare(bestMatch, key) === 1 && StringPrototypeLastIndexOf(key, '*') === patternIndex) { bestMatch = key; bestMatchSubpath = StringPrototypeSlice( packageSubpath, patternIndex, packageSubpath.length - patternTrailer.length); } } } if (bestMatch) { const target = exports[bestMatch]; const resolveResult = resolvePackageTarget( packageJSONUrl, target, bestMatchSubpath, bestMatch, base, true, false, conditions); if (resolveResult == null) { throwExportsNotFound(packageSubpath, packageJSONUrl, base); } return resolveResult; } throwExportsNotFound(packageSubpath, packageJSONUrl, base); } function patternKeyCompare(a, b) { const aPatternIndex = StringPrototypeIndexOf(a, '*'); const bPatternIndex = StringPrototypeIndexOf(b, '*'); const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; if (baseLenA > baseLenB) return -1; if (baseLenB > baseLenA) return 1; if (aPatternIndex === -1) return 1; if (bPatternIndex === -1) return -1; if (a.length > b.length) return -1; if (b.length > a.length) return 1; return 0; } /** * @param {string} name * @param {string | URL | undefined} base * @param {Set} conditions * @returns {URL} */ function packageImportsResolve(name, base, conditions) { if (name === '#' || StringPrototypeStartsWith(name, '#/') || StringPrototypeEndsWith(name, '/')) { const reason = 'is not a valid internal imports specifier name'; throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); } let packageJSONUrl; const packageConfig = getPackageScopeConfig(base); if (packageConfig.exists) { packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); const imports = packageConfig.imports; if (imports) { if (ObjectPrototypeHasOwnProperty(imports, name) && !StringPrototypeIncludes(name, '*')) { const resolveResult = resolvePackageTarget( packageJSONUrl, imports[name], '', name, base, false, true, conditions ); if (resolveResult != null) { return resolveResult; } } else { let bestMatch = ''; let bestMatchSubpath; const keys = ObjectGetOwnPropertyNames(imports); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const patternIndex = StringPrototypeIndexOf(key, '*'); if (patternIndex !== -1 && StringPrototypeStartsWith(name, StringPrototypeSlice(key, 0, patternIndex))) { const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); if (name.length >= key.length && StringPrototypeEndsWith(name, patternTrailer) && patternKeyCompare(bestMatch, key) === 1 && StringPrototypeLastIndexOf(key, '*') === patternIndex) { bestMatch = key; bestMatchSubpath = StringPrototypeSlice( name, patternIndex, name.length - patternTrailer.length); } } } if (bestMatch) { const target = imports[bestMatch]; const resolveResult = resolvePackageTarget(packageJSONUrl, target, bestMatchSubpath, bestMatch, base, true, true, conditions); if (resolveResult != null) { return resolveResult; } } } } } throwImportNotDefined(name, packageJSONUrl, base); } /** * @param {URL} url * @returns {PackageType} */ function getPackageType(url) { const packageConfig = getPackageScopeConfig(url); return packageConfig.type; } /** * @param {string} specifier * @param {string | URL | undefined} base * @returns {{ packageName: string, packageSubpath: string, isScoped: boolean }} */ function parsePackageName(specifier, base) { let separatorIndex = StringPrototypeIndexOf(specifier, '/'); let validPackageName = true; let isScoped = false; if (specifier[0] === '@') { isScoped = true; if (separatorIndex === -1 || specifier.length === 0) { validPackageName = false; } else { separatorIndex = StringPrototypeIndexOf( specifier, '/', separatorIndex + 1); } } const packageName = separatorIndex === -1 ? specifier : StringPrototypeSlice(specifier, 0, separatorIndex); // Package name cannot have leading . and cannot have percent-encoding or // \\ separators. if (RegExpPrototypeExec(invalidPackageNameRegEx, packageName) !== null) validPackageName = false; if (!validPackageName) { throw new ERR_INVALID_MODULE_SPECIFIER( specifier, 'is not a valid package name', fileURLToPath(base)); } const packageSubpath = '.' + (separatorIndex === -1 ? '' : StringPrototypeSlice(specifier, separatorIndex)); return { packageName, packageSubpath, isScoped }; } /** * @param {string} specifier * @param {string | URL | undefined} base * @param {Set} conditions * @returns {resolved: URL, format? : string} */ function packageResolve(specifier, base, conditions) { if (NativeModule.canBeRequiredByUsers(specifier) && NativeModule.canBeRequiredWithoutScheme(specifier)) { return new URL('node:' + specifier); } const { packageName, packageSubpath, isScoped } = parsePackageName(specifier, base); // ResolveSelf const packageConfig = getPackageScopeConfig(base); if (packageConfig.exists) { const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); if (packageConfig.name === packageName && packageConfig.exports !== undefined && packageConfig.exports !== null) { return packageExportsResolve( packageJSONUrl, packageSubpath, packageConfig, base, conditions); } } let packageJSONUrl = new URL('./node_modules/' + packageName + '/package.json', base); let packageJSONPath = fileURLToPath(packageJSONUrl); let lastPath; do { const stat = tryStatSync(StringPrototypeSlice(packageJSONPath, 0, packageJSONPath.length - 13)); if (!stat.isDirectory()) { lastPath = packageJSONPath; packageJSONUrl = new URL((isScoped ? '../../../../node_modules/' : '../../../node_modules/') + packageName + '/package.json', packageJSONUrl); packageJSONPath = fileURLToPath(packageJSONUrl); continue; } // Package match. const packageConfig = getPackageConfig(packageJSONPath, specifier, base); if (packageConfig.exports !== undefined && packageConfig.exports !== null) { return packageExportsResolve( packageJSONUrl, packageSubpath, packageConfig, base, conditions); } if (packageSubpath === '.') { return legacyMainResolve( packageJSONUrl, packageConfig, base ); } return new URL(packageSubpath, packageJSONUrl); // Cross-platform root check. } while (packageJSONPath.length !== lastPath.length); // eslint can't handle the above code. // eslint-disable-next-line no-unreachable throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base)); } /** * @param {string} specifier * @returns {boolean} */ function isBareSpecifier(specifier) { return specifier[0] && specifier[0] !== '/' && specifier[0] !== '.'; } function isRelativeSpecifier(specifier) { if (specifier[0] === '.') { if (specifier.length === 1 || specifier[1] === '/') return true; if (specifier[1] === '.') { if (specifier.length === 2 || specifier[2] === '/') return true; } } return false; } function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) { if (specifier === '') return false; if (specifier[0] === '/') return true; return isRelativeSpecifier(specifier); } /** * @param {string} specifier * @param {string | URL | undefined} base * @param {Set} conditions * @param {boolean} preserveSymlinks * @returns {url: URL, format?: string} */ function moduleResolve(specifier, base, conditions, preserveSymlinks) { const isRemote = base.protocol === 'http:' || base.protocol === 'https:'; // Order swapped from spec for minor perf gain. // Ok since relative URLs cannot parse as URLs. let resolved; if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { resolved = new URL(specifier, base); } else if (!isRemote && specifier[0] === '#') { resolved = packageImportsResolve(specifier, base, conditions); } else { try { resolved = new URL(specifier); } catch { if (!isRemote) { resolved = packageResolve(specifier, base, conditions); } } } if (resolved.protocol !== 'file:') { return resolved; } return finalizeResolution(resolved, base, preserveSymlinks); } /** * Try to resolve an import as a CommonJS module * @param {string} specifier * @param {string} parentURL * @returns {boolean|string} */ function resolveAsCommonJS(specifier, parentURL) { try { const parent = fileURLToPath(parentURL); const tmpModule = new CJSModule(parent, null); tmpModule.paths = CJSModule._nodeModulePaths(parent); let found = CJSModule._resolveFilename(specifier, tmpModule, false); // If it is a relative specifier return the relative path // to the parent if (isRelativeSpecifier(specifier)) { found = relative(parent, found); // Add '.separator if the path does not start with '..separator' // This should be a safe assumption because when loading // esm modules there should be always a file specified so // there should not be a specifier like '..' or '.' if (!StringPrototypeStartsWith(found, `..${sep}`)) { found = `.${sep}${found}`; } } else if (isBareSpecifier(specifier)) { // If it is a bare specifier return the relative path within the // module const pkg = StringPrototypeSplit(specifier, '/')[0]; const index = StringPrototypeIndexOf(found, pkg); if (index !== -1) { found = StringPrototypeSlice(found, index); } } // Normalize the path separator to give a valid suggestion // on Windows if (process.platform === 'win32') { found = RegExpPrototypeSymbolReplace(new RegExp(`\\${sep}`, 'g'), found, '/'); } return found; } catch { return false; } } // TODO(@JakobJingleheimer): de-dupe `specifier` & `parsed` function checkIfDisallowedImport(specifier, parsed, parsedParentURL) { if (parsedParentURL) { const parentURL = fileURLToPath(parsedParentURL?.href); if ( parsedParentURL.protocol === 'http:' || parsedParentURL.protocol === 'https:' ) { if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { // data: and blob: disallowed due to allowing file: access via // indirection if (parsed && parsed.protocol !== 'https:' && parsed.protocol !== 'http:' ) { throw new ERR_NETWORK_IMPORT_DISALLOWED( specifier, parentURL, 'remote imports cannot import from a local location.' ); } return { url: parsed.href }; } if (NativeModule.canBeRequiredByUsers(specifier) && NativeModule.canBeRequiredWithoutScheme(specifier)) { throw new ERR_NETWORK_IMPORT_DISALLOWED( specifier, parentURL, 'remote imports cannot import from a local location.' ); } throw new ERR_NETWORK_IMPORT_DISALLOWED( specifier, parentURL, 'only relative and absolute specifiers are supported.' ); } } } function throwIfUnsupportedURLProtocol(url) { if (url.protocol !== 'file:' && url.protocol !== 'data:' && url.protocol !== 'node:') { throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url); } } function throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports) { if ( parsed && parsed.protocol !== 'file:' && parsed.protocol !== 'data:' && ( !experimentalNetworkImports || ( parsed.protocol !== 'https:' && parsed.protocol !== 'http:' ) ) ) { throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, ArrayPrototypeConcat( 'file', 'data', experimentalNetworkImports ? ['https', 'http'] : [], )); } } async function defaultResolve(specifier, context = {}, defaultResolveUnused) { let { parentURL, conditions } = context; if (parentURL && policy?.manifest) { const redirects = policy.manifest.getDependencyMapper(parentURL); if (redirects) { const { resolve, reaction } = redirects; const destination = resolve(specifier, new SafeSet(conditions)); let missing = true; if (destination === true) { missing = false; } else if (destination) { const href = destination.href; return { url: href }; } if (missing) { // Prevent network requests from firing if resolution would be banned. // Network requests can extract data by doing things like putting // secrets in query params reaction(new ERR_MANIFEST_DEPENDENCY_MISSING( parentURL, specifier, ArrayPrototypeJoin([...conditions], ', ')) ); } } } let parsedParentURL; if (parentURL) { try { parsedParentURL = new URL(parentURL); } catch { // Ignore exception } } let parsed; try { if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { parsed = new URL(specifier, parsedParentURL); } else { parsed = new URL(specifier); } if (parsed.protocol === 'data:' || (experimentalNetworkImports && ( parsed.protocol === 'https:' || parsed.protocol === 'http:' ) ) ) { return { url: parsed.href }; } } catch { // Ignore exception } // There are multiple deep branches that can either throw or return; instead // of duplicating that deeply nested logic for the possible returns, DRY and // check for a return. This seems the least gnarly. const maybeReturn = checkIfDisallowedImport( specifier, parsed, parsedParentURL, ); if (maybeReturn) return maybeReturn; // This must come after checkIfDisallowedImport if (parsed && parsed.protocol === 'node:') return { url: specifier }; throwIfUnsupportedURLScheme(parsed, experimentalNetworkImports); const isMain = parentURL === undefined; if (isMain) { parentURL = pathToFileURL(`${process.cwd()}/`).href; // This is the initial entry point to the program, and --input-type has // been passed as an option; but --input-type can only be used with // --eval, --print or STDIN string input. It is not allowed with file // input, to avoid user confusion over how expansive the effect of the // flag should be (i.e. entry point only, package scope surrounding the // entry point, etc.). if (typeFlag) throw new ERR_INPUT_TYPE_NOT_ALLOWED(); } conditions = getConditionsSet(conditions); let url; try { url = moduleResolve( specifier, parentURL, conditions, isMain ? preserveSymlinksMain : preserveSymlinks ); } catch (error) { // Try to give the user a hint of what would have been the // resolved CommonJS module if (error.code === 'ERR_MODULE_NOT_FOUND' || error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') { if (StringPrototypeStartsWith(specifier, 'file://')) { specifier = fileURLToPath(specifier); } const found = resolveAsCommonJS(specifier, parentURL); if (found) { // Modify the stack and message string to include the hint const lines = StringPrototypeSplit(error.stack, '\n'); const hint = `Did you mean to import ${found}?`; error.stack = ArrayPrototypeShift(lines) + '\n' + hint + '\n' + ArrayPrototypeJoin(lines, '\n'); error.message += `\n${hint}`; } } throw error; } throwIfUnsupportedURLProtocol(url); return { // Do NOT cast `url` to a string: that will work even when there are real // problems, silencing them url: url.href, format: defaultGetFormatWithoutErrors(url, context), }; } module.exports = { DEFAULT_CONDITIONS, defaultResolve, encodedSepRegEx, getPackageScopeConfig, getPackageType, packageExportsResolve, packageImportsResolve, }; // cycle const { defaultGetFormatWithoutErrors, } = require('internal/modules/esm/get_format'); if (policy) { const $defaultResolve = defaultResolve; module.exports.defaultResolve = async function defaultResolve( specifier, context ) { const ret = await $defaultResolve(specifier, context, $defaultResolve); // This is a preflight check to avoid data exfiltration by query params etc. policy.manifest.mightAllow(ret.url, () => new ERR_MANIFEST_DEPENDENCY_MISSING( context.parentURL, specifier, context.conditions ) ); return ret; }; } 'use strict'; // This is needed to avoid cycles in esm/resolve <-> cjs/loader require('internal/modules/cjs/loader'); const { Array, ArrayIsArray, ArrayPrototypeJoin, ArrayPrototypePush, FunctionPrototypeBind, FunctionPrototypeCall, ObjectAssign, ObjectCreate, ObjectSetPrototypeOf, PromiseAll, RegExpPrototypeExec, SafeArrayIterator, SafeWeakMap, StringPrototypeStartsWith, globalThis, } = primordials; const { MessageChannel } = require('internal/worker/io'); const { ERR_INTERNAL_ASSERTION, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_RETURN_PROPERTY_VALUE, ERR_INVALID_RETURN_VALUE, ERR_UNKNOWN_MODULE_FORMAT } = require('internal/errors').codes; const { pathToFileURL, isURLInstance, URL } = require('internal/url'); const { emitExperimentalWarning } = require('internal/util'); const { isAnyArrayBuffer, isArrayBufferView, } = require('internal/util/types'); const ModuleMap = require('internal/modules/esm/module_map'); const ModuleJob = require('internal/modules/esm/module_job'); const { defaultResolve, DEFAULT_CONDITIONS, } = require('internal/modules/esm/resolve'); const { initializeImportMeta } = require('internal/modules/esm/initialize_import_meta'); const { defaultLoad } = require('internal/modules/esm/load'); const { translators } = require( 'internal/modules/esm/translators'); const { getOptionValue } = require('internal/options'); const { fetchModule, } = require('internal/modules/esm/fetch_module'); /** * Prevent the specifier resolution warning from being printed twice */ let emittedSpecifierResolutionWarning = false; /** * An ESMLoader instance is used as the main entry point for loading ES modules. * Currently, this is a singleton -- there is only one used for loading * the main module and everything in its dependency graph. */ class ESMLoader { /** * Prior to ESM loading. These are called once before any modules are started. * @private * @property {Function[]} globalPreloaders First-in-first-out list of * preload hooks. */ #globalPreloaders = []; /** * Phase 2 of 2 in ESM loading. * @private * @property {Function[]} loaders First-in-first-out list of loader hooks. */ #loaders = [ defaultLoad, ]; /** * Phase 1 of 2 in ESM loading. * @private * @property {Function[]} resolvers First-in-first-out list of resolver hooks */ #resolvers = [ defaultResolve, ]; #importMetaInitializer = initializeImportMeta; /** * Map of already-loaded CJS modules to use */ cjsCache = new SafeWeakMap(); /** * The index for assigning unique URLs to anonymous module evaluation */ evalIndex = 0; /** * Registry of loaded modules, akin to `require.cache` */ moduleMap = new ModuleMap(); /** * Methods which translate input code or other information into ES modules */ translators = translators; constructor() { if (getOptionValue('--experimental-loader')) { emitExperimentalWarning('Custom ESM Loaders'); } if (getOptionValue('--experimental-network-imports')) { emitExperimentalWarning('Network Imports'); } if (getOptionValue('--experimental-specifier-resolution') === 'node' && !emittedSpecifierResolutionWarning) { process.emitWarning( 'The Node.js specifier resolution flag is experimental. It could change or be removed at any time.', 'ExperimentalWarning' ); emittedSpecifierResolutionWarning = true; } } static pluckHooks({ globalPreload, resolve, load, // obsolete hooks: dynamicInstantiate, getFormat, getGlobalPreloadCode, getSource, transformSource, }) { const obsoleteHooks = []; const acceptedHooks = ObjectCreate(null); if (getGlobalPreloadCode) { globalPreload ??= getGlobalPreloadCode; process.emitWarning( 'Loader hook "getGlobalPreloadCode" has been renamed to "globalPreload"' ); } if (dynamicInstantiate) ArrayPrototypePush( obsoleteHooks, 'dynamicInstantiate' ); if (getFormat) ArrayPrototypePush( obsoleteHooks, 'getFormat', ); if (getSource) ArrayPrototypePush( obsoleteHooks, 'getSource', ); if (transformSource) ArrayPrototypePush( obsoleteHooks, 'transformSource', ); if (obsoleteHooks.length) process.emitWarning( `Obsolete loader hook(s) supplied and will be ignored: ${ ArrayPrototypeJoin(obsoleteHooks, ', ') }`, 'DeprecationWarning', ); // Use .bind() to avoid giving access to the Loader instance when called. if (globalPreload) { acceptedHooks.globalPreloader = FunctionPrototypeBind(globalPreload, null); } if (resolve) { acceptedHooks.resolver = FunctionPrototypeBind(resolve, null); } if (load) { acceptedHooks.loader = FunctionPrototypeBind(load, null); } return acceptedHooks; } /** * Collect custom/user-defined hook(s). After all hooks have been collected, * calls global preload hook(s). * @param {object | object[]} customLoaders A list of exports from * user-defined loaders (as returned by ESMLoader.import()). */ async addCustomLoaders( customLoaders = [], ) { if (!ArrayIsArray(customLoaders)) customLoaders = [customLoaders]; for (let i = 0; i < customLoaders.length; i++) { const exports = customLoaders[i]; const { globalPreloader, resolver, loader, } = ESMLoader.pluckHooks(exports); if (globalPreloader) ArrayPrototypePush( this.#globalPreloaders, FunctionPrototypeBind(globalPreloader, null), // [1] ); if (resolver) ArrayPrototypePush( this.#resolvers, FunctionPrototypeBind(resolver, null), // [1] ); if (loader) ArrayPrototypePush( this.#loaders, FunctionPrototypeBind(loader, null), // [1] ); } // [1] ensure hook function is not bound to ESMLoader instance this.preload(); } async eval( source, url = pathToFileURL(`${process.cwd()}/[eval${++this.evalIndex}]`).href ) { const evalInstance = (url) => { const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); const module = new ModuleWrap(url, undefined, source, 0, 0); callbackMap.set(module, { importModuleDynamically: (specifier, { url }, importAssertions) => { return this.import(specifier, this.getBaseURL(url), importAssertions); } }); return module; }; const job = new ModuleJob( this, url, undefined, evalInstance, false, false); this.moduleMap.set(url, undefined, job); const { module } = await job.run(); return { namespace: module.getNamespace(), }; } /** * Returns the url to use for the resolution of a given cache key url * These are not guaranteed to be the same. * * In WHATWG HTTP spec for ESM the cache key is the non-I/O bound * synchronous resolution using only string operations * ~= resolveImportMap(new URL(specifier, importerHREF)) * * The url used for subsequent resolution is the response URL after * all redirects have been resolved. * * https://example.com/foo redirecting to https://example.com/bar * would have a cache key of https://example.com/foo and baseURL * of https://example.com/bar * * MUST BE SYNCHRONOUS for import.meta initialization * MUST BE CALLED AFTER receiving the url body due to I/O * @param {string} url * @returns {string} */ getBaseURL(url) { if ( StringPrototypeStartsWith(url, 'http:') || StringPrototypeStartsWith(url, 'https:') ) { // The request & response have already settled, so they are in // fetchModule's cache, in which case, fetchModule returns // immediately and synchronously url = fetchModule(new URL(url), { parentURL: url }).resolvedHREF; // This should only occur if the module hasn't been fetched yet if (typeof url !== 'string') { throw new ERR_INTERNAL_ASSERTION(`Base url for module ${url} not loaded.`); } } return url; } /** * Get a (possibly still pending) module job from the cache, * or create one and return its Promise. * @param {string} specifier The string after `from` in an `import` statement, * or the first parameter of an `import()` * expression * @param {string | undefined} parentURL The URL of the module importing this * one, unless this is the Node.js entry * point. * @param {Record} importAssertions Validations for the * module import. * @returns {Promise} The (possibly pending) module job */ async getModuleJob(specifier, parentURL, importAssertions) { let importAssertionsForResolve; if (this.#loaders.length !== 1) { // We can skip cloning if there are no user provided loaders because // the Node.js default resolve hook does not use import assertions. importAssertionsForResolve = ObjectAssign(ObjectCreate(null), importAssertions); } const { format, url } = await this.resolve(specifier, parentURL, importAssertionsForResolve); let job = this.moduleMap.get(url, importAssertions.type); // CommonJS will set functions for lazy job evaluation. if (typeof job === 'function') { this.moduleMap.set(url, undefined, job = job()); } if (job === undefined) { job = this.#createModuleJob(url, importAssertions, parentURL, format); } return job; } /** * Create and cache an object representing a loaded module. * @param {string} url The absolute URL that was resolved for this module * @param {Record} importAssertions Validations for the * module import. * @param {string} [parentURL] The absolute URL of the module importing this * one, unless this is the Node.js entry point * @param {string} [format] The format hint possibly returned by the * `resolve` hook * @returns {Promise} The (possibly pending) module job */ #createModuleJob(url, importAssertions, parentURL, format) { const moduleProvider = async (url, isMain) => { const { format: finalFormat, source, } = await this.load(url, { format, importAssertions, }); const translator = translators.get(finalFormat); if (!translator) { throw new ERR_UNKNOWN_MODULE_FORMAT(finalFormat, url); } return FunctionPrototypeCall(translator, this, url, source, isMain); }; const inspectBrk = ( parentURL === undefined && getOptionValue('--inspect-brk') ); const job = new ModuleJob( this, url, importAssertions, moduleProvider, parentURL === undefined, inspectBrk ); this.moduleMap.set(url, importAssertions.type, job); return job; } /** * This method is usually called indirectly as part of the loading processes. * Internally, it is used directly to add loaders. Use directly with caution. * * This method must NOT be renamed: it functions as a dynamic import on a * loader module. * * @param {string | string[]} specifiers Path(s) to the module. * @param {string} parentURL Path of the parent importing the module. * @param {Record} importAssertions Validations for the * module import. * @returns {Promise} A list of module export(s). */ async import(specifiers, parentURL, importAssertions) { const wasArr = ArrayIsArray(specifiers); if (!wasArr) specifiers = [specifiers]; const count = specifiers.length; const jobs = new Array(count); for (let i = 0; i < count; i++) { jobs[i] = this.getModuleJob(specifiers[i], parentURL, importAssertions) .then((job) => job.run()) .then(({ module }) => module.getNamespace()); } const namespaces = await PromiseAll(new SafeArrayIterator(jobs)); return wasArr ? namespaces : namespaces[0]; } /** * Provide source that is understood by one of Node's translators. * * The internals of this WILL change when chaining is implemented, * depending on the resolution/consensus from #36954 * @param {string} url The URL/path of the module to be loaded * @param {object} context Metadata about the module * @returns {object} */ async load(url, context = {}) { const defaultLoader = this.#loaders[0]; const loader = this.#loaders.length === 1 ? defaultLoader : this.#loaders[1]; const loaded = await loader(url, context, defaultLoader); if (typeof loaded !== 'object') { throw new ERR_INVALID_RETURN_VALUE( 'object', 'loader load', loaded, ); } const { format, source, } = loaded; if (format == null) { const dataUrl = RegExpPrototypeExec( /^data:([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/, url, ); throw new ERR_UNKNOWN_MODULE_FORMAT( dataUrl ? dataUrl[1] : format, url); } if (typeof format !== 'string') { throw new ERR_INVALID_RETURN_PROPERTY_VALUE( 'string', 'loader resolve', 'format', format, ); } if ( source != null && typeof source !== 'string' && !isAnyArrayBuffer(source) && !isArrayBufferView(source) ) throw ERR_INVALID_RETURN_PROPERTY_VALUE( 'string, an ArrayBuffer, or a TypedArray', 'loader load', 'source', source ); return { format, source, }; } preload() { const count = this.#globalPreloaders.length; if (!count) return; for (let i = 0; i < count; i++) { const channel = new MessageChannel(); const { port1: insidePreload, port2: insideLoader, } = channel; insidePreload.unref(); insideLoader.unref(); const preload = this.#globalPreloaders[i]({ port: insideLoader }); if (preload == null) return; if (typeof preload !== 'string') { throw new ERR_INVALID_RETURN_VALUE( 'string', 'loader globalPreloadCode', preload, ); } const { compileFunction } = require('vm'); const preloadInit = compileFunction( preload, ['getBuiltin', 'port', 'setImportMetaCallback'], { filename: '', } ); const { NativeModule } = require('internal/bootstrap/loaders'); // We only allow replacing the importMetaInitializer during preload, // after preload is finished, we disable the ability to replace it // // This exposes accidentally setting the initializer too late by // throwing an error. let finished = false; let replacedImportMetaInitializer = false; let next = this.#importMetaInitializer; try { // Calls the compiled preload source text gotten from the hook // Since the parameters are named we use positional parameters // see compileFunction above to cross reference the names FunctionPrototypeCall( preloadInit, globalThis, // Param getBuiltin (builtinName) => { if (NativeModule.canBeRequiredByUsers(builtinName) && NativeModule.canBeRequiredWithoutScheme(builtinName)) { return require(builtinName); } throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); }, // Param port insidePreload, // Param setImportMetaCallback (fn) => { if (finished || typeof fn !== 'function') { throw new ERR_INVALID_ARG_TYPE('fn', fn); } replacedImportMetaInitializer = true; const parent = next; next = (meta, context) => { return fn(meta, context, parent); }; }); } finally { finished = true; if (replacedImportMetaInitializer) { this.#importMetaInitializer = next; } } } } importMetaInitialize(meta, context) { this.#importMetaInitializer(meta, context); } /** * Resolve the location of the module. * * The internals of this WILL change when chaining is implemented, * depending on the resolution/consensus from #36954. * @param {string} originalSpecifier The specified URL path of the module to * be resolved. * @param {string} [parentURL] The URL path of the module's parent. * @param {ImportAssertions} [importAssertions] Assertions from the import * statement or expression. * @returns {{ url: string }} */ async resolve( originalSpecifier, parentURL, importAssertions = ObjectCreate(null) ) { const isMain = parentURL === undefined; if ( !isMain && typeof parentURL !== 'string' && !isURLInstance(parentURL) ) throw new ERR_INVALID_ARG_TYPE( 'parentURL', ['string', 'URL'], parentURL, ); const conditions = DEFAULT_CONDITIONS; const defaultResolver = this.#resolvers[0]; const resolver = this.#resolvers.length === 1 ? defaultResolver : this.#resolvers[1]; const resolution = await resolver( originalSpecifier, { conditions, importAssertions, parentURL, }, defaultResolver, ); if (typeof resolution !== 'object') { throw new ERR_INVALID_RETURN_VALUE( 'object', 'loader resolve', resolution, ); } const { format, url } = resolution; if ( format != null && typeof format !== 'string' ) { throw new ERR_INVALID_RETURN_PROPERTY_VALUE( 'string', 'loader resolve', 'format', format, ); } if (typeof url !== 'string') { // non-strings can be coerced to a url string throw new ERR_INVALID_RETURN_PROPERTY_VALUE( 'string', 'loader resolve', 'url', url, ); } new URL(url); // Intentionally trigger error if `url` is invalid return { format, url, }; } } ObjectSetPrototypeOf(ESMLoader.prototype, null); exports.ESMLoader = ESMLoader;