or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-builder.mdcommand-system.mdfilesystem-tools.mdhttp-tools.mdindex.mdpackage-manager-tools.mdpatching-tools.mdprint-tools.mdprompt-tools.mdsemver-tools.mdstring-tools.mdsystem-tools.mdtemplate-tools.md
tile.json

patching-tools.mddocs/

File Patching

Advanced file manipulation utilities for updating, appending, modifying, and patching file contents programmatically with pattern matching and conditional operations.

Capabilities

File Content Checking

Functions for verifying the existence of patterns and content within files.

/**
 * Check if a pattern exists in a file
 * @param filename - Path to file to check
 * @param pattern - String or regex pattern to search for
 * @returns Promise resolving to true if pattern exists
 */
exists(filename: string, pattern: string | RegExp): Promise<boolean>;

Pattern Checking Examples:

import { patching } from "gluegun";

// Check for string patterns
const hasReactImport = await patching.exists("src/App.js", "import React");
const hasApiKey = await patching.exists(".env", "API_KEY=");

// Check for regex patterns
const hasVersion = await patching.exists("package.json", /\"version\":\s*\"[\d\.]+\"/);
const hasExport = await patching.exists("index.js", /^export\s+/m);

if (!hasReactImport) {
  // Add React import
}

File Content Updates

Functions for programmatically updating file contents using callback functions.

/**
 * Update file contents using a callback function
 * @param filename - Path to file to update
 * @param callback - Function that receives current contents and returns new contents
 * @returns Promise resolving to updated contents, object, or false if failed
 */
update(filename: string, callback: (contents: any) => any): Promise<string | object | boolean>;

Content Update Examples:

import { patching } from "gluegun";

// Update JSON file
await patching.update("package.json", (contents) => {
  const pkg = JSON.parse(contents);
  pkg.version = "1.2.0";
  pkg.scripts.deploy = "npm run build && npm publish";
  return JSON.stringify(pkg, null, 2);
});

// Update text file with string manipulation
await patching.update("README.md", (contents) => {
  return contents.replace(/# Old Title/, "# New Title");
});

// Conditional updates
await patching.update("config.js", (contents) => {
  if (!contents.includes("production: true")) {
    return contents.replace(
      "module.exports = {",
      "module.exports = {\n  production: true,"
    );
  }
  return contents;
});

Content Insertion

Functions for adding content to files at the beginning or end.

/**
 * Append content to the end of a file
 * @param filename - Path to target file
 * @param contents - Content to append
 * @returns Promise resolving to new contents or false if failed
 */
append(filename: string, contents: string): Promise<string | boolean>;

/**
 * Prepend content to the beginning of a file
 * @param filename - Path to target file
 * @param contents - Content to prepend
 * @returns Promise resolving to new contents or false if failed
 */
prepend(filename: string, contents: string): Promise<string | boolean>;

Content Insertion Examples:

import { patching } from "gluegun";

// Add import to beginning of file
await patching.prepend("src/App.js", "import './App.css';\n");

// Add export to end of file
await patching.append("src/utils.js", "\nexport { newFunction };");

// Add configuration entry
await patching.append(".gitignore", "\n# Build outputs\ndist/\nbuild/\n");

// Add documentation
await patching.append("README.md", `

## New Feature

This version includes a new feature for handling...
`);

Search and Replace

Simple find-and-replace operations for file contents.

/**
 * Replace all occurrences of a search pattern with replacement text
 * @param filename - Path to target file
 * @param search - String to search for
 * @param replace - Replacement string
 * @returns Promise resolving to new contents or false if failed
 */
replace(filename: string, search: string, replace: string): Promise<string | boolean>;

Search and Replace Examples:

import { patching } from "gluegun";

// Update API endpoints
await patching.replace(
  "src/config.js",
  "api.staging.com",
  "api.production.com"
);

// Update version references
await patching.replace(
  "docs/README.md",
  "version 1.0.0",
  "version 1.1.0"
);

// Replace placeholder values
await patching.replace(
  "template.html",
  "{{APP_NAME}}",
  "My Application"
);

Advanced Patching

Sophisticated patching operations with multiple options and conditional logic.

/**
 * Apply multiple patch operations to a file
 * @param filename - Path to target file
 * @param options - Array of patch operation objects
 * @returns Promise resolving to new contents or false if failed
 */
patch(filename: string, ...options: GluegunPatchingPatchOptions[]): Promise<string | boolean>;

interface GluegunPatchingPatchOptions {
  /** String to insert */
  insert?: string;
  /** Insert before this pattern (string or regex) */
  before?: string | RegExp;
  /** Insert after this pattern (string or regex) */
  after?: string | RegExp;
  /** Replace this pattern (string or regex) */
  replace?: string | RegExp;
  /** Delete this pattern (string or regex) */
  delete?: string | RegExp;
  /** Force operation even if pattern already exists */
  force?: boolean;
}

Force Option Behavior:

The force option controls whether insertions are applied when the content already exists in the file:

  • force: false (default): Skip insertion if the content already exists, preventing duplicates
  • force: true: Always perform the insertion, even if content already exists
// Safe insertion (default behavior) 
await patching.patch("config.js", {
  insert: "const API_URL = 'https://api.example.com';",
  before: /^module\.exports/,
  force: false // Won't duplicate if line already exists
});

// Forced insertion (always inserts)
await patching.patch("config.js", {
  insert: "/* Updated configuration */",
  before: /^module\.exports/,  
  force: true // Will insert even if comment already exists
});

Force Option Use Cases:

// Idempotent setup scripts
await patching.patch("package.json", {
  insert: '"build": "webpack --mode=production",',
  after: /"scripts":\s*{/,
  force: false // Safe for repeated script runs
});

// Version stamping (always update)
await patching.patch("src/version.js", {
  replace: /VERSION = ['"][^'"]*['"]/,
  insert: `VERSION = '${newVersion}'`,
  force: true // Always update version
});

// Development vs Production differences
await patching.patch("config.js", {
  insert: `const DEBUG = ${isDevelopment};`,
  before: /^module\.exports/,
  force: isDevelopment // Force in dev, safe in prod
});

Advanced Patching Examples:

import { patching } from "gluegun";

// Complex package.json patching
await patching.patch("package.json", 
  {
    // Add new script after existing scripts
    insert: '    "deploy": "npm run build && npm publish",',
    after: /"scripts":\s*{/
  },
  {
    // Add new dependency
    insert: '    "axios": "^1.0.0",',
    after: /"dependencies":\s*{/
  },
  {
    // Update existing field
    replace: /"version":\s*"[\d\.]+"/,
    insert: '"version": "2.0.0"'
  }
);

// Modify configuration file with multiple operations
await patching.patch("webpack.config.js",
  {
    // Add import if not present
    insert: "const path = require('path');\n",
    before: /^module\.exports/m,
    force: false // Don't add if already exists
  },
  {
    // Replace output path
    replace: /output:\s*{[^}]*}/,
    insert: `output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash].js'
  }`
  },
  {
    // Delete old configuration
    delete: /\/\*\s*old config\s*\*\/[\s\S]*?\/\*\s*end old config\s*\*\//
  }
);

// Conditional insertion based on file content
await patching.patch("src/App.js",
  {
    insert: "import React from 'react';\n",
    before: /^import/m
  },
  {
    insert: "\nexport default App;",
    after: /^function App\(\)/m,
    force: false
  }
);

Pattern Matching Options

The patching system supports both string and regular expression patterns for flexible content matching.

String Patterns:

  • Exact string matching
  • Case-sensitive by default
  • Useful for simple find-and-replace operations

Regular Expression Patterns:

  • Advanced pattern matching with captures
  • Multiline matching with m flag
  • Global replacement with g flag
  • Case-insensitive matching with i flag

Pattern Examples:

import { patching } from "gluegun";

// String pattern matching
await patching.exists("config.js", "production: true");

// Regex pattern matching
await patching.exists("src/App.js", /^import\s+React/m);
await patching.exists("package.json", /"version":\s*"[\d\.]+"/);

// Complex regex for multiline matching
await patching.patch("server.js",
  {
    replace: /app\.listen\(\d+,\s*\(\)\s*=>\s*{[\s\S]*?}\);/,
    insert: `app.listen(process.env.PORT || 3000, () => {
  console.log('Server started on port', process.env.PORT || 3000);
});`
  }
);

Complete Patching Interface

interface GluegunPatching {
  /**
   * Check if pattern exists in file
   * @param filename - File path
   * @param pattern - Pattern to search for
   * @returns True if pattern exists
   */
  exists(filename: string, pattern: string | RegExp): Promise<boolean>;

  /**
   * Update file using callback function
   * @param filename - File path
   * @param callback - Update function
   * @returns Updated contents or false
   */
  update(filename: string, callback: (contents: any) => any): Promise<string | object | boolean>;

  /**
   * Append content to file
   * @param filename - File path
   * @param contents - Content to append
   * @returns New contents or false
   */
  append(filename: string, contents: string): Promise<string | boolean>;

  /**
   * Prepend content to file
   * @param filename - File path
   * @param contents - Content to prepend
   * @returns New contents or false
   */
  prepend(filename: string, contents: string): Promise<string | boolean>;

  /**
   * Replace pattern in file
   * @param filename - File path
   * @param search - Pattern to find
   * @param replace - Replacement text
   * @returns New contents or false
   */
  replace(filename: string, search: string, replace: string): Promise<string | boolean>;

  /**
   * Apply multiple patch operations
   * @param filename - File path
   * @param options - Patch operations
   * @returns New contents or false
   */
  patch(filename: string, ...options: GluegunPatchingPatchOptions[]): Promise<string | boolean>;
}

interface GluegunPatchingPatchOptions {
  insert?: string;
  before?: string | RegExp;
  after?: string | RegExp;
  replace?: string | RegExp;
  delete?: string | RegExp;
  force?: boolean;
}

Comprehensive Usage Example:

// CLI command for setting up a new project
export = {
  name: "setup",
  description: "Setup project configuration files",
  run: async (toolbox) => {
    const { patching, filesystem, print } = toolbox;
    
    print.info("Setting up project configuration...");
    
    // Update package.json with new scripts and dependencies
    await patching.patch("package.json",
      {
        insert: '    "dev": "webpack serve --mode development",',
        after: /"scripts":\s*{/
      },
      {
        insert: '    "build": "webpack --mode production",',
        after: /"dev":/
      },
      {
        insert: '    "webpack": "^5.0.0",',
        after: /"devDependencies":\s*{/
      }
    );
    
    // Add gitignore entries if not present
    if (!(await patching.exists(".gitignore", "node_modules"))) {
      await patching.append(".gitignore", "\n# Dependencies\nnode_modules/\n");
    }
    
    if (!(await patching.exists(".gitignore", "dist/"))) {
      await patching.append(".gitignore", "\n# Build output\ndist/\nbuild/\n");
    }
    
    // Update existing config file or create new one
    if (filesystem.exists("webpack.config.js")) {
      await patching.patch("webpack.config.js",
        {
          replace: /entry:\s*['"][^'"]*['"]/,
          insert: "entry: './src/index.js'"
        },
        {
          replace: /mode:\s*['"][^'"]*['"]/,
          insert: "mode: process.env.NODE_ENV || 'development'"
        }
      );
    } else {
      filesystem.write("webpack.config.js", `
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  mode: process.env.NODE_ENV || 'development'
};
      `.trim());
    }
    
    print.success("Project configuration completed!");
  }
};