From 87d6fd7e696ee02178a8dc33a51e8e59bdc61d68 Mon Sep 17 00:00:00 2001 From: bcoe Date: Sun, 8 Aug 2021 13:48:01 -0700 Subject: [PATCH] fs: add recursive cp method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces recursive cp method, based on fs-extra implementation. PR-URL: https://github.com/nodejs/node/pull/39372 Fixes: https://github.com/nodejs/node/issues/35880 Refs: https://github.com/nodejs/tooling/issues/98 Reviewed-By: Matteo Collina Reviewed-By: Michaƫl Zasso Reviewed-By: Antoine du Hamel Reviewed-By: James M Snell Reviewed-By: Ian Sutherland --- LICENSE | 19 + doc/api/errors.md | 69 ++ doc/api/fs.md | 91 +++ lib/fs.js | 50 ++ lib/internal/errors.js | 11 + lib/internal/fs/cp/cp-sync.js | 331 ++++++++ lib/internal/fs/cp/cp.js | 384 +++++++++ lib/internal/fs/promises.js | 16 +- lib/internal/fs/utils.js | 27 + test/fixtures/copy/kitchen-sink/README.md | 1 + .../fixtures/copy/kitchen-sink/a/b/README2.md | 1 + test/fixtures/copy/kitchen-sink/a/b/index.js | 3 + .../fixtures/copy/kitchen-sink/a/c/README2.md | 1 + .../copy/kitchen-sink/a/c/d/README3.md | 1 + .../fixtures/copy/kitchen-sink/a/c/d/index.js | 3 + test/fixtures/copy/kitchen-sink/a/c/index.js | 3 + test/fixtures/copy/kitchen-sink/a/index.js | 3 + test/fixtures/copy/kitchen-sink/index.js | 3 + test/parallel/test-fs-cp.mjs | 763 ++++++++++++++++++ tools/license-builder.sh | 3 + 20 files changed, 1782 insertions(+), 1 deletion(-) create mode 100644 lib/internal/fs/cp/cp-sync.js create mode 100644 lib/internal/fs/cp/cp.js create mode 100644 test/fixtures/copy/kitchen-sink/README.md create mode 100644 test/fixtures/copy/kitchen-sink/a/b/README2.md create mode 100644 test/fixtures/copy/kitchen-sink/a/b/index.js create mode 100644 test/fixtures/copy/kitchen-sink/a/c/README2.md create mode 100644 test/fixtures/copy/kitchen-sink/a/c/d/README3.md create mode 100644 test/fixtures/copy/kitchen-sink/a/c/d/index.js create mode 100644 test/fixtures/copy/kitchen-sink/a/c/index.js create mode 100644 test/fixtures/copy/kitchen-sink/a/index.js create mode 100644 test/fixtures/copy/kitchen-sink/index.js create mode 100644 test/parallel/test-fs-cp.mjs diff --git a/LICENSE b/LICENSE index 82a9eafafe50d2..29bd53a3610f89 100644 --- a/LICENSE +++ b/LICENSE @@ -1584,3 +1584,22 @@ The externally maintained libraries used by Node.js are: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + +- node-fs-extra, located at lib/internal/fs/cp, is licensed as follows: + """ + (The MIT License) + + Copyright (c) 2011-2017 JP Richardson + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files + (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ diff --git a/doc/api/errors.md b/doc/api/errors.md index 9b1a556d2b0e0f..67dc1781d0bb1c 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1115,6 +1115,74 @@ added: v14.0.0 Used when a feature that is not available to the current platform which is running Node.js is used. + +### `ERR_FS_CP_DIR_TO_NON_DIR` + + +An attempt was made to copy a directory to a non-directory (file, symlink, +etc.) using [`fs.cp()`][]. + + +### `ERR_FS_CP_EEXIST` + + +An attempt was made to copy over a file that already existed with +[`fs.cp()`][], with the `force` and `errorOnExist` set to `true`. + + +### `ERR_FS_CP_EINVAL` + + +When using [`fs.cp()`][], `src` or `dest` pointed to an invalid path. + + +### `ERR_FS_CP_FIFO_PIPE` + + +An attempt was made to copy a named pipe with [`fs.cp()`][]. + + +### `ERR_FS_CP_NON_DIR_TO_DIR` + + +An attempt was made to copy a non-directory (file, symlink, etc.) to a directory +using [`fs.cp()`][]. + + +### `ERR_FS_CP_SOCKET` + + +An attempt was made to copy to a socket with [`fs.cp()`][]. + + +### `ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY` + + +When using [`fs.cp()`][], a symlink in `dest` pointed to a subdirectory +of `src`. + + +### `ERR_FS_CP_UNKNOWN` + + +An attempt was made to copy to an unknown file type with [`fs.cp()`][]. + ### `ERR_FS_EISDIR` @@ -2822,6 +2890,7 @@ The native call from `process.cpuUsage` could not be processed. [`dgram.remoteAddress()`]: dgram.md#dgram_socket_remoteaddress [`errno`(3) man page]: https://man7.org/linux/man-pages/man3/errno.3.html [`fs.Dir`]: fs.md#fs_class_fs_dir +[`fs.cp()`]: fs.md#fs_fs_cp_src_dest_options_callback [`fs.readFileSync`]: fs.md#fs_fs_readfilesync_path_options [`fs.readdir`]: fs.md#fs_fs_readdir_path_options_callback [`fs.symlink()`]: fs.md#fs_fs_symlink_target_path_type_callback diff --git a/doc/api/fs.md b/doc/api/fs.md index f17f05597533d8..a98a937c411675 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -739,6 +739,37 @@ try { } ``` +### `fsPromises.cp(src, dest[, options])` + + +> Stability: 1 - Experimental + +* `src` {string|URL} source path to copy. +* `dest` {string|URL} destination path to copy to. +* `options` {Object} + * `dereference` {boolean} dereference symlinks. **Default:** `false`. + * `errorOnExist` {boolean} when `force` is `false`, and the destination + exists, throw an error. **Default:** `false`. + * `filter` {Function} Function to filter copied files/directories. Return + `true` to copy the item, `false` to ignore it. Can also return a `Promise` + that resolves to `true` or `false` **Default:** `undefined`. + * `force` {boolean} overwrite existing file or directory. _The copy + operation will ignore errors if you set this to false and the destination + exists. Use the `errorOnExist` option to change this behavior. + **Default:** `true`. + * `preserveTimestamps` {boolean} When `true` timestamps from `src` will + be preserved. **Default:** `false`. + * `recursive` {boolean} copy directories recursively **Default:** `false` +* Returns: {Promise} Fulfills with `undefined` upon success. + +Asynchronously copies the entire directory structure from `src` to `dest`, +including subdirectories and files. + +When copying a directory to another directory, globs are not supported and +behavior is similar to `cp dir1/ dir2/`. + ### `fsPromises.lchmod(path, mode)` + +> Stability: 1 - Experimental + +* `src` {string|URL} source path to copy. +* `dest` {string|URL} destination path to copy to. +* `options` {Object} + * `dereference` {boolean} dereference symlinks. **Default:** `false`. + * `errorOnExist` {boolean} when `force` is `false`, and the destination + exists, throw an error. **Default:** `false`. + * `filter` {Function} Function to filter copied files/directories. Return + `true` to copy the item, `false` to ignore it. Can also return a `Promise` + that resolves to `true` or `false` **Default:** `undefined`. + * `force` {boolean} overwrite existing file or directory. _The copy + operation will ignore errors if you set this to false and the destination + exists. Use the `errorOnExist` option to change this behavior. + **Default:** `true`. + * `preserveTimestamps` {boolean} When `true` timestamps from `src` will + be preserved. **Default:** `false`. + * `recursive` {boolean} copy directories recursively **Default:** `false` +* `callback` {Function} + +Asynchronously copies the entire directory structure from `src` to `dest`, +including subdirectories and files. + +When copying a directory to another directory, globs are not supported and +behavior is similar to `cp dir1/ dir2/`. + ### `fs.createReadStream(path[, options])` + +> Stability: 1 - Experimental + +* `src` {string|URL} source path to copy. +* `dest` {string|URL} destination path to copy to. +* `options` {Object} + * `dereference` {boolean} dereference symlinks. **Default:** `false`. + * `errorOnExist` {boolean} when `force` is `false`, and the destination + exists, throw an error. **Default:** `false`. + * `filter` {Function} Function to filter copied files/directories. Return + `true` to copy the item, `false` to ignore it. **Default:** `undefined` + * `force` {boolean} overwrite existing file or directory. _The copy + operation will ignore errors if you set this to false and the destination + exists. Use the `errorOnExist` option to change this behavior. + **Default:** `true`. + * `preserveTimestamps` {boolean} When `true` timestamps from `src` will + be preserved. **Default:** `false`. + * `recursive` {boolean} copy directories recursively **Default:** `false` + +Synchronously copies the entire directory structure from `src` to `dest`, +including subdirectories and files. + +When copying a directory to another directory, globs are not supported and +behavior is similar to `cp dir1/ dir2/`. + ### `fs.existsSync(path)`