or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

asset-resolution.mdcustom-resolvers.mderror-handling.mdindex.mdpackage-resolution.mdresolution-context.mdresolution-engine.md
tile.json

tessl/npm-metro-resolver

Implementation of Metro's resolution logic for JavaScript modules, assets, and packages within React Native and Metro bundler projects.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/metro-resolver@0.83.x

To install, run

npx @tessl/cli install tessl/npm-metro-resolver@0.83.0

index.mddocs/

Metro Resolver

Metro Resolver is a JavaScript library that implements Metro's module resolution logic, providing the core functionality for resolving JavaScript modules, assets, and packages within React Native and Metro bundler projects. It handles complex resolution scenarios including package.json exports, imports, platform-specific files, asset resolution, and custom resolver implementations.

Package Information

  • Package Name: metro-resolver
  • Package Type: npm
  • Language: JavaScript/TypeScript
  • Installation: npm install metro-resolver

Core Imports

const Resolver = require("metro-resolver");
const { resolve } = Resolver;

For ES modules:

import Resolver from "metro-resolver";
const { resolve } = Resolver;

TypeScript imports (with type definitions):

import { 
  resolve,
  type ResolutionContext, 
  type Resolution,
  type CustomResolver 
} from "metro-resolver";

Basic Usage

const Resolver = require("metro-resolver");

// Create a resolution context (typically provided by Metro bundler)
const context = {
  allowHaste: false,
  assetExts: new Set(['png', 'jpg', 'jpeg', 'gif']),
  sourceExts: ['js', 'json', 'ts', 'tsx'],
  mainFields: ['browser', 'main'],
  fileSystemLookup: (path) => ({ exists: true, type: 'f', realPath: path }),
  doesFileExist: (path) => true,
  getPackage: (packagePath) => require(packagePath),
  getPackageForModule: (modulePath) => null,
  nodeModulesPaths: ['/node_modules'],
  originModulePath: '/app/src/index.js',
  preferNativePlatform: false,
  resolveAsset: (dirPath, name, ext) => [`${dirPath}/${name}${ext}`],
  resolveHasteModule: () => undefined,
  resolveHastePackage: () => undefined,
  redirectModulePath: (path) => path,
  customResolverOptions: {},
  disableHierarchicalLookup: false,
  extraNodeModules: null,
  dev: false,
  unstable_conditionNames: ['import', 'require'],
  unstable_conditionsByPlatform: {},
  unstable_enablePackageExports: true,
  unstable_logWarning: console.warn
};

// Resolve a module
const resolution = Resolver.resolve(context, 'react-native', 'ios');
console.log(resolution);
// { type: 'sourceFile', filePath: '/node_modules/react-native/index.js' }

Architecture

Metro Resolver is built around several key components:

  • Core Resolution Engine: The main resolve function that orchestrates the resolution process
  • Resolution Context: Configuration object containing all resolver settings and helper functions
  • Package Resolution: Logic for resolving package entry points, exports, and imports fields
  • File System Integration: Abstracted file system operations for flexibility across environments
  • Error Handling: Comprehensive error types for different resolution failure scenarios
  • Asset Resolution: Specialized handling for asset files with scaling variants
  • Platform Support: Platform-specific file resolution for React Native development

Capabilities

Core Resolution

Main resolution function that handles all module resolution scenarios including relative paths, absolute paths, bare specifiers, and Haste modules.

function resolve(
  context: ResolutionContext,
  moduleName: string,
  platform: string | null
): Resolution;

type Resolution = FileResolution | Readonly<{ type: 'empty' }>;

type FileResolution = AssetResolution | SourceFileResolution;

interface SourceFileResolution {
  readonly type: 'sourceFile';
  readonly filePath: string;
}

interface AssetResolution {
  readonly type: 'assetFiles';
  readonly filePaths: ReadonlyArray<string>;
}

Resolution Engine

Resolution Context

Configuration system that provides all the settings, file system operations, and helper functions needed for module resolution.

