@11ty/recursive-copy

中文文档
5.2.0发布于 2026/11/03ISCTypeScriptCommonJS构建来源已验证近 30 天下载 68.5万

A fork of recursive-copy: Simple, flexible file copy utility

原始使用文档
文档目录8 节
  1. Features
  2. Examples
  3. Usage
  4. copy(src, dest, [options], [callback])
  5. copy.shouldCopy(filePath, [options])
  6. Events
  7. ErrorInfo
  8. CopyOperation

@11ty/recursive-copy

A temporary fork of timkendrick/recursive-copy to satisfy https://github.com/11ty/eleventy/issues/3299 as Eleventy slowly moves to use Node native API fs.cp.

  • v5.x requires Node 20+
  • v4.x requires Node 18+
  • v3.x requires Node 0.10+
  • v2.x requires Node 0.10+

Simple, flexible file copy utility

Features

  • Recursively copy whole directory hierarchies
  • Choose which files are copied by passing a filter function, regular expression or glob
  • Rename files dynamically, including changing the output path
  • Transform file contents using streams
  • Choose whether to overwrite existing files
  • Choose whether to copy system files
  • Filters out junk files by default
  • Emits start, finish and error events for each file that is processed
  • Optional promise-based interface

Examples

Node-style callback interface

var copy = require('@11ty/recursive-copy');

copy('src', 'dest', function(error, results) {
    if (error) {
        console.error('Copy failed: ' + error);
    } else {
        console.info('Copied ' + results.length + ' files');
    }
});

Promise interface

var copy = require('@11ty/recursive-copy');

copy('src', 'dest')
    .then(function(results) {
        console.info('Copied ' + results.length + ' files');
    })
    .catch(function(error) {
        console.error('Copy failed: ' + error);
    });

ES2015+ usage

import copy from '@11ty/recursive-copy';

try {
    const results = await copy('src', 'dest');
    console.info('Copied ' + results.length + ' files');
} catch (error) {
    console.error('Copy failed: ' + error);
}

Advanced options

var copy = require('@11ty/recursive-copy');

var path = require('path');
var through = require('through2');

var options = {
    overwrite: true,
    expand: true,
    dot: true,
    junk: true,
    filter: [
        '**/*',
        '!.htpasswd'
    ],
    rename: function(filePath) {
        return filePath + '.orig';
    },
    transform: function(src, dest, stats) {
        if (path.extname(src) !== '.txt') { return null; }
        return through(function(chunk, enc, done)  {
            var output = chunk.toString().toUpperCase();
            done(null, output);
        });
    }
};

copy('src', 'dest', options)
    .on(copy.events.COPY_FILE_START, function(copyOperation) {
        console.info('Copying file ' + copyOperation.src + '...');
    })
    .on(copy.events.COPY_FILE_COMPLETE, function(copyOperation) {
        console.info('Copied to ' + copyOperation.dest);
    })
    .on(copy.events.ERROR, function(error, copyOperation) {
        console.error('Unable to copy ' + copyOperation.dest);
    })
    .then(function(results) {
        console.info(results.length + ' file(s) copied');
    })
    .catch(function(error) {
        return console.error('Copy failed: ' + error);
    });

Usage

copy(src, dest, [options], [callback])

Recursively copy files and folders from src to dest

Arguments:

NameTypeRequiredDefaultDescription
srcstringYesN/ASource file/folder path
deststringYesN/ADestination file/folder path
options.overwritebooleanNofalseWhether to overwrite destination files
options.expandbooleanNofalseWhether to expand symbolic links
options.dotbooleanNofalseWhether to copy files beginning with a .
options.junkbooleanNofalseWhether to copy OS junk files (e.g. .DS_Store, Thumbs.db)
options.filterfunction, RegExp, string, arrayNonullFilter function / regular expression / glob that determines which files to copy (uses maximatch)
options.renamefunctionNonullFunction that maps source paths to destination paths
options.transformfunctionNonullFunction that returns a transform stream used to modify file contents
options.resultsbooleanNotrueWhether to return an array of copy results
options.concurrencynumberNo255Maximum number of simultaneous copy operations
options.debugbooleanNofalseWhether to log debug information
options.streamsbooleanNofalseWhether to copy file contents with streams instead of fs.copyFile (the previous behavior)
callbackfunctionNonullCallback, invoked on success/failure

