td-web-cli 0.1.34 → 0.1.35

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.
@@ -1,7 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  /**
3
- * 主函数:从插入目录按指定 keys 批量插入到源目录
4
- * @param program Commander 实例(保留扩展可能)
3
+ * 主函数:从插入目录按指定 keys 批量插入到被插入目录
5
4
  */
6
5
  export declare function jsonInsert(program: Command): Promise<void>;
7
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/jsonInsert/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoFpC;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBA0JhD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/jsonInsert/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0HpC;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBAoHhD"}
@@ -2,90 +2,111 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { input, select, Separator } from '@inquirer/prompts';
4
4
  import { logger, loggerError, normalizeGitBashPath, readJsonFile, writeJsonFile, getJsonFilesInLangDir, validatePathInput, } from '../../../utils/index.js';
5
+ /** 获取目录下的一级子文件夹名称 */
6
+ function getSubDirectories(dirPath) {
7
+ return fs.readdirSync(dirPath).filter((name) => fs.statSync(path.join(dirPath, name)).isDirectory());
8
+ }
9
+ /** 校验 JSON 文件路径输入 */
10
+ function validateJsonFileInput(value) {
11
+ const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
12
+ if (!cleaned)
13
+ return '路径不能为空';
14
+ const normalized = normalizeGitBashPath(cleaned);
15
+ if (!fs.existsSync(normalized))
16
+ return '文件不存在,请输入有效路径';
17
+ if (!fs.statSync(normalized).isFile())
18
+ return '请输入 JSON 文件路径';
19
+ if (!normalized.endsWith('.json'))
20
+ return '请输入 .json 文件路径';
21
+ return true;
22
+ }
5
23
  /**
6
- * 将指定 keys 从插入对象合并到源对象
7
- * @param baseObj 源 JSON 对象(会被原地修改)
8
- * @param insertObj 待插入的 JSON 对象
9
- * @param keys 需要插入的键数组
10
- * @param langKey 当前语言标识(用于日志)
11
- * @param strategy 冲突处理策略:直接覆盖或手动选择
12
- * @returns 返回修改后的源对象(与 baseObj 同一引用)
24
+ * JSON 文件中提取待插入的 key 列表
25
+ * 支持对象(取顶层 key)或字符串数组两种格式
13
26
  */
