{"id":11918,"library":"recursive-copy","title":"Recursive File Copy Utility","description":"recursive-copy is a robust and flexible utility for copying files and directories within a Node.js environment. It is currently stable at version 2.0.14 and appears to be actively maintained, with a focus on reliability and advanced use cases for filesystem operations. Key features include recursive directory copying, sophisticated filtering using functions, regular expressions, or globs, dynamic file renaming, and stream-based content transformation. It differentiates itself by integrating with `graceful-fs` and `mkdirp` to handle common filesystem errors, automatically filters out OS junk files by default, and provides an event-driven interface alongside traditional callback and promise-based APIs. This makes it suitable for complex build processes or file manipulation tasks where fine-grained control and error handling are crucial, offering a more feature-rich alternative to basic `fs.copyFile` or `fs.cp` methods.","status":"active","version":"2.0.14","language":"javascript","source_language":"en","source_url":"https://github.com/timkendrick/recursive-copy","tags":["javascript","copy","recursive","file","directory","folder","symlink","fs","rename","typescript"],"install":[{"cmd":"npm install recursive-copy","lang":"bash","label":"npm"},{"cmd":"yarn add recursive-copy","lang":"bash","label":"yarn"},{"cmd":"pnpm add recursive-copy","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Used internally to filter out OS-specific junk files (e.g., .DS_Store, Thumbs.db) by default.","package":"junk","optional":false},{"reason":"Employed to enhance file system robustness and handle common errors gracefully, especially on busy systems.","package":"graceful-fs","optional":false},{"reason":"Used for creating destination directories recursively, ensuring parent directories exist before files are copied.","package":"mkdirp","optional":false},{"reason":"A dependency for stream-based transformations, allowing for efficient content manipulation during copy.","package":"through2","optional":false}],"imports":[{"note":"The primary export is a default export, even though the variable name 'copy' is common. For CommonJS, use `const copy = require('recursive-copy');`.","wrong":"import { copy } from 'recursive-copy';","symbol":"copy","correct":"import copy from 'recursive-copy';"},{"note":"Events are exposed as properties on the default `copy` function, not as a separate named export. Access them via `copy.events`.","wrong":"import { events } from 'recursive-copy';","symbol":"copy.events","correct":"import copy from 'recursive-copy';\nconst { COPY_FILE_START, ERROR } = copy.events;"},{"note":"For TypeScript users, the `Options` interface can be imported as a named type for stricter type checking when configuring copy operations.","symbol":"Options","correct":"import copy, { type Options } from 'recursive-copy';"}],"quickstart":{"code":"import copy from 'recursive-copy';\nimport path from 'path';\nimport fs from 'fs';\n\nconst sourceDir = path.join(process.cwd(), 'temp_src');\nconst destDir = path.join(process.cwd(), 'temp_dest');\n\n// Create dummy source files for demonstration\nfs.mkdirSync(sourceDir, { recursive: true });\nfs.writeFileSync(path.join(sourceDir, 'file1.txt'), 'Hello world!');\nfs.writeFileSync(path.join(sourceDir, '.dotfile'), 'Hidden content.');\nfs.mkdirSync(path.join(sourceDir, 'subdir'), { recursive: true });\nfs.writeFileSync(path.join(sourceDir, 'subdir', 'file2.js'), 'console.log(\"JS file\");');\n\nasync function runCopyExample() {\n  try {\n    console.log(`Copying from ${sourceDir} to ${destDir}...`);\n    const results = await copy(sourceDir, destDir, {\n      overwrite: true, // Overwrite if destination files exist\n      dot: true,     // Copy dotfiles\n      filter: ['**/*', '!*.log'] // Copy all files except .log files\n    });\n    console.info(`Copied ${results.length} files successfully.`);\n    results.forEach(op => console.log(`  - ${op.src} -> ${op.dest}`));\n  } catch (error) {\n    console.error('Copy failed: ' + error.message);\n  } finally {\n    // Clean up temporary directories\n    fs.rmSync(sourceDir, { recursive: true, force: true });\n    fs.rmSync(destDir, { recursive: true, force: true });\n    console.log('Temporary directories cleaned up.');\n  }\n}\n\nrunCopyExample();","lang":"typescript","description":"This quickstart demonstrates how to recursively copy a directory, including dotfiles, with specific filters, using the promise-based `async/await` syntax. It also includes cleanup."},"warnings":[{"fix":"Set the `overwrite: true` option in the `options` object when calling `copy()` if existing files should be replaced.","message":"By default, `recursive-copy` will NOT overwrite existing files in the destination. If a file with the same name exists at the destination, the copy operation for that specific file will be skipped without an error, unless `overwrite: true` is specified.","severity":"gotcha","affected_versions":">=2.0.0"},{"fix":"To include dotfiles in the copy operation, set the `dot: true` option in the `options` object.","message":"Files starting with a dot (e.g., `.env`, `.gitkeep`, `.DS_Store`) are not copied by default. This can lead to unexpected omissions if not explicitly configured.","severity":"gotcha","affected_versions":">=2.0.0"},{"fix":"Set `expand: true` in the `options` object to make `recursive-copy` resolve and copy the actual content pointed to by symbolic links, rather than the links themselves.","message":"Symbolic links are copied as symbolic links by default (`expand: false`). If you intend to copy the *content* of the files or directories that symlinks point to, this behavior must be changed.","severity":"gotcha","affected_versions":">=2.0.0"},{"fix":"Thoroughly test your `filter` options on a small dataset. For glob patterns, ensure they correctly reflect paths relative to your `src` directory. Consider using a `filter` function for complex logic to gain explicit control and debuggability.","message":"Incorrectly configured `filter` options (regex, glob, or function) can lead to files being unexpectedly included or excluded. Glob patterns are relative to the `src` directory, which can be a common source of error.","severity":"gotcha","affected_versions":">=2.0.0"},{"fix":"Ensure the Node.js process has appropriate read permissions for the source path and write permissions for the destination path. This may involve running the application with elevated privileges or adjusting directory permissions on the operating system.","message":"File system permissions errors (EPERM) are common when the Node.js process lacks the necessary write privileges for the destination directory, or read privileges for the source. While `graceful-fs` helps, it cannot circumvent OS-level permission restrictions.","severity":"gotcha","affected_versions":">=2.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure the process has write access to the `dest` directory and its parent folders. This might require changing directory permissions (`chmod`) or running the application with administrative privileges.","cause":"The Node.js process does not have sufficient write permissions to create or modify files in the specified destination directory.","error":"EPERM: operation not permitted, open 'destination/path/file.txt'"},{"fix":"Verify that the `src` argument points to an actual, existing file or directory on the file system and that the Node.js process has read access to it.","cause":"The source path (`src`) provided to `copy()` does not exist or is inaccessible.","error":"ENOENT: no such file or directory, stat 'source/path/non-existent-file.txt'"},{"fix":"Review your `transform` function. Ensure it always emits `Buffer` or `string` data for file content, and correctly handles `done(null, chunk)` or `done(null, null)` for the end of a stream.","cause":"This error typically occurs within a custom `transform` stream if it attempts to pass an invalid or `undefined` chunk to the next stream stage, or doesn't properly handle `null` (end-of-stream) chunks.","error":"TypeError: Expected a 'Buffer' or 'string', but got undefined"}],"ecosystem":"npm"}