1'use strict'; 2 3const { 4 ArrayIsArray, 5 ArrayPrototypeJoin, 6 ArrayPrototypeShift, 7 JSONParse, 8 JSONStringify, 9 ObjectFreeze, 10 ObjectGetOwnPropertyNames, 11 ObjectPrototypeHasOwnProperty, 12 RegExp, 13 RegExpPrototypeExec, 14 RegExpPrototypeSymbolReplace, 15 SafeMap, 16 SafeSet, 17 String, 18 StringPrototypeEndsWith, 19 StringPrototypeIncludes, 20 StringPrototypeIndexOf, 21 StringPrototypeLastIndexOf, 22 StringPrototypeReplace, 23 StringPrototypeSlice, 24 StringPrototypeSplit, 25 StringPrototypeStartsWith, 26} = primordials; 27const internalFS = require('internal/fs/utils'); 28const { BuiltinModule } = require('internal/bootstrap/loaders'); 29const { 30 realpathSync, 31 statSync, 32 Stats, 33} = require('fs'); 34const { getOptionValue } = require('internal/options'); 35const pendingDeprecation = getOptionValue('--pending-deprecation'); 36// Do not eagerly grab .manifest, it may be in TDZ 37const policy = getOptionValue('--experimental-policy') ? 38 require('internal/process/policy') : 39 null; 40const { sep, relative, resolve } = require('path'); 41const preserveSymlinks = getOptionValue('--preserve-symlinks'); 42const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); 43const experimentalNetworkImports = 44 getOptionValue('--experimental-network-imports'); 45const typeFlag = getOptionValue('--input-type'); 46const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); 47const { 48 ERR_INPUT_TYPE_NOT_ALLOWED, 49 ERR_INVALID_ARG_VALUE, 50 ERR_INVALID_MODULE_SPECIFIER, 51 ERR_INVALID_PACKAGE_CONFIG, 52 ERR_INVALID_PACKAGE_TARGET, 53 ERR_MANIFEST_DEPENDENCY_MISSING, 54 ERR_MODULE_NOT_FOUND, 55 ERR_PACKAGE_IMPORT_NOT_DEFINED, 56 ERR_PACKAGE_PATH_NOT_EXPORTED, 57 ERR_UNSUPPORTED_DIR_IMPORT, 58 ERR_NETWORK_IMPORT_DISALLOWED, 59} = require('internal/errors').codes; 60 61const { Module: CJSModule } = require('internal/modules/cjs/loader'); 62const packageJsonReader = require('internal/modules/package_json_reader'); 63const { getPackageConfig, getPackageScopeConfig } = require('internal/modules/esm/package_config'); 64 65/** 66 * @typedef {import('internal/modules/esm/package_config.js').PackageConfig} PackageConfig 67 */ 68 69 70const userConditions = getOptionValue('--conditions'); 71const noAddons = getOptionValue('--no-addons'); 72const addonConditions = noAddons ? [] : ['node-addons']; 73 74const DEFAULT_CONDITIONS = ObjectFreeze([ 75 'node', 76 'import', 77 ...addonConditions, 78 ...userConditions, 79]); 80 81const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); 82 83const emittedPackageWarnings = new SafeSet(); 84 85function emitTrailingSlashPatternDeprecation(match, pjsonUrl, base) { 86 const pjsonPath = fileURLToPath(pjsonUrl); 87 if (emittedPackageWarnings.has(pjsonPath + '|' + match)) 88 return; 89 emittedPackageWarnings.add(pjsonPath + '|' + match); 90 process.emitWarning( 91 `Use of deprecated trailing slash pattern mapping "${match}" in the ` + 92 `"exports" field module resolution of the package at ${pjsonPath}${ 93 base ? ` imported from ${fileURLToPath(base)}` : 94 ''}. Mapping specifiers ending in "/" is no longer supported.`, 95 'DeprecationWarning', 96 'DEP0155', 97 ); 98} 99 100const doubleSlashRegEx = /[/\\][/\\]/; 101 102function emitInvalidSegmentDeprecation(target, request, match, pjsonUrl, internal, base, isTarget) { 103 if (!pendingDeprecation) { return; } 104 const pjsonPath = fileURLToPath(pjsonUrl); 105 const double = RegExpPrototypeExec(doubleSlashRegEx, isTarget ? target : request) !== null; 106 process.emitWarning( 107 `Use of deprecated ${double ? 'double slash' : 108 'leading or trailing slash matching'} resolving "${target}" for module ` + 109 `request "${request}" ${request !== match ? `matched to "${match}" ` : '' 110 }in the "${internal ? 'imports' : 'exports'}" field module resolution of the package at ${ 111 pjsonPath}${base ? ` imported from ${fileURLToPath(base)}` : ''}.`, 112 'DeprecationWarning', 113 'DEP0166', 114 ); 115} 116 117/** 118 * @param {URL} url 119 * @param {URL} packageJSONUrl 120 * @param {string | URL | undefined} base 121 * @param {string} [main] 122 * @returns {void} 123 */ 124function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) { 125 const format = defaultGetFormatWithoutErrors(url); 126 if (format !== 'module') 127 return; 128 const path = fileURLToPath(url); 129 const pkgPath = fileURLToPath(new URL('.', packageJSONUrl)); 130 const basePath = fileURLToPath(base); 131 if (!main) { 132 process.emitWarning( 133 `No "main" or "exports" field defined in the package.json for ${pkgPath 134 } resolving the main entry point "${ 135 StringPrototypeSlice(path, pkgPath.length)}", imported from ${basePath 136 }.\nDefault "index" lookups for the main are deprecated for ES modules.`, 137 'DeprecationWarning', 138 'DEP0151', 139 ); 140 } else if (resolve(pkgPath, main) !== path) { 141 process.emitWarning( 142 `Package ${pkgPath} has a "main" field set to "${main}", ` + 143 `excluding the full filename and extension to the resolved file at "${ 144 StringPrototypeSlice(path, pkgPath.length)}", imported from ${ 145 basePath}.\n Automatic extension resolution of the "main" field is ` + 146 'deprecated for ES modules.', 147 'DeprecationWarning', 148 'DEP0151', 149 ); 150 } 151} 152 153/** 154 * @param {string[]} [conditions] 155 * @returns {Set<string>} 156 */ 157function getConditionsSet(conditions) { 158 if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) { 159 if (!ArrayIsArray(conditions)) { 160 throw new ERR_INVALID_ARG_VALUE('conditions', conditions, 161 'expected an array'); 162 } 163 return new SafeSet(conditions); 164 } 165 return DEFAULT_CONDITIONS_SET; 166} 167 168const realpathCache = new SafeMap(); 169 170/** 171 * @param {string | URL} path 172 * @returns {import('fs').Stats} 173 */ 174const tryStatSync = 175 (path) => statSync(path, { throwIfNoEntry: false }) ?? new Stats(); 176 177/** 178 * @param {string | URL} url 179 * @returns {boolean} 180 */ 181function fileExists(url) { 182 return statSync(url, { throwIfNoEntry: false })?.isFile() ?? false; 183} 184 185/** 186 * Legacy CommonJS main resolution: 187 * 1. let M = pkg_url + (json main field) 188 * 2. TRY(M, M.js, M.json, M.node) 189 * 3. TRY(M/index.js, M/index.json, M/index.node) 190 * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node) 191 * 5. NOT_FOUND 192 * @param {URL} packageJSONUrl 193 * @param {PackageConfig} packageConfig 194 * @param {string | URL | undefined} base 195 * @returns {URL} 196 */ 197function legacyMainResolve(packageJSONUrl, packageConfig, base) { 198 let guess; 199 if (packageConfig.main !== undefined) { 200 // Note: fs check redundances will be handled by Descriptor cache here. 201 if (fileExists(guess = new URL(`./${packageConfig.main}`, 202 packageJSONUrl))) { 203 return guess; 204 } else if (fileExists(guess = new URL(`./${packageConfig.main}.js`, 205 packageJSONUrl))); 206 else if (fileExists(guess = new URL(`./${packageConfig.main}.json`, 207 packageJSONUrl))); 208 else if (fileExists(guess = new URL(`./${packageConfig.main}.node`, 209 packageJSONUrl))); 210 else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`, 211 packageJSONUrl))); 212 else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`, 213 packageJSONUrl))); 214 else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`, 215 packageJSONUrl))); 216 else guess = undefined; 217 if (guess) { 218 emitLegacyIndexDeprecation(guess, packageJSONUrl, base, 219 packageConfig.main); 220 return guess; 221 } 222 // Fallthrough. 223 } 224 if (fileExists(guess = new URL('./index.js', packageJSONUrl))); 225 // So fs. 226 else if (fileExists(guess = new URL('./index.json', packageJSONUrl))); 227 else if (fileExists(guess = new URL('./index.node', packageJSONUrl))); 228 else guess = undefined; 229 if (guess) { 230 emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main); 231 return guess; 232 } 233 // Not found. 234 throw new ERR_MODULE_NOT_FOUND( 235 fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); 236} 237 238/** 239 * @param {URL} search 240 * @returns {URL | undefined} 241 */ 242function resolveExtensionsWithTryExactName(search) { 243 if (fileExists(search)) return search; 244 return resolveExtensions(search); 245} 246 247const extensions = ['.js', '.json', '.node', '.mjs']; 248 249/** 250 * @param {URL} search 251 * @returns {URL | undefined} 252 */ 253function resolveExtensions(search) { 254 for (let i = 0; i < extensions.length; i++) { 255 const extension = extensions[i]; 256 const guess = new URL(`${search.pathname}${extension}`, search); 257 if (fileExists(guess)) return guess; 258 } 259 return undefined; 260} 261 262/** 263 * @param {URL} search 264 * @returns {URL | undefined} 265 */ 266function resolveDirectoryEntry(search) { 267 const dirPath = fileURLToPath(search); 268 const pkgJsonPath = resolve(dirPath, 'package.json'); 269 if (fileExists(pkgJsonPath)) { 270 const pkgJson = packageJsonReader.read(pkgJsonPath); 271 if (pkgJson.containsKeys) { 272 const { main } = JSONParse(pkgJson.string); 273 if (main != null) { 274 const mainUrl = pathToFileURL(resolve(dirPath, main)); 275 return resolveExtensionsWithTryExactName(mainUrl); 276 } 277 } 278 } 279 return resolveExtensions(new URL('index', search)); 280} 281 282const encodedSepRegEx = /%2F|%5C/i; 283/** 284 * @param {URL} resolved 285 * @param {string | URL | undefined} base 286 * @param {boolean} preserveSymlinks 287 * @returns {URL | undefined} 288 */ 289function finalizeResolution(resolved, base, preserveSymlinks) { 290 if (RegExpPrototypeExec(encodedSepRegEx, resolved.pathname) !== null) 291 throw new ERR_INVALID_MODULE_SPECIFIER( 292 resolved.pathname, 'must not include encoded "/" or "\\" characters', 293 fileURLToPath(base)); 294 295 let path = fileURLToPath(resolved); 296 if (getOptionValue('--experimental-specifier-resolution') === 'node') { 297 let file = resolveExtensionsWithTryExactName(resolved); 298 299 // Directory 300 if (file === undefined) { 301 file = StringPrototypeEndsWith(path, '/') ? 302 (resolveDirectoryEntry(resolved) || resolved) : resolveDirectoryEntry(new URL(`${resolved}/`)); 303 304 if (file === resolved) return file; 305 306 if (file === undefined) { 307 throw new ERR_MODULE_NOT_FOUND( 308 resolved.pathname, fileURLToPath(base), 'module'); 309 } 310 } 311 // If `preserveSymlinks` is false, `resolved` is returned and `path` 312 // is used only to check that the resolved path exists. 313 resolved = file; 314 path = fileURLToPath(resolved); 315 } 316 317 const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? 318 StringPrototypeSlice(path, -1) : path); 319 if (stats.isDirectory()) { 320 const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base)); 321 err.url = String(resolved); 322 throw err; 323 } else if (!stats.isFile()) { 324 if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { 325 process.send({ 'watch:require': [path || resolved.pathname] }); 326 } 327 throw new ERR_MODULE_NOT_FOUND( 328 path || resolved.pathname, base && fileURLToPath(base), 'module'); 329 } 330 331 if (!preserveSymlinks) { 332 const real = realpathSync(path, { 333 [internalFS.realpathCacheKey]: realpathCache, 334 }); 335 const { search, hash } = resolved; 336 resolved = 337 pathToFileURL(real + (StringPrototypeEndsWith(path, sep) ? '/' : '')); 338 resolved.search = search; 339 resolved.hash = hash; 340 } 341 342 return resolved; 343} 344 345/** 346 * @param {string} specifier 347 * @param {URL} packageJSONUrl 348 * @param {string | URL | undefined} base 349 */ 350function importNotDefined(specifier, packageJSONUrl, base) { 351 return new ERR_PACKAGE_IMPORT_NOT_DEFINED( 352 specifier, packageJSONUrl && fileURLToPath(new URL('.', packageJSONUrl)), 353 fileURLToPath(base)); 354} 355 356/** 357 * @param {string} subpath 358 * @param {URL} packageJSONUrl 359 * @param {string | URL | undefined} base 360 */ 361function exportsNotFound(subpath, packageJSONUrl, base) { 362 return new ERR_PACKAGE_PATH_NOT_EXPORTED( 363 fileURLToPath(new URL('.', packageJSONUrl)), subpath, 364 base && fileURLToPath(base)); 365} 366 367/** 368 * 369 * @param {string} request 370 * @param {string} match 371 * @param {URL} packageJSONUrl 372 * @param {boolean} internal 373 * @param {string | URL | undefined} base 374 */ 375function throwInvalidSubpath(request, match, packageJSONUrl, internal, base) { 376 const reason = `request is not a valid match in pattern "${match}" for the "${ 377 internal ? 'imports' : 'exports'}" resolution of ${ 378 fileURLToPath(packageJSONUrl)}`; 379 throw new ERR_INVALID_MODULE_SPECIFIER(request, reason, 380 base && fileURLToPath(base)); 381} 382 383function invalidPackageTarget( 384 subpath, target, packageJSONUrl, internal, base) { 385 if (typeof target === 'object' && target !== null) { 386 target = JSONStringify(target, null, ''); 387 } else { 388 target = `${target}`; 389 } 390 return new ERR_INVALID_PACKAGE_TARGET( 391 fileURLToPath(new URL('.', packageJSONUrl)), subpath, target, 392 internal, base && fileURLToPath(base)); 393} 394 395const 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; 396const deprecatedInvalidSegmentRegEx = /(^|\\|\/)((\.|%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; 397const invalidPackageNameRegEx = /^\.|%|\\/; 398const patternRegEx = /\*/g; 399 400/** 401 * 402 * @param {string} target 403 * @param {*} subpath 404 * @param {*} match 405 * @param {*} packageJSONUrl 406 * @param {*} base 407 * @param {*} pattern 408 * @param {*} internal 409 * @param {*} isPathMap 410 * @param {*} conditions 411 * @returns {URL} 412 */ 413function resolvePackageTargetString( 414 target, 415 subpath, 416 match, 417 packageJSONUrl, 418 base, 419 pattern, 420 internal, 421 isPathMap, 422 conditions, 423) { 424 425 if (subpath !== '' && !pattern && target[target.length - 1] !== '/') 426 throw invalidPackageTarget(match, target, packageJSONUrl, internal, base); 427 428 if (!StringPrototypeStartsWith(target, './')) { 429 if (internal && !StringPrototypeStartsWith(target, '../') && 430 !StringPrototypeStartsWith(target, '/')) { 431 let isURL = false; 432 try { 433 new URL(target); 434 isURL = true; 435 } catch { 436 // Continue regardless of error. 437 } 438 if (!isURL) { 439 const exportTarget = pattern ? 440 RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : 441 target + subpath; 442 return packageResolve( 443 exportTarget, packageJSONUrl, conditions); 444 } 445 } 446 throw invalidPackageTarget(match, target, packageJSONUrl, internal, base); 447 } 448 449 if (RegExpPrototypeExec(invalidSegmentRegEx, StringPrototypeSlice(target, 2)) !== null) { 450 if (RegExpPrototypeExec(deprecatedInvalidSegmentRegEx, StringPrototypeSlice(target, 2)) === null) { 451 if (!isPathMap) { 452 const request = pattern ? 453 StringPrototypeReplace(match, '*', () => subpath) : 454 match + subpath; 455 const resolvedTarget = pattern ? 456 RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : 457 target; 458 emitInvalidSegmentDeprecation(resolvedTarget, request, match, packageJSONUrl, internal, base, true); 459 } 460 } else { 461 throw invalidPackageTarget(match, target, packageJSONUrl, internal, base); 462 } 463 } 464 465 const resolved = new URL(target, packageJSONUrl); 466 const resolvedPath = resolved.pathname; 467 const packagePath = new URL('.', packageJSONUrl).pathname; 468 469 if (!StringPrototypeStartsWith(resolvedPath, packagePath)) 470 throw invalidPackageTarget(match, target, packageJSONUrl, internal, base); 471 472 if (subpath === '') return resolved; 473 474 if (RegExpPrototypeExec(invalidSegmentRegEx, subpath) !== null) { 475 const request = pattern ? StringPrototypeReplace(match, '*', () => subpath) : match + subpath; 476 if (RegExpPrototypeExec(deprecatedInvalidSegmentRegEx, subpath) === null) { 477 if (!isPathMap) { 478 const resolvedTarget = pattern ? 479 RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : 480 target; 481 emitInvalidSegmentDeprecation(resolvedTarget, request, match, packageJSONUrl, internal, base, false); 482 } 483 } else { 484 throwInvalidSubpath(request, match, packageJSONUrl, internal, base); 485 } 486 } 487 488 if (pattern) { 489 return new URL( 490 RegExpPrototypeSymbolReplace(patternRegEx, resolved.href, () => subpath), 491 ); 492 } 493 494 return new URL(subpath, resolved); 495} 496 497/** 498 * @param {string} key 499 * @returns {boolean} 500 */ 501function isArrayIndex(key) { 502 const keyNum = +key; 503 if (`${keyNum}` !== key) return false; 504 return keyNum >= 0 && keyNum < 0xFFFF_FFFF; 505} 506 507/** 508 * 509 * @param {*} packageJSONUrl 510 * @param {string|[string]} target 511 * @param {*} subpath 512 * @param {*} packageSubpath 513 * @param {*} base 514 * @param {*} pattern 515 * @param {*} internal 516 * @param {*} isPathMap 517 * @param {*} conditions 518 * @returns {URL|null} 519 */ 520function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, 521 base, pattern, internal, isPathMap, conditions) { 522 if (typeof target === 'string') { 523 return resolvePackageTargetString( 524 target, subpath, packageSubpath, packageJSONUrl, base, pattern, internal, 525 isPathMap, conditions); 526 } else if (ArrayIsArray(target)) { 527 if (target.length === 0) { 528 return null; 529 } 530 531 let lastException; 532 for (let i = 0; i < target.length; i++) { 533 const targetItem = target[i]; 534 let resolveResult; 535 try { 536 resolveResult = resolvePackageTarget( 537 packageJSONUrl, targetItem, subpath, packageSubpath, base, pattern, 538 internal, isPathMap, conditions); 539 } catch (e) { 540 lastException = e; 541 if (e.code === 'ERR_INVALID_PACKAGE_TARGET') { 542 continue; 543 } 544 throw e; 545 } 546 if (resolveResult === undefined) { 547 continue; 548 } 549 if (resolveResult === null) { 550 lastException = null; 551 continue; 552 } 553 return resolveResult; 554 } 555 if (lastException === undefined || lastException === null) 556 return lastException; 557 throw lastException; 558 } else if (typeof target === 'object' && target !== null) { 559 const keys = ObjectGetOwnPropertyNames(target); 560 for (let i = 0; i < keys.length; i++) { 561 const key = keys[i]; 562 if (isArrayIndex(key)) { 563 throw new ERR_INVALID_PACKAGE_CONFIG( 564 fileURLToPath(packageJSONUrl), base, 565 '"exports" cannot contain numeric property keys.'); 566 } 567 } 568 for (let i = 0; i < keys.length; i++) { 569 const key = keys[i]; 570 if (key === 'default' || conditions.has(key)) { 571 const conditionalTarget = target[key]; 572 const resolveResult = resolvePackageTarget( 573 packageJSONUrl, conditionalTarget, subpath, packageSubpath, base, 574 pattern, internal, isPathMap, conditions); 575 if (resolveResult === undefined) 576 continue; 577 return resolveResult; 578 } 579 } 580 return undefined; 581 } else if (target === null) { 582 return null; 583 } 584 throw invalidPackageTarget(packageSubpath, target, packageJSONUrl, internal, 585 base); 586} 587 588/** 589 * 590 * @param {import('internal/modules/esm/package_config.js').Exports} exports 591 * @param {URL} packageJSONUrl 592 * @param {string | URL | undefined} base 593 * @returns {boolean} 594 */ 595function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { 596 if (typeof exports === 'string' || ArrayIsArray(exports)) return true; 597 if (typeof exports !== 'object' || exports === null) return false; 598 599 const keys = ObjectGetOwnPropertyNames(exports); 600 let isConditionalSugar = false; 601 let i = 0; 602 for (let j = 0; j < keys.length; j++) { 603 const key = keys[j]; 604 const curIsConditionalSugar = key === '' || key[0] !== '.'; 605 if (i++ === 0) { 606 isConditionalSugar = curIsConditionalSugar; 607 } else if (isConditionalSugar !== curIsConditionalSugar) { 608 throw new ERR_INVALID_PACKAGE_CONFIG( 609 fileURLToPath(packageJSONUrl), base, 610 '"exports" cannot contain some keys starting with \'.\' and some not.' + 611 ' The exports object must either be an object of package subpath keys' + 612 ' or an object of main entry condition name keys only.'); 613 } 614 } 615 return isConditionalSugar; 616} 617 618/** 619 * @param {URL} packageJSONUrl 620 * @param {string} packageSubpath 621 * @param {PackageConfig} packageConfig 622 * @param {string | URL | undefined} base 623 * @param {Set<string>} conditions 624 * @returns {URL} 625 */ 626function packageExportsResolve( 627 packageJSONUrl, packageSubpath, packageConfig, base, conditions) { 628 let exports = packageConfig.exports; 629 if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) 630 exports = { '.': exports }; 631 632 if (ObjectPrototypeHasOwnProperty(exports, packageSubpath) && 633 !StringPrototypeIncludes(packageSubpath, '*') && 634 !StringPrototypeEndsWith(packageSubpath, '/')) { 635 const target = exports[packageSubpath]; 636 const resolveResult = resolvePackageTarget( 637 packageJSONUrl, target, '', packageSubpath, base, false, false, false, 638 conditions, 639 ); 640 641 if (resolveResult == null) { 642 throw exportsNotFound(packageSubpath, packageJSONUrl, base); 643 } 644 645 return resolveResult; 646 } 647 648 let bestMatch = ''; 649 let bestMatchSubpath; 650 const keys = ObjectGetOwnPropertyNames(exports); 651 for (let i = 0; i < keys.length; i++) { 652 const key = keys[i]; 653 const patternIndex = StringPrototypeIndexOf(key, '*'); 654 if (patternIndex !== -1 && 655 StringPrototypeStartsWith(packageSubpath, 656 StringPrototypeSlice(key, 0, patternIndex))) { 657 // When this reaches EOL, this can throw at the top of the whole function: 658 // 659 // if (StringPrototypeEndsWith(packageSubpath, '/')) 660 // throwInvalidSubpath(packageSubpath) 661 // 662 // To match "imports" and the spec. 663 if (StringPrototypeEndsWith(packageSubpath, '/')) 664 emitTrailingSlashPatternDeprecation(packageSubpath, packageJSONUrl, 665 base); 666 const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); 667 if (packageSubpath.length >= key.length && 668 StringPrototypeEndsWith(packageSubpath, patternTrailer) && 669 patternKeyCompare(bestMatch, key) === 1 && 670 StringPrototypeLastIndexOf(key, '*') === patternIndex) { 671 bestMatch = key; 672 bestMatchSubpath = StringPrototypeSlice( 673 packageSubpath, patternIndex, 674 packageSubpath.length - patternTrailer.length); 675 } 676 } 677 } 678 679 if (bestMatch) { 680 const target = exports[bestMatch]; 681 const resolveResult = resolvePackageTarget( 682 packageJSONUrl, 683 target, 684 bestMatchSubpath, 685 bestMatch, 686 base, 687 true, 688 false, 689 StringPrototypeEndsWith(packageSubpath, '/'), 690 conditions); 691 692 if (resolveResult == null) { 693 throw exportsNotFound(packageSubpath, packageJSONUrl, base); 694 } 695 return resolveResult; 696 } 697 698 throw exportsNotFound(packageSubpath, packageJSONUrl, base); 699} 700 701function patternKeyCompare(a, b) { 702 const aPatternIndex = StringPrototypeIndexOf(a, '*'); 703 const bPatternIndex = StringPrototypeIndexOf(b, '*'); 704 const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; 705 const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; 706 if (baseLenA > baseLenB) return -1; 707 if (baseLenB > baseLenA) return 1; 708 if (aPatternIndex === -1) return 1; 709 if (bPatternIndex === -1) return -1; 710 if (a.length > b.length) return -1; 711 if (b.length > a.length) return 1; 712 return 0; 713} 714 715/** 716 * @param {string} name 717 * @param {string | URL | undefined} base 718 * @param {Set<string>} conditions 719 * @returns {URL} 720 */ 721function packageImportsResolve(name, base, conditions) { 722 if (name === '#' || StringPrototypeStartsWith(name, '#/') || 723 StringPrototypeEndsWith(name, '/')) { 724 const reason = 'is not a valid internal imports specifier name'; 725 throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); 726 } 727 let packageJSONUrl; 728 const packageConfig = getPackageScopeConfig(base); 729 if (packageConfig.exists) { 730 packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); 731 const imports = packageConfig.imports; 732 if (imports) { 733 if (ObjectPrototypeHasOwnProperty(imports, name) && 734 !StringPrototypeIncludes(name, '*')) { 735 const resolveResult = resolvePackageTarget( 736 packageJSONUrl, imports[name], '', name, base, false, true, false, 737 conditions, 738 ); 739 if (resolveResult != null) { 740 return resolveResult; 741 } 742 } else { 743 let bestMatch = ''; 744 let bestMatchSubpath; 745 const keys = ObjectGetOwnPropertyNames(imports); 746 for (let i = 0; i < keys.length; i++) { 747 const key = keys[i]; 748 const patternIndex = StringPrototypeIndexOf(key, '*'); 749 if (patternIndex !== -1 && 750 StringPrototypeStartsWith(name, 751 StringPrototypeSlice(key, 0, 752 patternIndex))) { 753 const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); 754 if (name.length >= key.length && 755 StringPrototypeEndsWith(name, patternTrailer) && 756 patternKeyCompare(bestMatch, key) === 1 && 757 StringPrototypeLastIndexOf(key, '*') === patternIndex) { 758 bestMatch = key; 759 bestMatchSubpath = StringPrototypeSlice( 760 name, patternIndex, name.length - patternTrailer.length); 761 } 762 } 763 } 764 765 if (bestMatch) { 766 const target = imports[bestMatch]; 767 const resolveResult = resolvePackageTarget(packageJSONUrl, target, 768 bestMatchSubpath, 769 bestMatch, base, true, 770 true, false, conditions); 771 if (resolveResult != null) { 772 return resolveResult; 773 } 774 } 775 } 776 } 777 } 778 throw importNotDefined(name, packageJSONUrl, base); 779} 780 781/** 782 * @param {URL} url 783 * @returns {import('internal/modules/esm/package_config.js').PackageType} 784 */ 785function getPackageType(url) { 786 const packageConfig = getPackageScopeConfig(url); 787 return packageConfig.type; 788} 789 790/** 791 * @param {string} specifier 792 * @param {string | URL | undefined} base 793 * @returns {{ packageName: string, packageSubpath: string, isScoped: boolean }} 794 */ 795function parsePackageName(specifier, base) { 796 let separatorIndex = StringPrototypeIndexOf(specifier, '/'); 797 let validPackageName = true; 798 let isScoped = false; 799 if (specifier[0] === '@') { 800 isScoped = true; 801 if (separatorIndex === -1 || specifier.length === 0) { 802 validPackageName = false; 803 } else { 804 separatorIndex = StringPrototypeIndexOf( 805 specifier, '/', separatorIndex + 1); 806 } 807 } 808 809 const packageName = separatorIndex === -1 ? 810 specifier : StringPrototypeSlice(specifier, 0, separatorIndex); 811 812 // Package name cannot have leading . and cannot have percent-encoding or 813 // \\ separators. 814 if (RegExpPrototypeExec(invalidPackageNameRegEx, packageName) !== null) 815 validPackageName = false; 816 817 if (!validPackageName) { 818 throw new ERR_INVALID_MODULE_SPECIFIER( 819 specifier, 'is not a valid package name', fileURLToPath(base)); 820 } 821 822 const packageSubpath = '.' + (separatorIndex === -1 ? '' : 823 StringPrototypeSlice(specifier, separatorIndex)); 824 825 return { packageName, packageSubpath, isScoped }; 826} 827 828/** 829 * @param {string} specifier 830 * @param {string | URL | undefined} base 831 * @param {Set<string>} conditions 832 * @returns {resolved: URL, format? : string} 833 */ 834function packageResolve(specifier, base, conditions) { 835 if (BuiltinModule.canBeRequiredByUsers(specifier) && 836 BuiltinModule.canBeRequiredWithoutScheme(specifier)) { 837 return new URL('node:' + specifier); 838 } 839 840 const { packageName, packageSubpath, isScoped } = 841 parsePackageName(specifier, base); 842 843 // ResolveSelf 844 const packageConfig = getPackageScopeConfig(base); 845 if (packageConfig.exists) { 846 const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); 847 if (packageConfig.name === packageName && 848 packageConfig.exports !== undefined && packageConfig.exports !== null) { 849 return packageExportsResolve( 850 packageJSONUrl, packageSubpath, packageConfig, base, conditions); 851 } 852 } 853 854 let packageJSONUrl = 855 new URL('./node_modules/' + packageName + '/package.json', base); 856 let packageJSONPath = fileURLToPath(packageJSONUrl); 857 let lastPath; 858 do { 859 const stat = tryStatSync(StringPrototypeSlice(packageJSONPath, 0, 860 packageJSONPath.length - 13)); 861 if (!stat.isDirectory()) { 862 lastPath = packageJSONPath; 863 packageJSONUrl = new URL((isScoped ? 864 '../../../../node_modules/' : '../../../node_modules/') + 865 packageName + '/package.json', packageJSONUrl); 866 packageJSONPath = fileURLToPath(packageJSONUrl); 867 continue; 868 } 869 870 // Package match. 871 const packageConfig = getPackageConfig(packageJSONPath, specifier, base); 872 if (packageConfig.exports !== undefined && packageConfig.exports !== null) { 873 return packageExportsResolve( 874 packageJSONUrl, packageSubpath, packageConfig, base, conditions); 875 } 876 if (packageSubpath === '.') { 877 return legacyMainResolve( 878 packageJSONUrl, 879 packageConfig, 880 base, 881 ); 882 } 883 884 return new URL(packageSubpath, packageJSONUrl); 885 // Cross-platform root check. 886 } while (packageJSONPath.length !== lastPath.length); 887 888 // eslint can't handle the above code. 889 // eslint-disable-next-line no-unreachable 890 throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base)); 891} 892 893/** 894 * @param {string} specifier 895 * @returns {boolean} 896 */ 897function isBareSpecifier(specifier) { 898 return specifier[0] && specifier[0] !== '/' && specifier[0] !== '.'; 899} 900 901function isRelativeSpecifier(specifier) { 902 if (specifier[0] === '.') { 903 if (specifier.length === 1 || specifier[1] === '/') return true; 904 if (specifier[1] === '.') { 905 if (specifier.length === 2 || specifier[2] === '/') return true; 906 } 907 } 908 return false; 909} 910 911function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) { 912 if (specifier === '') return false; 913 if (specifier[0] === '/') return true; 914 return isRelativeSpecifier(specifier); 915} 916 917/** 918 * @param {string} specifier 919 * @param {string | URL | undefined} base 920 * @param {Set<string>} conditions 921 * @param {boolean} preserveSymlinks 922 * @returns {url: URL, format?: string} 923 */ 924function moduleResolve(specifier, base, conditions, preserveSymlinks) { 925 const isRemote = base.protocol === 'http:' || 926 base.protocol === 'https:'; 927 // Order swapped from spec for minor perf gain. 928 // Ok since relative URLs cannot parse as URLs. 929 let resolved; 930 if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { 931 resolved = new URL(specifier, base); 932 } else if (!isRemote && specifier[0] === '#') { 933 resolved = packageImportsResolve(specifier, base, conditions); 934 } else { 935 try { 936 resolved = new URL(specifier); 937 } catch { 938 if (!isRemote) { 939 resolved = packageResolve(specifier, base, conditions); 940 } 941 } 942 } 943 if (resolved.protocol !== 'file:') { 944 return resolved; 945 } 946 return finalizeResolution(resolved, base, preserveSymlinks); 947} 948 949/** 950 * Try to resolve an import as a CommonJS module 951 * @param {string} specifier 952 * @param {string} parentURL 953 * @returns {boolean|string} 954 */ 955function resolveAsCommonJS(specifier, parentURL) { 956 try { 957 const parent = fileURLToPath(parentURL); 958 const tmpModule = new CJSModule(parent, null); 959 tmpModule.paths = CJSModule._nodeModulePaths(parent); 960 961 let found = CJSModule._resolveFilename(specifier, tmpModule, false); 962 963 // If it is a relative specifier return the relative path 964 // to the parent 965 if (isRelativeSpecifier(specifier)) { 966 found = relative(parent, found); 967 // Add '.separator if the path does not start with '..separator' 968 // This should be a safe assumption because when loading 969 // esm modules there should be always a file specified so 970 // there should not be a specifier like '..' or '.' 971 if (!StringPrototypeStartsWith(found, `..${sep}`)) { 972 found = `.${sep}${found}`; 973 } 974 } else if (isBareSpecifier(specifier)) { 975 // If it is a bare specifier return the relative path within the 976 // module 977 const pkg = StringPrototypeSplit(specifier, '/')[0]; 978 const index = StringPrototypeIndexOf(found, pkg); 979 if (index !== -1) { 980 found = StringPrototypeSlice(found, index); 981 } 982 } 983 // Normalize the path separator to give a valid suggestion 984 // on Windows 985 if (process.platform === 'win32') { 986 found = RegExpPrototypeSymbolReplace(new RegExp(`\\${sep}`, 'g'), 987 found, '/'); 988 } 989 return found; 990 } catch { 991 return false; 992 } 993} 994 995// TODO(@JakobJingleheimer): de-dupe `specifier` & `parsed` 996function checkIfDisallowedImport(specifier, parsed, parsedParentURL) { 997 if (parsedParentURL) { 998 // Avoid accessing the `protocol` property due to the lazy getters. 999 const parentProtocol = parsedParentURL.protocol; 1000 if ( 1001 parentProtocol === 'http:' || 1002 parentProtocol === 'https:' 1003 ) { 1004 if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { 1005 // Avoid accessing the `protocol` property due to the lazy getters. 1006 const parsedProtocol = parsed?.protocol; 1007 // data: and blob: disallowed due to allowing file: access via 1008 // indirection 1009 if (parsedProtocol && 1010 parsedProtocol !== 'https:' && 1011 parsedProtocol !== 'http:' 1012 ) { 1013 throw new ERR_NETWORK_IMPORT_DISALLOWED( 1014 specifier, 1015 parsedParentURL, 1016 'remote imports cannot import from a local location.', 1017 ); 1018 } 1019 1020 return { url: parsed.href }; 1021 } 1022 if (BuiltinModule.canBeRequiredByUsers(specifier) && 1023 BuiltinModule.canBeRequiredWithoutScheme(specifier)) { 1024 throw new ERR_NETWORK_IMPORT_DISALLOWED( 1025 specifier, 1026 parsedParentURL, 1027 'remote imports cannot import from a local location.', 1028 ); 1029 } 1030 1031 throw new ERR_NETWORK_IMPORT_DISALLOWED( 1032 specifier, 1033 parsedParentURL, 1034 'only relative and absolute specifiers are supported.', 1035 ); 1036 } 1037 } 1038} 1039 1040 1041async function defaultResolve(specifier, context = {}) { 1042 let { parentURL, conditions } = context; 1043 if (parentURL && policy?.manifest) { 1044 const redirects = policy.manifest.getDependencyMapper(parentURL); 1045 if (redirects) { 1046 const { resolve, reaction } = redirects; 1047 const destination = resolve(specifier, new SafeSet(conditions)); 1048 let missing = true; 1049 if (destination === true) { 1050 missing = false; 1051 } else if (destination) { 1052 const href = destination.href; 1053 return { url: href }; 1054 } 1055 if (missing) { 1056 // Prevent network requests from firing if resolution would be banned. 1057 // Network requests can extract data by doing things like putting 1058 // secrets in query params 1059 reaction(new ERR_MANIFEST_DEPENDENCY_MISSING( 1060 parentURL, 1061 specifier, 1062 ArrayPrototypeJoin([...conditions], ', ')), 1063 ); 1064 } 1065 } 1066 } 1067 1068 let parsedParentURL; 1069 if (parentURL) { 1070 try { 1071 parsedParentURL = new URL(parentURL); 1072 } catch { 1073 // Ignore exception 1074 } 1075 } 1076 1077 let parsed; 1078 try { 1079 if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { 1080 parsed = new URL(specifier, parsedParentURL); 1081 } else { 1082 parsed = new URL(specifier); 1083 } 1084 1085 // Avoid accessing the `protocol` property due to the lazy getters. 1086 const protocol = parsed.protocol; 1087 if (protocol === 'data:' || 1088 (experimentalNetworkImports && 1089 ( 1090 protocol === 'https:' || 1091 protocol === 'http:' 1092 ) 1093 ) 1094 ) { 1095 return { __proto__: null, url: parsed.href }; 1096 } 1097 } catch { 1098 // Ignore exception 1099 } 1100 1101 // There are multiple deep branches that can either throw or return; instead 1102 // of duplicating that deeply nested logic for the possible returns, DRY and 1103 // check for a return. This seems the least gnarly. 1104 const maybeReturn = checkIfDisallowedImport( 1105 specifier, 1106 parsed, 1107 parsedParentURL, 1108 ); 1109 1110 if (maybeReturn) return maybeReturn; 1111 1112 // This must come after checkIfDisallowedImport 1113 if (parsed && parsed.protocol === 'node:') return { url: specifier }; 1114 1115 1116 const isMain = parentURL === undefined; 1117 if (isMain) { 1118 parentURL = pathToFileURL(`${process.cwd()}/`).href; 1119 1120 // This is the initial entry point to the program, and --input-type has 1121 // been passed as an option; but --input-type can only be used with 1122 // --eval, --print or STDIN string input. It is not allowed with file 1123 // input, to avoid user confusion over how expansive the effect of the 1124 // flag should be (i.e. entry point only, package scope surrounding the 1125 // entry point, etc.). 1126 if (typeFlag) throw new ERR_INPUT_TYPE_NOT_ALLOWED(); 1127 } 1128 1129 conditions = getConditionsSet(conditions); 1130 let url; 1131 try { 1132 url = moduleResolve( 1133 specifier, 1134 parentURL, 1135 conditions, 1136 isMain ? preserveSymlinksMain : preserveSymlinks, 1137 ); 1138 } catch (error) { 1139 // Try to give the user a hint of what would have been the 1140 // resolved CommonJS module 1141 if (error.code === 'ERR_MODULE_NOT_FOUND' || 1142 error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') { 1143 if (StringPrototypeStartsWith(specifier, 'file://')) { 1144 specifier = fileURLToPath(specifier); 1145 } 1146 const found = resolveAsCommonJS(specifier, parentURL); 1147 if (found) { 1148 // Modify the stack and message string to include the hint 1149 const lines = StringPrototypeSplit(error.stack, '\n'); 1150 const hint = `Did you mean to import ${found}?`; 1151 error.stack = 1152 ArrayPrototypeShift(lines) + '\n' + 1153 hint + '\n' + 1154 ArrayPrototypeJoin(lines, '\n'); 1155 error.message += `\n${hint}`; 1156 } 1157 } 1158 throw error; 1159 } 1160 1161 return { 1162 // Do NOT cast `url` to a string: that will work even when there are real 1163 // problems, silencing them 1164 url: url.href, 1165 format: defaultGetFormatWithoutErrors(url, context), 1166 }; 1167} 1168 1169module.exports = { 1170 DEFAULT_CONDITIONS, 1171 defaultResolve, 1172 encodedSepRegEx, 1173 getPackageScopeConfig, 1174 getPackageType, 1175 packageExportsResolve, 1176 packageImportsResolve, 1177}; 1178 1179// cycle 1180const { 1181 defaultGetFormatWithoutErrors, 1182} = require('internal/modules/esm/get_format'); 1183 1184if (policy) { 1185 const $defaultResolve = defaultResolve; 1186 module.exports.defaultResolve = async function defaultResolve( 1187 specifier, 1188 context, 1189 ) { 1190 const ret = await $defaultResolve(specifier, context); 1191 // This is a preflight check to avoid data exfiltration by query params etc. 1192 policy.manifest.mightAllow(ret.url, () => 1193 new ERR_MANIFEST_DEPENDENCY_MISSING( 1194 context.parentURL, 1195 specifier, 1196 context.conditions, 1197 ), 1198 ); 1199 return ret; 1200 }; 1201} 1202