or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bundling.mdcli-commands.mdconfiguration.mddevelopment-server.mdindex.md
tile.json

configuration.mddocs/

Configuration

Configuration system for customizing Metro behavior, transformers, resolvers, and build options. Metro supports project-specific configurations, environment-based settings, and extensive customization of the bundling pipeline.

Capabilities

Load Configuration

Loads Metro configuration from various sources including config files, command line arguments, and defaults.

/**
 * Load Metro configuration from multiple sources
 * @param argv - Command line arguments and options
 * @returns Promise resolving to complete Metro configuration
 */
function loadConfig(argv: Object): Promise<ConfigT>;

/**
 * Resolve configuration file path
 * @param configPath - Optional explicit config file path
 * @returns Promise resolving to config file info or null
 */
function resolveConfig(configPath?: string): Promise<{
  filepath: string;
  isEmpty: boolean;
} | null>;

Usage Example:

import { loadConfig, resolveConfig } from "metro";

// Load config with project root
const config = await loadConfig({
  projectRoot: process.cwd(),
  watchFolders: ["./packages"],
});

// Find config file
const configInfo = await resolveConfig();
if (configInfo) {
  console.log("Using config:", configInfo.filepath);
}

Merge Configuration

Combines user configuration with Metro defaults, allowing selective overrides.

/**
 * Merge user configuration with Metro defaults
 * @param defaultConfig - Base Metro configuration
 * @param config - User configuration overrides
 * @returns Merged configuration object
 */
function mergeConfig(
  defaultConfig: ConfigT,
  config: InputConfigT
): ConfigT;

/**
 * Get default Metro configuration for a project
 * @param projectRoot - Root directory of the project
 * @returns Promise resolving to default configuration
 */
function getDefaultConfig(projectRoot: string): Promise<ConfigT>;

Usage Example:

import { getDefaultConfig, mergeConfig } from "metro";

// Get defaults and customize
const defaultConfig = await getDefaultConfig(process.cwd());

const customConfig = mergeConfig(defaultConfig, {
  transformer: {
    babelTransformerPath: require.resolve("@react-native/metro-babel-transformer"),
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
  resolver: {
    alias: {
      "@": "./src",
    },
  },
});

Configuration Structure

Complete Configuration Interface

interface ConfigT {
  /** Caching configuration */
  cacheStores: ReadonlyArray<CacheStore<TransformResultWithSource<>>>;
  cacheVersion: string;
  
  /** Project structure */
  projectRoot: string;
  watchFolders: ReadonlyArray<string>;
  
  /** Resolver configuration */
  resolver: ResolverConfig;
  
  /** Transformer configuration */
  transformer: TransformerConfig;
  
  /** Serializer configuration */
  serializer: SerializerConfig;
  
  /** Server configuration */
  server: ServerConfig;
  
  /** Watcher configuration */
  watcher: WatcherConfig;
  
  /** Reporter for build events */
  reporter: Reporter;
  
  /** Maximum worker processes */
  maxWorkers: number;
  
  /** Reset cache on startup */
  resetCache: boolean;
}

interface InputConfigT {
  /** Project root directory */
  projectRoot?: string;
  /** Additional directories to watch */
  watchFolders?: ReadonlyArray<string>;
  /** Resolver configuration overrides */
  resolver?: Partial<ResolverConfig>;
  /** Transformer configuration overrides */
  transformer?: Partial<TransformerConfig>;
  /** Serializer configuration overrides */
  serializer?: Partial<SerializerConfig>;
  /** Server configuration overrides */
  server?: Partial<ServerConfig>;
  /** Watcher configuration overrides */
  watcher?: Partial<WatcherConfig>;
  /** Custom reporter */
  reporter?: Reporter;
  /** Maximum worker processes */
  maxWorkers?: number;
  /** Reset cache on startup */
  resetCache?: boolean;
}

Resolver Configuration

interface ResolverConfig {
  /** Alias mappings for module resolution */
  alias: {[from: string]: string};
  
  /** Alternative names for entry files */
  aliasFields: ReadonlyArray<string>;
  
  /** Asset file extensions */
  assetExts: ReadonlyArray<string>;
  
  /** Directories to search for modules */
  blockList: RegExp | ReadonlyArray<RegExp>;
  
  /** Custom dependency extractor */
  dependencyExtractor?: string;
  
  /** Enable symlink resolution */
  enableSymlinks: boolean;
  
  /** Node.js modules to exclude from bundle */
  nodeModulesPaths: ReadonlyArray<string>;
  
  /** Platforms to resolve */
  platforms: ReadonlyArray<string>;
  
  /** Source file extensions */
  sourceExts: ReadonlyArray<string>;
  
  /** Custom resolver function */
  resolverMainFields: ReadonlyArray<string>;
  
  /** Transform cache key */
  hasteImplModulePath?: string;
}

Usage Example:

const config = {
  resolver: {
    alias: {
      "@components": "./src/components",
      "@utils": "./src/utils",
    },
    assetExts: ["png", "jpg", "jpeg", "gif", "svg", "webp"],
    sourceExts: ["js", "jsx", "ts", "tsx", "json"],
    platforms: ["ios", "android", "web"],
    nodeModulesPaths: ["./node_modules"],
  },
};

Transformer Configuration

interface TransformerConfig {
  /** Path to Babel transformer */
  babelTransformerPath: string;
  
  /** Transform options factory */
  getTransformOptions: (
    entryPoints: ReadonlyArray<string>,
    options: {
      dev: boolean;
      hot: boolean;
      minify: boolean;
      platform?: string;
    },
    getDependenciesOf: (path: string) => Set<string>
  ) => Promise<TransformProfile>;
  
  /** Additional Babel plugins */
  transformVariants: TransformVariants;
  
  /** Worker pool path */
  workerPath: string;
  
  /** Worker pool size */
  maxWorkerCount: number;
  
  /** Enable global cache */
  globalPrefix: string;
}

interface TransformVariants {
  /** Named transform profiles for different scenarios */
  [name: string]: TransformProfile;
}

interface TransformProfile {
  dev: boolean;
  hot: boolean;
  minify: boolean;
  platform?: string;
  transform: {
    experimentalImportSupport: boolean;
    inlineRequires: boolean;
    nonInlinedRequires?: ReadonlyArray<string>;
    unstable_allowRequireContext: boolean;
  };
}

Usage Example:

const config = {
  transformer: {
    babelTransformerPath: require.resolve("@react-native/metro-babel-transformer"),
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
        unstable_allowRequireContext: true,
      },
    }),
  },
};

