CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-resolve-exports

A tiny, correct, general-purpose, and configurable "exports" and "imports" resolver without file-system reliance

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

Resolve.exports

Resolve.exports is a tiny (952b), correct, general-purpose, and configurable "exports" and "imports" resolver for Node.js package.json fields without file-system reliance. It provides spec-compliant resolution of module paths based on conditional exports, supporting both CommonJS and ES modules.

Package Information

  • Package Name: resolve.exports
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install resolve.exports

Core Imports

import { resolve, exports, imports, legacy } from "resolve.exports";

For CommonJS:

const { resolve, exports, imports, legacy } = require("resolve.exports");

Basic Usage

import { resolve, exports } from "resolve.exports";

// Example package.json-like object
const pkg = {
  name: "my-package",
  exports: {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    },
    "./utils": {
      "import": "./dist/utils.mjs",
      "require": "./dist/utils.js"
    }
  }
};

// Resolve main entry point for ES modules
const result = resolve(pkg, ".", { require: false });
console.log(result); // ["./dist/index.mjs"]

// Resolve specific export for CommonJS
const utilsResult = exports(pkg, "./utils", { require: true });
console.log(utilsResult); // ["./dist/utils.js"]

Capabilities

Main Resolution Function

The primary entry point that automatically routes to exports() or imports() based on the entry value.

/**
 * Convenience helper that automatically routes to exports() or imports() based on input value
 * @param pkg - Package.json-like object containing exports/imports
 * @param input - Target entry identifier (defaults to ".")
 * @param options - Resolution options
 * @returns Array of resolved paths or void if no match
 */
function resolve<T = Package>(
  pkg: T,
  input?: string,
  options?: Options
): Imports.Output | Exports.Output | void;

Exports Resolution

Resolves package.json "exports" field mappings according to Node.js specification.

/**
 * Resolves package.json "exports" field mappings
 * @param pkg - Package.json-like object containing exports
 * @param input - Export specifier to resolve (defaults to ".")
 * @param options - Resolution options
 * @returns Array of resolved paths or void if no match
 * @throws Error if entry not found or no matching conditions
 */
function exports<T = Package>(
  pkg: T,
  input?: string,
  options?: Options
): Exports.Output | void;

Usage Example:

const pkg = {
  name: "example",
  exports: {
    ".": {
      "node": "./dist/node.js",
      "browser": "./dist/browser.js",
      "default": "./dist/index.js"
    },
    "./feature": "./dist/feature.js"
  }
};

// Resolve for browser environment
const browserPath = exports(pkg, ".", { browser: true });
console.log(browserPath); // ["./dist/browser.js"]

// Resolve specific feature
const featurePath = exports(pkg, "./feature");
console.log(featurePath); // ["./dist/feature.js"]

Imports Resolution

Resolves package.json "imports" field mappings for internal package imports starting with "#".

/**
 * Resolves package.json "imports" field mappings
 * @param pkg - Package.json-like object containing imports
 * @param input - Import specifier (must start with "#")
 * @param options - Resolution options
 * @returns Array of resolved paths or void if no match
 * @throws Error if target not found or no matching conditions
 */
function imports<T = Package>(
  pkg: T,
  input: string,
  options?: Options
): Imports.Output | void;

Usage Example:

const pkg = {
  name: "example",
  imports: {
    "#utils": {
      "node": "./src/utils-node.js",
      "browser": "./src/utils-browser.js"
    },
    "#config/*": "./config/*.json"
  }
};

// Resolve internal import
const utilsPath = imports(pkg, "#utils", { browser: true });
console.log(utilsPath); // ["./src/utils-browser.js"]

// Resolve wildcard import
const configPath = imports(pkg, "#config/database");
console.log(configPath); // ["./config/database.json"]

Legacy Resolution

Resolves legacy package.json fields ("main", "module", "browser") as fallback when exports are not available.

/**
 * Resolves legacy package.json fields (main, module, browser) as fallback
 */
function legacy<T = Package>(
  pkg: T,
  options: { browser: true; fields?: readonly string[] }
): Browser | void;

function legacy<T = Package>(
  pkg: T,
  options: { browser: string; fields?: readonly string[] }
): string | false | void;

function legacy<T = Package>(
  pkg: T,
  options: { browser: false; fields?: readonly string[] }
): string | void;

function legacy<T = Package>(
  pkg: T,
  options?: {
    browser?: boolean | string;
    fields?: readonly string[];
  }
): Browser | string;

