@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.
Files changed (54) hide show
  1. package/.editorconfig +9 -0
  2. package/.husky/commit-msg +4 -0
  3. package/.husky/pre-commit +3 -0
  4. package/.prettierignore +28 -0
  5. package/.prettierrc.js +41 -0
  6. package/LICENSE +21 -0
  7. package/README.md +239 -0
  8. package/bin/renames.js +415 -0
  9. package/commitlint.config.js +42 -0
  10. package/config/default.config.json +20 -0
  11. package/eslint.config.js +196 -0
  12. package/index.js +14 -0
  13. package/jest.config.js +51 -0
  14. package/jsconfig.json +9 -0
  15. package/lib/batch-rename.js +190 -0
  16. package/lib/create-config.js +265 -0
  17. package/lib/generate-filename.js +182 -0
  18. package/lib/read-list.js +87 -0
  19. package/lib/utils/get-basename.js +18 -0
  20. package/lib/utils/get-extension.js +20 -0
  21. package/lib/utils/is-empty-object.js +23 -0
  22. package/lib/utils/is-file-exists.js +26 -0
  23. package/lib/utils/is-function.js +29 -0
  24. package/lib/utils/pad-start.js +31 -0
  25. package/lib/utils/pad-zero.js +24 -0
  26. package/lib/utils/read-dir.js +73 -0
  27. package/lib/utils/read-file.js +56 -0
  28. package/lib/utils/remove-file.js +60 -0
  29. package/lib/utils/rename.js +74 -0
  30. package/lib/utils/replace-index-chapter.js +53 -0
  31. package/lib/utils/show-warning-log.js +20 -0
  32. package/lib/utils/sort-files.js +157 -0
  33. package/lib/utils/strip-non-digit.js +16 -0
  34. package/lib/utils/terminal-link.js +16 -0
  35. package/lib/utils/to-index-chapter.js +19 -0
  36. package/lib/utils/write-file.js +35 -0
  37. package/package.json +114 -0
  38. package/tests/batch-rename.spec.js +123 -0
  39. package/tests/get-basename.spec.js +23 -0
  40. package/tests/get-extension.spec.js +24 -0
  41. package/tests/is-empty-object.spec.js +73 -0
  42. package/tests/is-file-exsits.spec.js +17 -0
  43. package/tests/is-function.spec.js +63 -0
  44. package/tests/pad-start.spec.js +27 -0
  45. package/tests/pad-zero.spec.js +15 -0
  46. package/tests/read-dir.spec.js +42 -0
  47. package/tests/read-file.spec.js +52 -0
  48. package/tests/read-list.spec.js +91 -0
  49. package/tests/rename.spec.js +50 -0
  50. package/tests/replace-index-chapter.spec.js +40 -0
  51. package/tests/sort-files.spec.js +221 -0
  52. package/tests/strip-non-digit.spec.js +15 -0
  53. package/tests/to-index-chapter.spec.js +31 -0
  54. package/tests/write-file.spec.js +34 -0
