Implementation of Metro's resolution logic for JavaScript modules, assets, and packages within React Native and Metro bundler projects.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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);
}
}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);
}
}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);
}
}Error thrown when resolution encounters unsupported scenarios.
class FailedToResolveUnsupportedError extends Error {
constructor(message: string);
}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);
}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;
}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"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;
}
}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:
Usage Example:
const context = {
// ... other config
unstable_logWarning: (message) => {
console.warn(`[Metro Resolver Warning]: ${message}`);
}
};