1'use strict'; 2 3const { 4 ArrayIsArray, 5 ArrayPrototypeJoin, 6 ArrayPrototypeShift, 7 JSONParse, 8 JSONStringify, 9 ObjectFreeze, 10 ObjectGetOwnPropertyNames, 11 ObjectPrototypeHasOwnProperty, 12 RegExp, 13 RegExpPrototypeTest, 14 SafeMap, 15 SafeSet, 16 String, 17 StringPrototypeEndsWith, 18 StringPrototypeIndexOf, 19 StringPrototypeReplace, 20 StringPrototypeSlice, 21 StringPrototypeSplit, 22 StringPrototypeStartsWith, 23 StringPrototypeSubstr, 24} = primordials; 25const internalFS = require('internal/fs/utils'); 26const { NativeModule } = require('internal/bootstrap/loaders'); 27const { 28 realpathSync, 29 statSync, 30 Stats, 31} = require('fs'); 32const { getOptionValue } = require('internal/options'); 33const { sep, relative } = require('path'); 34const preserveSymlinks = getOptionValue('--preserve-symlinks'); 35const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); 36const typeFlag = getOptionValue('--input-type'); 37const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); 38const { 39 ERR_INPUT_TYPE_NOT_ALLOWED, 40 ERR_INVALID_ARG_VALUE, 41 ERR_INVALID_MODULE_SPECIFIER, 42 ERR_INVALID_PACKAGE_CONFIG, 43 ERR_INVALID_PACKAGE_TARGET, 44 ERR_MODULE_NOT_FOUND, 45 ERR_PACKAGE_IMPORT_NOT_DEFINED, 46 ERR_PACKAGE_PATH_NOT_EXPORTED, 47 ERR_UNSUPPORTED_DIR_IMPORT, 48 ERR_UNSUPPORTED_ESM_URL_SCHEME, 49} = require('internal/errors').codes; 50const { Module: CJSModule } = require('internal/modules/cjs/loader'); 51 52const packageJsonReader = require('internal/modules/package_json_reader'); 53const userConditions = getOptionValue('--conditions'); 54const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]); 55const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); 56 57 58function getConditionsSet(conditions) { 59 if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) { 60 if (!ArrayIsArray(conditions)) { 61 throw new ERR_INVALID_ARG_VALUE('conditions', conditions, 62 'expected an array'); 63 } 64 return new SafeSet(conditions); 65 } 66 return DEFAULT_CONDITIONS_SET; 67} 68 69const realpathCache = new SafeMap(); 70const packageJSONCache = new SafeMap(); /* string -> PackageConfig */ 71 72function tryStatSync(path) { 73 try { 74 return statSync(path); 75 } catch { 76 return new Stats(); 77 } 78} 79 80function getPackageConfig(path, specifier, base) { 81 const existing = packageJSONCache.get(path); 82 if (existing !== undefined) { 83 return existing; 84 } 85 const source = packageJsonReader.read(path).string; 86 if (source === undefined) { 87 const packageConfig = { 88 pjsonPath: path, 89 exists: false, 90 main: undefined, 91 name: undefined, 92 type: 'none', 93 exports: undefined, 94 imports: undefined, 95 }; 96 packageJSONCache.set(path, packageConfig); 97 return packageConfig; 98 } 99 100 let packageJSON; 101 try { 102 packageJSON = JSONParse(source); 103 } catch (error) { 104 throw new ERR_INVALID_PACKAGE_CONFIG( 105 path, 106 (base ? `"${specifier}" from ` : '') + fileURLToPath(base || specifier), 107 error.message 108 ); 109 } 110 111 let { imports, main, name, type } = packageJSON; 112 const { exports } = packageJSON; 113 if (typeof imports !== 'object' || imports === null) imports = undefined; 114 if (typeof main !== 'string') main = undefined; 115 if (typeof name !== 'string') name = undefined; 116 // Ignore unknown types for forwards compatibility 117 if (type !== 'module' && type !== 'commonjs') type = 'none'; 118 119 const packageConfig = { 120 pjsonPath: path, 121 exists: true, 122 main, 123 name, 124 type, 125 exports, 126 imports, 127 }; 128 packageJSONCache.set(path, packageConfig); 129 return packageConfig; 130} 131 132function getPackageScopeConfig(resolved) { 133 let packageJSONUrl = new URL('./package.json', resolved); 134 while (true) { 135 const packageJSONPath = packageJSONUrl.pathname; 136 if (StringPrototypeEndsWith(packageJSONPath, 'node_modules/package.json')) 137 break; 138 const packageConfig = getPackageConfig(fileURLToPath(packageJSONUrl), 139 resolved); 140 if (packageConfig.exists) return packageConfig; 141 142 const lastPackageJSONUrl = packageJSONUrl; 143 packageJSONUrl = new URL('../package.json', packageJSONUrl); 144 145 // Terminates at root where ../package.json equals ../../package.json 146 // (can't just check "/package.json" for Windows support). 147 if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) break; 148 } 149 const packageJSONPath = fileURLToPath(packageJSONUrl); 150 const packageConfig = { 151 pjsonPath: packageJSONPath, 152 exists: false, 153 main: undefined, 154 name: undefined, 155 type: 'none', 156 exports: undefined, 157 imports: undefined, 158 }; 159 packageJSONCache.set(packageJSONPath, packageConfig); 160 return packageConfig; 161} 162 163/* 164 * Legacy CommonJS main resolution: 165 * 1. let M = pkg_url + (json main field) 166 * 2. TRY(M, M.js, M.json, M.node) 167 * 3. TRY(M/index.js, M/index.json, M/index.node) 168 * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node) 169 * 5. NOT_FOUND 170 */ 171function fileExists(url) { 172 return tryStatSync(fileURLToPath(url)).isFile(); 173} 174 175function legacyMainResolve(packageJSONUrl, packageConfig, base) { 176 let guess; 177 if (packageConfig.main !== undefined) { 178 // Note: fs check redundances will be handled by Descriptor cache here. 179 if (fileExists(guess = new URL(`./${packageConfig.main}`, 180 packageJSONUrl))) { 181 return guess; 182 } 183 if (fileExists(guess = new URL(`./${packageConfig.main}.js`, 184 packageJSONUrl))) { 185 return guess; 186 } 187 if (fileExists(guess = new URL(`./${packageConfig.main}.json`, 188 packageJSONUrl))) { 189 return guess; 190 } 191 if (fileExists(guess = new URL(`./${packageConfig.main}.node`, 192 packageJSONUrl))) { 193 return guess; 194 } 195 if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`, 196 packageJSONUrl))) { 197 return guess; 198 } 199 if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`, 200 packageJSONUrl))) { 201 return guess; 202 } 203 if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`, 204 packageJSONUrl))) { 205 return guess; 206 } 207 // Fallthrough. 208 } 209 if (fileExists(guess = new URL('./index.js', packageJSONUrl))) { 210 return guess; 211 } 212 // So fs. 213 if (fileExists(guess = new URL('./index.json', packageJSONUrl))) { 214 return guess; 215 } 216 if (fileExists(guess = new URL('./index.node', packageJSONUrl))) { 217 return guess; 218 } 219 // Not found. 220 throw new ERR_MODULE_NOT_FOUND( 221 fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); 222} 223 224function resolveExtensionsWithTryExactName(search) { 225 if (fileExists(search)) return search; 226 return resolveExtensions(search); 227} 228 229const extensions = ['.js', '.json', '.node', '.mjs']; 230function resolveExtensions(search) { 231 for (let i = 0; i < extensions.length; i++) { 232 const extension = extensions[i]; 233 const guess = new URL(`${search.pathname}${extension}`, search); 234 if (fileExists(guess)) return guess; 235 } 236 return undefined; 237} 238 239function resolveIndex(search) { 240 return resolveExtensions(new URL('index', search)); 241} 242 243const encodedSepRegEx = /%2F|%2C/i; 244function finalizeResolution(resolved, base) { 245 if (RegExpPrototypeTest(encodedSepRegEx, resolved.pathname)) 246 throw new ERR_INVALID_MODULE_SPECIFIER( 247 resolved.pathname, 'must not include encoded "/" or "\\" characters', 248 fileURLToPath(base)); 249 250 const path = fileURLToPath(resolved); 251 if (getOptionValue('--experimental-specifier-resolution') === 'node') { 252 let file = resolveExtensionsWithTryExactName(resolved); 253 if (file !== undefined) return file; 254 if (!StringPrototypeEndsWith(path, '/')) { 255 file = resolveIndex(new URL(`${resolved}/`)); 256 if (file !== undefined) return file; 257 } else { 258 return resolveIndex(resolved) || resolved; 259 } 260 throw new ERR_MODULE_NOT_FOUND( 261 resolved.pathname, fileURLToPath(base), 'module'); 262 } 263 264 const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? 265 StringPrototypeSlice(path, -1) : path); 266 if (stats.isDirectory()) { 267 const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base)); 268 err.url = String(resolved); 269 throw err; 270 } else if (!stats.isFile()) { 271 throw new ERR_MODULE_NOT_FOUND( 272 path || resolved.pathname, base && fileURLToPath(base), 'module'); 273 } 274 275 return resolved; 276} 277 278function throwImportNotDefined(specifier, packageJSONUrl, base) { 279 throw new ERR_PACKAGE_IMPORT_NOT_DEFINED( 280 specifier, packageJSONUrl && fileURLToPath(new URL('.', packageJSONUrl)), 281 fileURLToPath(base)); 282} 283 284function throwExportsNotFound(subpath, packageJSONUrl, base) { 285 throw new ERR_PACKAGE_PATH_NOT_EXPORTED( 286 fileURLToPath(new URL('.', packageJSONUrl)), subpath, 287 base && fileURLToPath(base)); 288} 289 290function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) { 291 const reason = `request is not a valid subpath for the "${internal ? 292 'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}`; 293 throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason, 294 base && fileURLToPath(base)); 295} 296 297function throwInvalidPackageTarget( 298 subpath, target, packageJSONUrl, internal, base) { 299 if (typeof target === 'object' && target !== null) { 300 target = JSONStringify(target, null, ''); 301 } else { 302 target = `${target}`; 303 } 304 throw new ERR_INVALID_PACKAGE_TARGET( 305 fileURLToPath(new URL('.', packageJSONUrl)), subpath, target, 306 internal, base && fileURLToPath(base)); 307} 308 309const invalidSegmentRegEx = /(^|\\|\/)(\.\.?|node_modules)(\\|\/|$)/; 310const patternRegEx = /\*/g; 311 312function resolvePackageTargetString( 313 target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) { 314 if (subpath !== '' && !pattern && target[target.length - 1] !== '/') 315 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 316 317 if (!StringPrototypeStartsWith(target, './')) { 318 if (internal && !StringPrototypeStartsWith(target, '../') && 319 !StringPrototypeStartsWith(target, '/')) { 320 let isURL = false; 321 try { 322 new URL(target); 323 isURL = true; 324 } catch {} 325 if (!isURL) { 326 const exportTarget = pattern ? 327 StringPrototypeReplace(target, patternRegEx, subpath) : 328 target + subpath; 329 return packageResolve(exportTarget, packageJSONUrl, conditions); 330 } 331 } 332 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 333 } 334 335 if (RegExpPrototypeTest(invalidSegmentRegEx, StringPrototypeSlice(target, 2))) 336 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 337 338 const resolved = new URL(target, packageJSONUrl); 339 const resolvedPath = resolved.pathname; 340 const packagePath = new URL('.', packageJSONUrl).pathname; 341 342 if (!StringPrototypeStartsWith(resolvedPath, packagePath)) 343 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 344 345 if (subpath === '') return resolved; 346 347 if (RegExpPrototypeTest(invalidSegmentRegEx, subpath)) 348 throwInvalidSubpath(match + subpath, packageJSONUrl, internal, base); 349 350 if (pattern) 351 return new URL(StringPrototypeReplace(resolved.href, patternRegEx, 352 subpath)); 353 return new URL(subpath, resolved); 354} 355 356/** 357 * @param {string} key 358 * @returns {boolean} 359 */ 360function isArrayIndex(key) { 361 const keyNum = +key; 362 if (`${keyNum}` !== key) return false; 363 return keyNum >= 0 && keyNum < 0xFFFF_FFFF; 364} 365 366function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, 367 base, pattern, internal, conditions) { 368 if (typeof target === 'string') { 369 return resolvePackageTargetString( 370 target, subpath, packageSubpath, packageJSONUrl, base, pattern, internal, 371 conditions); 372 } else if (ArrayIsArray(target)) { 373 if (target.length === 0) 374 return null; 375 376 let lastException; 377 for (let i = 0; i < target.length; i++) { 378 const targetItem = target[i]; 379 let resolved; 380 try { 381 resolved = resolvePackageTarget( 382 packageJSONUrl, targetItem, subpath, packageSubpath, base, pattern, 383 internal, conditions); 384 } catch (e) { 385 lastException = e; 386 if (e.code === 'ERR_INVALID_PACKAGE_TARGET') 387 continue; 388 throw e; 389 } 390 if (resolved === undefined) 391 continue; 392 if (resolved === null) { 393 lastException = null; 394 continue; 395 } 396 return resolved; 397 } 398 if (lastException === undefined || lastException === null) 399 return lastException; 400 throw lastException; 401 } else if (typeof target === 'object' && target !== null) { 402 const keys = ObjectGetOwnPropertyNames(target); 403 for (let i = 0; i < keys.length; i++) { 404 const key = keys[i]; 405 if (isArrayIndex(key)) { 406 throw new ERR_INVALID_PACKAGE_CONFIG( 407 fileURLToPath(packageJSONUrl), base, 408 '"exports" cannot contain numeric property keys.'); 409 } 410 } 411 for (let i = 0; i < keys.length; i++) { 412 const key = keys[i]; 413 if (key === 'default' || conditions.has(key)) { 414 const conditionalTarget = target[key]; 415 const resolved = resolvePackageTarget( 416 packageJSONUrl, conditionalTarget, subpath, packageSubpath, base, 417 pattern, internal, conditions); 418 if (resolved === undefined) 419 continue; 420 return resolved; 421 } 422 } 423 return undefined; 424 } else if (target === null) { 425 return null; 426 } 427 throwInvalidPackageTarget(packageSubpath, target, packageJSONUrl, internal, 428 base); 429} 430 431function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { 432 if (typeof exports === 'string' || ArrayIsArray(exports)) return true; 433 if (typeof exports !== 'object' || exports === null) return false; 434 435 const keys = ObjectGetOwnPropertyNames(exports); 436 let isConditionalSugar = false; 437 let i = 0; 438 for (let j = 0; j < keys.length; j++) { 439 const key = keys[j]; 440 const curIsConditionalSugar = key === '' || key[0] !== '.'; 441 if (i++ === 0) { 442 isConditionalSugar = curIsConditionalSugar; 443 } else if (isConditionalSugar !== curIsConditionalSugar) { 444 throw new ERR_INVALID_PACKAGE_CONFIG( 445 fileURLToPath(packageJSONUrl), base, 446 '"exports" cannot contain some keys starting with \'.\' and some not.' + 447 ' The exports object must either be an object of package subpath keys' + 448 ' or an object of main entry condition name keys only.'); 449 } 450 } 451 return isConditionalSugar; 452} 453 454/** 455 * @param {URL} packageJSONUrl 456 * @param {string} packageSubpath 457 * @param {object} packageConfig 458 * @param {string} base 459 * @param {Set<string>} conditions 460 * @returns {URL} 461 */ 462function packageExportsResolve( 463 packageJSONUrl, packageSubpath, packageConfig, base, conditions) { 464 let exports = packageConfig.exports; 465 if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) 466 exports = { '.': exports }; 467 468 if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) { 469 const target = exports[packageSubpath]; 470 const resolved = resolvePackageTarget( 471 packageJSONUrl, target, '', packageSubpath, base, false, false, conditions 472 ); 473 if (resolved === null || resolved === undefined) 474 throwExportsNotFound(packageSubpath, packageJSONUrl, base); 475 return { resolved, exact: true }; 476 } 477 478 let bestMatch = ''; 479 const keys = ObjectGetOwnPropertyNames(exports); 480 for (let i = 0; i < keys.length; i++) { 481 const key = keys[i]; 482 if (key[key.length - 1] === '*' && 483 StringPrototypeStartsWith(packageSubpath, 484 StringPrototypeSlice(key, 0, -1)) && 485 packageSubpath.length >= key.length && 486 key.length > bestMatch.length) { 487 bestMatch = key; 488 } else if (key[key.length - 1] === '/' && 489 StringPrototypeStartsWith(packageSubpath, key) && 490 key.length > bestMatch.length) { 491 bestMatch = key; 492 } 493 } 494 495 if (bestMatch) { 496 const target = exports[bestMatch]; 497 const pattern = bestMatch[bestMatch.length - 1] === '*'; 498 const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length - 499 (pattern ? 1 : 0)); 500 const resolved = resolvePackageTarget(packageJSONUrl, target, subpath, 501 bestMatch, base, pattern, false, 502 conditions); 503 if (resolved === null || resolved === undefined) 504 throwExportsNotFound(packageSubpath, packageJSONUrl, base); 505 return { resolved, exact: pattern }; 506 } 507 508 throwExportsNotFound(packageSubpath, packageJSONUrl, base); 509} 510 511function packageImportsResolve(name, base, conditions) { 512 if (name === '#' || StringPrototypeStartsWith(name, '#/')) { 513 const reason = 'is not a valid internal imports specifier name'; 514 throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); 515 } 516 let packageJSONUrl; 517 const packageConfig = getPackageScopeConfig(base); 518 if (packageConfig.exists) { 519 packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); 520 const imports = packageConfig.imports; 521 if (imports) { 522 if (ObjectPrototypeHasOwnProperty(imports, name)) { 523 const resolved = resolvePackageTarget( 524 packageJSONUrl, imports[name], '', name, base, false, true, conditions 525 ); 526 if (resolved !== null) 527 return { resolved, exact: true }; 528 } else { 529 let bestMatch = ''; 530 const keys = ObjectGetOwnPropertyNames(imports); 531 for (let i = 0; i < keys.length; i++) { 532 const key = keys[i]; 533 if (key[key.length - 1] === '*' && 534 StringPrototypeStartsWith(name, 535 StringPrototypeSlice(key, 0, -1)) && 536 name.length >= key.length && 537 key.length > bestMatch.length) { 538 bestMatch = key; 539 } else if (key[key.length - 1] === '/' && 540 StringPrototypeStartsWith(name, key) && 541 key.length > bestMatch.length) { 542 bestMatch = key; 543 } 544 } 545 546 if (bestMatch) { 547 const target = imports[bestMatch]; 548 const pattern = bestMatch[bestMatch.length - 1] === '*'; 549 const subpath = StringPrototypeSubstr(name, bestMatch.length - 550 (pattern ? 1 : 0)); 551 const resolved = resolvePackageTarget( 552 packageJSONUrl, target, subpath, bestMatch, base, pattern, true, 553 conditions); 554 if (resolved !== null) 555 return { resolved, exact: pattern }; 556 } 557 } 558 } 559 } 560 throwImportNotDefined(name, packageJSONUrl, base); 561} 562 563function getPackageType(url) { 564 const packageConfig = getPackageScopeConfig(url); 565 return packageConfig.type; 566} 567 568/** 569 * @param {string} specifier 570 * @param {URL} base 571 * @param {Set<string>} conditions 572 * @returns {URL} 573 */ 574function packageResolve(specifier, base, conditions) { 575 let separatorIndex = StringPrototypeIndexOf(specifier, '/'); 576 let validPackageName = true; 577 let isScoped = false; 578 if (specifier[0] === '@') { 579 isScoped = true; 580 if (separatorIndex === -1 || specifier.length === 0) { 581 validPackageName = false; 582 } else { 583 separatorIndex = StringPrototypeIndexOf( 584 specifier, '/', separatorIndex + 1); 585 } 586 } 587 588 const packageName = separatorIndex === -1 ? 589 specifier : StringPrototypeSlice(specifier, 0, separatorIndex); 590 591 // Package name cannot have leading . and cannot have percent-encoding or 592 // separators. 593 for (let i = 0; i < packageName.length; i++) { 594 if (packageName[i] === '%' || packageName[i] === '\\') { 595 validPackageName = false; 596 break; 597 } 598 } 599 600 if (!validPackageName) { 601 throw new ERR_INVALID_MODULE_SPECIFIER( 602 specifier, 'is not a valid package name', fileURLToPath(base)); 603 } 604 605 const packageSubpath = '.' + (separatorIndex === -1 ? '' : 606 StringPrototypeSlice(specifier, separatorIndex)); 607 608 // ResolveSelf 609 const packageConfig = getPackageScopeConfig(base); 610 if (packageConfig.exists) { 611 const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); 612 if (packageConfig.name === packageName && 613 packageConfig.exports !== undefined && packageConfig.exports !== null) { 614 return packageExportsResolve( 615 packageJSONUrl, packageSubpath, packageConfig, base, conditions 616 ).resolved; 617 } 618 } 619 620 let packageJSONUrl = 621 new URL('./node_modules/' + packageName + '/package.json', base); 622 let packageJSONPath = fileURLToPath(packageJSONUrl); 623 let lastPath; 624 do { 625 const stat = tryStatSync(StringPrototypeSlice(packageJSONPath, 0, 626 packageJSONPath.length - 13)); 627 if (!stat.isDirectory()) { 628 lastPath = packageJSONPath; 629 packageJSONUrl = new URL((isScoped ? 630 '../../../../node_modules/' : '../../../node_modules/') + 631 packageName + '/package.json', packageJSONUrl); 632 packageJSONPath = fileURLToPath(packageJSONUrl); 633 continue; 634 } 635 636 // Package match. 637 const packageConfig = getPackageConfig(packageJSONPath, specifier, base); 638 if (packageConfig.exports !== undefined && packageConfig.exports !== null) 639 return packageExportsResolve( 640 packageJSONUrl, packageSubpath, packageConfig, base, conditions 641 ).resolved; 642 if (packageSubpath === '.') 643 return legacyMainResolve(packageJSONUrl, packageConfig, base); 644 return new URL(packageSubpath, packageJSONUrl); 645 // Cross-platform root check. 646 } while (packageJSONPath.length !== lastPath.length); 647 648 // eslint can't handle the above code. 649 // eslint-disable-next-line no-unreachable 650 throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base)); 651} 652 653function isBareSpecifier(specifier) { 654 return specifier[0] && specifier[0] !== '/' && specifier[0] !== '.'; 655} 656 657function isRelativeSpecifier(specifier) { 658 if (specifier[0] === '.') { 659 if (specifier.length === 1 || specifier[1] === '/') return true; 660 if (specifier[1] === '.') { 661 if (specifier.length === 2 || specifier[2] === '/') return true; 662 } 663 } 664 return false; 665} 666 667function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) { 668 if (specifier === '') return false; 669 if (specifier[0] === '/') return true; 670 return isRelativeSpecifier(specifier); 671} 672 673/** 674 * @param {string} specifier 675 * @param {URL} base 676 * @param {Set<string>} conditions 677 * @returns {URL} 678 */ 679function moduleResolve(specifier, base, conditions) { 680 // Order swapped from spec for minor perf gain. 681 // Ok since relative URLs cannot parse as URLs. 682 let resolved; 683 if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { 684 resolved = new URL(specifier, base); 685 } else if (specifier[0] === '#') { 686 ({ resolved } = packageImportsResolve(specifier, base, conditions)); 687 } else { 688 try { 689 resolved = new URL(specifier); 690 } catch { 691 resolved = packageResolve(specifier, base, conditions); 692 } 693 } 694 return finalizeResolution(resolved, base); 695} 696 697/** 698 * Try to resolve an import as a CommonJS module 699 * @param {string} specifier 700 * @param {string} parentURL 701 * @returns {boolean|string} 702 */ 703function resolveAsCommonJS(specifier, parentURL) { 704 try { 705 const parent = fileURLToPath(parentURL); 706 const tmpModule = new CJSModule(parent, null); 707 tmpModule.paths = CJSModule._nodeModulePaths(parent); 708 709 let found = CJSModule._resolveFilename(specifier, tmpModule, false); 710 711 // If it is a relative specifier return the relative path 712 // to the parent 713 if (isRelativeSpecifier(specifier)) { 714 found = relative(parent, found); 715 // Add '.separator if the path does not start with '..separator' 716 // This should be a safe assumption because when loading 717 // esm modules there should be always a file specified so 718 // there should not be a specifier like '..' or '.' 719 if (!StringPrototypeStartsWith(found, `..${sep}`)) { 720 found = `.${sep}${found}`; 721 } 722 } else if (isBareSpecifier(specifier)) { 723 // If it is a bare specifier return the relative path within the 724 // module 725 const pkg = StringPrototypeSplit(specifier, '/')[0]; 726 const index = StringPrototypeIndexOf(found, pkg); 727 if (index !== -1) { 728 found = StringPrototypeSlice(found, index); 729 } 730 } 731 // Normalize the path separator to give a valid suggestion 732 // on Windows 733 if (process.platform === 'win32') { 734 found = StringPrototypeReplace(found, new RegExp(`\\${sep}`, 'g'), '/'); 735 } 736 return found; 737 } catch { 738 return false; 739 } 740} 741 742function defaultResolve(specifier, context = {}, defaultResolveUnused) { 743 let { parentURL, conditions } = context; 744 let parsed; 745 try { 746 parsed = new URL(specifier); 747 if (parsed.protocol === 'data:') { 748 return { 749 url: specifier 750 }; 751 } 752 } catch {} 753 if (parsed && parsed.protocol === 'node:') 754 return { url: specifier }; 755 if (parsed && parsed.protocol !== 'file:' && parsed.protocol !== 'data:') 756 throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed); 757 if (NativeModule.canBeRequiredByUsers(specifier)) { 758 return { 759 url: 'node:' + specifier 760 }; 761 } 762 if (parentURL && StringPrototypeStartsWith(parentURL, 'data:')) { 763 // This is gonna blow up, we want the error 764 new URL(specifier, parentURL); 765 } 766 767 const isMain = parentURL === undefined; 768 if (isMain) { 769 parentURL = pathToFileURL(`${process.cwd()}/`).href; 770 771 // This is the initial entry point to the program, and --input-type has 772 // been passed as an option; but --input-type can only be used with 773 // --eval, --print or STDIN string input. It is not allowed with file 774 // input, to avoid user confusion over how expansive the effect of the 775 // flag should be (i.e. entry point only, package scope surrounding the 776 // entry point, etc.). 777 if (typeFlag) 778 throw new ERR_INPUT_TYPE_NOT_ALLOWED(); 779 } 780 781 conditions = getConditionsSet(conditions); 782 let url; 783 try { 784 url = moduleResolve(specifier, parentURL, conditions); 785 } catch (error) { 786 // Try to give the user a hint of what would have been the 787 // resolved CommonJS module 788 if (error.code === 'ERR_MODULE_NOT_FOUND' || 789 error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') { 790 if (StringPrototypeStartsWith(specifier, 'file://')) { 791 specifier = fileURLToPath(specifier); 792 } 793 const found = resolveAsCommonJS(specifier, parentURL); 794 if (found) { 795 // Modify the stack and message string to include the hint 796 const lines = StringPrototypeSplit(error.stack, '\n'); 797 const hint = `Did you mean to import ${found}?`; 798 error.stack = 799 ArrayPrototypeShift(lines) + '\n' + 800 hint + '\n' + 801 ArrayPrototypeJoin(lines, '\n'); 802 error.message += `\n${hint}`; 803 } 804 } 805 throw error; 806 } 807 808 if (isMain ? !preserveSymlinksMain : !preserveSymlinks) { 809 const urlPath = fileURLToPath(url); 810 const real = realpathSync(urlPath, { 811 [internalFS.realpathCacheKey]: realpathCache 812 }); 813 const old = url; 814 url = pathToFileURL(real + (urlPath.endsWith(sep) ? '/' : '')); 815 url.search = old.search; 816 url.hash = old.hash; 817 } 818 819 return { url: `${url}` }; 820} 821 822module.exports = { 823 DEFAULT_CONDITIONS, 824 defaultResolve, 825 encodedSepRegEx, 826 getPackageType, 827 packageExportsResolve, 828 packageImportsResolve 829}; 830