@yaohaixiao/renames.js 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +9 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +3 -0
- package/.prettierignore +28 -0
- package/.prettierrc.js +41 -0
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/bin/renames.js +415 -0
- package/commitlint.config.js +42 -0
- package/config/default.config.json +20 -0
- package/eslint.config.js +196 -0
- package/index.js +14 -0
- package/jest.config.js +51 -0
- package/jsconfig.json +9 -0
- package/lib/batch-rename.js +190 -0
- package/lib/create-config.js +265 -0
- package/lib/generate-filename.js +182 -0
- package/lib/read-list.js +87 -0
- package/lib/utils/get-basename.js +18 -0
- package/lib/utils/get-extension.js +20 -0
- package/lib/utils/is-empty-object.js +23 -0
- package/lib/utils/is-file-exists.js +26 -0
- package/lib/utils/is-function.js +29 -0
- package/lib/utils/pad-start.js +31 -0
- package/lib/utils/pad-zero.js +24 -0
- package/lib/utils/read-dir.js +73 -0
- package/lib/utils/read-file.js +56 -0
- package/lib/utils/remove-file.js +60 -0
- package/lib/utils/rename.js +74 -0
- package/lib/utils/replace-index-chapter.js +53 -0
- package/lib/utils/show-warning-log.js +20 -0
- package/lib/utils/sort-files.js +157 -0
- package/lib/utils/strip-non-digit.js +16 -0
- package/lib/utils/terminal-link.js +16 -0
- package/lib/utils/to-index-chapter.js +19 -0
- package/lib/utils/write-file.js +35 -0
- package/package.json +114 -0
- package/tests/batch-rename.spec.js +123 -0
- package/tests/get-basename.spec.js +23 -0
- package/tests/get-extension.spec.js +24 -0
- package/tests/is-empty-object.spec.js +73 -0
- package/tests/is-file-exsits.spec.js +17 -0
- package/tests/is-function.spec.js +63 -0
- package/tests/pad-start.spec.js +27 -0
- package/tests/pad-zero.spec.js +15 -0
- package/tests/read-dir.spec.js +42 -0
- package/tests/read-file.spec.js +52 -0
- package/tests/read-list.spec.js +91 -0
- package/tests/rename.spec.js +50 -0
- package/tests/replace-index-chapter.spec.js +40 -0
- package/tests/sort-files.spec.js +221 -0
- package/tests/strip-non-digit.spec.js +15 -0
- package/tests/to-index-chapter.spec.js +31 -0
- package/tests/write-file.spec.js +34 -0
package/lib/read-list.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import getExtension from './utils/get-extension.js';
|
|
4
|
+
import isFileExists from './utils/is-file-exists.js';
|
|
5
|
+
import readFile from './utils/read-file.js';
|
|
6
|
+
import showWarningLog from './utils/show-warning-log.js';
|
|
7
|
+
|
|
8
|
+
// 辅助函数:过滤文件列表中的空值
|
|
9
|
+
const filter = (list) => list.filter((item) => item !== '' && item !== null);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* # 同步读取文件名列表文件中的文件名数据,返回文件名数组
|
|
13
|
+
*
|
|
14
|
+
* @function readList
|
|
15
|
+
* @param {string} listPath - 文件名列表文件路径
|
|
16
|
+
* @param {string} [prop=''] - 可选,当前读取的 .json 文件是 JSON 对象格式时,指定当局去的对象属性名.
|
|
17
|
+
* Default is `''`
|
|
18
|
+
* @returns {Array} - 返回读取列表文件的数组数据,文件不存在则返回 false
|
|
19
|
+
*/
|
|
20
|
+
const readList = (listPath, prop = '') => {
|
|
21
|
+
const { resolve } = path;
|
|
22
|
+
const { parse } = JSON;
|
|
23
|
+
// 解析绝对路径(避免相对路径混乱)
|
|
24
|
+
const finalListPath = resolve(listPath);
|
|
25
|
+
let items = [];
|
|
26
|
+
|
|
27
|
+
// 检测文件是否存在
|
|
28
|
+
if (!isFileExists(finalListPath)) {
|
|
29
|
+
showWarningLog('跳过', finalListPath, '文件不存在或已被删除。');
|
|
30
|
+
return items;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 同步读取文件(utf8 编码直接返回字符串,不加编码返回 Buffer)
|
|
34
|
+
const content = readFile(finalListPath);
|
|
35
|
+
const extname = getExtension(finalListPath).toLowerCase();
|
|
36
|
+
// 按行分割(兼容 Windows \r\n 和 Linux \n 换行符) split(/\r?\n/) 匹配两种换行符,filter(Boolean) 过滤空行(可选)
|
|
37
|
+
const pattern = /\r?\n/;
|
|
38
|
+
const { isArray } = Array;
|
|
39
|
+
|
|
40
|
+
switch (extname) {
|
|
41
|
+
case '.txt': {
|
|
42
|
+
items = content.split(pattern).filter(Boolean);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case '.json': {
|
|
46
|
+
items = parse(content);
|
|
47
|
+
|
|
48
|
+
// 读取 JSON 数组数据
|
|
49
|
+
if (isArray(items)) {
|
|
50
|
+
items = filter(items);
|
|
51
|
+
} else if (prop && items[prop]) {
|
|
52
|
+
// 读取 JSON 对象的某个属性值,且该属性值是数组格式
|
|
53
|
+
if (isArray(items[prop])) {
|
|
54
|
+
items = filter(items[prop]);
|
|
55
|
+
} else {
|
|
56
|
+
items = [];
|
|
57
|
+
showWarningLog(
|
|
58
|
+
'警告',
|
|
59
|
+
`${finalListPath} 中JSON对象的 ${prop}`,
|
|
60
|
+
'属性不是数组格式数据。',
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
items = [];
|
|
65
|
+
showWarningLog(
|
|
66
|
+
'警告',
|
|
67
|
+
`${finalListPath} 中的数据`,
|
|
68
|
+
'不是JSON数组格式。',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
default: {
|
|
75
|
+
showWarningLog(
|
|
76
|
+
'警告',
|
|
77
|
+
'仅支持 .txt 和 .json',
|
|
78
|
+
'格式文件的数据分析读取。',
|
|
79
|
+
);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return items;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default readList;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import getExtension from './get-extension.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* # 获取不含扩展名的文件名
|
|
7
|
+
*
|
|
8
|
+
* @function getBasename
|
|
9
|
+
* @param {string} filename - 文件名(路径)字符串
|
|
10
|
+
* @returns {string} - 返回文件名中去掉扩展名部分的字符串
|
|
11
|
+
*/
|
|
12
|
+
const getBasename = (filename) => {
|
|
13
|
+
const extname = getExtension(filename);
|
|
14
|
+
|
|
15
|
+
return path.basename(filename, extname);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default getBasename;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* # 获取扩展名(含.)
|
|
5
|
+
*
|
|
6
|
+
* @function getExtension
|
|
7
|
+
* @param {string} filename - 文件名(路径)字符串
|
|
8
|
+
* @returns {string} - 返回文件名中扩展名部分的字符串,例如:'.jpg'
|
|
9
|
+
*/
|
|
10
|
+
const getExtension = (filename) => {
|
|
11
|
+
const extname = path.extname(filename);
|
|
12
|
+
|
|
13
|
+
if (!extname) {
|
|
14
|
+
return filename;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return extname;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default getExtension;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # 判断一个值是否为空对象(纯对象 + 无自身可枚举属性)
|
|
3
|
+
*
|
|
4
|
+
* @function isEmptyObject
|
|
5
|
+
* @param {object} val - 要检测的值
|
|
6
|
+
* @returns {boolean} - 是为空对象返回 true,否则返回 false
|
|
7
|
+
*/
|
|
8
|
+
const isEmptyObject = (val) => {
|
|
9
|
+
// 先判断是否为纯对象(排除 null、数组、函数、基本类型等)
|
|
10
|
+
if (typeof val !== 'object' || val === null) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 判断是否为空数组
|
|
15
|
+
if (Array.isArray(val) && val.length === 0) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 判断是否有自身可枚举属性
|
|
20
|
+
return Object.keys(val).length === 0;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default isEmptyObject;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* # 同步检测文件是否存在
|
|
6
|
+
*
|
|
7
|
+
* @function isFileExists
|
|
8
|
+
* @param {string} filePath - 检测的文件路径
|
|
9
|
+
* @param {string} [basePath=''] - 可选,基础路径。. Default is `''`
|
|
10
|
+
* @returns {boolean} - 文件存在返回 true,否则返回 false
|
|
11
|
+
*/
|
|
12
|
+
const isFileExists = (filePath = '', basePath = '') => {
|
|
13
|
+
const { resolve } = path;
|
|
14
|
+
|
|
15
|
+
if (!filePath) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const finalFilePath = basePath
|
|
20
|
+
? resolve(basePath, filePath)
|
|
21
|
+
: resolve(filePath);
|
|
22
|
+
|
|
23
|
+
return fs.existsSync(finalFilePath);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default isFileExists;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # 判断一个值是否为函数(兼容所有函数类型)
|
|
3
|
+
*
|
|
4
|
+
* @function isFunction
|
|
5
|
+
* @param {Function} val - 要判断的值
|
|
6
|
+
* @returns {boolean} - 返回是否为函数的检测结果
|
|
7
|
+
*/
|
|
8
|
+
const isFunction = (val) => {
|
|
9
|
+
// 基础判断:排除 null/undefined/非对象类型
|
|
10
|
+
if (val == null || (typeof val !== 'function' && typeof val !== 'object')) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 核心判断:通过原型链和内部属性区分函数与其他对象
|
|
16
|
+
*
|
|
17
|
+
* 1. 普通函数/箭头函数/异步函数/生成器函数:prototype 存在(箭头函数除外,但 typeof 为 function)
|
|
18
|
+
* 2. DOM 方法(如 document.createElement):typeof 为 function
|
|
19
|
+
* 3. 类(class):typeof 为 function,但实例化需要 new,仍属于函数类型
|
|
20
|
+
* 4. 排除其他对象(如数组、对象字面量等)
|
|
21
|
+
*/
|
|
22
|
+
return (
|
|
23
|
+
// 处理某些特殊环境下 typeof 误判为 object 的函数(极少数情况)
|
|
24
|
+
typeof val === 'function' ||
|
|
25
|
+
Object.prototype.toString.call(val) === '[object Function]'
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default isFunction;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # 自定义 padStart 方法,在字符串开头填充字符至目标长度
|
|
3
|
+
*
|
|
4
|
+
* @function padStart
|
|
5
|
+
* @param {string | number} val - 原始字符串(需处理的字符串)
|
|
6
|
+
* @param {number | string} [length=2] - 可选,最终目标字符串长度. Default is `2`
|
|
7
|
+
* @param {string} [padString=' '] - 可选:填充字符. Default is `' '`
|
|
8
|
+
* @returns {string} - 返回填充后的字符串
|
|
9
|
+
*/
|
|
10
|
+
const padStart = (val, length = 2, padString = ' ') => {
|
|
11
|
+
// 类型转换:确保 val 是字符串(原生 padStart 会隐式转换)
|
|
12
|
+
const originalStr = String(val);
|
|
13
|
+
// 处理填充字符:默认空格,若为非字符串则转为字符串,空字符串则返回原串(无填充)
|
|
14
|
+
let [finalLength, padChar] = [length, padString];
|
|
15
|
+
|
|
16
|
+
// length 参数为字符串类型,则当作 padString 使用,此时默认 length = 2
|
|
17
|
+
if (typeof finalLength === 'string') {
|
|
18
|
+
padChar = length;
|
|
19
|
+
finalLength = 2;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (padChar === '' || finalLength <= originalStr.length) {
|
|
23
|
+
// 处理目标长度:若目标长度 ≤ 原字符串长度,或者没有设置 pad 填充字符,直接返回原串
|
|
24
|
+
return originalStr;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// node 环境下已支持 String.prototype.padStart 原生方法
|
|
28
|
+
return originalStr.padStart(finalLength, padChar);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default padStart;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import padStart from './pad-start.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* # 数字/字符串补零(前置补0,确保指定长度)
|
|
5
|
+
*
|
|
6
|
+
* @function padZero
|
|
7
|
+
* @param {number | string} val - 要补零的数字或字符串(如 27、'27')
|
|
8
|
+
* @param {number} [length=2] - 可选,目标总长度(如 3 → '027',4 → '0027'). Default is `2`
|
|
9
|
+
* @returns {string} - 返回补零后的字符串
|
|
10
|
+
*/
|
|
11
|
+
const padZero = (val, length = 2) => {
|
|
12
|
+
// 转换为字符串(处理数字/字符串输入)
|
|
13
|
+
const str = String(val);
|
|
14
|
+
|
|
15
|
+
// 验证目标长度(必须大于0,否则返回原字符串)
|
|
16
|
+
if (length <= 0 || str.length >= length) {
|
|
17
|
+
return str;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 前置补0到目标长度(padStart(目标长度, 补位字符))
|
|
21
|
+
return padStart(str, length, '0');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default padZero;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import isFileExists from './is-file-exists.js';
|
|
5
|
+
import isFunction from './is-function.js';
|
|
6
|
+
import showWarningLog from './show-warning-log.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* # (同步)读取文件夹中的文件
|
|
10
|
+
*
|
|
11
|
+
* @function readDir
|
|
12
|
+
* @param {string} dirPath - 文件夹的路径
|
|
13
|
+
* @param {string | object | Function} [options='utf8'] - 可选,读取文件夹的配置参数. Default
|
|
14
|
+
* is `'utf8'`
|
|
15
|
+
* @param {Function} [callback=null] - 可选,读取完数据后的处理函数. Default is `null`
|
|
16
|
+
* @returns {string[]} - 返回读取文件夹中的文件数组信息
|
|
17
|
+
*/
|
|
18
|
+
const readDir = (dirPath, options = 'utf8', callback = null) => {
|
|
19
|
+
const { readdirSync, lstatSync } = fs;
|
|
20
|
+
const { resolve } = path;
|
|
21
|
+
const finalDirPath = resolve(dirPath);
|
|
22
|
+
let files = [];
|
|
23
|
+
let afterRead = callback;
|
|
24
|
+
|
|
25
|
+
if (!isFileExists(finalDirPath)) {
|
|
26
|
+
showWarningLog('跳过', finalDirPath, '文件夹不存在或已被删除。');
|
|
27
|
+
return files;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const stats = lstatSync(finalDirPath);
|
|
31
|
+
|
|
32
|
+
if (!stats.isDirectory()) {
|
|
33
|
+
showWarningLog('跳过', finalDirPath, '不是有效的文件夹。');
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const defaultOptions = {
|
|
38
|
+
// 显式指定 UTF-8 编码(默认值,可省略)
|
|
39
|
+
encoding: 'utf8',
|
|
40
|
+
withFileTypes: false,
|
|
41
|
+
};
|
|
42
|
+
let mergedOptions = {};
|
|
43
|
+
|
|
44
|
+
// options 作为 callback 函数
|
|
45
|
+
if (isFunction(options)) {
|
|
46
|
+
afterRead = options;
|
|
47
|
+
mergedOptions = {
|
|
48
|
+
...defaultOptions,
|
|
49
|
+
};
|
|
50
|
+
} else {
|
|
51
|
+
mergedOptions = {
|
|
52
|
+
...defaultOptions,
|
|
53
|
+
...(typeof options === 'object' && options !== null ? options : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const encodings = ['utf8', 'ascii', 'buffer', null];
|
|
58
|
+
|
|
59
|
+
// 若传入的 encoding 非法,回退到默认的 utf8
|
|
60
|
+
if (!encodings.includes(mergedOptions.encoding)) {
|
|
61
|
+
mergedOptions.encoding = defaultOptions.encoding;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
files = readdirSync(finalDirPath, mergedOptions);
|
|
65
|
+
|
|
66
|
+
if (isFunction(afterRead)) {
|
|
67
|
+
afterRead(files);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return files;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default readDir;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import isFileExists from './is-file-exists.js';
|
|
5
|
+
import isFunction from './is-function.js';
|
|
6
|
+
import showWarningLog from './show-warning-log.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* # (同步)读取文本文件数据
|
|
10
|
+
*
|
|
11
|
+
* @function readFile
|
|
12
|
+
* @param {string} filePath - 读取文件的路径
|
|
13
|
+
* @param {object | string | Function} [options='utf8'] -
|
|
14
|
+
* 可选,读取文件的配置信息或者文件读取完成后的处理函数. Default is `'utf8'`
|
|
15
|
+
* @param {Function} [callback=null] - 可选,文件读取完成后的处理函数. Default is `null`
|
|
16
|
+
* @returns {Buffer | string} - 返回读取文件的文本字符串或者Buffer数据
|
|
17
|
+
*/
|
|
18
|
+
const readFile = (filePath, options = 'utf8', callback = null) => {
|
|
19
|
+
const { resolve } = path;
|
|
20
|
+
const finalFilePath = resolve(filePath);
|
|
21
|
+
const defaultOptions = {
|
|
22
|
+
encoding: 'utf8',
|
|
23
|
+
flag: 'r',
|
|
24
|
+
};
|
|
25
|
+
let finalOptions = {};
|
|
26
|
+
let content = '';
|
|
27
|
+
let afterRead = callback;
|
|
28
|
+
|
|
29
|
+
if (!isFileExists(finalFilePath)) {
|
|
30
|
+
showWarningLog('跳过', finalFilePath, '不存在或已被删除。');
|
|
31
|
+
return content;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (isFunction(options)) {
|
|
35
|
+
afterRead = options;
|
|
36
|
+
finalOptions = { ...defaultOptions };
|
|
37
|
+
} else {
|
|
38
|
+
finalOptions =
|
|
39
|
+
typeof options === 'string'
|
|
40
|
+
? {
|
|
41
|
+
...defaultOptions,
|
|
42
|
+
encoding: options,
|
|
43
|
+
}
|
|
44
|
+
: { ...defaultOptions, ...options };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
content = fs.readFileSync(finalFilePath, finalOptions);
|
|
48
|
+
|
|
49
|
+
if (isFunction(afterRead)) {
|
|
50
|
+
afterRead(content);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return content;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default readFile;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import isFileExists from './is-file-exists.js';
|
|
5
|
+
import isFunction from './is-function.js';
|
|
6
|
+
import showWarningLog from './show-warning-log.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* # 同步删除指定文件或目录(支持递归删除目录内容)
|
|
10
|
+
*
|
|
11
|
+
* @function removeFile
|
|
12
|
+
* @param {string} filePath - 要删除的文件/目录路径(相对路径或绝对路径)
|
|
13
|
+
* @param {object | Function} [options={}] - 删除配置选项(可选,继承 fs.rmSync
|
|
14
|
+
* 的配置参数)或者执行完成后的回调函数. Default is `{}`
|
|
15
|
+
* @param {boolean} [options.recursive=true] - 可选,递归删除目录下的所有文件和子目录. Default is
|
|
16
|
+
* `true`
|
|
17
|
+
* @param {boolean} [options.force=true] - 可选,忽略不存在的路径,避免抛出错误. Default is `true`
|
|
18
|
+
* @param {number} [options.maxRetries=0] - 可选,删除失败时的重试次数. Default is `0`
|
|
19
|
+
* @param {number} [options.retryDelay=100] - 可选,重试间隔时间(毫秒). Default is `100`
|
|
20
|
+
* @param {Function} [callback=null] - 可选,执行完成后的回调函数. Default is `null`
|
|
21
|
+
* @returns {boolean} - 文件不存在时,返回 false,删除成功,返回 true.
|
|
22
|
+
*/
|
|
23
|
+
const removeFile = (filePath, options = {}, callback = null) => {
|
|
24
|
+
const { resolve } = path;
|
|
25
|
+
const finalPath = resolve(filePath);
|
|
26
|
+
const defaultOptions = {
|
|
27
|
+
recursive: true,
|
|
28
|
+
force: true,
|
|
29
|
+
maxRetries: 0,
|
|
30
|
+
retryDelay: 100,
|
|
31
|
+
};
|
|
32
|
+
let finalOptions = {};
|
|
33
|
+
let afterRemove = callback;
|
|
34
|
+
|
|
35
|
+
if (!isFileExists(finalPath)) {
|
|
36
|
+
showWarningLog('跳过', finalPath, '文件不存在或已被删除。');
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// options 类型为函数时,options 作为 afterRemove 回调函数使用,则直接使用默认配置
|
|
41
|
+
if (isFunction(options)) {
|
|
42
|
+
afterRemove = options;
|
|
43
|
+
finalOptions = { ...defaultOptions };
|
|
44
|
+
} else {
|
|
45
|
+
finalOptions = {
|
|
46
|
+
...defaultOptions,
|
|
47
|
+
...(typeof options === 'object' && options !== null ? options : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fs.rmSync(finalPath, finalOptions);
|
|
52
|
+
|
|
53
|
+
if (isFunction(afterRemove)) {
|
|
54
|
+
afterRemove(filePath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default removeFile;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
import getBasename from './get-basename.js';
|
|
6
|
+
import getExtension from './get-extension.js';
|
|
7
|
+
import isFileExists from './is-file-exists.js';
|
|
8
|
+
import isFunction from './is-function.js';
|
|
9
|
+
import showWarningLog from './show-warning-log.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* # 重命名文件
|
|
13
|
+
*
|
|
14
|
+
* @function rename
|
|
15
|
+
* @param {string} oldFilePath - 原来的文件名
|
|
16
|
+
* @param {string} newFilePath - 新的文件名
|
|
17
|
+
* @param {boolean | Function} [force=false] - 可选,是否强制重命名. Default is `false`
|
|
18
|
+
* @param {Function} [callback=null] - 可选,读取完毕后的处理函数. Default is `null`
|
|
19
|
+
* @returns {boolean} - 数据不对或者文件不存在,返回 false,重命名成功,返回 true
|
|
20
|
+
*/
|
|
21
|
+
const rename = (oldFilePath, newFilePath, force = false, callback = null) => {
|
|
22
|
+
// 检测文件是否存在
|
|
23
|
+
if (!isFileExists(oldFilePath)) {
|
|
24
|
+
showWarningLog('跳过', oldFilePath, '文件不存在或已被删除。');
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const oldFilename = getBasename(oldFilePath);
|
|
29
|
+
const oldExtname = getExtension(oldFilePath);
|
|
30
|
+
|
|
31
|
+
const newFilename = getBasename(newFilePath);
|
|
32
|
+
const newExtname = getExtension(newFilePath);
|
|
33
|
+
|
|
34
|
+
let afterRename = callback;
|
|
35
|
+
let isForce;
|
|
36
|
+
|
|
37
|
+
if (isFunction(force)) {
|
|
38
|
+
afterRename = force;
|
|
39
|
+
} else {
|
|
40
|
+
isForce = force;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 避免重复命名(如果新文件名已存在并且未设置强制重命名,则跳过重命名)
|
|
44
|
+
if (
|
|
45
|
+
!isForce &&
|
|
46
|
+
((oldFilename === newFilename && oldExtname === newExtname) ||
|
|
47
|
+
isFileExists(newFilePath))
|
|
48
|
+
) {
|
|
49
|
+
showWarningLog(
|
|
50
|
+
'跳过',
|
|
51
|
+
`${oldFilename}${oldExtname}`,
|
|
52
|
+
'文件名已存在无需修改。',
|
|
53
|
+
);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 执行重命名
|
|
58
|
+
fs.renameSync(oldFilePath, newFilePath);
|
|
59
|
+
|
|
60
|
+
console.log(
|
|
61
|
+
chalk.greenBright(`成功:`),
|
|
62
|
+
chalk.blue(`${oldFilename}${oldExtname}`),
|
|
63
|
+
chalk.blueBright(` → `),
|
|
64
|
+
chalk.green(`${newFilename}${newExtname}`),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (isFunction(afterRename)) {
|
|
68
|
+
afterRename();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default rename;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import getBasename from './get-basename.js';
|
|
2
|
+
import isFunction from './is-function.js';
|
|
3
|
+
import padZero from './pad-zero.js';
|
|
4
|
+
import toIndexChapter from './to-index-chapter.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* # 替换文件名中的章节信息
|
|
8
|
+
*
|
|
9
|
+
* @function replaceIndexChapter
|
|
10
|
+
* @param {string} filename - 原始的文件名字符串
|
|
11
|
+
* @param {object | Function} [chapter] - 可选,过滤文件名中的索引编号的处理函数
|
|
12
|
+
* @param {string} chapter.name - 文件名中的整个索引编号的字符出,例如:第04章
|
|
13
|
+
* @param {string} chapter.index - 件名中的索引编号的字符,例如:04
|
|
14
|
+
* @param {number} chapter.number - 件名中的索引编号的数值,例如:4
|
|
15
|
+
* @param {number | boolean} [length=2] - 可选,自动填充索引编号的长度. Default is `2`
|
|
16
|
+
* @param {object} [options={}] - 可选,其他的格式化配置参数. Default is `{}`
|
|
17
|
+
* @param {string} [options.prefix='第'] - 可选,索引部分的前缀. Default is `'第'`
|
|
18
|
+
* @param {string} [options.suffix='集'] - 可选,索引部分的后缀. Default is `'集'`
|
|
19
|
+
* @param {string} [options.delimiter=':'] - 可选,用来拆分索引部分和正式名的分割符号. Default is
|
|
20
|
+
* `':'`
|
|
21
|
+
* @returns {string} - 返回替换了章节信息的文件名字符串
|
|
22
|
+
*/
|
|
23
|
+
const replaceIndexChapter = (filename, chapter, length = 2, options = {}) => {
|
|
24
|
+
const basename = getBasename(filename);
|
|
25
|
+
|
|
26
|
+
const { prefix = '第', suffix = '集', delimiter = ':' } = options;
|
|
27
|
+
let padZeroIndex = true;
|
|
28
|
+
|
|
29
|
+
// 关闭了自动填充索引编号
|
|
30
|
+
if (length === false) {
|
|
31
|
+
padZeroIndex = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!chapter) {
|
|
35
|
+
return basename;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const originChapter = isFunction(chapter) ? chapter(basename) : chapter;
|
|
39
|
+
const { index, number, name } = originChapter;
|
|
40
|
+
const indexChapter = toIndexChapter(
|
|
41
|
+
padZeroIndex ? padZero(number, length) : index,
|
|
42
|
+
false,
|
|
43
|
+
{
|
|
44
|
+
prefix,
|
|
45
|
+
suffix,
|
|
46
|
+
delimiter,
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return basename.replace(name, indexChapter);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default replaceIndexChapter;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* # 显示警告提示信息
|
|
5
|
+
*
|
|
6
|
+
* @function showWarningLog
|
|
7
|
+
* @param {string} title - 警告类型的文本,例如:"警告"
|
|
8
|
+
* @param {string} keyword - 警告内容的关键内容文本
|
|
9
|
+
* @param {string} detail - 警告内容的详细描述文本
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
const showWarningLog = (title, keyword, detail) => {
|
|
13
|
+
console.log(
|
|
14
|
+
chalk.yellow(`${title}:`),
|
|
15
|
+
chalk.blue(`${keyword}`),
|
|
16
|
+
chalk.yellowBright(`${detail}`),
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default showWarningLog;
|