td-web-cli 0.1.14 → 0.1.16

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 项目名称
2
2
 
3
- 一个基于 node.js 和 typeScript 的高效 cli 工具,用于提升工作效率。
3
+ 一个基于 Node.js 和 TypeScript 的高效 CLI 工具,用于提升工作效率。
4
4
 
5
5
  ---
6
6
 
@@ -18,11 +18,11 @@
18
18
 
19
19
  ## 环境要求
20
20
 
21
- - node.js >= 20.x(推荐使用 22.x 或更高版本)
21
+ - Node.js >= 20.x(推荐使用 22.x 或更高版本)
22
22
  - npm >= 10.x
23
- - typeScript >= 5.x
23
+ - TypeScript >= 5.x
24
24
 
25
- > **注意**:部分依赖可能要求 node.js 20 及以上版本,请根据实际情况调整。
25
+ > **注意**:部分依赖可能要求 Node.js 20 及以上版本,请根据实际情况调整。
26
26
 
27
27
  ---
28
28
 
@@ -31,8 +31,8 @@
31
31
  1. 克隆仓库
32
32
 
33
33
  ```bash
34
- git clone <仓库地址>
35
- cd your-project
34
+ git clone https://github.com/qianyuzzf/td-web-cli.git
35
+ cd td-web-cli
36
36
  ```
37
37
 
38
38
  2. 安装依赖
@@ -47,8 +47,8 @@ npm install
47
47
 
48
48
  ### 代码结构
49
49
 
50
- - `src/` - typeScript 源代码目录
51
- - `dist/` - 编译后的 javaScript 输出目录
50
+ - `src/` - TypeScript 源代码目录
51
+ - `dist/` - 编译后的 JavaScript 输出目录
52
52
  - `release.js` - 发布脚本(可直接运行或编译后运行)
53
53
 
54
54
  ### 运行开发环境
@@ -59,7 +59,7 @@ npm link
59
59
  td-web-cli # 确保本机未全局安装该工具
60
60
  ```
61
61
 
62
- 通过 `npm link`,本地调试 cli 命令 `td-web-cli`。
62
+ 通过 `npm link`,本地调试 CLI 命令 `td-web-cli`。
63
63
 
64
64
  ---
65
65
 
@@ -67,7 +67,7 @@ td-web-cli # 确保本机未全局安装该工具
67
67
 
68
68
  ### 命令行执行
69
69
 
70
- 执行 cli 工具:
70
+ 执行 CLI 工具:
71
71
 
72
72
  ```bash
73
73
  td-web-cli
@@ -75,13 +75,13 @@ td-web-cli
75
75
 
76
76
  ### 功能说明
77
77
 