package/bin/renames.js ADDED
@@ -0,0 +1,415 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'node:path';
4
+ import { execSync } from 'node:child_process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { program } from 'commander';
7
+ import chalk from 'chalk';
8
+ import { select, input } from '@inquirer/prompts';
9
+
10
+ import createConfig from '../lib/create-config.js';
11
+ import isFileExists from '../lib/utils/is-file-exists.js';
12
+ import isEmptyObject from '../lib/utils/is-empty-object.js';
13
+ import readFile from '../lib/utils/read-file.js';
14
+ import renames from '../index.js';
15
+ import showWarningLog from '../lib/utils/show-warning-log.js';
16
+ import terminalLink from '../lib/utils/terminal-link.js';
17
+
18
+ const { resolve, dirname } = path;
19
+ const currentFilePath = dirname(fileURLToPath(import.meta.url));
20
+ const pkg = JSON.parse(readFile(resolve(currentFilePath, '../package.json')));
21
+ const { version, author, description } = pkg;
22
+
23
+ // 配置文件相关常量
24
+ const CONFIG_FILE_NAME = 'renames.config.js';
25
+ const DEMO_DIR_PATH = String.raw`C:\Downloads\Videos`;
26
+ const DEMO_LIST_PATH = String.raw`C:\Downloads\names.txt`;
27
+ const DEMO_LIST_DATA = '新的开始,完美结局';
28
+ const DEMO_PREFIX = '动画片';
29
+ const DEMO_SUFFIX = '1080p';
30
+ const DEMO_CONNECTOR = '-';
31
+ const DEMO_BASENAME = '第01话:新的开始';
32
+ const DEMO_FILE_NAME = `${DEMO_BASENAME}.mp4`;
33
+ const DEMO_FULL_FILE_NAME = `${DEMO_PREFIX}${DEMO_CONNECTOR}${DEMO_BASENAME}${DEMO_CONNECTOR}${DEMO_SUFFIX}.mp4`;
34
+ const DEFAULT_SORT = 'name';
35
+ const DEFAULT_ORDER = 'asc';
36
+ const DEFAULT_SENSITIVITY = 'base';
37
+ const DEFAULT_INDEX_PREFIX = '第';
38
+ const DEFAULT_INDEX_SUFFIX = '集';
39
+ const DEFAULT_DELIMITER = ':';
40
+ const DEFAULT_START_INDEX = 0;
41
+
42
+ // 配置文件路径
43
+ const CONFIG_PATH = resolve(currentFilePath, `../${CONFIG_FILE_NAME}`);
44
+ const CONFIG_PATH_URL = `file://${CONFIG_PATH}`;
45
+ const DEFAULT_CONFIG_PATH = resolve(
46
+ currentFilePath,
47
+ '../config/default.config.json',
48
+ );
49
+
50
+ /**
51
+ * # 辅助函数:解析布尔型 CLI 参数
52
+ *
53
+ * @function parseBooleanOption
54
+ * @param {string} enable - 输入的参数值
55
+ * @param {boolean} defaultValue - 默认值
56
+ * @returns {boolean} 解析后的布尔值
57
+ */
58
+ const parseBooleanOption = (enable, defaultValue = false) => {
59
+ switch (enable) {
60
+ case '1':
61
+ case 'true': {
62
+ return true;
63
+ }
64
+ case '0':
65
+ case 'false': {
66
+ return false;
67
+ }
68
+ default: {
69
+ return defaultValue;
70
+ }
71
+ }
72
+ };
73
+
74
+ /**
75
+ * # 辅助函数:解析数值型 CLI 参数
76
+ *
77
+ * @function parseNumberOption
78
+ * @param {string} value - 输入的参数值
79
+ * @param {number} defaultValue - 默认值
80
+ * @param {string} paramName - 参数名(用于错误提示)
81
+ * @returns {number} 解析后的数值
82
+ */
83
+ const parseNumberOption = (
84
+ value,
85
+ defaultValue = DEFAULT_START_INDEX,
86
+ paramName = '--startIndex',
87
+ ) => {
88
+ const numValue = Number(value);
89
+ if (Number.isNaN(numValue)) {
90
+ showWarningLog(
91
+ '警告',
92
+ `${value} 为无效数字`,
93
+ `${paramName} 必须传入合法数值,将使用默认值"${defaultValue}"。`,
94
+ );
95
+ return defaultValue;
96
+ }
97
+ return numValue;
98
+ };
99
+
100
+ /**
101
+ * # 辅助函数:获取通用的 CLI 选项配置
102
+ *
103
+ * @function getCommonOptions
104
+ * @returns {Array} 选项配置数组
105
+ */
106
+ const getCommonOptions = () => [
107
+ {
108
+ flags: '--names, --namesList <namesList>',
109
+ description: `可选,文件名列表数组数据,例如:"${DEMO_LIST_DATA}"。或者文件名列表文件的路径,例如:"${DEMO_LIST_PATH}"。`,
110
+ },
111
+ {
112
+ flags: '--prefix <prefix>',
113
+ description: `可选,文件名的前缀字符串,例如:"${DEMO_FULL_FILE_NAME}"中的"${DEMO_PREFIX}"`,
114
+ },
115
+ {
116
+ flags: '--suffix <suffix>',
117
+ description: `可选,文件名的后缀字符串,例如:"${DEMO_FULL_FILE_NAME}"中的"${DEMO_SUFFIX}"`,
118
+ },
119
+ {
120
+ flags: '--connector <connector>',
121
+ description: `可选,文件名的前/后缀字符串间的连接字符串,例如:"${DEMO_FULL_FILE_NAME}"中的"${DEMO_CONNECTOR}"`,
122
+ },
123
+ {
124
+ flags: '--autoIndex [enable]',
125
+ description: '可选,是否自动生成索引编号(default:false)',
126
+ parser: (enable) => parseBooleanOption(enable, false),
127
+ },
128
+ {
129
+ flags: '--startIndex <startIndex>',
130
+ description: `可选,索引编号起始值(default:${DEFAULT_START_INDEX})`,
131
+ parser: (index) => parseNumberOption(index, DEFAULT_START_INDEX),
132
+ },
133
+ {
134
+ flags: '--indexPadZero [enable]',
135
+ description: '可选,是否自动用"0"填充索引编号(default:true)',
136
+ parser: (enable) => parseBooleanOption(enable, true),
137
+ },
138
+ {
139
+ flags: '--indexPrefix <indexPrefix>',
140
+ description: `可选,索引编号的前缀字符串,例如:"${DEMO_FILE_NAME}"中的"第"`,
141
+ defaultValue: DEFAULT_INDEX_PREFIX,
142
+ },
143
+ {
144
+ flags: '--indexSuffix <indexSuffix>',
145
+ description: `可选,索引编号的后缀字符串,例如:"${DEMO_FILE_NAME}"中的"话"`,
146
+ defaultValue: DEFAULT_INDEX_SUFFIX,
147
+ },
148
+ {
149
+ flags: '--delimiter <delimiter>',
150
+ description: `可选,索引编号和的前/后缀字符串间的连接符,例如:"${DEMO_FILE_NAME}"中的":"`,
151
+ defaultValue: DEFAULT_DELIMITER,
152
+ },
153
+ {
154
+ flags: '-f, --force [enable]',
155
+ description: '可选,是否强制重命名(default:false)',
156
+ parser: (enable) => parseBooleanOption(enable, false),
157
+ },
158
+ {
159
+ flags: '--ext, --extname <extname>',
160
+ description: '可选,重命名后的扩展名,例如:".txt"',
161
+ },
162
+ {
163
+ flags: '--sort, --sortBy <sortBy>',
164
+ description:
165
+ '可选,排序类型,可选项:name、type、size、birthtime 和 modify-time',
166
+ defaultValue: DEFAULT_SORT,
167
+ },
168
+ {
169
+ flags: '--order <order>',
170
+ description: '可选,排序方式,可选项:desc 和 asc',
171
+ defaultValue: DEFAULT_ORDER,
172
+ },
173
+ {
174
+ flags: '--sensitivity <sensitivity>',
175
+ description:
176
+ '可选,排序方式为 name 时,大小写/重音处理的方式,可选项:base、accent、case 和 variant',
177
+ defaultValue: DEFAULT_SENSITIVITY,
178
+ },
179
+ ];
180
+
181
+ /**
182
+ * # 辅助函数:为 Commander 实例添加通用选项
183
+ *
184
+ * @function addCommonOptions
185
+ * @param {object} commanderInstance - Commander 实例
186
+ */
187
+ const addCommonOptions = (commanderInstance) => {
188
+ const commonOptions = getCommonOptions();
189
+
190
+ for (const option of commonOptions) {
191
+ if (option.parser) {
192
+ commanderInstance.option(
193
+ option.flags,
194
+ option.description,
195
+ option.parser,
196
+ option.defaultValue,
197
+ );
198
+ } else if (option.defaultValue) {
199
+ commanderInstance.option(
200
+ option.flags,
201
+ option.description,
202
+ option.defaultValue,
203
+ );
204
+ } else {
205
+ commanderInstance.option(option.flags, option.description);
206
+ }
207
+ }
208
+ };
209
+
210
+ /**
211
+ * # 辅助函数:处理文件名列表参数
212
+ *
213
+ * @function processNameList
214
+ * @param {string} nameList - 输入的文件名列表(路径或逗号分隔字符串)
215
+ * @returns {Array | string} 解析后的数组或原路径
216
+ */
217
+ const processNameList = (nameList) => {
218
+ if (!nameList) return '';
219
+
220
+ // 如果不是文件路径,解析为数组
221
+ if (!isFileExists(nameList)) {
222
+ return nameList.split(',').map((item) => item.trim());
223
+ }
224
+
225
+ return nameList;
226
+ };
227
+
228
+ /**
229
+ * # 辅助函数:获取最终的文件夹路径
230
+ *
231
+ * @function getFinalDirPath
232
+ * @param {string} dirPath - 命令行传入的路径
233
+ * @param {object} defaults - 默认配置
234
+ * @returns {Promise<string>} 最终的文件夹路径
235
+ */
236
+ async function getFinalDirPath(dirPath, defaults) {
237
+ // 优先使用命令行传入的路径
238
+ if (dirPath) {
239
+ return dirPath;
240
+ }
241
+
242
+ // 使用默认配置中的路径
243
+ if (defaults?.dirPath) {
244
+ return defaults.dirPath;
245
+ }
246
+
247
+ // 交互式获取路径
248
+ return await input({
249
+ message: chalk.greenBright(
250
+ `请输入需要执行重命名操作的文件夹路径(例如:"${DEMO_DIR_PATH}"):`,
251
+ ),
252
+ });
253
+ }
254
+
255
+ /**
256
+ * # 辅助函数:执行帮助命令
257
+ *
258
+ * @function executeHelpCommand
259
+ */
260
+ const executeHelpCommand = () => {
261
+ execSync('renames -h', {
262
+ encoding: 'utf8',
263
+ stdio: 'inherit',
264
+ });
265
+ };
266
+
267
+ /**
268
+ * # 辅助函数:执行初始化命令
269
+ *
270
+ * @function executeInitCommand
271
+ */
272
+ const executeInitCommand = () => {
273
+ execSync('renames init -h', {
274
+ encoding: 'utf8',
275
+ stdio: 'inherit',
276
+ });
277
+ };
278
+
279
+ // 配置主程序
280
+ const mainCommander = program
281
+ .name('renames')
282
+ .description(description)
283
+ .version(`renames.js [version:${version}]\n(C) ${author},保留所有权利。`)
284
+ .usage('[arguments|command] [options]');
285
+
286
+ // 主程序添加通用选项
287
+ addCommonOptions(mainCommander);
288
+
289
+ // 主命令逻辑
290
+ mainCommander
291
+ .argument(
292
+ '[dir-path]',
293
+ `可选,目标文件夹(绝对或相对)路径,如不设置,则使用 ${CONFIG_FILE_NAME} 中的 dirPath 属性。`,
294
+ )
295
+ .action(async (dirPath, options) => {
296
+ let answer;
297
+
298
+ // 无配置文件且无选项时,提供交互式选择
299
+ if (!isFileExists(CONFIG_PATH) && isEmptyObject(options)) {
300
+ answer = await select({
301
+ message:
302
+ `请指定命令或参数,或者创建 ${CONFIG_FILE_NAME} 配置文件,\n` +
303
+ '执行命令:renames -h,将显示 renames 命令的详细帮助信息,\n' +
304
+ '执行命令:renames init,将创建命令配置文件,请选择后序操作?',
305
+ pageSize: 3,
306
+ choices: [
307
+ {
308
+ name: '执行命令:renames -h',
309
+ value: 'help',
310
+ description: '执行 renames -h 命令,显示帮助信息',
311
+ },
312
+ {
313
+ name: '执行命令:renames init 命令',
314
+ value: 'init',
315
+ description: '执行命令:renames init,创建配置文件',
316
+ },
317
+ {
318
+ name: '退出',
319
+ value: 'exit',
320
+ disabled: false,
321
+ },
322
+ ],
323
+ });
324
+ }
325
+
326
+ switch (answer) {
327
+ case 'help': {
328
+ executeHelpCommand();
329
+ break;
330
+ }
331
+ case 'init': {
332
+ executeInitCommand();
333
+ break;
334
+ }
335
+ case 'exit': {
336
+ console.log(chalk.green('\n已退出!'));
337
+ break;
338
+ }
339
+ default: {
340
+ // 获取配置文件内容
341
+ const content = readFile(DEFAULT_CONFIG_PATH, { encoding: 'utf8' });
342
+ const defaults = isFileExists(CONFIG_PATH)
343
+ ? await import(`../${CONFIG_FILE_NAME}`)
344
+ : JSON.parse(content);
345
+ const config = defaults.default || defaults;
346
+
347
+ // 获取最终的文件夹路径
348
+ const finalDirPath = await getFinalDirPath(dirPath, config);
349
+
350
+ // 合并配置并处理文件名列表
351
+ const finalOptions = { ...config, ...options };
352
+
353
+ finalOptions.namesList = processNameList(finalOptions.namesList);
354
+
355
+ if (finalDirPath) {
356
+ // 执行重命名
357
+ renames(finalDirPath, finalOptions);
358
+ } else if (isFileExists(CONFIG_PATH)) {
359
+ // 提示修改配置文件中的 dirPath
360
+ showWarningLog(
361
+ '警告',
362
+ `${terminalLink(CONFIG_PATH, CONFIG_PATH_URL)}`,
363
+ '已生成,可配置 dirPath 属性后再执行 renames 命令。',
364
+ );
365
+ } else {
366
+ showWarningLog(
367
+ '警告',
368
+ '执行重命名操作的文件夹路径',
369
+ '未接受到任何信息,已退出程序。',
370
+ );
371
+ }
372
+ break;
373
+ }
374
+ }
375
+ });
376
+
377
+ // 配置 init 命令
378
+ const initCommand = program
379
+ .command('init')
380
+ .description(`用以生成"${CONFIG_FILE_NAME}"配置文件`);
381
+
382
+ // init 命令添加 dirPath 选项
383
+ initCommand.option(
384
+ '--dir, --dirPath <dirPath>',
385
+ `可选,目标文件夹(绝对或相对)路径,例如:${DEMO_DIR_PATH}`,
386
+ );
387
+
388
+ // init 命令添加通用选项
389
+ addCommonOptions(initCommand);
390
+
391
+ // init 命令逻辑
392
+ initCommand.action((options) => {
393
+ createConfig(options)
394
+ .then(({ isCancel }) => {
395
+ if (!isCancel) {
396
+ console.log(
397
+ chalk.green(
398
+ `\n配置文件 ${terminalLink(CONFIG_PATH, CONFIG_PATH_URL)} 已生成!`,
399
+ ),
400
+ );
401
+ return false;
402
+ }
403
+
404
+ console.log(
405
+ chalk.yellowBright(
406
+ `\n已取消重写 ${terminalLink(CONFIG_PATH, CONFIG_PATH_URL)} 配置文件!`,
407
+ ),
408
+ );
409
+ })
410
+ .catch((error) => {
411
+ console.log(chalk.red(`生成配置文件失败:${error.message}`));
412
+ });
413
+ });
414
+
415
+ program.parse(process.argv);
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Commitlint.config.js - git commit 校验配置
3
+ *
4
+ * Commit 类型: build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
5
+ * ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交 docs:文档更新
6
+ * feat:新增功能 fix:bug 修复 pref:性能优化 refactor:重构代码(既没有新增功能,也没有修复 bug)
7
+ * style:不影响程序逻辑的代码修改(修改空白字符,补全缺失的分号等) test:新增测试用例或是更新现有测试 revert:回滚某个更早之前的提交
8
+ * chore:不属于以上类型的其他类型(日常事务)
9
+ *
10
+ * @see https://github.com/conventional-changelog/commitlint
11
+ * @see https://typicode.github.io/husky/#/?id=uninstall
12
+ *
13
+ * Created By: Yaohaixiao
14
+ * Update: 2026.1.10
15
+ */
16
+ const TYPES = [
17
+ 'build',
18
+ 'ci',
19
+ 'chore',
20
+ 'docs',
21
+ 'feat',
22
+ 'fix',
23
+ 'pref',
24
+ 'refactor',
25
+ 'revert',
26
+ 'style',
27
+ 'test',
28
+ ];
29
+
30
+ export default {
31
+ extends: ['@commitlint/config-conventional'],
32
+ rules: {
33
+ 'type-enum': [2, 'always', TYPES],
34
+ 'type-case': [0],
35
+ 'type-empty': [0],
36
+ 'scope-empty': [0],
37
+ 'scope-case': [0],
38
+ 'subject-full-stop': [0, 'never'],
39
+ 'subject-case': [0, 'never'],
40
+ 'header-max-length': [0, 'always', 72],
41
+ },
42
+ };
@@ -0,0 +1,20 @@
1
+ {
2
+ "dirPath": "",
3
+ "namesList": "",
4
+ "prefix": "",
5
+ "suffix": "",
6
+ "connector": "",
7
+ "autoIndex": false,
8
+ "startIndex": 0,
9
+ "indexPadZero": true,
10
+ "indexPrefix": "第",
11
+ "indexSuffix": "集",
12
+ "delimiter": ":",
13
+ "force": false,
14
+ "extname": "",
15
+ "filter": null,
16
+ "sortBy": "name",
17
+ "order": "desc",
18
+ "sensitivity": "base",
19
+ "format": null
20
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * # Eslint.config.js - eslint 配置
3
+ *
4
+ * Created By: Yaohaixiao Update: 2026.1.10
5
+ */
6
+ import eslintJs from '@eslint/js';
7
+ import { defineConfig } from 'eslint/config';
8
+ // Added for .gitignore
9
+ import { includeIgnoreFile } from '@eslint/compat';
10
+
11
+ // Added for .gitignore path
12
+ import { fileURLToPath } from 'node:url';
13
+ import globals from 'globals';
14
+
15
+ import eslintPluginJsdoc from 'eslint-plugin-jsdoc';
16
+ import eslintPluginN from 'eslint-plugin-n';
17
+ import eslintPluginUnicorn from 'eslint-plugin-unicorn';
18
+ import eslintConfigPrettier from 'eslint-config-prettier';
19
+ import jestPlugin from 'eslint-plugin-jest';
20
+
21
+ const gitignorePath = fileURLToPath(new URL('.gitignore', import.meta.url));
22
+
23
+ export default defineConfig(
24
+ // 1. Handle .gitignore patterns
25
+ includeIgnoreFile(gitignorePath),
26
+
27
+ // 2. 忽略指定目录/文件(全局生效)
28
+ {
29
+ ignores: [
30
+ 'node_modules/',
31
+ 'dist/',
32
+ 'build/',
33
+ 'coverage/',
34
+ 'public/',
35
+ 'logs/',
36
+ '*.config.js',
37
+ '**/*.min.js',
38
+ ],
39
+ },
40
+
41
+ // 3. Global linter options
42
+ {
43
+ linterOptions: {
44
+ // Enable reporting of unused disable directives
45
+ reportUnusedDisableDirectives: true,
46
+ },
47
+ },
48
+
49
+ // 4. Base configurations for all relevant files
50
+ eslintJs.configs.recommended, // Basic ESLint recommended rules
51
+
52
+ // 5. JSDoc configuration
53
+ {
54
+ plugins: { jsdoc: eslintPluginJsdoc },
55
+ rules: {
56
+ ...eslintPluginJsdoc.configs.recommended.rules,
57
+ 'jsdoc/require-jsdoc': 0,
58
+ 'jsdoc/tag-lines': [2, 'any', { startLines: 1 }],
59
+ 'jsdoc/require-param-type': 0,
60
+ 'jsdoc/require-returns-type': 0,
61
+ 'jsdoc/no-types': 0,
62
+ 'jsdoc/no-defaults': 0,
63
+ // Was in TS override, better here
64
+ 'jsdoc/require-returns-check': 0,
65
+ // 核心配置:关闭注释首字母大写约束规则
66
+ 'jsdoc/capitalized-comments': 0,
67
+ // 可选:关闭关联的描述开头格式规则(若仍有报错补充配置)
68
+ 'jsdoc/require-description-start': 0,
69
+ 'jsdoc/reject-function-type': 0,
70
+ },
71
+ settings: {
72
+ jsdoc: {
73
+ mode: 'jsdoc',
74
+ tagNamePreference: { category: 'category' },
75
+ },
76
+ },
77
+ },
78
+
79
+ // 6. Node plugin configuration
80
+ {
81
+ plugins: { n: eslintPluginN },
82
+ rules: {
83
+ ...eslintPluginN.configs.recommended.rules,
84
+ 'n/file-extension-in-import': [2, 'always'],
85
+ 'n/no-missing-import': 0,
86
+ 'n/no-unpublished-import': 0,
87
+ },
88
+ },
89
+
90
+ // 7. Unicorn plugin configuration
91
+ {
92
+ plugins: { unicorn: eslintPluginUnicorn },
93
+ rules: {
94
+ ...eslintPluginUnicorn.configs.recommended.rules,
95
+ 'unicorn/no-null': 0,
96
+ 'unicorn/prevent-abbreviations': 0,
97
+ 'unicorn/prefer-code-point': 0,
98
+ 'unicorn/no-for-loop': 0,
99
+ 'unicorn/no-array-callback-reference': 0,
100
+ 'unicorn/prefer-spread': 0,
101
+ 'unicorn/no-useless-undefined': 0,
102
+ 'unicorn/no-array-reduce': 0,
103
+ 'unicorn/prefer-array-find': 0,
104
+ 'unicorn/prefer-module': 0,
105
+ 'unicorn/prefer-at': 0,
106
+ 'unicorn/prefer-string-replace-all': 0,
107
+ 'unicorn/prefer-switch': [2, { emptyDefaultCase: 'do-nothing-comment' }],
108
+ },
109
+ },
110
+
111
+ // 8. Jest 专属配置(核心:插件与规则在同一个配置对象内)
112
+ {
113
+ // 仅对测试文件生效,精准匹配
114
+ files: ['**/tests/**/*.js'],
115
+
116
+ ignores: ['node_modules/', 'dist/'],
117
+
118
+ // 1. 注册 jest 插件(必须步骤,解决「找不到 plugin jest」)
119
+ plugins: {
120
+ // 键名「jest」需与规则前缀「jest/xxx」对应
121
+ jest: jestPlugin,
122
+ },
123
+
124
+ // 2. 配置环境(导入 Jest 全局变量,顺带解决 describe 未定义)
125
+ languageOptions: {
126
+ globals: {
127
+ ...jestPlugin.environments.globals.globals,
128
+ ...globals.node,
129
+ },
130
+ ecmaVersion: 'latest',
131
+ sourceType: 'module',
132
+ },
133
+
134
+ // 3. 启用 Jest 规则(包括 jest/expect-expect)
135
+ rules: {
136
+ // 继承 Jest 推荐规则(包含 jest/expect-expect,无需单独手动写)
137
+ ...jestPlugin.configs.recommended.rules,
138
+ // 可选:手动自定义 jest/expect-expect 规则级别(error/warn/off)
139
+ 'jest/expect-expect': ['error', { assertFunctionNames: ['expect'] }],
140
+ },
141
+ },
142
+
143
+ // 9. Global custom rules and language options
144
+ {
145
+ languageOptions: {
146
+ globals: {
147
+ // 保留 console, process(Jest 运行在 Node 中)
148
+ ...globals.node,
149
+ },
150
+ parserOptions: {
151
+ projectService: {
152
+ allowDefaultProject: ['*.js'],
153
+ },
154
+ },
155
+ ecmaVersion: 'latest',
156
+ sourceType: 'module',
157
+ },
158
+ rules: {
159
+ 'array-callback-return': [2, { allowImplicit: true }],
160
+ 'no-lonely-if': 2,
161
+ 'no-proto': 2,
162
+ eqeqeq: [2, 'smart'],
163
+ 'no-caller': 2,
164
+ 'dot-notation': 2,
165
+ 'no-var': 2,
166
+ 'prefer-const': 2,
167
+ 'prefer-arrow-callback': [2, { allowNamedFunctions: true }],
168
+ 'arrow-body-style': [2, 'as-needed'],
169
+ 'object-shorthand': 2,
170
+ 'prefer-template': 2,
171
+ 'one-var': [2, 'never'],
172
+ 'prefer-destructuring': [2, { object: true }],
173
+ 'capitalized-comments': 0,
174
+ 'multiline-comment-style': [2, 'starred-block'],
175
+ 'spaced-comment': 2,
176
+ yoda: [2, 'never'],
177
+ curly: [2, 'multi-line'],
178
+ 'no-else-return': [2, { allowElseIf: false }],
179
+ 'no-unused-expressions': 2,
180
+ 'no-useless-call': 2,
181
+ 'no-use-before-define': [2, 'nofunc'],
182
+ 'no-constant-binary-expression': 2,
183
+ 'no-void': 2,
184
+ },
185
+ settings: {
186
+ 'import/resolver': {
187
+ jest: {
188
+ jestConfigFile: './jest.config.js',
189
+ },
190
+ },
191
+ },
192
+ },
193
+
194
+ // 10. Prettier - must be the last configuration to override styling rules
195
+ eslintConfigPrettier,
196
+ );
package/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import batchRename from './lib/batch-rename.js';
2
+
3
+ /**
4
+ * # 批量重命名(指定文件夹)文件名
5
+ *
6
+ * @function renames
7
+ * @param {string} dirPath - 指定的文件夹路径
8
+ * @param {object} [options] - 可选,指定重命名的配置参数信息
9
+ * @returns {boolean} - 数据异常时返回 false,数据处理完成,返回 true.
10
+ * @see lib/batch-rename.js
11
+ */
12
+ const renames = (dirPath, options) => batchRename(dirPath, options);
13
+
14
+ export default renames;