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

utilities.mddocs/

Utilities

Essential utility modules for XML parsing, code generation, warning management, history tracking, and other common operations used throughout the @expo/config-plugins ecosystem.

XML Processing

XML

Utilities for parsing, manipulating, and writing XML files with proper error handling.

namespace XML {
  function readXMLAsync(options: {
    path: string;
    fallback?: string | null;
  }): Promise<XMLObject>;
  
  function writeXMLAsync(options: {
    path: string;
    xml: any;
  }): Promise<void>;
}

interface XMLObject {
  [key: string]: XMLValue | undefined;
}

type XMLValue = boolean | number | string | null | XMLArray | XMLObject;
interface XMLArray extends Array<XMLValue> {}

Usage:

// Read XML file
const manifestXml = await XML.readXMLAsync({
  path: "path/to/AndroidManifest.xml"
});

// Write XML file
await XML.writeXMLAsync({
  path: "path/to/output.xml",
  xml: xmlObject
});

Code Generation

CodeGenerator

Utilities for merging generated content with existing files using tagged sections.

namespace CodeGenerator {
  function mergeContents(options: MergeContentsOptions): MergeResults;
  function removeContents(src: string, tag: string): string;
  function removeGeneratedContents(src: string, tag: string): string;
  
  interface MergeContentsOptions {
    src: string;
    newSrc: string;
    tag: string;
    anchor?: RegExp;
    offset?: number;
    comment?: string;
  }
  
  interface MergeResults {
    contents: string;
    didClear: boolean;
    didMerge: boolean;
  }
}

Usage:

// Merge generated code into existing file
const result = CodeGenerator.mergeContents({
  src: existingFileContents,
  newSrc: generatedCode,
  tag: "expo-config-plugin",
  anchor: /\/\* GENERATED_SECTION_START \*\//,
  comment: "// Generated by @expo/config-plugins"
});

if (result.didMerge) {
  await fs.writeFile(filePath, result.contents);
}

Warning Management

WarningAggregator

Utilities for collecting and reporting warnings during plugin execution.

namespace WarningAggregator {
  function addWarningIOS(property: string, message: string): void;
  function addWarningAndroid(property: string, message: string): void;
  function flushWarningsAsync(): Promise<void>;
}

Usage:

// Add platform-specific warnings
WarningAggregator.addWarningIOS(
  "bundleIdentifier",
  "Bundle identifier conflict detected in Info.plist"
);

WarningAggregator.addWarningAndroid(
  "permissions",
  "Duplicate permission found in AndroidManifest.xml"
);

// Flush all warnings to console
await WarningAggregator.flushWarningsAsync();

History Tracking

History

Utilities for tracking plugin execution history and preventing duplicate operations.

namespace History {
  function getHistoryItem(config: ExpoConfig, name: string): any;
  function addHistoryItem(config: ExpoConfig, item: HistoryItem): ExpoConfig;
  
  interface HistoryItem {
    name: string;
    version?: string;
    data?: any;
  }
}

Usage:

// Check if plugin has already run
const historyItem = History.getHistoryItem(config, "withCustomPlugin");
if (historyItem) {
  console.log("Plugin already executed with version:", historyItem.version);
  return config;
}

// Add plugin to history
config = History.addHistoryItem(config, {
  name: "withCustomPlugin",
  version: "1.0.0",
  data: { customOption: true }
});

Object Manipulation

obj

Utilities for safe object property access and manipulation.

namespace obj {
  function get(object: any, path: string, defaultValue?: any): any;
  function set(object: any, path: string, value: any): any;
}

Usage:

// Safe property access
const bundleId = obj.get(config, "ios.bundleIdentifier", "com.example.default");

// Set nested property
obj.set(config, "ios.infoPlist.CustomKey", "CustomValue");

Build Properties

BuildProperties.types

Type definitions for iOS and Android build properties configuration.

interface PluginConfigTypeiOS {
  deploymentTarget?: string;
  useFrameworks?: "static" | "dynamic";
  ccache?: boolean;
  flipper?: boolean;
  proguardMinifyEnabled?: boolean;
  shrinkResources?: boolean;
  packagingOptions?: {
    pickFirst?: string[];
    exclude?: string[];
    merge?: string[];
    doNotStrip?: string[];
  };
}

interface PluginConfigTypeAndroid {
  compileSdkVersion?: number;
  targetSdkVersion?: number;
  buildToolsVersion?: string;
  minSdkVersion?: number;
  proguardMinifyEnabled?: boolean;
  shrinkResources?: boolean;
  packagingOptions?: {
    pickFirst?: string[];
    exclude?: string[];
    merge?: string[];
    doNotStrip?: string[];
  };
}

File System Utilities