78
- - 多语言 exceljson 工具。[功能说明](./docs/i18n/excel2json.md)
78
+ - 多语言 ExcelJSON 工具。[详细功能说明请点击这里](https://github.com/qianyuzzf/td-web-cli/blob/master/docs/i18n/excel2json.md)
79
79
 
80
80
  ---
81
81
 
82
82
  ## 构建
83
83
 
84
- 使用 typeScript 编译项目:
84
+ 使用 TypeScript 编译项目:
85
85
 
86
86
  ```bash
87
87
  npm run build
@@ -105,7 +105,7 @@ npm run release
105
105
  - 依赖安装
106
106
  - 项目构建
107
107
  - 发布到 npm
108
- - 提交并推送 git 代码
108
+ - 提交并推送 Git 代码
109
109
 
110
110
  ---
111
111
 
@@ -113,17 +113,17 @@ npm run release
113
113
 
114
114
  ```
115
115
  .
116
- ├── src/ # typeScript 源代码
116
+ ├── src/ # TypeScript 源代码
117
117
  │ └── index.ts # 入口文件
118
118
  ├── dist/ # 编译输出目录
119
119
  ├── release.js # 发布脚本
120
120
  ├── setting.json # 配置文件
121
121
  ├── package.json # 项目配置
122
- ├── tsconfig.json # typeScript 配置
122
+ ├── tsconfig.json # TypeScript 配置
123
123
  ├── README.md # 项目说明文件
124
- ├── .gitignore # git 忽略文件
125
- ├── .prettierignore # prettier 忽略文件
126
- ├── .prettierrc # prettier 配置文件
124
+ ├── .gitignore # Git 忽略文件
125
+ ├── .prettierignore # Prettier 忽略文件
126
+ ├── .prettierrc # Prettier 配置文件
127
127
  └── package-lock.json # 依赖锁定文件
128
128
  ```
129
129
 
@@ -1,6 +1,7 @@
1
1
  declare const api: {
2
2
  LANGUAGE_TOOL_V2_LANGUAGES: string;
3
3
  LANGUAGE_TOOL_V2_CHECK: string;
4
+ UNPKG_HOLIDAY_CALENDAR: string;
4
5
  };
5
6
  export default api;
6
7
  //# sourceMappingURL=interface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/api/interface.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,GAAG;;;CAGR,CAAC;AAEF,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/api/interface.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,GAAG;;;;CAIR,CAAC;AAEF,eAAe,GAAG,CAAC"}
@@ -1,5 +1,6 @@
1
1
  const api = {
2
- LANGUAGE_TOOL_V2_LANGUAGES: 'https://api.languagetool.org/v2/languages',
3
- LANGUAGE_TOOL_V2_CHECK: 'https://api.languagetool.org/v2/check',
2
+ LANGUAGE_TOOL_V2_LANGUAGES: 'https://api.languagetool.org/v2/languages', // 获取支持的语言列表
3
+ LANGUAGE_TOOL_V2_CHECK: 'https://api.languagetool.org/v2/check', // 调用语言检测接口,检测文本中的语言错误
4
+ UNPKG_HOLIDAY_CALENDAR: 'https://unpkg.com/holiday-calendar@1.3.0/data/CN', // 获取假期日历数据
4
5
  };
5
6
  export default api;
package/dist/index.js CHANGED
@@ -5,8 +5,9 @@
5
5
  */
6
6
  import { Command } from 'commander';
7
7
  import { select, Separator } from '@inquirer/prompts';
8
- import { i18n } from './modules/i18n/index.js';
9
8
  import { logger, loggerError } from './utils/index.js';
9
+ import { i18n } from './modules/i18n/index.js';
10
+ import { tools } from './modules/tools/index.js';
10
11
  const program = new Command();
11
12
  /**
12
13
  * 主程序入口函数
@@ -25,6 +26,11 @@ async function main() {
25
26
  value: 'i18n',
26
27
  description: '国际化相关功能',
27
28
  },
29
+ {
30
+ name: '小工具',
31
+ value: 'tools',
32
+ description: '小工具相关功能',
33
+ },
28
34
  ];
29
35
  // 交互式选择模块
30
36
  const answer = await select({
@@ -51,6 +57,11 @@ async function main() {
51
57
  await i18n(program);
52
58
  logger.info(`${selectedModule.name}模块执行完成`);
53
59
  break;
60
+ case 'tools':
61
+ logger.info(`${selectedModule.name}模块开始执行`);
62
+ await tools(program);
63
+ logger.info(`${selectedModule.name}模块执行完成`);
64
+ break;
54
65
  default:
55
66
  logger.warn(`${selectedModule.name}模块暂未实现,程序已退出`);
56
67
  process.exit(0);
@@ -1,9 +1,9 @@
1
1
  import { Command } from 'commander';
2
2
  /**
3
- * exceljson功能主函数
4
- * 读取用户输入的excel路径,解析内容,根据配置生成多语言json文件
3
+ * ExcelJSON功能主函数
4
+ * 读取用户输入的Excel路径,解析内容,根据配置生成多语言JSON文件
5
5
  * 并对配置文件中所有语言对应的词条进行语言检测
6
- * 如果有相同的json key,则在key前面加上6位编码,保证唯一性
6
+ * 如果有相同的JSON KEY,则在KEY前面加上6位编码,保证唯一性
7
7
  * @param program Commander命令行实例
8
8
  */
9
9
  export declare function excel2json(program: Command): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/excel2json/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0LpC;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBAoVhD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/excel2json/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0LpC;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBAsVhD"}
@@ -34,17 +34,17 @@ function loadConfig(configPath) {
34
34
  return json.i18n;
35
35
  }
36
36
  /**
37
- * 匹配excel表头列名对应的语言key,支持大小写不敏感匹配
38
- * 先匹配语言key本身,再匹配语言名称数组(包含关系)
37
+ * 匹配Excel表头列名对应的语言KEY,支持大小写不敏感匹配
38
+ * 先匹配语言KEY本身,再匹配语言名称数组(包含关系)
39
39
  * @param colName 表头列名
40
40
  * @param langs 语言映射
41
- * @returns 匹配到的语言key,未匹配返回null
41
+ * @returns 匹配到的语言KEY,未匹配返回null
42
42
  */
43
43
  function matchLangKey(colName, langs) {
44
44
  if (!colName)
45
45
  return null;
46
46
  const colNameLower = colName.toLowerCase();
47
- // 先尝试匹配语言key
47
+ // 先尝试匹配语言KEY
48
48
  for (const langKey of Object.keys(langs)) {
49
49
  if (langKey.toLowerCase() === colNameLower) {
50
50
  return langKey;
@@ -135,10 +135,10 @@ function parseCheckResultPerEntry(checkResult, texts) {
135
135
  return entryErrors;
136
136
  }
137
137
  /**
138
- * exceljson功能主函数
139
- * 读取用户输入的excel路径,解析内容,根据配置生成多语言json文件
138
+ * ExcelJSON功能主函数
139
+ * 读取用户输入的Excel路径,解析内容,根据配置生成多语言JSON文件
140
140
  * 并对配置文件中所有语言对应的词条进行语言检测
141
- * 如果有相同的json key,则在key前面加上6位编码,保证唯一性
141
+ * 如果有相同的JSON KEY,则在KEY前面加上6位编码,保证唯一性
142
142
  * @param program Commander命令行实例
143
143
  */
144
144
  export async function excel2json(program) {
@@ -173,7 +173,7 @@ export async function excel2json(program) {
173
173
  });
174
174
  }
175
175
  for (const lang of languageTools) {
176
- // 尝试根据语言名称匹配配置中的语言key
176
+ // 尝试根据语言名称匹配配置中的语言KEY
177
177
  const lowerName = lang.name.toLowerCase();
178
178
  const matchedKey = langNameToKey[lowerName] ||
179
179
  Object.keys(i18nConfig.langs).find((k) => k.toLowerCase() === lowerName);
@@ -188,9 +188,9 @@ export async function excel2json(program) {
188
188
  logger.warn(`获取在线语言列表失败,使用本地配置 longCodes,错误:${normalizeError(error).stack}`);
189
189
  console.warn('获取在线语言列表失败,使用本地配置 longCodes');
190
190
  }
191
- // 交互式输入excel文件路径并校验
191
+ // 交互式输入Excel文件路径并校验
192
192
  const answer = await input({
193
- message: '请输入excel文件路径:',
193
+ message: '请输入Excel文件路径:',
194
194
  validate: (value) => {
195
195
  const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
196
196
  if (cleaned.length === 0)
@@ -198,19 +198,19 @@ export async function excel2json(program) {
198
198
  if (!fs.existsSync(cleaned))
199
199
  return '文件不存在,请输入有效路径';
200
200
  if (!/\.(xls|xlsx)$/i.test(cleaned))
201
- return '请输入有效的excel文件路径(.xls或.xlsx)';
201
+ return '请输入有效的Excel文件路径(.xls或.xlsx)';
202
202
  return true;
203
203
  },
204
204
  });
205
205
  // 规范化路径,支持相对路径转绝对路径,去除首尾引号
206
206
  const excelPath = path.resolve(process.cwd(), answer.trim().replace(/^['"]|['"]$/g, ''));
207
207
  try {
208
- logger.info(`开始读取excel文件:${excelPath}`, true);
209
- // 读取excel文件
208
+ logger.info(`开始读取Excel文件:${excelPath}`, true);
209
+ // 读取Excel文件
210
210
  const workbook = XLSX.readFile(excelPath);
211
211
  const firstSheetName = workbook.SheetNames[0];
212
212
  if (!firstSheetName) {
213
- logger.error('excel文件没有任何工作表,程序已退出');
213
+ logger.error('Excel文件没有任何工作表,程序已退出');
214
214
  console.error('程序执行时发生异常,已记录日志,程序已退出');
215
215
  process.exit(1);
216
216
  }
@@ -225,7 +225,7 @@ export async function excel2json(program) {
225
225
  logger.info('开始解析表头', true);
226
226
  // 处理表头行,去除空格,转成字符串
227
227
  const headerRow = rows[0].map((cell) => (cell ? String(cell).trim() : ''));
228
- // 根据表头匹配语言列,建立列索引到语言key的映射
228
+ // 根据表头匹配语言列,建立列索引到语言KEY的映射
229
229
  const colIndexToLangKey = {};
230
230
  headerRow.forEach((colName, idx) => {
231
231
  const langKey = matchLangKey(colName, i18nConfig.langs);
@@ -243,20 +243,20 @@ export async function excel2json(program) {
243
243
  }
244
244
  const defaultColNum = Number(defaultColIndex);
245
245
  // 初始化所有语言词条对象(包括默认语言)
246
- // 用于存储最终的翻译key-value对,key可能会被重新编码以避免重复
246
+ // 用于存储最终的翻译KEY-VALUE对,KEY可能会被重新编码以避免重复
247
247
  const langTranslations = {};
248
248
  Object.values(colIndexToLangKey).forEach((langKey) => {
249
249
  langTranslations[langKey] = {};
250
250
  });
251
251
  logger.info('开始解析数据行', true);
252
- // 记录所有出现过的key,检测重复,格式:langKey => Set of keys
252
+ // 记录所有出现过的KEY,检测重复,格式:langKey => Set of keys
253
253
  const langKeySets = {};
254
254
  Object.keys(langTranslations).forEach((langKey) => {
255
255
  langKeySets[langKey] = new Set();
256
256
  });
257
257
  // 遍历数据行,提取所有语言词条
258
- // key统一用默认语言列的值,其他语言对应的列为翻译内容
259
- const langKeysMap = {}; // 语言key => 词条数组(用于检测)
258
+ // KEY统一用默认语言列的值,其他语言对应的列为翻译内容
259
+ const langKeysMap = {}; // 语言KEY => 词条数组(用于检测)
260
260
  Object.keys(langTranslations).forEach((langKey) => {
261
261
  langKeysMap[langKey] = [];
262
262
  });
@@ -267,15 +267,15 @@ export async function excel2json(program) {
267
267
  continue;
268
268
  let key = String(keyCell).trim();
269
269
  key = trimQuotes(key); // 去除引号
270
- // 跳过空key,避免写入无效数据
270
+ // 跳过空KEY,避免写入无效数据
271
271
  if (key.length === 0)
272
272
  continue;
273
- // 判断默认语言key是否重复,若重复则重新编码
273
+ // 判断默认语言KEY是否重复,若重复则重新编码
274
274
  if (langKeySets[defaultLang].has(key)) {
275
275
  key = formatKey(key);
276
276
  }
277
277
  langKeySets[defaultLang].add(key);
278
- // 默认语言的词条即key本身
278
+ // 默认语言的词条即KEY本身
279
279
  langTranslations[defaultLang][key] = key;
280
280
  langKeysMap[defaultLang].push(key);
281
281
  // 其他语言词条
@@ -286,7 +286,7 @@ export async function excel2json(program) {
286
286
  const valCell = row[colIdx];
287
287
  if (valCell !== undefined && valCell !== null && valCell !== '') {
288
288
  const valStr = String(valCell);
289
- // 判断该语言的key是否重复,若重复则重新编码key
289
+ // 判断该语言的KEY是否重复,若重复则重新编码KEY
290
290
  let finalKey = key;
291
291
  if (langKeySets[langKey].has(finalKey)) {
292
292
  finalKey = formatKey(finalKey);
@@ -301,7 +301,7 @@ export async function excel2json(program) {
301
301
  }
302
302
  }
303
303
  }
304
- // 语言检测结果映射,语言key => 每条词条的错误描述数组
304
+ // 语言检测结果映射,语言KEY => 每条词条的错误描述数组
305
305
  const langCheckErrorsMap = {};
306
306
  // 对所有语言词条批量进行语言检测(包括默认语言)
307
307
  const langKeysEntries = Object.entries(langKeysMap);
@@ -343,7 +343,7 @@ export async function excel2json(program) {
343
343
  }
344
344
  }
345
345
  }
346
- // 输出目录:excel文件所在目录下的“lang_时间戳”文件夹
346
+ // 输出目录:Excel文件所在目录下的“lang_时间戳”文件夹
347
347
  const excelDir = path.dirname(excelPath);
348
348
  const timestamp = getTimestamp();
349
349
  const outputRoot = path.join(excelDir, `lang_${timestamp}`);
@@ -351,12 +351,12 @@ export async function excel2json(program) {
351
351
  fs.mkdirSync(outputRoot, { recursive: true });
352
352
  }
353
353
  logger.info(`开始生成语言文件,输出目录:${outputRoot}`, true);
354
- // 按语言生成对应的json文件,默认语言的key=value不生成文件
354
+ // 按语言生成对应的JSON文件,默认语言的KEY=VALUE不生成文件
355
355
  for (const [langKey, translations] of Object.entries(langTranslations)) {
356
356
  if (Object.keys(translations).length === 0)
357
357
  continue;
358
358
  if (langKey === defaultLang) {
359
- logger.info(`跳过默认语言(${langKey})的json文件生成`, true);
359
+ logger.info(`跳过默认语言(${langKey})的JSON文件生成`, true);
360
360
  continue; // 跳过默认语言文件生成
361
361
  }
362
362
  const langDir = path.join(outputRoot, langKey);
@@ -369,21 +369,21 @@ export async function excel2json(program) {
369
369
  });
370
370
  logger.info(`已生成语言文件:${filePath}`, true);
371
371
  }
372
- // 生成语言检测结果excel文件
373
- logger.info('开始生成语言检测结果excel文件', true);
374
- // 构造检测结果excel的表头:默认语言列 + 其他语言列(对应原文列名)
375
- // 这里表头用原excel的表头中对应语言列的值
372
+ // 生成语言检测结果Excel文件
373
+ logger.info('开始生成语言检测结果Excel文件', true);
374
+ // 构造检测结果Excel的表头:默认语言列 + 其他语言列(对应原文列名)
375
+ // 这里表头用原Excel的表头中对应语言列的值
376
376
  const errorSheetHeader = [];
377
- // 按列索引顺序遍历,匹配语言key,构造表头
377
+ // 按列索引顺序遍历,匹配语言KEY,构造表头
378
378
  Object.entries(colIndexToLangKey)
379
379
  .sort((a, b) => Number(a[0]) - Number(b[0]))
380
380
  .forEach(([colIdxStr, langKey]) => {
381
381
  const colIdx = Number(colIdxStr);
382
- // 表头为原excel表头中对应列的文字
382
+ // 表头为原Excel表头中对应列的文字
383
383
  errorSheetHeader.push(headerRow[colIdx]);
384
384
  });
385
- // 构造检测结果excel的内容,每一列对应语言检测错误描述
386
- // 每行对应原excel中一条数据行
385
+ // 构造检测结果Excel的内容,每一列对应语言检测错误描述
386
+ // 每行对应原Excel中一条数据行
387
387
  const dataRowCount = rows.length - 1;
388
388
  const errorSheetData = [errorSheetHeader];
389
389
  for (let i = 0; i < dataRowCount; i++) {
@@ -405,14 +405,14 @@ export async function excel2json(program) {
405
405
  });
406
406
  errorSheetData.push(rowErrors);
407
407
  }
408
- // 生成excel工作簿和工作表
408
+ // 生成Excel工作簿和工作表
409
409
  const errorWorkbook = XLSX.utils.book_new();
410
410
  const errorSheet = XLSX.utils.aoa_to_sheet(errorSheetData);
411
411
  XLSX.utils.book_append_sheet(errorWorkbook, errorSheet, 'LanguageCheckResults');
412
- // 写入检测结果excel文件,固定文件名 lang_check_results.xlsx
412
+ // 写入检测结果Excel文件,固定文件名 lang_check_results.xlsx
413
413
  const errorExcelPath = path.join(outputRoot, `lang_check_results.xlsx`);
414
414
  XLSX.writeFile(errorWorkbook, errorExcelPath);
415
- logger.info(`语言检测结果excel文件已生成:${errorExcelPath}`, true);
415
+ logger.info(`语言检测结果Excel文件已生成:${errorExcelPath}`, true);
416
416
  // 最终完成提示,包含输出目录
417
417
  logger.info(`全部转换完成,语言文件输出目录:${outputRoot}`, true);
418
418
  }
@@ -1,6 +1,6 @@
1
1
  import { select, Separator } from '@inquirer/prompts';
2
- import { excel2json } from './excel2json/index.js';
3
2
  import { logger, loggerError } from '../../utils/index.js';
3
+ import { excel2json } from './excel2json/index.js';
4
4
  /**
5
5
  * 国际化模块主入口
6
6
  * 提供多个国际化相关功能的交互式选择
@@ -17,19 +17,19 @@ export async function i18n(program) {
17
17
  description: '从所给路径中提取词条信息',
18
18
  },
19
19
  {
20
- name: 'jsonexcel',
20
+ name: 'JSONExcel',
21
21
  value: 'json2excel',
22
- description: '将json格式的词条信息转换为excel表格',
22
+ description: '将JSON格式的词条信息转换为Excel表格',
23
23
  },
24
24
  {
25
- name: 'exceljson',
25
+ name: 'ExcelJSON',
26
26
  value: 'excel2json',
27
- description: '将excel表格转换为json格式的词条信息',
27
+ description: '将Excel表格转换为JSON格式的词条信息',
28
28
  },
29
29
  {
30
- name: 'json合并',
30
+ name: 'JSON合并',
31
31
  value: 'jsonMerge',
32
- description: '合并多个json格式的词条信息文件',
32
+ description: '合并多个JSON格式的词条信息文件',
33
33
  },
34
34
  ];
35
35
  // 交互式选择需要执行的功能
@@ -1,3 +1,3 @@
1
1
  export function run() {
2
- console.log('jsonexcel');
2
+ console.log('JSONExcel');
3
3
  }
@@ -1,3 +1,3 @@
1
1
  export function run() {
2
- console.log('json合并');
2
+ console.log('JSON合并');
3
3
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 获取节假日信息并打印最近三个节假日详情,节假日名称黄色,序号无颜色
3
+ * 并打印请求和处理进度提示
4
+ */
5
+ export declare function getHolidayTime(): Promise<void>;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/getHolidayTime/index.ts"],"names":[],"mappings":"AA6HA;;;GAGG;AACH,wBAAsB,cAAc,kBAoCnC"}
@@ -0,0 +1,111 @@
1
+ import { getData } from '../../../api/index.js';
2
+ import api from '../../../api/interface.js';
3
+ import { logger, loggerError } from '../../../utils/index.js';
4
+ /**
5
+ * 计算从今天起最近的几个节假日信息
6
+ * @param data 节假日数据
7
+ * @param count 需要返回的节假日数量,默认3
8
+ * @returns 最近count个节假日的汇总信息数组
9
+ */
10
+ function getNearestHolidays(data, count = 3) {
11
+ const today = new Date();
12
+ // 过滤出所有节假日和调休日
13
+ const holidays = data.dates.filter((d) => d.type.toLowerCase() === 'public_holiday');
14
+ const transferWorkdays = data.dates.filter((d) => d.type.toLowerCase() === 'transfer_workday');
15
+ // 解析日期字符串为Date对象,确保时区正确
16
+ function parseDate(dateStr) {
17
+ return new Date(dateStr + 'T00:00:00');
18
+ }
19
+ // 按日期升序排序节假日
20
+ holidays.sort((a, b) => parseDate(a.date).getTime() - parseDate(b.date).getTime());
21
+ // 筛选今天及以后日期的节假日
22
+ const upcomingHolidays = holidays.filter((h) => parseDate(h.date) >= today);
23
+ // 将连续日期且名称相同的节假日合并为一个节日区间
24
+ const grouped = [];
25
+ let currentGroup = null;
26
+ for (const h of upcomingHolidays) {
27
+ const hd = parseDate(h.date);
28
+ if (!currentGroup) {
29
+ // 初始化第一个节日组
30
+ currentGroup = {
31
+ name: h.name,
32
+ daysUntil: Math.floor((hd.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)),
33
+ holidayDates: [h.date],
34
+ hasTransferWorkday: false,
35
+ transferWorkdays: [],
36
+ };
37
+ }
38
+ else {
39
+ // 判断是否是连续日期且名称相同,合并到当前组
40
+ const lastDate = parseDate(currentGroup.holidayDates[currentGroup.holidayDates.length - 1]);
41
+ const diffDays = (hd.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24);
42
+ if (diffDays === 1 && h.name === currentGroup.name) {
43
+ currentGroup.holidayDates.push(h.date);
44
+ }
45
+ else {
46
+ // 不连续或名称不同,保存当前组,开启新组
47
+ grouped.push(currentGroup);
48
+ currentGroup = {
49
+ name: h.name,
50
+ daysUntil: Math.floor((hd.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)),
51
+ holidayDates: [h.date],
52
+ hasTransferWorkday: false,
53
+ transferWorkdays: [],
54
+ };
55
+ }
56
+ }
57
+ }
58
+ if (currentGroup) {
59
+ grouped.push(currentGroup);
60
+ }
61
+ // 为每个节假日组匹配调休日(调休日距离节假日区间前后7天内视为相关)
62
+ for (const group of grouped) {
63
+ const start = parseDate(group.holidayDates[0]);
64
+ const end = parseDate(group.holidayDates[group.holidayDates.length - 1]);
65
+ const relatedTransfers = transferWorkdays.filter((t) => {
66
+ const td = parseDate(t.date);
67
+ return (Math.abs(td.getTime() - start.getTime()) <= 7 * 24 * 60 * 60 * 1000 ||
68
+ Math.abs(td.getTime() - end.getTime()) <= 7 * 24 * 60 * 60 * 1000);
69
+ });
70
+ if (relatedTransfers.length > 0) {
71
+ group.hasTransferWorkday = true;
72
+ group.transferWorkdays = relatedTransfers.map((t) => t.date);
73
+ }
74
+ }
75
+ // 返回最近count个节假日
76
+ return grouped.slice(0, count);
77
+ }
78
+ /**
79
+ * 获取节假日信息并打印最近三个节假日详情,节假日名称黄色,序号无颜色
80
+ * 并打印请求和处理进度提示
81
+ */
82
+ export async function getHolidayTime() {
83
+ try {
84
+ logger.info('开始获取节假日数据...', true);
85
+ const year = new Date().getFullYear();
86
+ const url = api.UNPKG_HOLIDAY_CALENDAR + `/${year}.json`;
87
+ const data = await getData(url);
88
+ logger.info('节假日数据请求成功,开始计算最近节假日...', true);
89
+ const nearest = getNearestHolidays(data, 3);
90
+ console.log('\x1b[36m%s\x1b[0m', '=== 最近三个节假日信息 ==='); // 青色标题
91
+ nearest.forEach((holiday, index) => {
92
+ console.log(`第${index + 1}个节假日: \x1b[33m\x1b[1m${holiday.name}\x1b[0m`); // 节假日名称黄色加粗
93
+ console.log(`距离今天还有: \x1b[32m${holiday.daysUntil} 天\x1b[0m`); // 绿色天数
94
+ console.log(`放假日期: \x1b[35m${holiday.holidayDates.join(', ')}\x1b[0m`); // 紫色日期
95
+ if (holiday.hasTransferWorkday) {
96
+ console.log(`包含调休日期: \x1b[31m${holiday.transferWorkdays.join(', ')}\x1b[0m`); // 红色调休日
97
+ }
98
+ else {
99
+ console.log('\x1b[37m无调休\x1b[0m'); // 白色无调休
100
+ }
101
+ console.log('\x1b[90m---------------------\x1b[0m'); // 灰色分割线
102
+ });
103
+ logger.info('节假日信息打印完成。', true);
104
+ }
105
+ catch (error) {
106
+ // 记录错误日志,方便排查
107
+ loggerError(error, logger);
108
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
109
+ process.exit(1);
110
+ }
111
+ }
@@ -0,0 +1,13 @@
1
+ type HolidayInfo = {
2
+ name: string;
3
+ start: Date;
4
+ end: Date;
5
+ daysLeft: number;
6
+ };
7
+ /**
8
+ * 获取最近两个节假日放假时间和距离天数
9
+ */
10
+ export declare function getHolidayTime(currentDate?: Date): Promise<HolidayInfo[]>;
11
+ export declare function holidayTime(): Promise<void>;
12
+ export {};
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/holidayTime/index.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAuEF;;GAEG;AACH,wBAAsB,cAAc,CAClC,WAAW,OAAa,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CA+DxB;AAED,wBAAsB,WAAW,kBAUhC"}
@@ -0,0 +1,116 @@
1
+ import axios from 'axios';
2
+ import { Lunar } from 'lunar-javascript';
3
+ const lunarHolidays = [
4
+ { name: '春节', lunarMonth: 1, lunarDay: 1, duration: 7 },
5
+ { name: '端午节', lunarMonth: 5, lunarDay: 5, duration: 3 },
6
+ { name: '中秋节', lunarMonth: 8, lunarDay: 15, duration: 3 },
7
+ ];
8
+ /**
9
+ * 解析 yyyy-mm-dd 字符串成 Date(00:00:00)
10
+ */
11
+ function parseDate(dateStr) {
12
+ return new Date(dateStr + 'T00:00:00');
13
+ }
14
+ /**
15
+ * 获取农历节日对应当年公历放假起止日期
16
+ */
17
+ function getLunarHolidayDates(year) {
18
+ return lunarHolidays.map((h) => {
19
+ const lunar = Lunar.fromYmd(year, h.lunarMonth, h.lunarDay);
20
+ const start = lunar.getSolar().toDate();
21
+ const end = new Date(start);
22
+ end.setDate(start.getDate() + h.duration - 1);
23
+ return { name: h.name, start, end, daysLeft: 0 };
24
+ });
25
+ }
26
+ /**
27
+ * 合并连续且同名的节假日日期区间
28
+ */
29
+ function mergeHolidays(holidays) {
30
+ if (holidays.length === 0)
31
+ return [];
32
+ holidays.sort((a, b) => a.start.getTime() - b.start.getTime());
33
+ const merged = [];
34
+ for (const h of holidays) {
35
+ const last = merged[merged.length - 1];
36
+ if (last &&
37
+ h.name === last.name &&
38
+ h.start.getTime() === last.end.getTime() + 86400000) {
39
+ // 连续且同名,延长结束日期
40
+ last.end = h.end;
41
+ }
42
+ else {
43
+ merged.push({ ...h });
44
+ }
45
+ }
46
+ return merged;
47
+ }
48
+ /**
49
+ * 计算两个日期相差天数(date2 - date1)
50
+ */
51
+ function daysBetween(date1, date2) {
52
+ const msPerDay = 1000 * 60 * 60 * 24;
53
+ return Math.floor((date2.getTime() - date1.getTime()) / msPerDay);
54
+ }
55
+ /**
56
+ * 获取最近两个节假日放假时间和距离天数
57
+ */
58
+ export async function getHolidayTime(currentDate = new Date()) {
59
+ const year = currentDate.getFullYear();
60
+ const today = new Date(currentDate.toDateString());
61
+ // 1. 获取当年官方节假日数据(含调休)
62
+ const res = await axios.get(`http://timor.tech/api/holiday/year`);
63
+ if (res.status !== 200) {
64
+ throw new Error('获取节假日数据失败');
65
+ }
66
+ const data = res.data;
67
+ // 2. 从API数据中筛选放假日期(holiday=true)
68
+ const apiHolidaysRaw = Object.entries(data.holiday)
69
+ .filter(([_, info]) => info.holiday)
70
+ .map(([date, info]) => ({
71
+ name: info.name,
72
+ start: parseDate(date),
73
+ end: parseDate(date),
74
+ daysLeft: 0,
75
+ }));
76
+ // 3. 合并连续放假日期(同名且连续)
77
+ const apiHolidays = mergeHolidays(apiHolidaysRaw);
78
+ // 4. 计算农历节日对应公历日期,补充可能遗漏的节日
79
+ const lunarHols = getLunarHolidayDates(year);
80
+ // 5. 合并API节假日和农历节假日(避免重复)
81
+ const allHolidays = [...apiHolidays];
82
+ lunarHols.forEach((lh) => {
83
+ const exists = allHolidays.some((h) => h.name === lh.name &&
84
+ h.start.getTime() === lh.start.getTime() &&
85
+ h.end.getTime() === lh.end.getTime());
86
+ if (!exists) {
87
+ allHolidays.push(lh);
88
+ }
89
+ });
90
+ // 6. 计算距离今天的天数,如果节日已过则计算下一年对应日期距离
91
+ const futureHolidays = allHolidays.map((h) => {
92
+ let daysLeft = daysBetween(today, h.start);
93
+ if (daysLeft < 0) {
94
+ // 节日已过,计算下一年对应节日日期
95
+ const nextYearStart = new Date(h.start);
96
+ nextYearStart.setFullYear(nextYearStart.getFullYear() + 1);
97
+ const nextYearEnd = new Date(h.end);
98
+ nextYearEnd.setFullYear(nextYearEnd.getFullYear() + 1);
99
+ daysLeft = daysBetween(today, nextYearStart);
100
+ return { name: h.name, start: nextYearStart, end: nextYearEnd, daysLeft };
101
+ }
102
+ return { ...h, daysLeft };
103
+ });
104
+ // 7. 按距离排序,返回最近两个节日
105
+ futureHolidays.sort((a, b) => a.daysLeft - b.daysLeft);
106
+ return futureHolidays.slice(0, 2);
107
+ }
108
+ export async function holidayTime() {
109
+ return getHolidayTime().then((holidays) => {
110
+ holidays.forEach((h) => {
111
+ console.log(`距离${h.name}放假还有 ${h.daysLeft} 天,放假时间:${h.start
112
+ .toISOString()
113
+ .slice(0, 10)} 至 ${h.end.toISOString().slice(0, 10)}`);
114
+ });
115
+ });
116
+ }
@@ -0,0 +1,8 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * 小工具模块主入口
4
+ * 提供多个小工具相关功能的交互式选择
5
+ * @param program Commander命令行实例,用于传递参数和配置
6
+ */
7
+ export declare function tools(program: Command): Promise<void>;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,OAAO,iBAoD3C"}
@@ -0,0 +1,56 @@
1
+ import { select, Separator } from '@inquirer/prompts';
2
+ import { logger, loggerError } from '../../utils/index.js';
3
+ import { getHolidayTime } from './getHolidayTime/index.js';
4
+ /**
5
+ * 小工具模块主入口
6
+ * 提供多个小工具相关功能的交互式选择
7
+ * @param program Commander命令行实例,用于传递参数和配置
8
+ */
9
+ export async function tools(program) {
10
+ try {
11
+ logger.info('小工具模块启动,等待用户选择功能');
12
+ // 定义可用功能选项
13
+ const moduleChoices = [
14
+ {
15
+ name: '获取假期时间',
16
+ value: 'getHolidayTime',
17
+ description: '获取今年的假期时间列表',
18
+ },
19
+ ];
20
+ // 交互式选择需要执行的功能
21
+ const answer = await select({
22
+ message: '请选择要执行的功能:',
23
+ choices: [
24
+ ...moduleChoices,
25
+ new Separator(), // 分割线,方便未来扩展更多功能
26
+ ],
27
+ default: 'getHolidayTime', // 默认选项
28
+ pageSize: 10, // 最大显示选项数
29
+ loop: true, // 是否循环滚动选项
30
+ });
31
+ // 查找选择功能的名称,方便日志输出
32
+ const selectedModule = moduleChoices.find((item) => item.value === answer);
33
+ if (!selectedModule) {
34
+ logger.warn('未选择有效功能,程序已退出');
35
+ process.exit(0);
36
+ }
37
+ logger.info(`用户选择功能:${selectedModule.name}`);
38
+ // 根据选择执行对应功能
39
+ switch (answer) {
40
+ case 'getHolidayTime':
41
+ logger.info(`${selectedModule.name}功能开始执行`);
42
+ await getHolidayTime();
43
+ logger.info(`${selectedModule.name}功能执行完成`);
44
+ break;
45
+ default:
46
+ logger.warn(`${selectedModule.name}功能暂未实现,程序已退出`);
47
+ process.exit(0);
48
+ }
49
+ }
50
+ catch (error) {
51
+ // 记录错误日志,方便排查
52
+ loggerError(error, logger);
53
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
54
+ process.exit(1);
55
+ }
56
+ }
@@ -0,0 +1,2 @@
1
+ export declare function printFish(): void;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/printFish/index.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,IAAI,IAAI,CA6ChC"}
@@ -0,0 +1,46 @@
1
+ export function printFish() {
2
+ console.log(`
3
+ ╔══════════════════════════════════════════════════════════════╗
4
+ ║ 🐟✨ 摸 鱼 盛 宴 ✨🐟 ║
5
+ ╠══════════════════════════════════════════════════════════════╣
6
+ ║ ║
7
+ ║ ~~~~~ ~~~ ~~~~~~~~ ~~~~~ ~~~ ║
8
+ ║ ~~~~ ~~~ ~~~~~~~~ ~~ ~~~~ ~~~ ~~~ ║
9
+ ║ ~~~ ~~~~~~~~~~ ~~~~~ ~~~~~~~~~~ ║
10
+ ║ ║
11
+ ║ ╭─────────╮ ║
12
+ ║ │ 👤 │ ║
13
+ ║ │ /|\\ │ ║
14
+ ║ │ / \\ │ ║
15
+ ║ ╰────┬────╯ ║
16
+ ║ │ ║
17
+ ║ │ ║
18
+ ║ │ 🎣 ║
19
+ ║ ↓ ║
20
+ ║ ║
21
+ ║ ╭────────────────────────────────────╮ ║
22
+ ║ │ ░░░░░░░░░░░░░░░░░░░░░░░░ │ ║
23
+ ║ │ ░░ 🌊 湖 塘 🌊 ░░ │ ║
24
+ ║ │ ░ ░ │ ║
25
+ ║ │ ░ 🐠 🐟 🐡 ░ │ ║
26
+ ║ │░ /≻\\ />\\ <\\≺ ░ │ ║
27
+ ║ │░ / \\ /___\\ / \\ ░ │ ║
28
+ ║ │░ o >< ))> (🐟 ) <) >< o ░ │ ║
29
+ ║ │░ \\__/ \\_/ \\__/ ░ │ ║
30
+ ║ │ ░ ░ │ ║
31
+ ║ │ ░░ ░░ │ ║
32
+ ║ │ ░░░░░░░░░░░░░░░░░░░░░░░░ │ ║
33
+ ║ ╰────────────────────────────────────╯ ║
34
+ ║ ║
35
+ ║ ~~~ ~~~~~~~~~~ ~~~~~ ~~~~~~~~~~ ║
36
+ ║ ~~~~ ~~~ ~~~~~~~~ ~~ ~~~~ ~~~ ~~~ ║
37
+ ║ ~~~~~ ~~~ ~~~~~~~~ ~~~~~ ~~~ ║
38
+ ║ ║
39
+ ║ 💬 "老板不在家,摸鱼乐哈哈!" ║
40
+ ║ 📈 摸鱼指数:███████████░░░░░░░ 75% ║
41
+ ║ ⚡ 摸鱼效率:██████████████░░░░ 85% ║
42
+ ║ 🛡️ 隐蔽指数:██████████████████ 100% ║
43
+ ║ ║
44
+ ╚══════════════════════════════════════════════════════════════╝
45
+ `);
46
+ }
@@ -1,6 +1,6 @@
1
- # 多语言 exceljson 工具
1
+ # 多语言 ExcelJSON 工具
2
2
 
3
- 该工具基于配置文件,实现将多语言 excel 文件转换为对应的 json 翻译文件,并支持语言检测和重复 key 处理,方便多语言项目的词条管理与质量控制。
3
+ 该工具基于配置文件,实现将多语言 Excel 文件转换为对应的 JSON 翻译文件,并支持语言检测和重复 Key 处理,方便多语言项目的词条管理与质量控制。
4
4
 
5
5
  ---
6
6
 
@@ -9,58 +9,58 @@
9
9
  1. **配置文件读取与解析**
10
10
  - 从本地 `setting.json` 文件读取国际化配置,包括默认语言、语言映射和语言长代码。
11
11
 
12
- 2. **excel 表头语言列匹配**
13
- - 根据配置自动识别 excel 表头中的语言列,支持大小写不敏感匹配和语言名称模糊匹配。
12
+ 2. **Excel 表头语言列匹配**
13
+ - 根据配置自动识别 Excel 表头中的语言列,支持大小写不敏感匹配和语言名称模糊匹配。
14
14
 
15
- 3. **excel 文件读取与数据解析**
16
- - 读取用户指定路径的 excel 文件,提取第一个工作表的内容。
15
+ 3. **Excel 文件读取与数据解析**
16
+ - 读取用户指定路径的 Excel 文件,提取第一个工作表的内容。
17
17
  - 解析表头和数据行,提取多语言词条。
18
18
 
19
- 4. **重复 json key 处理**
20
- - 对默认语言列的 key 进行去重处理,若存在重复,则在 key 前添加随机生成的6位编码前缀,确保 json key 唯一。
21
- - 其他语言的 key 也进行同样处理,避免冲突。
19
+ 4. **重复 JSON Key 处理**
20
+ - 对默认语言列的 Key 进行去重处理,若存在重复,则在 Key 前添加随机生成的6位编码前缀,确保 JSON Key 唯一。
21
+ - 其他语言的 Key 也进行同样处理,避免冲突。
22
22
 
23
23
  5. **批量语言检测**
24
24
  - 将所有语言词条拼接成字符串,一次性调用语言检测接口,减少请求次数,提高检测效率。
25
25
  - 解析检测结果,拆分到每条词条对应的错误信息。
26
26
 
27
- 6. **生成多语言 json 文件**
28
- - 为每个语言生成对应的 json 翻译文件,默认语言文件不生成。
27
+ 6. **生成多语言 JSON 文件**
28
+ - 为每个语言生成对应的 JSON 翻译文件,默认语言文件不生成。
29
29
 
30
- 7. **生成语言检测结果 excel 文件**
31
- - 生成包含所有语言检测错误信息的 excel 文件,方便查看和修正。
30
+ 7. **生成语言检测结果 Excel 文件**
31
+ - 生成包含所有语言检测错误信息的 Excel 文件,方便查看和修正。
32
32
 
33
33
  8. **命令行交互与日志记录**
34
- - 通过命令行交互输入 excel 文件路径。
34
+ - 通过命令行交互输入 Excel 文件路径。
35
35
  - 详细日志记录执行过程、错误和警告,便于排查问题。
36
36
 
37
37
  ---
38
38
 
39
39
  ## 使用说明
40
40
 
41
- 1. 准备好符合格式的 excel 文件,第一行为表头,包含语言列(如 `en`, `zh-CN` 等)。
41
+ 1. 准备好符合格式的 Excel 文件,第一行为表头,包含语言列(如 `en`, `zh-CN` 等)。
42
42
  2. 配置好 `setting.json`,包含国际化相关配置。
43
- 3. 运行工具,输入 excel 文件路径。
44
- 4. 程序将自动解析 excel,生成多语言 json 文件和语言检测结果 excel
45
- 5. 结果文件输出在 excel 文件所在目录的 `lang_时间戳` 文件夹中。
43
+ 3. 运行工具,输入 Excel 文件路径。
44
+ 4. 程序将自动解析 Excel,生成多语言 JSON 文件和语言检测结果 Excel
45
+ 5. 结果文件输出在 Excel 文件所在目录的 `lang_时间戳` 文件夹中。
46
46
 
47
47
  ---
48
48
 
49
49
  ## 代码结构简述
50
50
 
51
- - `formatKey` / `decodeKey`:处理重复 key,添加或去除随机编码前缀。
51
+ - `formatKey` / `decodeKey`:处理重复 Key,添加或去除随机编码前缀。
52
52
  - `loadConfig`:读取并校验配置文件。
53
- - `matchLangKey`:匹配 excel 表头语言列。
53
+ - `matchLangKey`:匹配 Excel 表头语言列。
54
54
  - `batchCheckTexts` / `parseCheckResultPerEntry`:批量调用语言检测并解析结果。
55
- - `excel2json`:主函数,完成 excel 读取、解析、语言检测、文件生成等流程。
55
+ - `excel2json`:主函数,完成 Excel 读取、解析、语言检测、文件生成等流程。
56
56
 
57
57
  ---
58
58
 
59
59
  ## 备注
60
60
 
61
- - 默认语言的 json 文件不会生成,默认语言词条作为 key 使用。
61
+ - 默认语言的 JSON 文件不会生成,默认语言词条作为 Key 使用。
62
62
  - 语言检测依赖在线服务,若获取语言列表失败则使用本地配置。
63
- - 重复 key 处理确保生成的 json 文件中不会有冲突的键名。
63
+ - 重复 Key 处理确保生成的 JSON 文件中不会有冲突的键名。
64
64
 
65
65
  ---
66
66
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "td-web-cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "A CLI tool for efficiency",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -17,6 +17,9 @@
17
17
  "format": "prettier --write \"src/**/*.{js,ts,tsx,json,md}\"",
18
18
  "release": "node ./release.js"
19
19
  },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
20
23
  "keywords": [],
21
24
  "author": "",
22
25
  "license": "ISC",