or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-cpy

Copy files with glob pattern support, progress reporting, and advanced file operations

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/cpy@12.0.x

To install, run

npx @tessl/cli install tessl/npm-cpy@12.0.0

index.mddocs/

Cpy

Cpy is a high-performance Node.js file copying utility that supports glob patterns, directory traversal, and advanced file operations. It provides fast file copying through cloning mechanisms when possible, resilient operations using graceful-fs, and comprehensive copying options including progress reporting, file filtering, and concurrent operations.

Package Information

  • Package Name: cpy
  • Package Type: npm
  • Language: JavaScript (ES modules)
  • Installation: npm install cpy
  • Node.js: Requires Node.js 20+

Core Imports

import cpy from "cpy";

For CommonJS:

const cpy = require("cpy");

With TypeScript types:

import cpy, { type Options, type Entry, type ProgressData, type ProgressEmitter } from "cpy";

Basic Usage

import cpy from "cpy";

// Copy single file
await cpy("source.txt", "destination");

// Copy multiple files with glob patterns
await cpy([
  "source/*.png", // Copy all .png files
  "!source/goat.png", // Ignore goat.png
], "destination");

// Copy with options
await cpy("src/**/*.js", "destination", { 
  flat: true,
  onProgress: progress => {
    console.log(`Progress: ${Math.round(progress.percent * 100)}%`);
  }
});

console.log("Files copied!");

Architecture

Cpy is built around several key components:

  • Main Function: Single export function cpy() that handles all file copying operations
  • Glob Support: Full glob pattern support with negation patterns for flexible file selection
  • Progress System: Built-in progress reporting with both callback and event-based patterns
  • Entry Objects: Rich file metadata objects with path information and properties
  • Error Handling: Custom CpyError class for detailed error reporting
  • Concurrency: Configurable concurrent operations for optimal performance

Capabilities

File Copying

Copy files and directories with comprehensive options and glob pattern support.

/**
 * Copy files from source to destination with optional configuration
 * @param source - Files to copy (string or array of strings/globs)
 * @param destination - Destination directory
 * @param options - Copy configuration options
 * @returns Promise resolving to array of destination file paths, with progress event emitter
 */
function cpy(
  source: string | readonly string[],
  destination: string,
  options?: Options
): Promise<string[]> & ProgressEmitter;

Usage Examples:

// Copy node_modules to destination/node_modules
await cpy("node_modules", "destination");

// Copy node_modules content to destination
await cpy("node_modules/**", "destination");

// Copy specific file types with exclusions
await cpy([
  "src/**/*.{js,ts}",
  "!src/**/*.test.{js,ts}",
  "!src/temp/**"
], "build");

// Copy with file renaming
await cpy("*.txt", "destination", {
  rename: basename => `backup-${basename}`
});

// Advanced filtering
await cpy("**/*", "destination", {
  filter: file => !file.name.startsWith('.') && file.extension !== 'tmp'
});

Progress Reporting

Monitor copy operations with detailed progress information.

interface ProgressData {
  /** Number of files copied so far */
  completedFiles: number;
  /** Total number of files to copy */
  totalFiles: number;
  /** Number of bytes copied so far */
  completedSize: number;
  /** Progress percentage as a value between 0 and 1 */
  percent: number;
  /** Absolute source path of the current file being copied */
  sourcePath: string;
  /** Absolute destination path of the current file being copied */
  destinationPath: string;
}

Usage Examples:

// Using onProgress callback (recommended)
await cpy("source/**", "destination", {
  onProgress: progress => {
    console.log(`${progress.completedFiles}/${progress.totalFiles} files`);
    console.log(`${Math.round(progress.percent * 100)}% complete`);
    console.log(`Current: ${progress.sourcePath}`);
  }
});

// Using event emitter (deprecated)
await cpy("source/**", "destination").on("progress", progress => {
  console.log(`Progress: ${Math.round(progress.percent * 100)}%`);
});

File Filtering

Filter files during copying with custom predicates.

interface Entry {
  /** Resolved absolute path to the file */
  readonly path: string;
  /** Relative path to the file from cwd */
  readonly relativePath: string;
  /** Filename with extension */
  readonly name: string;
  /** Filename without extension */
  readonly nameWithoutExtension: string;
  /** File extension without dot */
  readonly extension: string;
}

Usage Examples:

// Filter by file extension
await cpy("src/**/*", "destination", {
  filter: file => file.extension === "js" || file.extension === "ts"
});