Serializer Configuration

interface SerializerConfig {
  /** Custom module ID factory */
  createModuleIdFactory: () => (path: string) => number;
  
  /** Custom serializer for bundle output */
  customSerializer?: CustomSerializer;
  
  /** Paths to exclude from bundle */
  getModulesRunBeforeMainModule: (entryFilePath: string) => ReadonlyArray<string>;
  
  /** Polyfills to include */
  getPolyfills: () => ReadonlyArray<string>;
  
  /** Runtime code to inject */
  getRunModuleStatement: (moduleId: number | string) => string;
  
  /** Post-process modules */
  postProcessBundleSourcemap: PostProcessBundleSourcemap;
  
  /** Process module filter */
  processModuleFilter: (module: Module) => boolean;
}

type CustomSerializer = (
  entryPoint: string,
  preModules: ReadonlyArray<Module>,
  graph: ReadOnlyGraph,
  options: SerializerOptions
) => string | {code: string; map: string};

Server Configuration

interface ServerConfig {
  /** Server port */
  port: number;
  
  /** Enable global hotkey support */
  useGlobalHotkey: boolean;
  
  /** Middleware enhancement function */
  enhanceMiddleware?: (
    middleware: Middleware,
    server: MetroServer
  ) => Middleware;
  
  /** Verify connections before processing */
  verifyConnections: boolean;
}

Watcher Configuration

interface WatcherConfig {
  /** Additional directories to watch */
  additionalExts: ReadonlyArray<string>;
  
  /** Watch mode implementation */
  watchman: {
    /** Defer initial scan */
    defer_initial_scan: boolean;
  };
  
  /** Health check configuration */
  healthCheck: {
    /** Enable health checks */
    enabled: boolean;
    /** Health check interval */
    interval: number;
    /** Health check timeout */
    timeout: number;
  };
}

Configuration Files

Configuration File Formats

Metro looks for configuration in several locations:

// metro.config.js
module.exports = {
  projectRoot: __dirname,
  watchFolders: ['./packages'],
  
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
  
  resolver: {
    alias: {
      '@': './src',
    },
  },
};

// metro.config.json
{
  "projectRoot": ".",
  "watchFolders": ["./packages"],
  "resolver": {
    "alias": {
      "@": "./src" 
    }
  }
}

// package.json
{
  "name": "my-app",
  "metro": {
    "resolver": {
      "alias": {
        "@": "./src"
      }
    }
  }
}

Environment-Specific Configuration

// metro.config.js
const baseConfig = {
  projectRoot: __dirname,
};

if (process.env.NODE_ENV === 'production') {
  module.exports = {
    ...baseConfig,
    transformer: {
      minifierPath: 'metro-minify-terser',
      getTransformOptions: async () => ({
        transform: {
          inlineRequires: true,
        },
      }),
    },
  };
} else {
  module.exports = {
    ...baseConfig,
    transformer: {
      getTransformOptions: async () => ({
        transform: {
          inlineRequires: false,
        },
      }),
    },
  };
}

Advanced Configuration

Custom Transformers

// Custom Babel transformer
module.exports = {
  transformer: {
    babelTransformerPath: require.resolve('./custom-transformer.js'),
  },
};

// custom-transformer.js
const {transform} = require('@babel/core');

module.exports.transform = function({src, filename, options}) {
  return transform(src, {
    filename,
    ...options,
    plugins: [
      // Custom Babel plugins
      require.resolve('./my-babel-plugin'),
    ],
  });
};

Custom Resolvers

module.exports = {
  resolver: {
    resolverMainFields: ['react-native', 'browser', 'main'],
    resolveRequest: (context, modulePath, platform) => {
      // Custom resolution logic
      if (modulePath.startsWith('virtual:')) {
        return {
          filePath: generateVirtualModule(modulePath),
          type: 'sourceFile',
        };
      }
      
      // Fall back to default resolution
      return context.resolveRequest(context, modulePath, platform);
    },
  },
};

Performance Optimization

module.exports = {
  // Increase worker count for faster builds
  maxWorkers: Math.max(require('os').cpus().length - 1, 1),
  
  // Optimize transformer
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        // Enable inline requires for smaller bundles
        inlineRequires: true,
        // Enable experimental import support
        experimentalImportSupport: true,
      },
    }),
  },
  
  // Cache optimization
  cacheStores: [
    new FileStore({
      root: path.join(os.tmpdir(), 'metro-cache'),
    }),
  ],
};