All files / src/node-resolve/package resolvePackageImportsExports.ts

9.67% Statements 3/31
0% Branches 0/32
0% Functions 0/3
10.71% Lines 3/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110  1x   1x                                                                                                                                                                                                                 1x  
/* eslint-disable no-await-in-loop */
import resolvePackageTarget from "./resolvePackageTarget";
 
import { InvalidModuleSpecifierError } from "./utils";
 
/**
 * Implementation of Node's `PATTERN_KEY_COMPARE` function
 */
function nodePatternKeyCompare(keyA: string, keyB: string) {
  // Let baseLengthA be the index of "*" in keyA plus one, if keyA contains "*", or the length of keyA otherwise.
  const baseLengthA = keyA.includes("*") ? keyA.indexOf("*") + 1 : keyA.length;
  // Let baseLengthB be the index of "*" in keyB plus one, if keyB contains "*", or the length of keyB otherwise.
  const baseLengthB = keyB.includes("*") ? keyB.indexOf("*") + 1 : keyB.length;
 
  // if baseLengthA is greater, return -1, if lower 1
  const rval = baseLengthB - baseLengthA;
  if (rval !== 0) return rval;
 
  // If keyA does not contain "*", return 1.
  if (!keyA.includes("*")) return 1;
  // If keyB does not contain "*", return -1.
  if (!keyB.includes("*")) return -1;
 
  // If the length of keyA is greater than the length of keyB, return -1.
  // If the length of keyB is greater than the length of keyA, return 1.
  // Else Return 0.
  return keyB.length - keyA.length;
}
 
interface ParamObject {
  matchKey: string;
  matchObj: Record<string, string>;
  isImports?: boolean;
  validateImportsExports: boolean;
}
async function resolvePackageImportsExports(
  context: any,
  { matchKey, matchObj, isImports, validateImportsExports = true }: ParamObject,
) {
  // If matchKey is a key of matchObj and does not contain "*", then
  if (!matchKey.includes("*") && matchKey in matchObj) {
    // Let target be the value of matchObj[matchKey].
    const target = matchObj[matchKey];
    // Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions).
    const resolved = await resolvePackageTarget(context, {
      target,
      patternMatch: "",
      isImports,
      validateImportsExports,
    });
    return resolved;
  }
 
  // Let expansionKeys be the list of keys of matchObj containing only a single "*"
  const expansionKeys = Object.keys(matchObj)
    // Assert: ends with "/" or contains only a single "*".
    .filter((k) => k.endsWith("/") || k.includes("*"))
    // sorted by the sorting function PATTERN_KEY_COMPARE which orders in descending order of specificity.
    .sort(nodePatternKeyCompare);
 
  // For each key expansionKey in expansionKeys, do
  for (const expansionKey of expansionKeys) {
    const indexOfAsterisk = expansionKey.indexOf("*");
    // Let patternBase be the substring of expansionKey up to but excluding the first "*" character.
    const patternBase =
      indexOfAsterisk === -1
        ? expansionKey
        : expansionKey.substring(0, indexOfAsterisk);
 
    // If matchKey starts with but is not equal to patternBase, then
    if (matchKey.startsWith(patternBase) && matchKey !== patternBase) {
      // Let patternTrailer be the substring of expansionKey from the index after the first "*" character.
      const patternTrailer =
        indexOfAsterisk !== -1
          ? expansionKey.substring(indexOfAsterisk + 1)
          : "";
 
      // If patternTrailer has zero length,
      if (
        patternTrailer.length === 0 ||
        // or if matchKey ends with patternTrailer and the length of matchKey is greater than or equal to the length of expansionKey, then
        (matchKey.endsWith(patternTrailer) &&
          matchKey.length >= expansionKey.length)
      ) {
        // Let target be the value of matchObj[expansionKey].
        const target = matchObj[expansionKey];
        // Let patternMatch be the substring of matchKey starting at the index of the length of patternBase up to the length
        // of matchKey minus the length of patternTrailer.
        const patternMatch = matchKey.substring(
          patternBase.length,
          matchKey.length - patternTrailer.length,
        );
        // Return the result of PACKAGE_TARGET_RESOLVE
        const resolved = await resolvePackageTarget(context, {
          target,
          patternMatch,
          isImports,
          validateImportsExports,
        });
        return resolved;
      }
    }
  }
  if (validateImportsExports) {
    throw new InvalidModuleSpecifierError(context, isImports);
  }
}
 
export default resolvePackageImportsExports;