|
4 | 4 | ArrayIsArray, |
5 | 5 | JSONParse, |
6 | 6 | JSONStringify, |
| 7 | + ObjectFreeze, |
7 | 8 | ObjectGetOwnPropertyNames, |
8 | 9 | ObjectPrototypeHasOwnProperty, |
9 | 10 | SafeMap, |
| 11 | + SafeSet, |
10 | 12 | StringPrototypeEndsWith, |
11 | 13 | StringPrototypeIncludes, |
12 | 14 | StringPrototypeIndexOf, |
@@ -35,6 +37,7 @@ const typeFlag = getOptionValue('--input-type'); |
35 | 37 | const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); |
36 | 38 | const { |
37 | 39 | ERR_INPUT_TYPE_NOT_ALLOWED, |
| 40 | + ERR_INVALID_ARG_VALUE, |
38 | 41 | ERR_INVALID_MODULE_SPECIFIER, |
39 | 42 | ERR_INVALID_PACKAGE_CONFIG, |
40 | 43 | ERR_INVALID_PACKAGE_TARGET, |
@@ -43,6 +46,20 @@ const { |
43 | 46 | ERR_UNSUPPORTED_ESM_URL_SCHEME, |
44 | 47 | } = require('internal/errors').codes; |
45 | 48 |
|
| 49 | +const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import']); |
| 50 | +const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); |
| 51 | + |
| 52 | +function getConditionsSet(conditions) { |
| 53 | + if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) { |
| 54 | + if (!ArrayIsArray(conditions)) { |
| 55 | + throw new ERR_INVALID_ARG_VALUE('conditions', conditions, |
| 56 | + 'expected an array'); |
| 57 | + } |
| 58 | + return new SafeSet(conditions); |
| 59 | + } |
| 60 | + return DEFAULT_CONDITIONS_SET; |
| 61 | +} |
| 62 | + |
46 | 63 | const realpathCache = new SafeMap(); |
47 | 64 | const packageJSONCache = new SafeMap(); /* string -> PackageConfig */ |
48 | 65 |
|
@@ -310,14 +327,18 @@ function resolveExportsTargetString( |
310 | 327 | return subpathResolved; |
311 | 328 | } |
312 | 329 |
|
313 | | -function isArrayIndex(key /* string */) { /* -> boolean */ |
| 330 | +/** |
| 331 | + * @param {string} key |
| 332 | + * @returns {boolean} |
| 333 | + */ |
| 334 | +function isArrayIndex(key) { |
314 | 335 | const keyNum = +key; |
315 | 336 | if (`${keyNum}` !== key) return false; |
316 | 337 | return keyNum >= 0 && keyNum < 0xFFFF_FFFF; |
317 | 338 | } |
318 | 339 |
|
319 | 340 | function resolveExportsTarget( |
320 | | - packageJSONUrl, target, subpath, packageSubpath, base) { |
| 341 | + packageJSONUrl, target, subpath, packageSubpath, base, conditions) { |
321 | 342 | if (typeof target === 'string') { |
322 | 343 | const resolved = resolveExportsTargetString( |
323 | 344 | target, subpath, packageSubpath, packageJSONUrl, base); |
@@ -332,7 +353,8 @@ function resolveExportsTarget( |
332 | 353 | let resolved; |
333 | 354 | try { |
334 | 355 | resolved = resolveExportsTarget( |
335 | | - packageJSONUrl, targetItem, subpath, packageSubpath, base); |
| 356 | + packageJSONUrl, targetItem, subpath, packageSubpath, base, |
| 357 | + conditions); |
336 | 358 | } catch (e) { |
337 | 359 | lastException = e; |
338 | 360 | if (e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' || |
@@ -357,11 +379,12 @@ function resolveExportsTarget( |
357 | 379 | } |
358 | 380 | for (let i = 0; i < keys.length; i++) { |
359 | 381 | const key = keys[i]; |
360 | | - if (key === 'node' || key === 'import' || key === 'default') { |
| 382 | + if (key === 'default' || conditions.has(key)) { |
361 | 383 | const conditionalTarget = target[key]; |
362 | 384 | try { |
363 | 385 | return resolveExportsTarget( |
364 | | - packageJSONUrl, conditionalTarget, subpath, packageSubpath, base); |
| 386 | + packageJSONUrl, conditionalTarget, subpath, packageSubpath, base, |
| 387 | + conditions); |
365 | 388 | } catch (e) { |
366 | 389 | if (e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') continue; |
367 | 390 | throw e; |
@@ -397,16 +420,18 @@ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { |
397 | 420 | } |
398 | 421 |
|
399 | 422 |
|
400 | | -function packageMainResolve(packageJSONUrl, packageConfig, base) { |
| 423 | +function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) { |
401 | 424 | if (packageConfig.exists) { |
402 | 425 | const exports = packageConfig.exports; |
403 | 426 | if (exports !== undefined) { |
404 | 427 | if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) { |
405 | | - return resolveExportsTarget(packageJSONUrl, exports, '', '', base); |
| 428 | + return resolveExportsTarget(packageJSONUrl, exports, '', '', base, |
| 429 | + conditions); |
406 | 430 | } else if (typeof exports === 'object' && exports !== null) { |
407 | 431 | const target = exports['.']; |
408 | 432 | if (target !== undefined) |
409 | | - return resolveExportsTarget(packageJSONUrl, target, '', '', base); |
| 433 | + return resolveExportsTarget(packageJSONUrl, target, '', '', base, |
| 434 | + conditions); |
410 | 435 | } |
411 | 436 |
|
412 | 437 | throw new ERR_PACKAGE_PATH_NOT_EXPORTED(packageJSONUrl, '.'); |
@@ -434,9 +459,16 @@ function packageMainResolve(packageJSONUrl, packageConfig, base) { |
434 | 459 | fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); |
435 | 460 | } |
436 | 461 |
|
437 | | - |
| 462 | +/** |
| 463 | + * @param {URL} packageJSONUrl |
| 464 | + * @param {string} packageSubpath |
| 465 | + * @param {object} packageConfig |
| 466 | + * @param {string} base |
| 467 | + * @param {Set<string>} conditions |
| 468 | + * @returns {URL} |
| 469 | + */ |
438 | 470 | function packageExportsResolve( |
439 | | - packageJSONUrl, packageSubpath, packageConfig, base) /* -> URL */ { |
| 471 | + packageJSONUrl, packageSubpath, packageConfig, base, conditions) { |
440 | 472 | const exports = packageConfig.exports; |
441 | 473 | if (exports === undefined || |
442 | 474 | isConditionalExportsMainSugar(exports, packageJSONUrl, base)) { |
@@ -447,7 +479,7 @@ function packageExportsResolve( |
447 | 479 | if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) { |
448 | 480 | const target = exports[packageSubpath]; |
449 | 481 | const resolved = resolveExportsTarget( |
450 | | - packageJSONUrl, target, '', packageSubpath, base); |
| 482 | + packageJSONUrl, target, '', packageSubpath, base, conditions); |
451 | 483 | return finalizeResolution(resolved, base); |
452 | 484 | } |
453 | 485 |
|
@@ -466,7 +498,7 @@ function packageExportsResolve( |
466 | 498 | const target = exports[bestMatch]; |
467 | 499 | const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length); |
468 | 500 | const resolved = resolveExportsTarget( |
469 | | - packageJSONUrl, target, subpath, packageSubpath, base); |
| 501 | + packageJSONUrl, target, subpath, packageSubpath, base, conditions); |
470 | 502 | return finalizeResolution(resolved, base); |
471 | 503 | } |
472 | 504 |
|
@@ -478,7 +510,13 @@ function getPackageType(url) { |
478 | 510 | return packageConfig.type; |
479 | 511 | } |
480 | 512 |
|
481 | | -function packageResolve(specifier /* string */, base /* URL */) { /* -> URL */ |
| 513 | +/** |
| 514 | + * @param {string} specifier |
| 515 | + * @param {URL} base |
| 516 | + * @param {Set<string>} conditions |
| 517 | + * @returns {URL} |
| 518 | + */ |
| 519 | +function packageResolve(specifier, base, conditions) { |
482 | 520 | let separatorIndex = StringPrototypeIndexOf(specifier, '/'); |
483 | 521 | let validPackageName = true; |
484 | 522 | let isScoped = false; |
@@ -530,10 +568,11 @@ function packageResolve(specifier /* string */, base /* URL */) { /* -> URL */ |
530 | 568 | if (packageSubpath === './') { |
531 | 569 | return new URL('./', packageJSONUrl); |
532 | 570 | } else if (packageSubpath === '') { |
533 | | - return packageMainResolve(packageJSONUrl, packageConfig, base); |
| 571 | + return packageMainResolve(packageJSONUrl, packageConfig, base, |
| 572 | + conditions); |
534 | 573 | } else { |
535 | 574 | return packageExportsResolve( |
536 | | - packageJSONUrl, packageSubpath, packageConfig, base); |
| 575 | + packageJSONUrl, packageSubpath, packageConfig, base, conditions); |
537 | 576 | } |
538 | 577 | } |
539 | 578 | } |
@@ -559,10 +598,11 @@ function packageResolve(specifier /* string */, base /* URL */) { /* -> URL */ |
559 | 598 | if (packageSubpath === './') { |
560 | 599 | return new URL('./', packageJSONUrl); |
561 | 600 | } else if (packageSubpath === '') { |
562 | | - return packageMainResolve(packageJSONUrl, packageConfig, base); |
| 601 | + return packageMainResolve(packageJSONUrl, packageConfig, base, |
| 602 | + conditions); |
563 | 603 | } else if (packageConfig.exports !== undefined) { |
564 | 604 | return packageExportsResolve( |
565 | | - packageJSONUrl, packageSubpath, packageConfig, base); |
| 605 | + packageJSONUrl, packageSubpath, packageConfig, base, conditions); |
566 | 606 | } else { |
567 | 607 | return finalizeResolution( |
568 | 608 | new URL(packageSubpath, packageJSONUrl), base); |
@@ -587,7 +627,13 @@ function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) { |
587 | 627 | return false; |
588 | 628 | } |
589 | 629 |
|
590 | | -function moduleResolve(specifier /* string */, base /* URL */) { /* -> URL */ |
| 630 | +/** |
| 631 | + * @param {string} specifier |
| 632 | + * @param {URL} base |
| 633 | + * @param {Set<string>} conditions |
| 634 | + * @returns {URL} |
| 635 | + */ |
| 636 | +function moduleResolve(specifier, base, conditions) { |
591 | 637 | // Order swapped from spec for minor perf gain. |
592 | 638 | // Ok since relative URLs cannot parse as URLs. |
593 | 639 | let resolved; |
@@ -597,13 +643,14 @@ function moduleResolve(specifier /* string */, base /* URL */) { /* -> URL */ |
597 | 643 | try { |
598 | 644 | resolved = new URL(specifier); |
599 | 645 | } catch { |
600 | | - return packageResolve(specifier, base); |
| 646 | + return packageResolve(specifier, base, conditions); |
601 | 647 | } |
602 | 648 | } |
603 | 649 | return finalizeResolution(resolved, base); |
604 | 650 | } |
605 | 651 |
|
606 | | -function defaultResolve(specifier, { parentURL } = {}, defaultResolveUnused) { |
| 652 | +function defaultResolve(specifier, context = {}, defaultResolveUnused) { |
| 653 | + let { parentURL, conditions } = context; |
607 | 654 | let parsed; |
608 | 655 | try { |
609 | 656 | parsed = new URL(specifier); |
@@ -641,7 +688,8 @@ function defaultResolve(specifier, { parentURL } = {}, defaultResolveUnused) { |
641 | 688 | throw new ERR_INPUT_TYPE_NOT_ALLOWED(); |
642 | 689 | } |
643 | 690 |
|
644 | | - let url = moduleResolve(specifier, new URL(parentURL)); |
| 691 | + conditions = getConditionsSet(conditions); |
| 692 | + let url = moduleResolve(specifier, parentURL, conditions); |
645 | 693 |
|
646 | 694 | if (isMain ? !preserveSymlinksMain : !preserveSymlinks) { |
647 | 695 | const urlPath = fileURLToPath(url); |
@@ -658,6 +706,7 @@ function defaultResolve(specifier, { parentURL } = {}, defaultResolveUnused) { |
658 | 706 | } |
659 | 707 |
|
660 | 708 | module.exports = { |
| 709 | + DEFAULT_CONDITIONS, |
661 | 710 | defaultResolve, |
662 | 711 | getPackageType |
663 | 712 | }; |