or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

android-config.mdandroid-plugins.mdcore-plugin-system.mdindex.mdios-config.mdios-plugins.mdmod-system.mdutilities.md
tile.json

mod-system.mddocs/

Mod System

The mod system provides controlled file modification capabilities for native project files. It handles file reading/writing, lifecycle management, introspection mode, and provides a structured way to modify Android and iOS project files through typed interfaces.

Core Mod Functions

withMod

Apply a modifier to a specific platform and mod type.

function withMod<T>(
  config: ExportedConfig,
  options: {
    platform: ModPlatform;
    mod: string;
    action: Mod<T>;
  }
): ExportedConfig;

Parameters:

  • config: The Expo configuration object
  • options.platform: Target platform ('android' | 'ios')
  • options.mod: Name of the mod to apply (e.g., 'manifest', 'infoPlist')
  • options.action: Modification function to execute

Usage:

config = withMod(config, {
  platform: "android",
  mod: "manifest",
  action: async (config) => {
    const manifest = config.modResults;
    // Modify manifest
    return config;
  }
});

withBaseMod

Apply a base mod with provider functionality for file access.

function withBaseMod<T>(
  config: ExpoConfig,
  options: {
    platform: ModPlatform;
    mod: string;
    action: Mod<T>;
    isProvider?: boolean;
    isIntrospective?: boolean;
  }
): ExpoConfig;

Parameters:

  • config: The Expo configuration object
  • options.platform: Target platform
  • options.mod: Mod name
  • options.action: Modification function
  • options.isProvider: Whether this mod provides data to other mods
  • options.isIntrospective: Whether this mod supports introspection mode

Lifecycle Management

withDangerousMod

Apply a dangerous mod that runs before all other platform mods.

function withDangerousMod(
  config: ExpoConfig,
  platform: ModPlatform,
  action: Mod<unknown>
): ExpoConfig;

Usage:

config = withDangerousMod(config, "android", async (config) => {
  // Dangerous operations before other mods
  return config;
});

withFinalizedMod

Apply a finalized mod that runs after all other platform mods.

function withFinalizedMod(
  config: ExpoConfig,
  platform: ModPlatform, 
  action: Mod<unknown>
): ExpoConfig;

Mod Compilation

compileModsAsync

Compile and execute all mods for the given configuration.

function compileModsAsync(
  config: ExportedConfigWithProps,
  options: {
    projectRoot: string;
    platforms: ModPlatform[];
    introspect?: boolean;
    assertMissingModProviders?: boolean;
  }
): Promise<ExportedConfigWithProps>;

Parameters:

  • config: Configuration with mods to compile
  • options.projectRoot: Root directory of the project
  • options.platforms: Platforms to compile mods for
  • options.introspect: Whether to run in introspection mode
  • options.assertMissingModProviders: Whether to assert on missing providers

evalModsAsync

Evaluate mods in introspection mode without making file system changes.

function evalModsAsync(
  config: ExportedConfigWithProps,
  options: {
    projectRoot: string;
    platforms: ModPlatform[];
    introspect: true;
  }
): Promise<ExportedConfigWithProps>;

Base Mods System

withDefaultBaseMods

Apply default base mods for specified platforms.

function withDefaultBaseMods(
  config: ExpoConfig,
  options?: {
    platforms?: ModPlatform[];
    skipEmptyMods?: boolean;
  }
): ExpoConfig;

BaseMods

Object containing base mod utilities and providers.

const BaseMods: {
  withGeneratedBaseMods: typeof withGeneratedBaseMods;
  provider: typeof provider;
  withAndroidBaseMods: typeof withAndroidBaseMods;
  getAndroidModFileProviders: typeof getAndroidModFileProviders;
  withIosBaseMods: typeof withIosBaseMods;
  getIosModFileProviders: typeof getIosModFileProviders;
};

Mod Types and Interfaces

type Mod<Props = any> = ((
  config: ExportedConfigWithProps<Props>
) => OptionalPromise<ExportedConfigWithProps<Props>>) & {
  isProvider?: boolean;
  isIntrospective?: boolean;
};

interface ModProps<T = any> {
  readonly projectRoot: string;
  readonly platformProjectRoot: string;
  readonly modName: string;
  readonly platform: ModPlatform;
  readonly introspect: boolean;
  readonly projectName?: string;
  readonly ignoreExistingNativeFiles?: boolean;
  nextMod?: Mod<T>;
}

interface ExportedConfigWithProps<Data = any> extends ExpoConfig {
  modResults: Data;
  modRequest: ModProps<Data>;
  readonly modRawConfig: ExpoConfig;
  mods?: ModConfig | null;
}

interface ModConfig {
  android?: AndroidModConfig;
  ios?: IOSModConfig;
}

interface AndroidModConfig {
  dangerous?: Mod<unknown>;
  finalized?: Mod<unknown>;
  manifest?: Mod<AndroidManifest>;
  strings?: Mod<ResourceXML>;
  colors?: Mod<ResourceXML>;
  colorsNight?: Mod<ResourceXML>;
  styles?: Mod<ResourceXML>;
  mainActivity?: Mod<ApplicationProjectFile>;
  mainApplication?: Mod<ApplicationProjectFile>;
  appBuildGradle?: Mod<GradleProjectFile>;
  projectBuildGradle?: Mod<GradleProjectFile>;
  settingsGradle?: Mod<GradleProjectFile>;
  gradleProperties?: Mod<Properties.PropertiesItem[]>;
}

interface IOSModConfig {
  dangerous?: Mod<unknown>;
  finalized?: Mod<unknown>;
  infoPlist?: Mod<InfoPlist>;
  entitlements?: Mod<Plist>;
  expoPlist?: Mod<Plist>;
  xcodeproj?: Mod<XcodeProject>;
  appDelegate?: Mod<AppDelegateProjectFile>;
  podfileProperties?: Mod<Record<string, string>>;
}

Usage Examples

Custom File Modification

import { withMod } from "@expo/config-plugins";

const withCustomMod = (config) => {
  return withMod(config, "android", "manifest", async (config) => {
    const manifest = config.modResults;
    
    // Modify the Android manifest
    manifest.application[0].$["android:theme"] = "@style/CustomTheme";
    
    return config;
  });
};

Introspective Mod

const withIntrospectiveMod = (config) => {
  const introspectiveMod: Mod<InfoPlist> = async (config) => {
    if (config.modRequest.introspect) {
      // Only read/analyze, don't modify
      console.log("Current bundle ID:", config.modResults.CFBundleIdentifier);
      return config;
    }
    
    // Normal modification
    config.modResults.CFBundleIdentifier = "com.example.newid";
    return config;
  };
  
  introspectiveMod.isIntrospective = true;
  
  return withMod(config, "ios", "infoPlist", introspectiveMod);
};

Provider Mod

const withProviderMod = (config) => {
  const providerMod: Mod<any> = async (config) => {
    // Provide data to downstream mods
    config.modResults = {
      customData: "shared-value",
      timestamp: Date.now()
    };
    return config;
  };
  
  providerMod.isProvider = true;
  
  return withBaseMod(config, {
    platform: "android",
    mod: "customProvider",
    action: providerMod,
    isProvider: true
  });
};