From 8a888f23c6da6be7288f7a4497f7d794a09df467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=B8=E6=B0=B4=E8=8A=A6=E8=8B=87?= Date: Sun, 14 Jan 2024 20:09:37 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feature(lib):=20Copy=20or=20move=20?= =?UTF-8?q?function,=20including=20files=20or=20folders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/copy/dir/index.ts | 58 ++++++++++++++++++++++++++++++++++++++++++ src/copy/file/index.ts | 33 ++++++++++++++++++++++++ src/isDir/index.ts | 19 ++++++++++++++ src/isFile/index.ts | 19 ++++++++++++++ src/makeDir/index.ts | 2 +- src/move/dir/index.ts | 27 ++++++++++++++++++++ src/move/file/index.ts | 27 ++++++++++++++++++++ src/remove/index.ts | 7 +++++ 8 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 src/copy/dir/index.ts create mode 100644 src/copy/file/index.ts create mode 100644 src/isDir/index.ts create mode 100644 src/isFile/index.ts create mode 100644 src/move/dir/index.ts create mode 100644 src/move/file/index.ts diff --git a/src/copy/dir/index.ts b/src/copy/dir/index.ts new file mode 100644 index 0000000..580a72d --- /dev/null +++ b/src/copy/dir/index.ts @@ -0,0 +1,58 @@ +import fs from 'fs/promises'; +import path from 'path'; +import isDir from '@/isDir'; +import normalize from '@/normalize'; +import makeDir from '@/makeDir'; +import file from '@/copy/file'; + +/** + * 复制目录到新位置 (copy the directory to a new location) + * @param {string} source 源目录路径 (source directory path) + * @param {string} target 目标目录路径 (target directory path) + * @param {boolean} [overwrite=false] 是否覆盖已存在的文件 (whether to overwrite existing files) + * @returns {Promise} 是否复制成功 (whether the copy was successful) + */ +const dir = async (source: string, target: string, overwrite: boolean = false): Promise => { + try { + if (!isDir(source)) { + throw new Error('The source path is not a directory.'); + } else if (!isDir(target)) { + throw new Error('The target path is not a directory.'); + } + + const normalizedSource = normalize(source); + const normalizedTarget = normalize(target); + + await makeDir(normalizedTarget); + + const queue = [{ source: normalizedSource, target: normalizedTarget }]; + while (queue.length > 0) { + const task = queue.shift(); + + if (task) { + const { source: currentSource, target: currentTarget } = task; + + const items = await fs.readdir(currentSource, { withFileTypes: true }); + for (const item of items) { + const sourcePath = path.join(currentSource, item.name); + const targetPath = path.join(currentTarget, item.name); + + if (item.isDirectory()) { + (await makeDir(targetPath)) && queue.push({ source: sourcePath, target: targetPath }); + } else { + await file(sourcePath, targetPath, overwrite); + } + } + } + } + + return true; + } catch (err) { + if (err instanceof Error) { + throw err; + } + return false; + } +}; + +export default dir; diff --git a/src/copy/file/index.ts b/src/copy/file/index.ts new file mode 100644 index 0000000..6862d0f --- /dev/null +++ b/src/copy/file/index.ts @@ -0,0 +1,33 @@ +import fs from 'fs/promises'; +import normalize from '@/normalize'; +import makeDir from '@/makeDir'; + +/** + * 复制文件到新位置 (copy the file to a new location) + * @param {string} source 源文件路径 (source file path) + * @param {string} target 目标文件路径 (target file path) + * @param {boolean} [overwrite=false] 是否覆盖已存在的文件 (whether to overwrite existing files) + * @returns {Promise} 是否复制成功 (whether the copy was successful) + */ +const file = async (source: string, target: string, overwrite: boolean = false): Promise => { + try { + const normalizedSource = normalize(source); + const normalizedTarget = normalize(target); + + // 检查源文件和目标文件是否相同 + if (normalizedSource && normalizedTarget && normalizedSource !== normalizedTarget) { + await makeDir(normalizedTarget); + await fs.copyFile(normalizedSource, normalizedTarget, overwrite ? 0 : fs.constants.COPYFILE_EXCL); + } + + return true; + } catch (err) { + if (err instanceof Error) { + throw err; + } + + return false; + } +}; + +export default file; diff --git a/src/isDir/index.ts b/src/isDir/index.ts new file mode 100644 index 0000000..42ed8a0 --- /dev/null +++ b/src/isDir/index.ts @@ -0,0 +1,19 @@ +import fs from 'fs/promises'; +import normalize from '@/normalize'; + +/** + * 检查路径是否为目录。 + * @param {string} path 要检查的路径。 + * @returns {Promise} 如果路径是目录,则返回 true,否则返回 false。 + */ +const isDir = async (path: string): Promise => { + try { + const stats = await fs.stat(normalize(path)); + return stats.isDirectory(); + } catch (err) { + // 错误处理:如果路径不存在或无法访问,将被视为非目录 + return false; + } +}; + +export default isDir; diff --git a/src/isFile/index.ts b/src/isFile/index.ts new file mode 100644 index 0000000..f53cfeb --- /dev/null +++ b/src/isFile/index.ts @@ -0,0 +1,19 @@ +import fs from 'fs/promises'; +import normalize from '@/normalize'; + +/** + * 检查路径是否为文件。 + * @param {string} path 要检查的路径。 + * @returns {Promise} 如果路径是文件,则返回 true,否则返回 false。 + */ +const isFile = async (path: string): Promise => { + try { + const stats = await fs.stat(normalize(path)); + return stats.isFile(); + } catch (err) { + // 错误处理:如果路径不存在或无法访问,将被视为非文件 + return false; + } +}; + +export default isFile; diff --git a/src/makeDir/index.ts b/src/makeDir/index.ts index 693381d..949b2e9 100644 --- a/src/makeDir/index.ts +++ b/src/makeDir/index.ts @@ -6,7 +6,7 @@ import normalize from '@/normalize'; * 创建指定路径的文件夹,支持创建多级文件夹 (create the specified path of the folder, support to create multi-level folders) * @description 如果存在这个路径,则不会创建,如果不存在,则会创建;如果是文件路径,则取其父目录 (if this path exists, it will not be created, if it does not exist, it will be created; if it is a file path, then take its parent directory) * @param {string} dir 要创建的文件夹路径,如果是文件路径,则取其所在目录 (the path of the folder to create, if it is a file path, then take its parent directory) - * @returns {Promise} 如果创建成功,则返回 true;否则返回 false (returns `true` if the creation is successful, otherwise returns `false`) + * @returns {Promise} 如果创建成功或已存在,则返回 true;否则返回 false (if the creation is successful or exists, return true; otherwise return false) */ const createDir = async (dir: string): Promise => { try { diff --git a/src/move/dir/index.ts b/src/move/dir/index.ts new file mode 100644 index 0000000..6909f90 --- /dev/null +++ b/src/move/dir/index.ts @@ -0,0 +1,27 @@ +import remove from '@/remove'; +import copy from '@/copy/dir'; + +/** + * 移动文件到新位置 + * @param {string} source 源文件路径 + * @param {string} target 目标文件路径 + * @param {boolean} [overwrite=false] 是否覆盖已存在的文件 (whether to overwrite existing files) + * @returns {Promise} 是否移动成功 + */ +const dir = async (source: string, target: string, overwrite: boolean = false): Promise => { + try { + if (await copy(source, target, overwrite)) { + return await remove(source); + } + + return false; + } catch (err) { + if (err instanceof Error) { + throw err; + } + + return false; + } +}; + +export default dir; diff --git a/src/move/file/index.ts b/src/move/file/index.ts new file mode 100644 index 0000000..96887d0 --- /dev/null +++ b/src/move/file/index.ts @@ -0,0 +1,27 @@ +import remove from '@/remove'; +import copy from '@/copy/file'; + +/** + * 移动文件到新位置 + * @param {string} source 源文件路径 + * @param {string} target 目标文件路径 + * @param {boolean} [overwrite=false] 是否覆盖已存在的文件 (whether to overwrite existing files) + * @returns {Promise} 是否移动成功 + */ +const file = async (source: string, target: string, overwrite: boolean = false): Promise => { + try { + if (await copy(source, target, overwrite)) { + return await remove(source); + } + + return false; + } catch (err) { + if (err instanceof Error) { + throw err; + } + + return false; + } +}; + +export default file; diff --git a/src/remove/index.ts b/src/remove/index.ts index be2a71e..3c651fe 100644 --- a/src/remove/index.ts +++ b/src/remove/index.ts @@ -3,6 +3,13 @@ import { join } from 'path'; import normalize from '@/normalize'; import exists from '@/exists'; +/** + * 删除指定路径的文件或目录 (delete the file or directory of the specified path) + * @param {string | string[]} paths 要删除的文件或目录路径 (the file or directory path to delete) + * @param {boolean} [includeSubDirs=true] 是否包含子目录 (whether to include subdirectories) + * @param {number} [concurrency=5] 并发数 (concurrency) + * @returns {Promise} 是否删除成功 (whether the deletion is successful) + */ const remove = async (paths: string | string[], includeSubDirs: boolean = true, concurrency: number = 5): Promise => { try { const normalizedPaths = Array.isArray(paths) ? paths.map(normalize) : [normalize(paths)];