Skip to content

Commit

Permalink
✨ feature(lib): Copy or move function, including files or folders
Browse files Browse the repository at this point in the history
  • Loading branch information
kwooshung committed Jan 14, 2024
1 parent fc1a9e8 commit 8a888f2
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 1 deletion.
58 changes: 58 additions & 0 deletions src/copy/dir/index.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>} 是否复制成功 (whether the copy was successful)
*/
const dir = async (source: string, target: string, overwrite: boolean = false): Promise<boolean> => {
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;
33 changes: 33 additions & 0 deletions src/copy/file/index.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>} 是否复制成功 (whether the copy was successful)
*/
const file = async (source: string, target: string, overwrite: boolean = false): Promise<boolean> => {
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;
19 changes: 19 additions & 0 deletions src/isDir/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import fs from 'fs/promises';
import normalize from '@/normalize';

/**
* 检查路径是否为目录。
* @param {string} path 要检查的路径。
* @returns {Promise<boolean>} 如果路径是目录,则返回 true,否则返回 false。
*/
const isDir = async (path: string): Promise<boolean> => {
try {
const stats = await fs.stat(normalize(path));
return stats.isDirectory();
} catch (err) {
// 错误处理:如果路径不存在或无法访问,将被视为非目录
return false;
}
};

export default isDir;
19 changes: 19 additions & 0 deletions src/isFile/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import fs from 'fs/promises';
import normalize from '@/normalize';

/**
* 检查路径是否为文件。
* @param {string} path 要检查的路径。
* @returns {Promise<boolean>} 如果路径是文件,则返回 true,否则返回 false。
*/
const isFile = async (path: string): Promise<boolean> => {
try {
const stats = await fs.stat(normalize(path));
return stats.isFile();
} catch (err) {
// 错误处理:如果路径不存在或无法访问,将被视为非文件
return false;
}
};

export default isFile;
2 changes: 1 addition & 1 deletion src/makeDir/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>} 如果创建成功,则返回 true;否则返回 false (returns `true` if the creation is successful, otherwise returns `false`)
* @returns {Promise<boolean>} 如果创建成功或已存在,则返回 true;否则返回 false (if the creation is successful or exists, return true; otherwise return false)
*/
const createDir = async (dir: string): Promise<boolean> => {
try {
Expand Down
27 changes: 27 additions & 0 deletions src/move/dir/index.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>} 是否移动成功
*/
const dir = async (source: string, target: string, overwrite: boolean = false): Promise<boolean> => {
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;
27 changes: 27 additions & 0 deletions src/move/file/index.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>} 是否移动成功
*/
const file = async (source: string, target: string, overwrite: boolean = false): Promise<boolean> => {
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;
7 changes: 7 additions & 0 deletions src/remove/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>} 是否删除成功 (whether the deletion is successful)
*/
const remove = async (paths: string | string[], includeSubDirs: boolean = true, concurrency: number = 5): Promise<boolean> => {
try {
const normalizedPaths = Array.isArray(paths) ? paths.map(normalize) : [normalize(paths)];
Expand Down

0 comments on commit 8a888f2

Please sign in to comment.