Skip to content

Commit

Permalink
fs: add recursive copy method
Browse files Browse the repository at this point in the history
Introduces recursive copy method, based on fs-extra implementation

Refs: nodejs/tooling#98
  • Loading branch information
bcoe committed Jul 13, 2021
1 parent f65d748 commit 4107b45
Show file tree
Hide file tree
Showing 16 changed files with 1,255 additions and 1 deletion.
25 changes: 25 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -1584,3 +1584,28 @@ 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.
"""

- fs-extra, located at lib/internal/fs/copy, is licensed as follows:
"""
(The MIT License)

Copyright (c) 2011-2021 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.
"""
69 changes: 69 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,74 @@ added: v14.0.0
Used when a feature that is not available
to the current platform which is running Node.js is used.

<a id="ERR_FS_COPY_DIR_TO_NON_DIR"></a>
### `ERR_FS_COPY_DIR_TO_NON_DIR`
<!--
added: REPLACEME
-->

An attempt was made to copy a directory to a non-directory (file, symlink,
etc.) using [`fs.copy()`][].

<a id="ERR_FS_COPY_EEXIST"></a>
### `ERR_FS_COPY_EEXIST`
<!--
added: REPLACEME
-->

An attempt was made to copy over a file that already existed with
[`fs.copy()`][], with the `overwrite` and `errorOnExist` set to `true`.

<a id="ERR_FS_COPY_FIFO_PIPE"></a>
### `ERR_FS_COPY_FIFO_PIPE`
<!--
added: REPLACEME
-->

An attempt was made to copy a named pipe with [`fs.copy()`][].

<a id="ERR_FS_COPY_NON_DIR_TO_DIR"></a>
### `ERR_FS_COPY_NON_DIR_TO_DIR`
<!--
added: REPLACEME
-->

An attempt was made to copy a non-directory (file, symlink, etc.) to a directory
using [`fs.copy()`][].

<a id="ERR_FS_COPY_SOCKET"></a>
### `ERR_FS_COPY_SOCKET`
<!--
added: REPLACEME
-->

An attempt was made to copy to a socket with [`fs.copy()`][].

<a id="ERR_FS_COPY_SYMLINK_TO_SUBDIRECTORY"></a>
### `ERR_FS_COPY_SYMLINK_TO_SUBDIRECTORY`
<!--
added: REPLACEME
-->

When using [`fs.copy()`][], a symlink in `dest` pointed to a subdirectory
of `src`.

<a id="ERR_FS_COPY_TO_SUBDIRECTORY"></a>
### `ERR_FS_COPY_TO_SUBDIRECTORY`
<!--
added: REPLACEME
-->

When using [`fs.copy()`][], `dest` pointed to a subfolder in `src`.

<a id="ERR_FS_COPY_UNKNOWN"></a>
### `ERR_FS_COPY_UNKNOWN`
<!--
added: REPLACEME
-->

An attempt was made to copy to an unknown file type with [`fs.copy()`][].

<a id="ERR_FS_EISDIR"></a>
### `ERR_FS_EISDIR`

Expand Down Expand Up @@ -2818,6 +2886,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.copy()`]: fs.md#fs_fs_copy_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
Expand Down
27 changes: 27 additions & 0 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,33 @@ through any other `fs` operation may lead to undefined behavior.
See the POSIX close(2) documentation for more detail.
### `fs.copy(src, dest[, options], callback)`
<!-- YAML
added: REPLACEME
-->
* `src` {string|Buffer|URL} source path to copy.
* `dest` {string|Buffer|URL} destination path to copy to.
* `options` {Object|Function}
* `dereference` {boolean} dereference symlinks. **Default:** `false`.
* `errorOnExist` {boolean} when `overwrite` 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`
* `overwrite` {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`.
* `callback` {Function}
Asynchronously copies the entire directory structure from `src` to `dest`,
including subdirectories and files.
If a function is provided for `options`, it will be used as the `filter`
parameter.
### `fs.copyFile(src, dest[, mode], callback)`
<!-- YAML
added: v8.5.0
Expand Down
60 changes: 60 additions & 0 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const {
stringToSymlinkType,
toUnixTimestamp,
validateBufferArray,
validateCopyOptions,
validateOffsetLengthRead,
validateOffsetLengthWrite,
validatePath,
Expand Down Expand Up @@ -145,6 +146,8 @@ let truncateWarn = true;
let fs;

// Lazy loaded
let copyFn;
let copySyncFn;
let promises = null;
let ReadStream;
let WriteStream;
Expand Down Expand Up @@ -1075,6 +1078,12 @@ function ftruncateSync(fd, len = 0) {
handleErrorFromBinding(ctx);
}

function lazyLoadCopy() {
if (copyFn === undefined) {
({ copyFn } = require('internal/fs/copy/copy'));
({ copySyncFn } = require('internal/fs/copy/copy-sync'));
}
}

function lazyLoadRimraf() {
if (rimraf === undefined)
Expand Down Expand Up @@ -2741,6 +2750,55 @@ function mkdtempSync(prefix, options) {
return result;
}

/**
* Asynchronously copies `src` to `dest`. `src` can be a file, directory, or
* symlink. The contents of directories will be copied recursively.
* @param {string | Buffer | URL} src
* @param {string | Buffer | URL} dest
* @param {Object} [options]
* @param {() => any} callback
* @returns {void}
*/
function copy(src, dest, options, callback) {
if (typeof options === 'function' && !callback) {
callback = options;
options = {};
} else if (typeof options === 'function') {
options = { filter: options };
}
callback = makeCallback(callback);
options = options || {};
validateCopyOptions(options);
src = getValidatedPath(src);
dest = getValidatedPath(dest);
src = pathModule._makeLong(src);
dest = pathModule._makeLong(dest);
lazyLoadCopy();
copyFn(src, dest, options, callback);
}

/**
* Synchronously copies `src` to `dest`. `src` can be a file, directory, or
* symlink. The contents of directories will be copied recursively.
* @param {string | Buffer | URL} src
* @param {string | Buffer | URL} dest
* @param {Object} [options]
* @returns {void}
*/
function copySync(src, dest, options) {
if (typeof options === 'function') {
options = { filter: options };
}
options = options || {};
validateCopyOptions(options);
src = getValidatedPath(src);
dest = getValidatedPath(dest);
src = pathModule._makeLong(src);
dest = pathModule._makeLong(dest);
lazyLoadCopy();
copySyncFn(src, dest, options);
}

/**
* Asynchronously copies `src` to `dest`. By
* default, `dest` is overwritten if it already exists.
Expand Down Expand Up @@ -2852,6 +2910,8 @@ module.exports = fs = {
chmodSync,
close,
closeSync,
copy,
copySync,
copyFile,
copyFileSync,
createReadStream,
Expand Down
12 changes: 12 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,18 @@ E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM',
'The feature %s is unavailable on the current platform' +
', which is being used to run Node.js',
TypeError);
E('ERR_FS_COPY_DIR_TO_NON_DIR',
'Cannot overwrite directory with non-directory', SystemError);
E('ERR_FS_COPY_EEXIST', 'Target already exists', SystemError);
E('ERR_FS_COPY_FIFO_PIPE', 'Cannot copy a FIFO pipe', SystemError);
E('ERR_FS_COPY_NON_DIR_TO_DIR',
'Cannot overwrite non-directory with directory', SystemError);
E('ERR_FS_COPY_SOCKET', 'Cannot copy a socket file', SystemError);
E('ERR_FS_COPY_SYMLINK_TO_SUBDIRECTORY',
'Cannot overwrite symlink in subdirectory of self', SystemError);
E('ERR_FS_COPY_TO_SUBDIRECTORY',
'Cannot copy to a subdirectory of self', SystemError);
E('ERR_FS_COPY_UNKNOWN', 'Cannot copy a unknown file type', SystemError);
E('ERR_FS_EISDIR', 'Path is a directory', SystemError);
E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError);
E('ERR_FS_INVALID_SYMLINK_TYPE',
Expand Down
Loading

0 comments on commit 4107b45

Please sign in to comment.