fs

Enhanced file system utilities with better error handling.

namespace fs {
  function fileExists(filePath: string): boolean;
  function directoryExists(dirPath: string): boolean;
  function ensureDir(dirPath: string): Promise<void>;
}

Module Resolution

modules

Utilities for resolving and loading plugin modules.

namespace modules {
  function resolveModule(moduleId: string, fromDir: string): string;
  function requireModule(modulePath: string): any;
}

Plugin Resolution

plugin-resolver

Advanced plugin resolution and loading utilities.

namespace pluginResolver {
  function resolveConfigPluginFunction(
    projectRoot: string,
    pluginReference: string | ConfigPlugin
  ): ConfigPlugin;
  
  function resolveConfigPluginFunctionWithInfo(
    projectRoot: string,
    pluginReference: any
  ): {
    plugin: ConfigPlugin;
    pluginFile: string;
    pluginReference: string;
  };
}

Error Handling

errors

Custom error classes and error handling utilities.

class PluginError extends Error {
  constructor(message: string, cause?: string);
}

function createPluginError(message: string, cause?: string): PluginError;

Text Processing

matchBrackets

Utilities for parsing and matching brackets in code.

function matchBrackets(input: string, startIndex: number): number | null;
function findMatchingBracket(
  input: string,
  openBracket: string,
  closeBracket: string,
  startIndex: number
): number | null;

commonCodeMod

Common code modification utilities for source file manipulation.

function insertContentsAtOffset(
  contents: string,
  insertion: string,
  offset: number
): string;

function removeContentsAtOffset(
  contents: string,
  removalLength: number,
  offset: number
): string;

Internationalization

locales

Utilities for handling localization and internationalization.

function getLocales(projectRoot: string): string[];
function createLocaleDir(projectRoot: string, locale: string): Promise<void>;

File Globbing

glob

Enhanced file globbing utilities.

function glob(pattern: string, options?: GlobOptions): Promise<string[]>;
function globSync(pattern: string, options?: GlobOptions): string[];

interface GlobOptions {
  cwd?: string;
  ignore?: string[];
  absolute?: boolean;
}

Object Sorting

sortObject

Utilities for consistent object key sorting.

function sortObject<T extends Record<string, any>>(obj: T): T;
function sortObjectKeys(obj: any, sortKeys?: string[]): any;

Usage Examples

Complete Utility Integration

import { 
  XML, 
  CodeGenerator, 
  WarningAggregator, 
  History, 
  PluginError 
} from "@expo/config-plugins";

async function processConfigFile(
  projectRoot: string,
  configPath: string,
  newContent: string
) {
  try {
    // Check execution history
    const historyItem = History.getHistoryItem(config, "processConfigFile");
    if (historyItem) {
      WarningAggregator.addWarningIOS(
        "processConfigFile",
        "Configuration file already processed"
      );
      return config;
    }

    // Read and parse XML
    const existingXml = await XML.readXMLAsync(configPath);
    
    // Merge new content
    const mergeResult = CodeGenerator.mergeContents({
      src: JSON.stringify(existingXml, null, 2),
      newSrc: newContent,
      tag: "auto-generated-config",
      comment: "// Auto-generated configuration"
    });

    if (mergeResult.didMerge) {
      const updatedXml = JSON.parse(mergeResult.contents);
      await XML.writeXMLAsync(configPath, updatedXml);
      
      // Add to history
      config = History.addHistoryItem(config, {
        name: "processConfigFile",
        version: "1.0.0",
        data: { configPath, contentLength: newContent.length }
      });
    }

    // Flush any warnings
    await WarningAggregator.flushWarningsAsync();
    
    return config;
  } catch (error) {
    throw new PluginError(
      "Failed to process configuration file",
      error.message
    );
  }
}

Advanced Code Generation

async function updateSourceFile(
  filePath: string,
  newImports: string[],
  newCode: string
) {
  const existingContents = await fs.readFile(filePath, "utf8");
  
  // Add imports
  let updatedContents = existingContents;
  newImports.forEach(importStatement => {
    if (!updatedContents.includes(importStatement)) {
      updatedContents = `${importStatement}\n${updatedContents}`;
    }
  });
  
  // Merge generated code
  const mergeResult = CodeGenerator.mergeContents({
    src: updatedContents,
    newSrc: newCode,
    tag: "expo-plugin-generated",
    anchor: /\/\/ PLUGIN_ANCHOR_POINT/,
    offset: 1,
    comment: "// Generated by Expo config plugin"
  });
  
  if (mergeResult.didMerge || mergeResult.didClear) {
    await fs.writeFile(filePath, mergeResult.contents);
    console.log(`Updated ${filePath}`);
  }
}