14
- async function insertKeysIntoObject(baseObj, insertObj, keys, langKey, strategy) {
27
+ function extractKeysFromJsonFile(filePath) {
28
+ const content = fs.readFileSync(filePath, 'utf-8');
29
+ let parsed;
30
+ try {
31
+ parsed = JSON.parse(content);
32
+ }
33
+ catch {
34
+ throw new Error(`JSON 文件解析失败: ${filePath}`);
35
+ }
36
+ if (Array.isArray(parsed)) {
37
+ const keys = parsed
38
+ .filter((item) => typeof item === 'string')
39
+ .map((key) => key.trim())
40
+ .filter((key) => key.length > 0);
41
+ return [...new Set(keys)];
42
+ }
43
+ if (parsed && typeof parsed === 'object') {
44
+ return Object.keys(parsed);
45
+ }
46
+ throw new Error('JSON 文件格式无效,需为对象或字符串数组');
47
+ }
48
+ /**
49
+ * 将指定 keys 从插入对象合并到被插入对象
50
+ */
51
+ async function insertKeysIntoObject(baseObj, insertObj, keys, strategy) {
15
52
  for (const key of keys) {
16
- // 插入文件中不存在该键 → 跳过
17
53
  if (!(key in insertObj)) {
18
54
  logger.warn(`键 "${key}" 在插入文件中不存在,已跳过`, true);
19
55
  continue;
20
56
  }
21
57
  const insertVal = insertObj[key];
22
- // 源文件中不存在该键 → 直接新增
23
58
  if (!(key in baseObj)) {
24
59
  baseObj[key] = insertVal;
25
60
  logger.info(`新增键: ${key}`, true);
26
61
  continue;
27
62
  }
28
- // 键已存在且值相同 → 跳过
29
63
  if (baseObj[key] === insertVal) {
30
64
  logger.info(`键 "${key}" 值相同,已跳过`, true);
31
65
  continue;
32
66
  }
33
- // 键已存在且值不同 → 根据策略处理
34
67
  logger.info(`键 "${key}" 已存在且值不同`, true);
35
68
  if (strategy === 'overwrite') {
36
- // 直接覆盖
69
+ baseObj[key] = insertVal;
70
+ logger.info(`已用插入值覆盖键 "${key}"`, true);
71
+ continue;
72
+ }
73
+ const choice = await select({
74
+ message: `请选择要保留的值:`,
75
+ choices: [
76
+ { name: `被插入文件值: ${baseObj[key]}`, value: 'base' },
77
+ { name: `插入文件值: ${insertVal}`, value: 'insert' },
78
+ new Separator(),
79
+ ],
80
+ default: 'base',
81
+ loop: true,
82
+ });
83
+ if (choice === 'insert') {
37
84
  baseObj[key] = insertVal;
38
85
  logger.info(`已用插入值覆盖键 "${key}"`, true);
39
86
  }
40
87
  else {
41
- // 手动选择
42
- const choice = await select({
43
- message: `请选择要保留的值:`,
44
- choices: [
45
- { name: `源文件值: ${baseObj[key]}`, value: 'base' },
46
- { name: `插入文件值: ${insertVal}`, value: 'insert' },
47
- new Separator(), // 分割线,方便未来扩展更多功能
48
- ],
49
- default: 'base',
50
- loop: true,
51
- });
52
- if (choice === 'insert') {
53
- baseObj[key] = insertVal;
54
- logger.info(`已用插入值覆盖键 "${key}"`, true);
55
- }
56
- else {
57
- logger.info(`保留源文件值,键 "${key}" 未更改`, true);
58
- }
88
+ logger.info(`保留被插入文件值,键 "${key}" 未更改`, true);
59
89
  }
60
90
  }
61
91
  return baseObj;
62
92
  }
63
93
  /**
64
- * 主函数:从插入目录按指定 keys 批量插入到源目录
65
- * @param program Commander 实例(保留扩展可能)
94
+ * 主函数:从插入目录按指定 keys 批量插入到被插入目录
66
95
  */
67
96
  export async function jsonInsert(program) {
68
97
  try {
69
- // 获取源目录路径
70
98
  const srcDir = await input({
71
- message: '请输入源 JSON 文件夹路径(含语言子文件夹,如 cn/translate.json):',
99
+ message: '请输入被插入 JSON 文件夹路径(含语言子文件夹,如 cn/translate.json):',
72
100
  validate: validatePathInput,
73
101
  });
74
- // 获取待插入目录路径
75
102
  const insertDir = await input({
76
103
  message: '请输入待插入 JSON 文件夹路径(含语言子文件夹,如 cn/translate.json):',
77
104
  validate: validatePathInput,
78
105
  });
79
- // 获取需要插入的 keys
80
- const keysInput = await input({
81
- message: '请输入需要插入的 JSON key(多个 key 请用英文逗号分隔):',
82
- validate: (value) => {
83
- if (!value.trim())
84
- return '键不能为空';
85
- return true;
86
- },
106
+ const keysFileInput = await input({
107
+ message: '请输入 key 来源 JSON 文件路径(从中读取需要插入的 key):',
108
+ validate: validateJsonFileInput,
87
109
  });
88
- // 冲突处理策略选择
89
110
  const conflictStrategy = await select({
90
111
  message: '当目标键已存在且值不同时,请选择处理方式:',
91
112
  choices: [
@@ -96,69 +117,46 @@ export async function jsonInsert(program) {
96
117
  default: 'manual',
97
118
  loop: true,
98
119
  });
99
- // 路径标准化
100
120
  const srcPath = normalizeGitBashPath(srcDir);
101
121
  const insertPath = normalizeGitBashPath(insertDir);
102
- // 解析并清洗 keys
103
- const keys = keysInput
104
- .split(',')
105
- .map((k) => k.trim())
106
- .filter((k) => k.length > 0);
122
+ const keysFilePath = normalizeGitBashPath(keysFileInput);
123
+ const keys = extractKeysFromJsonFile(keysFilePath);
107
124
  if (keys.length === 0) {
108
- logger.info('未输入有效 key,操作取消');
125
+ logger.info('key 来源文件中未找到有效 key,操作取消');
109
126
  return;
110
127
  }
111
- logger.info(`源目录: ${srcPath}`);
128
+ logger.info(`被插入目录: ${srcPath}`);
112
129
  logger.info(`插入目录: ${insertPath}`);
130
+ logger.info(`key 来源文件: ${keysFilePath}`);
113
131
  logger.info(`待插入 key: ${keys.join(', ')}`);
114
132
  logger.info(`冲突处理策略: ${conflictStrategy === 'overwrite' ? '直接覆盖' : '手动选择'}`);
115
- // 获取共同的顶层语言子文件夹
116
- const srcLangDirs = fs
117
- .readdirSync(srcPath)
118
- .filter((f) => fs.statSync(path.join(srcPath, f)).isDirectory());
119
- const insertLangDirs = fs
120
- .readdirSync(insertPath)
121
- .filter((f) => fs.statSync(path.join(insertPath, f)).isDirectory());
122
- const commonLangDirs = srcLangDirs.filter((lang) => insertLangDirs.includes(lang));
133
+ const commonLangDirs = getSubDirectories(srcPath).filter((lang) => getSubDirectories(insertPath).includes(lang));
123
134
  if (commonLangDirs.length === 0) {
124
135
  logger.info('没有发现相同语言文件夹,操作取消');
125
136
  return;
126
137
  }
127
138
  logger.info(`发现 ${commonLangDirs.length} 个共同语言文件夹: ${commonLangDirs.join(', ')}`, true);
128
- // 逐语言文件夹处理
129
139
  for (const langKey of commonLangDirs) {
130
140
  logger.info(`${'='.repeat(60)}`, true);
131
141
  logger.info(`处理语言: ${langKey}`, true);
132
142
  const srcLangPath = path.join(srcPath, langKey);
133
143
  const insertLangPath = path.join(insertPath, langKey);
134
- const srcJsonFiles = getJsonFilesInLangDir(srcLangPath);
135
144
  const insertJsonFiles = getJsonFilesInLangDir(insertLangPath);
136
- // 找出该语言下共同存在的 JSON 文件
137
- const commonJsonFiles = srcJsonFiles.filter((file) => insertJsonFiles.includes(file));
145
+ const commonJsonFiles = getJsonFilesInLangDir(srcLangPath).filter((file) => insertJsonFiles.includes(file));
138
146
  if (commonJsonFiles.length === 0) {
139
147
  logger.info(`语言【${langKey}】下没有共同的 JSON 文件,跳过`, true);
140
148
  continue;
141
149
  }
142
150
  logger.info(`发现 ${commonJsonFiles.length} 个共同 JSON 文件: ${commonJsonFiles.join(', ')}`, true);
143
- // 逐文件插入
144
151
  for (const jsonFile of commonJsonFiles) {
145
152
  logger.info(`处理文件: ${jsonFile}`, true);
146
153
  const srcFile = path.join(srcLangPath, jsonFile);
147
154
  const insertFile = path.join(insertLangPath, jsonFile);
148
- if (!fs.existsSync(srcFile) || !fs.existsSync(insertFile)) {
149
- logger.warn(`文件缺失,跳过: ${jsonFile}`, true);
150
- continue;
151
- }
152
- // 读取 JSON
153
155
  const srcJson = readJsonFile(srcFile);
154
156
  const insertJson = readJsonFile(insertFile);
155
- const beforeCount = Object.keys(srcJson).length;
156
- logger.info(`源文件原有键数: ${beforeCount}`, true);
157
- // 执行插入
158
- const updated = await insertKeysIntoObject(srcJson, insertJson, keys, langKey, conflictStrategy);
159
- const afterCount = Object.keys(updated).length;
160
- logger.info(`插入后键数: ${afterCount}`, true);
161
- // 写回源文件
157
+ logger.info(`被插入文件原有键数: ${Object.keys(srcJson).length}`, true);
158
+ const updated = await insertKeysIntoObject(srcJson, insertJson, keys, conflictStrategy);
159
+ logger.info(`插入后键数: ${Object.keys(updated).length}`, true);
162
160
  writeJsonFile(srcFile, updated);
163
161
  logger.info(`文件 ${jsonFile} 已更新`, true);
164
162
  }
@@ -166,7 +164,7 @@ export async function jsonInsert(program) {
166
164
  }
167
165
  logger.info(`${'='.repeat(60)}`, true);
168
166
  logger.info(`所有插入操作完成!`, true);
169
- logger.info(`源目录已更新: ${srcPath}`, true);
167
+ logger.info(`被插入目录已更新: ${srcPath}`, true);
170
168
  }
171
169
  catch (error) {
172
170
  loggerError(error, logger);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "td-web-cli",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "A CLI tool for efficiency",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",