interface ResolutionContext {
  readonly allowHaste: boolean;
  readonly assetExts: ReadonlySet<string>;
  readonly sourceExts: ReadonlyArray<string>;
  readonly customResolverOptions: CustomResolverOptions;
  readonly disableHierarchicalLookup: boolean;
  readonly doesFileExist: DoesFileExist;
  readonly extraNodeModules?: Readonly<{ [string]: string }> | null;
  readonly dev: boolean;
  readonly fileSystemLookup: FileSystemLookup;
  readonly getPackage: (packageJsonPath: string) => PackageJson | null;
  readonly getPackageForModule: (absoluteModulePath: string) => PackageForModule | null;
  readonly mainFields: ReadonlyArray<string>;
  readonly originModulePath: string;
  readonly nodeModulesPaths: ReadonlyArray<string>;
  readonly preferNativePlatform: boolean;
  readonly resolveAsset: ResolveAsset;
  readonly redirectModulePath: (modulePath: string) => string | false;
  readonly resolveHasteModule: (name: string) => string | undefined;
  readonly resolveHastePackage: (name: string) => string | undefined;
  readonly resolveRequest?: CustomResolver;
  readonly dependency?: any;
  readonly isESMImport?: boolean;
  readonly unstable_conditionNames: ReadonlyArray<string>;
  readonly unstable_conditionsByPlatform: Readonly<{ [platform: string]: ReadonlyArray<string> }>;
  readonly unstable_enablePackageExports: boolean;
  readonly unstable_logWarning: (message: string) => void;
}

Resolution Context

Error Handling

Comprehensive error types for different resolution failure scenarios with detailed diagnostic information.

class FailedToResolveNameError extends Error {
  dirPaths: ReadonlyArray<string>;
  extraPaths: ReadonlyArray<string>;
}

class FailedToResolvePathError extends Error {
  candidates: FileAndDirCandidates;
}

class InvalidPackageError extends Error {
  fileCandidates: FileCandidates;
  indexCandidates: FileCandidates;
  mainModulePath: string;
  packageJsonPath: string;
}

class FailedToResolveUnsupportedError extends Error {
  constructor(message: string);
}

Error Handling

Package Resolution

Advanced package resolution supporting modern Node.js features like package.json exports/imports fields and conditional exports.

interface PackageJson {
  readonly name?: string;
  readonly main?: string;
  readonly exports?: ExportsField;
  readonly imports?: ExportsLikeMap;
}


interface PackageInfo {
  readonly packageJson: PackageJson;
  readonly rootPath: string;
}

Package Resolution

Asset Resolution

Specialized handling for asset files including support for scaling variants and platform-specific assets.

type ResolveAsset = (
  dirPath: string,
  assetName: string,
  extension: string
) => ReadonlyArray<string> | undefined;

function resolveAsset(
  context: ResolutionContext,
  filePath: string
): AssetResolution | null;

Asset Resolution

Custom Resolvers

Extensibility system allowing custom resolution logic to be integrated into the resolution pipeline.

type CustomResolver = (
  context: CustomResolutionContext,
  moduleName: string,
  platform: string | null
) => Resolution;

interface CustomResolutionContext extends ResolutionContext {
  readonly resolveRequest: CustomResolver;
}

type CustomResolverOptions = Readonly<{
  [option: string]: unknown;
}>;

Custom Resolvers

Utility Functions

Utility functions for working with resolution candidates and formatting.

function formatFileCandidates(candidates: FileCandidates): string;

Types

type Result<TResolution, TCandidates> =
  | { readonly type: 'resolved'; readonly resolution: TResolution }
  | { readonly type: 'failed'; readonly candidates: TCandidates };

type AssetFileResolution = ReadonlyArray<string>;

type FileCandidates =
  | { readonly type: 'asset'; readonly name: string }
  | {
      readonly type: 'sourceFile';
      filePathPrefix: string;
      readonly candidateExts: ReadonlyArray<string>;
    };

interface FileAndDirCandidates {
  readonly dir: FileCandidates | null;
  readonly file: FileCandidates | null;
}

type DoesFileExist = (filePath: string) => boolean;

type FileSystemLookup = (
  absoluteOrProjectRelativePath: string
) => { exists: false } | { exists: true; type: 'f' | 'd'; realPath: string };

interface PackageForModule extends PackageInfo {
  readonly packageRelativePath: string;
}

type ExportsLikeMap = Readonly<{
  [subpathOrCondition: string]: string | ExportsLikeMap | null;
}>;

type ExportMapWithFallbacks = Readonly<{
  [subpath: string]: string | ExportsLikeMap | null | ExportValueWithFallback;
}>;

type ExportValueWithFallback =
  | ReadonlyArray<ExportsLikeMap | string>
  | ReadonlyArray<ReadonlyArray<unknown>>;

type ExportsField =
  | string
  | ReadonlyArray<string>
  | ExportValueWithFallback
  | ExportsLikeMap
  | ExportMapWithFallbacks;

type FlattenedExportMap = ReadonlyMap<string, string | null>;

type NormalizedExportsLikeMap = Map<string, null | string | ExportsLikeMap>;