CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-metro-resolver

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

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

error-handling.mddocs/

Error Handling

Metro Resolver provides comprehensive error types for different resolution failure scenarios with detailed diagnostic information. Each error type includes specific information to help debug resolution issues.

Capabilities

FailedToResolveNameError

Error thrown when a module name cannot be resolved in the Haste module map or through node_modules lookup.

class FailedToResolveNameError extends Error {
  /** Node modules directories that were searched */
  dirPaths: ReadonlyArray<string>;
  
  /** Additional paths that were searched (e.g., extraNodeModules) */
  extraPaths: ReadonlyArray<string>;
  
  constructor(
    dirPaths: ReadonlyArray<string>,
    extraPaths: ReadonlyArray<string>
  );
}

Usage Example:

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

try {
  const result = Resolver.resolve(context, 'non-existent-package', null);
} catch (error) {
  if (error instanceof Resolver.FailedToResolveNameError) {
    console.log('Searched directories:', error.dirPaths);
    console.log('Extra paths:', error.extraPaths);
    console.log('Error message:', error.message);
  }
}

FailedToResolvePathError

Error thrown when a specific file path cannot be resolved (for relative or absolute imports).

class FailedToResolvePathError extends Error {
  /** File and directory candidates that were attempted */
  candidates: FileAndDirCandidates;
  
  constructor(candidates: FileAndDirCandidates);
}

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

Usage Example:

try {
  const result = Resolver.resolve(context, './missing-file', 'ios');
} catch (error) {
  if (error instanceof Resolver.FailedToResolvePathError) {
    console.log('File candidates:', error.candidates.file);
    console.log('Directory candidates:', error.candidates.dir);
  }
}

InvalidPackageError

Error thrown when a package.json has an invalid main field that cannot be resolved.

class InvalidPackageError extends Error {
  /** File candidates attempted for the main field */
  fileCandidates: FileCandidates;
  
  /** Index file candidates attempted as fallback */
  indexCandidates: FileCandidates;
  
  /** Full path to the main module that was attempted */
  mainModulePath: string;
  
  /** Full path to the package.json file */
  packageJsonPath: string;
  
  constructor(opts: {
    fileCandidates: FileCandidates;
    indexCandidates: FileCandidates;
    mainModulePath: string;
    packageJsonPath: string;
  });
}

Usage Example:

try {
  const result = Resolver.resolve(context, 'broken-package', null);
} catch (error) {
  if (error instanceof Resolver.InvalidPackageError) {
    console.log('Package.json path:', error.packageJsonPath);
    console.log('Main module path:', error.mainModulePath);
    console.log('File candidates:', error.fileCandidates);
    console.log('Index candidates:', error.indexCandidates);
  }
}

FailedToResolveUnsupportedError

Error thrown when resolution encounters unsupported scenarios.

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

Package Export/Import Errors

Specialized errors for package.json exports and imports field resolution failures.

class PackagePathNotExportedError extends Error {
  /** The package path that is not exported */
  packagePath: string;
  
  /** The subpath that was requested */
  subpath: string;
  
  constructor(opts: {
    packagePath: string;
    subpath: string;
  });
}

class PackageImportNotResolvedError extends Error {
  /** The import specifier that could not be resolved */
  importSpecifier: string;
  
  /** Reason for the resolution failure */
  reason: string;
  
  constructor(opts: {
    importSpecifier: string;
    reason: string;
  });
}

class InvalidPackageConfigurationError extends Error {
  /** Details about the invalid configuration */
  reason: string;
  
  constructor(reason: string);
}

File Candidates System

The resolver tracks attempted file paths to provide detailed error information.

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

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

Format File Candidates

Utility function to format file candidates for human-readable error messages.

/**
 * Format file candidates into a human-readable string
 * @param candidates - File candidates to format
 * @returns Formatted string representation
 */
function formatFileCandidates(candidates: FileCandidates): string;

Usage Examples:

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

// For source file candidates
const sourceCandidates = {
  type: 'sourceFile',
  filePathPrefix: '/app/src/component',
  candidateExts: ['.ios.js', '.native.js', '.js', '.ts']
};

console.log(formatFileCandidates(sourceCandidates));
// Output: "/app/src/component(.ios.js|.native.js|.js|.ts)"

// For asset candidates
const assetCandidates = {
  type: 'asset',
  name: 'icon.png'
};

console.log(formatFileCandidates(assetCandidates));
// Output: "icon.png"

Error Handling Best Practices

When working with Metro Resolver errors, follow these patterns:

Comprehensive Error Handling:

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

function resolveWithErrorHandling(context, moduleName, platform) {
  try {
    return Resolver.resolve(context, moduleName, platform);
  } catch (error) {
    if (error instanceof Resolver.FailedToResolveNameError) {
      console.error(`Failed to resolve module "${moduleName}"`);
      console.error('Searched in directories:', error.dirPaths);
      console.error('Additional paths:', error.extraPaths);
    } else if (error instanceof Resolver.FailedToResolvePathError) {
      console.error(`Failed to resolve path "${moduleName}"`);
      console.error('File candidates:', Resolver.formatFileCandidates(error.candidates.file));
      console.error('Directory candidates:', Resolver.formatFileCandidates(error.candidates.dir));
    } else if (error instanceof Resolver.InvalidPackageError) {
      console.error(`Invalid package configuration`);
      console.error('Package:', error.packageJsonPath);
      console.error('Main module:', error.mainModulePath);
    } else {
      console.error('Unexpected resolution error:', error.message);
    }
    throw error;
  }
}

Error Recovery Strategies:

function resolveWithFallback(context, moduleName, platform) {
  try {
    return Resolver.resolve(context, moduleName, platform);
  } catch (error) {
    if (error instanceof Resolver.FailedToResolveNameError) {
      // Try without platform-specific resolution
      if (platform) {
        try {
          return Resolver.resolve(context, moduleName, null);
        } catch (fallbackError) {
          // Handle nested error
        }
      }
    }
    throw error;
  }
}

Warning System

The resolver includes a warning system for non-fatal issues.

interface WarningConfiguration {
  /** Function to handle warning messages */
  unstable_logWarning: (message: string) => void;
}

Common Warning Scenarios:

  • Package exports field resolution failures (with fallback to file-based resolution)
  • Package imports field resolution failures (with fallback to file-based resolution)
  • Invalid package configuration (with fallback)

Usage Example:

const context = {
  // ... other config
  unstable_logWarning: (message) => {
    console.warn(`[Metro Resolver Warning]: ${message}`);
  }
};

docs

asset-resolution.md

custom-resolvers.md

error-handling.md

index.md

package-resolution.md

resolution-context.md

resolution-engine.md

tile.json