CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-cpy

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

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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)}%`);
    }
  });
}
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/cpy@12.0.x
Publish Source
CLI
Badge
tessl/npm-cpy badge