or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-resolve-exports

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

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/resolve.exports@2.0.x

To install, run

npx @tessl/cli install tessl/npm-resolve-exports@2.0.0

index.mddocs/

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"]