Usage Example:

const pkg = {
  name: "legacy-package",
  main: "./lib/index.js",
  module: "./lib/index.mjs",
  browser: "./lib/browser.js"
};

// Get main entry for Node.js
const nodeEntry = legacy(pkg, { browser: false });
console.log(nodeEntry); // "./lib/index.js"

// Get browser entry
const browserEntry = legacy(pkg, { browser: true });
console.log(browserEntry); // "./lib/browser.js"

// Custom field priority
const customEntry = legacy(pkg, { 
  browser: false,
  fields: ["module", "main"]
});
console.log(customEntry); // "./lib/index.mjs"

Types

Options Interface

Configuration options for resolution behavior.

interface Options {
  /**
   * When true, adds the "browser" conditions.
   * Otherwise the "node" condition is enabled.
   * @default false
   */
  browser?: boolean;

  /**
   * Any custom conditions to match.
   * @note Array order does not matter. Priority is determined by the key-order of conditions defined within a package's imports/exports mapping.
   * @default []
   */
  conditions?: readonly string[];

  /**
   * When true, adds the "require" condition.
   * Otherwise the "import" condition is enabled.
   * @default false
   */
  require?: boolean;

  /**
   * Prevents "require", "import", "browser", and/or "node" conditions from being added automatically.
   * When enabled, only `options.conditions` are added alongside the "default" condition.
   * @important Enabling this deviates from Node.js default behavior.
   * @default false
   */
  unsafe?: boolean;
}

Package Interface

Represents a package.json structure for resolution.

interface Package {
  name: string;
  version?: string;
  module?: string;
  main?: string;
  imports?: Imports;
  exports?: Exports;
  browser?: Browser;
  [key: string]: any;
}

Exports Types

Export mapping type definitions.

type Exports = Path | {
  [path: Exports.Entry]: Exports.Value;
  [cond: Condition]: Exports.Value;
};

namespace Exports {
  /** Allows "." and "./{name}" */
  type Entry = `.${string}`;

  /** strings must be internal paths */
  type Value = Path | null | {
    [c: Condition]: Value;
  } | Value[];

  type Output = Path[];
}

Imports Types

Import mapping type definitions.

type Imports = {
  [entry: Imports.Entry]: Imports.Value;
};

namespace Imports {
  type Entry = `#${string}`;
  
  /** External dependency name (not an internal path) */
  type External = string;

  /** strings are dependency names OR internal paths */
  type Value = External | Path | null | {
    [c: Condition]: Value;
  } | Value[];

  type Output = Array<External | Path>;
}

Common Types

Basic type definitions used throughout the API.

/**
 * A resolve condition
 * @example "node", "default", "production"
 */
type Condition = string;

/** An internal file path */
type Path = `./${string}`;

type Browser = string[] | string | {
  [file: Path | string]: string | false;
};

Error Handling

The package throws Error instances with specific message formats for resolution failures:

  • Missing "{entry}" specifier in "{name}" package - when the requested entry/target is not found in the package's exports/imports
  • No known conditions for "{entry}" specifier in "{name}" package - when no matching conditions are found for the entry
try {
  const result = exports(pkg, "./nonexistent");
} catch (error) {
  console.error(error.message); // Missing "./nonexistent" specifier in "my-package" package
}

Advanced Usage

Custom Conditions

const pkg = {
  name: "my-package",
  exports: {
    ".": {
      "development": "./src/index.js",
      "production": "./dist/index.js",
      "default": "./lib/index.js"
    }
  }
};

// Resolve with custom condition
const devPath = resolve(pkg, ".", { 
  conditions: ["development"],
  unsafe: true  // Only use custom conditions
});
console.log(devPath); // ["./src/index.js"]

Complex Conditional Exports

const pkg = {
  name: "universal-package",
  exports: {
    ".": {
      "import": {
        "browser": "./dist/browser.mjs",
        "node": "./dist/node.mjs"
      },
      "require": {
        "browser": "./dist/browser.js",
        "node": "./dist/node.js"
      }
    }
  }
};

// Browser + ES modules
const browserESM = resolve(pkg, ".", { browser: true, require: false });
console.log(browserESM); // ["./dist/browser.mjs"]

// Node.js + CommonJS
const nodeCJS = resolve(pkg, ".", { browser: false, require: true });
console.log(nodeCJS); // ["./dist/node.js"]
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/resolve.exports@2.0.x
Publish Source
CLI
Badge
tessl/npm-resolve-exports badge