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

resolution-engine.mddocs/

Resolution Engine

The core resolution engine handles all module resolution scenarios including relative paths, absolute paths, bare specifiers, and Haste modules. It orchestrates the entire resolution process through a series of specialized resolvers.

Capabilities

Main Resolve Function

The primary entry point for all module resolution operations.

/**
 * Resolve a module name to a file path or asset collection based on the resolution context
 * @param context - Resolution context containing configuration and helper functions
 * @param moduleName - Module name to resolve (relative, absolute, or bare specifier)
 * @param platform - Target platform (e.g., 'ios', 'android', 'web') for platform-specific resolution
 * @returns Resolution result containing file path or asset collection
 */
function resolve(
  context: ResolutionContext,
  moduleName: string,
  platform: string | null
): Resolution;

Usage Examples:

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

// Resolve relative import
const relativeResult = Resolver.resolve(context, './utils/helper', 'ios');
// { type: 'sourceFile', filePath: '/app/src/utils/helper.ios.js' }

// Resolve bare specifier
const packageResult = Resolver.resolve(context, 'lodash', null);
// { type: 'sourceFile', filePath: '/node_modules/lodash/index.js' }

// Resolve asset
const assetResult = Resolver.resolve(context, './icon.png', 'ios');
// { type: 'assetFiles', filePaths: ['./icon@2x.ios.png', './icon.ios.png'] }

// Handle empty resolution (excluded module)
const emptyResult = Resolver.resolve(context, 'excluded-module', null);
// { type: 'empty' }

Resolution Result Types

Different types of resolution outcomes based on the target module.

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 Process Flow

The resolver follows a systematic process:

  1. Custom Resolver Check: If a custom resolver is provided, delegate to it first
  2. Path Type Detection: Determine if the module name is relative, absolute, or bare specifier
  3. Relative/Absolute Resolution: Resolve using file system lookups for direct paths
  4. Subpath Import Resolution: Handle package.json imports field for # prefixed imports
  5. Module Redirection: Apply any path redirections before further resolution
  6. Haste Resolution: Check Haste module map if enabled
  7. Package Resolution: Resolve bare specifiers through node_modules hierarchy
  8. Error Handling: Throw appropriate errors for failed resolutions

Platform-Specific Resolution

The resolver supports platform-specific file resolution for React Native development.

Resolution Priority:

  1. Platform-specific file (e.g., Component.ios.js)
  2. Native platform file (e.g., Component.native.js) if preferNativePlatform is true
  3. Generic file (e.g., Component.js)

Usage Example:

// With platform = 'ios', the resolver will try:
// 1. ./Component.ios.js
// 2. ./Component.native.js (if preferNativePlatform: true)
// 3. ./Component.js
const result = Resolver.resolve(context, './Component', 'ios');

Extension Resolution

The resolver attempts multiple extensions based on configuration.

Resolution Order:

  1. Exact filename match (no extension)
  2. Source extensions in order (.js, .ts, .tsx, etc.)
  3. Asset extensions for asset files (.png, .jpg, etc.)

Hierarchical Lookup

For bare specifiers, the resolver searches through the node_modules hierarchy.

Search Order:

  1. Current directory's node_modules
  2. Parent directory's node_modules
  3. Continue up the directory tree
  4. Global node_modules paths from context

Example Hierarchy:

/project/src/components/Button.js
  -> /project/src/components/node_modules/package
  -> /project/src/node_modules/package
  -> /project/node_modules/package
  -> /node_modules/package

Empty Module Resolution

The resolver can return empty resolutions for excluded modules.

interface EmptyResolution {
  readonly type: 'empty';
}

This is useful for:

  • Conditional compilation
  • Platform-specific exclusions
  • Build-time module replacement

Usage Example:

// If redirectModulePath returns false for a module
const context = {
  // ... other config
  redirectModulePath: (path) => {
    if (path.includes('server-only')) return false;
    return path;
  }
};

const result = Resolver.resolve(context, 'server-only-module', null);
// { type: 'empty' }

docs

asset-resolution.md

custom-resolvers.md

error-handling.md

index.md

package-resolution.md

resolution-context.md

resolution-engine.md

tile.json