All files / lib/internal/modules/esm package_config.js

100% Statements 144/144
95.34% Branches 41/43
100% Functions 2/2
100% Lines 144/144

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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 146x 3470x 3470x 3470x 2436x 2436x 1034x 3164x 907x 907x 907x 907x 907x 907x 907x 907x 907x 907x 907x 907x 127x 127x 127x 127x 2251x 1x 1x 1x 1x 1x 1x 126x 126x 3470x 3470x 124x 124x 2205x 44x 44x 2250x 57x 57x 126x 3470x 35x 35x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 3470x 146x 146x 146x 146x 146x 146x 876x 876x 876x 3159x 3159x 8x 8x 3151x 3159x 579x 579x 2572x 2572x 2572x 2572x 2572x 2572x 3114x 289x 289x 3159x 297x 297x 297x 297x 297x 297x 297x 297x 297x 297x 297x 297x 876x 146x 146x 146x 146x 146x 146x  
'use strict';
 
const {
  JSONParse,
  ObjectPrototypeHasOwnProperty,
  SafeMap,
  StringPrototypeEndsWith,
} = primordials;
const { URL, fileURLToPath } = require('internal/url');
const {
  ERR_INVALID_PACKAGE_CONFIG,
} = require('internal/errors').codes;
 
const packageJsonReader = require('internal/modules/package_json_reader');
const { filterOwnProperties } = require('internal/util');
 
 
/**
 * @typedef {string | string[] | Record<string, unknown>} Exports
 * @typedef {'module' | 'commonjs'} PackageType
 * @typedef {{
 *   pjsonPath: string,
 *   exports?: ExportConfig,
 *   name?: string,
 *   main?: string,
 *   type?: PackageType,
 * }} PackageConfig
 */
 
/** @type {Map<string, PackageConfig>} */
const packageJSONCache = new SafeMap();
 
 
/**
 * @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 } = filterOwnProperties(packageJSON, ['imports', 'main', 'name', 'type']);
  const exports = ObjectPrototypeHasOwnProperty(packageJSON, 'exports') ? packageJSON.exports : undefined;
  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;
}
 
 
module.exports = {
  getPackageConfig,
  getPackageScopeConfig,
};