Returns:

Promise<Array> Promise, fulfilled with array of copy results:

[
    {
        "src": "/path/to/src",
        "dest": "/path/to/dest",
        "stats": <Stats>
    },
    {
        "src": "/path/to/src/file.txt",
        "dest": "/path/to/dest/file.txt",
        "stats": <Stats>
    },
    {
        "src": "/path/to/src/subfolder",
        "dest": "/path/to/dest/subfolder",
        "stats": <Stats>
    },
    {
        "src": "/path/to/src/subfolder/nested.txt",
        "dest": "/path/to/dest/subfolder/nested.txt",
        "stats": <Stats>
    }
]

copy.shouldCopy(filePath, [options])

Returns whether the dot and junk options keep a file, checked against its basename (filter is not applied).

copy.shouldCopy('src/.DS_Store', { dot: true }); // false
copy.shouldCopy('src/.htaccess', { dot: true }); // true

Events

The value returned by the copy function implements the EventEmitter interface, and emits the following events:

EventHandler signature
copy.events.ERRORfunction(error, ErrorInfo)
copy.events.COMPLETEfunction(Array<CopyOperation>)
copy.events.CREATE_DIRECTORY_STARTfunction(CopyOperation)
copy.events.CREATE_DIRECTORY_ERRORfunction(error, CopyOperation)
copy.events.CREATE_DIRECTORY_COMPLETEfunction(CopyOperation)
copy.events.CREATE_SYMLINK_STARTfunction(CopyOperation)
copy.events.CREATE_SYMLINK_ERRORfunction(error, CopyOperation)
copy.events.CREATE_SYMLINK_COMPLETEfunction(CopyOperation)
copy.events.COPY_FILE_STARTfunction(CopyOperation)
copy.events.COPY_FILE_ERRORfunction(error, CopyOperation)
copy.events.COPY_FILE_COMPLETEfunction(CopyOperation)

...where the types referred to in the handler signature are as follows:

ErrorInfo

PropertyTypeDescription
srcstringSource path of the file/folder/symlink that failed to copy
deststringDestination path of the file/folder/symlink that failed to copy

CopyOperation

PropertyTypeDescription
srcstringSource path of the relevant file/folder/symlink
deststringDestination path of the relevant file/folder/symlink
stats fs.StatsStats for the relevant file/folder/symlink

版本与发布记录

@11ty/recursive-copy 共发布 12 个版本,首次发布于 2024/07/11;过去 12 个月发布了 7 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
5.2.0latest2026/11/033 天前
4.0.62026/11/033 天前
5.1.02026/10/1620 天前
5.0.22026/03/197 个月前
4.0.42026/03/197 个月前
5.0.02025/12/1210 个月前

关于 @11ty/recursive-copy 的常见问题

@11ty/recursive-copy 是什么?

A fork of recursive-copy: Simple, flexible file copy utility

如何安装 @11ty/recursive-copy?

在项目目录运行 npm install @11ty/recursive-copy,或使用 pnpm add / yarn add / bun add。

@11ty/recursive-copy 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@11ty/recursive-copy 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@11ty/recursive-copy 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@11ty/recursive-copy 可以商用吗?

@11ty/recursive-copy 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@11ty/recursive-copy 的下载量有多少?

根据 npm 官方下载统计,@11ty/recursive-copy 在 2026/10/04 – 2026/11/02 这 30 天内被下载 684,678 次,日均约 22,823 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@11ty/recursive-copy 还在维护吗?

最新版本 5.2.0 发布于 2026/11/03(3 天前);至今共发布 12 个版本,其中过去 12 个月发布了 7 个;首次发布于 2024/07/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。