// Filter by file size or other criteria
await cpy("**/*", "destination", {
  filter: async file => {
    const stats = await fs.stat(file.path);
    return stats.size < 1000000; // Files smaller than 1MB
  }
});

// Complex filtering logic
await cpy("**/*", "destination", {
  filter: file => {
    // Skip hidden files
    if (file.name.startsWith(".")) return false;
    // Skip backup files
    if (file.name.endsWith(".bak")) return false;
    // Only include specific directories
    return file.relativePath.includes("src/") || file.relativePath.includes("lib/");
  }
});

Configuration Options

interface Options {
  /** Working directory to find source files (default: process.cwd()) */
  readonly cwd?: string;
  
  /** Flatten directory tree - put all files in same directory (default: false) */
  readonly flat?: boolean;
  
  /** Filename or function returning filename to rename every file */
  readonly rename?: string | ((basename: string) => string);
  
  /** Number of files being copied concurrently (default: os.availableParallelism()) */
  readonly concurrency?: number;
  
  /** Ignore junk files (default: true) */
  readonly ignoreJunk?: boolean;
  
  /** Function to filter files to copy */
  readonly filter?: (file: Entry) => boolean | Promise<boolean>;
  
  /** Progress callback function */
  readonly onProgress?: (progress: ProgressData) => void;
  
  /** Overwrite existing files (default: true) */
  readonly overwrite?: boolean;
}

// Note: Options interface extends GlobOptions from 'globby' package and CopyFileOptions from 'copy-file' package,
// providing additional options for glob pattern matching and file copying behavior.

Usage Examples:

// Flatten directory structure
await cpy("src/**/*.js", "destination", { flat: true });

// Set working directory
await cpy("*.js", "destination", { cwd: "/path/to/source" });

// Control concurrency
await cpy("**/*", "destination", { concurrency: 2 });

// Preserve junk files
await cpy("**/*", "destination", { ignoreJunk: false });

// Prevent overwriting existing files
await cpy("*.js", "destination", { overwrite: false });

// Rename during copy
await cpy("*.js", "destination", {
  rename: basename => `processed-${basename}`
});

// Function-based renaming
await cpy("**/*.js", "destination", {
  rename: basename => {
    const name = basename.replace(".js", "");
    return `${name}.processed.js`;
  }
});

Progress Emitter Interface

interface ProgressEmitter {
  /**
   * @deprecated Use onProgress option instead
   * Add progress event listener to the copy operation
   */
  on(
    event: "progress",
    handler: (progress: ProgressData) => void
  ): Promise<string[]>;
}

Error Handling

Cpy uses a custom error class for detailed error reporting. The CpyError class is not directly exportable but can be identified by checking the error name:

// CpyError class (internal, not directly importable)
class CpyError extends Error {
  name: "CpyError";
  cause?: Error;
}

Common Error Scenarios:

try {
  await cpy("nonexistent.txt", "destination");
} catch (error) {
  if (error.name === "CpyError") {
    console.log("Cpy error:", error.message);
    if (error.cause) {
      console.log("Caused by:", error.cause.message);
    }
  }
}

// Errors thrown for:
// - Missing source or destination parameters
// - Non-existent files (when not using globs)
// - Self-copy attempts
// - Permission errors
// - Invalid rename functions
// - File system errors

Advanced Usage Patterns

Multiple Source Patterns

// Mix of files, directories, and globs
await cpy([
  "readme.md",           // Single file
  "src/**/*.js",         // Glob pattern
  "assets/",             // Directory
  "!assets/temp/**",     // Negation pattern
], "destination");

Directory Copying Behavior

// Copy directory structure
await cpy("src", "destination");        // → destination/src/
await cpy("src/**", "destination");     // → destination/ (contents only)

// Glob vs explicit behavior differences
await cpy("src/*.js", "destination");   // Preserves relative to glob parent
await cpy("src/file.js", "destination"); // Preserves relative to cwd

Complex Workflows

// Multi-step copy with progress tracking
let totalFiles = 0;
let copiedFiles = 0;

const copyTasks = [
  { src: "src/**/*.js", dest: "build/js" },
  { src: "assets/**/*", dest: "build/assets" },
  { src: "docs/**/*.md", dest: "build/docs" }
];

for (const task of copyTasks) {
  await cpy(task.src, task.dest, {
    onProgress: progress => {
      console.log(`${task.src}: ${Math.round(progress.percent * 100)}%`);
    }
  });
}