@zzclub/z-cli 0.7.1 → 1.0.0

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 (64) hide show
  1. package/LICENSE +20 -20
  2. package/dist/commands/config.d.ts +9 -0
  3. package/dist/commands/config.d.ts.map +1 -0
  4. package/dist/commands/config.js +33 -0
  5. package/dist/commands/config.js.map +1 -0
  6. package/dist/commands/set.d.ts +11 -0
  7. package/dist/commands/set.d.ts.map +1 -0
  8. package/dist/commands/set.js +43 -0
  9. package/dist/commands/set.js.map +1 -0
  10. package/dist/commands/tiny/compressor.d.ts +11 -0
  11. package/dist/commands/tiny/compressor.d.ts.map +1 -0
  12. package/dist/commands/tiny/compressor.js +46 -0
  13. package/dist/commands/tiny/compressor.js.map +1 -0
  14. package/dist/commands/tiny/file-processor.d.ts +40 -0
  15. package/dist/commands/tiny/file-processor.d.ts.map +1 -0
  16. package/dist/commands/tiny/file-processor.js +137 -0
  17. package/dist/commands/tiny/file-processor.js.map +1 -0
  18. package/dist/commands/tiny/index.d.ts +15 -0
  19. package/dist/commands/tiny/index.d.ts.map +1 -0
  20. package/dist/commands/tiny/index.js +55 -0
  21. package/dist/commands/tiny/index.js.map +1 -0
  22. package/dist/commands/tiny/types.d.ts +55 -0
  23. package/dist/commands/tiny/types.d.ts.map +1 -0
  24. package/dist/commands/tiny/types.js +5 -0
  25. package/dist/commands/tiny/types.js.map +1 -0
  26. package/dist/core/config-manager.d.ts +38 -0
  27. package/dist/core/config-manager.d.ts.map +1 -0
  28. package/dist/core/config-manager.js +87 -0
  29. package/dist/core/config-manager.js.map +1 -0
  30. package/dist/core/logger.d.ts +17 -0
  31. package/dist/core/logger.d.ts.map +1 -0
  32. package/dist/core/logger.js +30 -0
  33. package/dist/core/logger.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +177 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/types/config.d.ts +32 -0
  39. package/dist/types/config.d.ts.map +1 -0
  40. package/dist/types/config.js +18 -0
  41. package/dist/types/config.js.map +1 -0
  42. package/dist/types/index.d.ts +4 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +3 -0
  45. package/dist/types/index.js.map +1 -0
  46. package/package.json +34 -19
  47. package/readme.md +457 -0
  48. package/CHANGELOG.md +0 -211
  49. package/README.md +0 -329
  50. package/src/command/config.js +0 -97
  51. package/src/command/i18n.js +0 -318
  52. package/src/command/index.js +0 -30
  53. package/src/command/picgo.js +0 -122
  54. package/src/command/replace.js +0 -81
  55. package/src/command/set.js +0 -47
  56. package/src/command/tiny.js +0 -328
  57. package/src/command/translate.js +0 -337
  58. package/src/config.json +0 -16
  59. package/src/index.js +0 -31
  60. package/src/translate-api/index.js +0 -77
  61. package/src/translate-api/md5.js +0 -231
  62. package/src/utils/common.js +0 -218
  63. package/src/utils/file.js +0 -61
  64. package/src/utils/picgo.js +0 -149
@@ -1,328 +0,0 @@
1
- import {
2
- writeFileContent,
3
- getLocalConfig,
4
- setHighLightStr,
5
- } from "../utils/common.js";
6
-
7
- import { getFormatedFileSize, replaceFileContent } from "../utils/file.js";
8
- import chalk from "chalk";
9
- import ora from "ora";
10
- import path from "node:path";
11
- import { dirname } from "node:path";
12
- import { fileURLToPath } from "node:url";
13
- import fs from "node:fs";
14
- import sharp from "sharp";
15
- import { picgoCmd } from "./picgo.js";
16
-
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = dirname(__filename);
19
-
20
- const supportFileTypes = ["png", "jpg", "jpeg", "gif", "webp"];
21
-
22
- export const tinyCmd = {
23
- name: "tiny",
24
- description: "压缩图片",
25
- options: [
26
- {
27
- flags: "-t, --type <fileType>",
28
- description: "转换后的图片类型",
29
- defaultValue: null,
30
- },
31
- {
32
- flags: "-f, --file <file>",
33
- description: "要压缩的图片文件",
34
- defaultValue: null,
35
- },
36
- {
37
- flags: "-d, --dir <dir>",
38
- description: "压缩文件夹内所有文件",
39
- defaultValue: null,
40
- },
41
- {
42
- flags: "-co, --condition <condition>",
43
- description: "压缩文件夹内所有名称包含[--condition]的图片文件",
44
- defaultValue: null,
45
- },
46
- {
47
- flags: "-q, --quality <quality>",
48
- description: "压缩质量(1-100)",
49
- defaultValue: 75,
50
- },
51
- {
52
- flags: "-c, --colours <colours>",
53
- description: "GIF色彩保留(2-256)",
54
- defaultValue: 128,
55
- },
56
- {
57
- flags: "-n, --name <name>",
58
- description: "指定文件名输出",
59
- defaultValue: "",
60
- },
61
- {
62
- flags: "-m, --max <max>",
63
- description: "限制要上传的文件大小(仅当开启 --picgo 时会用到)",
64
- defaultValue: 60,
65
- },
66
- {
67
- flags: "--picgo [type]",
68
- description: "是否调用picgo",
69
- defaultValue: null,
70
- },
71
- {
72
- flags: "--no-picgo [type]",
73
- description: "是否调用picgo",
74
- defaultValue: null,
75
- },
76
- {
77
- flags: "-ref, --replace-file <replaceFile>",
78
- description: "要替换内容的文件",
79
- defaultValue: "",
80
- },
81
- {
82
- flags: "--no-replace [type]",
83
- description:
84
- "是否替换指定文件的内容, 替换规则斤仅针对Obsidian, 请看readme.md",
85
- defaultValue: null,
86
- },
87
- {
88
- flags: "--replace [type]",
89
- description:
90
- "是否替换指定文件的内容, 替换规则斤仅针对Obsidian, 请看readme.md",
91
- defaultValue: null,
92
- },
93
- ],
94
- action: async (option) => {
95
- let spinner = ora();
96
- let config = await getLocalConfig();
97
- // console.log(`OPTION`, option);
98
- let { type, file, dir, condition, quality, colours } = option;
99
-
100
- if (!file && !dir && !condition) {
101
- spinner.fail("请指定要压缩的文件(--file=xxx)或文件夹(--dir=/a/b)");
102
- process.exit(1);
103
- }
104
-
105
- // 是否需要替换内容, true时 需要收集list<Map>
106
- let isNeedUploadAndReplace = !!(
107
- // option.picgo &&
108
- (option.replace && option.replaceFile)
109
- );
110
- let replaceMaps = [];
111
-
112
- function collectReplaceMaps(replaceMaps, { text, newText }) {
113
- if (isNeedUploadAndReplace) {
114
- let map = {
115
- text, // obsidian md 文件中 粘贴后的 值
116
- newText, // 此时是压缩后的文件名, 在picgo里再替换成上传后的
117
- };
118
- replaceMaps.push(map);
119
- }
120
- }
121
- async function tinyFile(file, statisticsCount, options = {}) {
122
- try {
123
- let stats = fs.statSync(file);
124
- let isFile = stats.isFile();
125
- if (isFile) {
126
- // 文件类型
127
- let extname = path.extname(file);
128
- // 带后缀的文件名
129
- let fileName = path.basename(file)?.split(".")[0];
130
- // 文件路径
131
- let dirPath = path.dirname(file);
132
- // 文件类型
133
- let filetype = extname.slice(1);
134
- let fileSize = stats.size;
135
- let beforeSize = getFormatedFileSize(fileSize);
136
- // 是否在支持的格式里
137
- // 如果正在批量压缩, 并且要求指定名称输出, 则加一个index后缀, 避免重名
138
- let customFileName;
139
- if (options.count && options.count > 1 && option.name) {
140
- customFileName = `${option.name}${options.order + 1}`;
141
- } else {
142
- customFileName = option.name || `${fileName}-tiny`;
143
- }
144
-
145
- let outputPath =
146
- path.resolve(process.cwd(), dirPath) +
147
- "/" +
148
- customFileName +
149
- extname;
150
- let inputPath = path.resolve(process.cwd(), file);
151
- if (extname.slice(1) === "gif") {
152
- // console.log(`option.colours`, option.colours);
153
- await sharp(inputPath, {
154
- animated: true,
155
- limitInputPixels: false,
156
- })
157
- .gif({
158
- colours: +option.colours,
159
- })
160
- .toFile(outputPath);
161
- let afterStats = fs.statSync(outputPath);
162
- let afterSize = getFormatedFileSize(afterStats.size);
163
- let offPercent = ((1 - afterStats.size / fileSize) * 100).toFixed(
164
- 2
165
- );
166
- spinner.succeed(
167
- `${chalk.red(beforeSize)} ${chalk.yellowBright(
168
- "=>"
169
- )} ${chalk.green(afterSize)} (${
170
- offPercent >= 0 ? chalk.green("↓") : chalk.red("↑")
171
- }${Math.abs(offPercent)}%)【 ${customFileName}${extname} 】`
172
- );
173
- // 收集压缩前后的文件名映射关系
174
- collectReplaceMaps(replaceMaps, {
175
- text: `${path.basename(file)}`,
176
- newText: `${customFileName}${extname}`,
177
- });
178
- statisticsCount.ok++;
179
- statisticsCount.okFiles.push(outputPath);
180
- } else {
181
- if (sharp(inputPath)[filetype]) {
182
- await sharp(path.resolve(process.cwd(), file))
183
- [filetype]({ quality: +quality })
184
- .toFile(outputPath);
185
- let afterStats = fs.statSync(outputPath);
186
- let afterSize = getFormatedFileSize(afterStats.size);
187
- let offPercent = ((1 - afterStats.size / fileSize) * 100).toFixed(
188
- 2
189
- );
190
- spinner.succeed(
191
- `${chalk.red(beforeSize)} ${chalk.yellowBright(
192
- "=>"
193
- )} ${chalk.green(afterSize)} (${
194
- offPercent >= 0 ? chalk.green("↓") : chalk.red("↑")
195
- }${Math.abs(offPercent)}%)【 ${customFileName}${extname} 】`
196
- );
197
- // 收集压缩前后的文件名映射关系
198
- collectReplaceMaps(replaceMaps, {
199
- text: `${path.basename(file)}`,
200
- newText: `${customFileName}${extname}`,
201
- });
202
- statisticsCount.ok++;
203
- statisticsCount.okFiles.push(outputPath);
204
- } else {
205
- spinner.fail(
206
- `不支持此文件类型[${filetype || fileName || file}]!`
207
- );
208
- statisticsCount.error++;
209
- statisticsCount.okFiles.push(outputPath);
210
- }
211
- }
212
- }
213
- } catch (err) {
214
- // console.log(`err`, err);
215
- spinner.fail("出错啦, 文件不存在或不支持此类型 \n" + err);
216
- statisticsCount.error++;
217
- statisticsCount.errFiles.push(outputPath);
218
- }
219
- }
220
-
221
- let statisticsCount = {
222
- ok: 0,
223
- okFiles: [],
224
- err: 0,
225
- errFiles: [],
226
- };
227
- // 指定了文件, 压缩
228
- if (file) {
229
- await tinyFile(file, statisticsCount);
230
- }
231
-
232
- // 指定文件夹, 翻译文件夹内所有文件
233
- if (dir || condition) {
234
- let inputDirPath = dir || "./";
235
- let files;
236
- try {
237
- // 读取所有文件
238
- files = fs.readdirSync(inputDirPath);
239
- // spinner.succeed(`共有${files.length}个文件`);
240
- } catch (err) {
241
- spinner.fail("出错啦, 文件夹似乎不存在");
242
- process.exit(1);
243
- }
244
-
245
- for (let i = 0; i < files.length; i++) {
246
- let file = files[i];
247
- let filePath = path.join(inputDirPath, file);
248
- let stats = fs.statSync(filePath);
249
- if (stats.isFile()) {
250
- if (condition) {
251
- let fileName = path.basename(file);
252
- let index = fileName.indexOf(condition);
253
- if (index > -1) {
254
- let tip = setHighLightStr(fileName, condition);
255
- spinner.succeed(`${tip}正在压缩`);
256
- await tinyFile(filePath, statisticsCount, {
257
- count: files.length,
258
- order: i,
259
- });
260
- }
261
- } else {
262
- spinner.succeed(`正在压缩:${file}`);
263
- await tinyFile(filePath, statisticsCount, {
264
- count: files.length,
265
- order: i,
266
- });
267
- }
268
- } else {
269
- spinner.fail(`${filePath}不是文件`);
270
- }
271
- }
272
- spinner.succeed(`压缩成功 ${chalk.green(statisticsCount.ok)} 个`);
273
- }
274
-
275
- // 自动替换wiki链接
276
- // 因为obsidian里已经有了一个插件, 可以自动上传到picgo并替换链接, 功能重复度很高
277
- // 后续考虑给作者提个pr, 加入压缩功能
278
- // console.log(`fileMaps`, replaceMaps);
279
- if (option.replace) {
280
- // 上传成功改的图片文件
281
- let tinyFiles = statisticsCount.okFiles;
282
- if (tinyFiles && tinyFiles.length) {
283
- // await batchUploadByPicGo(tinyFiles, option);
284
- if (!option.replace) process.exit(1);
285
- spinner.start("开始替换文件内容, 替换后请仔细检查!");
286
- console.log(`replaceMaps`, replaceMaps);
287
- const fileContent = await replaceFileContent(
288
- option.replaceFile,
289
- replaceMaps
290
- );
291
- console.log(`fileContent`, fileContent);
292
- if (!fileContent) {
293
- spinner.fail("替换文件内容失败! 请检查要替换的文件是否存在!");
294
- process.exit(1);
295
- }
296
- try {
297
- fs.writeFileSync(option.replaceFile, fileContent, "utf-8");
298
- spinner.succeed(
299
- `[${chalk.red(
300
- path.basename(option.replaceFile)
301
- )}]图片链接替换完成!请前往检查!`
302
- );
303
- } catch (err) {
304
- spinner.fail("图片链接替换失败!");
305
- }
306
- }
307
- // 处理完后不再上传picgo
308
- process.exit(1);
309
- }
310
- // 自动上传至picgo
311
- if (option.picgo) {
312
- spinner.start(`正在连接picgo`);
313
- await picgoCmd.action({
314
- tinyFiles: statisticsCount.okFiles,
315
- max: option.max,
316
- replace: isNeedUploadAndReplace,
317
- replaceFile: option.replaceFile,
318
- replaceMaps,
319
- });
320
- spinner.stop();
321
-
322
- // 只有开启了picgo后时 才会使用替换功能 后续替换功能会独立出去 目前仅为满足自己所需
323
- // if (option.replace && option.replaceFile) {
324
- // spinner.start(`开始替换内容, 替换后请仔细检查!`);
325
- // }
326
- }
327
- },
328
- };
@@ -1,337 +0,0 @@
1
- import path from "node:path";
2
- import fs from "node:fs";
3
- import chalk from "chalk";
4
- import { translate } from "../translate-api/index.js";
5
- import { readJsonFile } from "../utils/file.js";
6
- import { writeFileContent, getLocalConfig } from "../utils/common.js";
7
- import ora from "ora";
8
-
9
- const translateCmd = {
10
- name: "translate",
11
- alias: "trans",
12
- description: "中译英功能,支持批量和单个文件翻译",
13
- // options: ['-l, --language <language>', '转换为什么语言, 支持[zh]和[en]', 'en'],
14
- options: [
15
- {
16
- flags: "-l, --language <language>",
17
- description: "目前只支持从中文到英文",
18
- defaultValue: "en",
19
- },
20
- {
21
- flags: "-f, --file <file>",
22
- description: "转换文件的路径",
23
- defaultValue: null,
24
- },
25
- {
26
- flags: "-d, --dir <dirpath>",
27
- description: "转换文件夹的路径",
28
- defaultValue: null,
29
- },
30
- ],
31
- action: async (option) => {
32
- let filePath = option.file;
33
- let dirPath = option.dir;
34
- let file_spinner = ora();
35
- if (!filePath && !dirPath) {
36
- file_spinner.fail('请指定文件或目录')
37
- process.exit(1);
38
- }
39
-
40
- let config = await getLocalConfig();
41
- const translateConfig = config.translate;
42
- if (!translateConfig.account.appId || !translateConfig.account.key) {
43
- file_spinner.fail("请先设置appId和key后再使用翻译功能");
44
- process.exit(1);
45
- }
46
- // 有文件夹路径时忽略文件
47
- if (dirPath) {
48
- dirPath = path.resolve(process.cwd(), dirPath);
49
- let stat;
50
- try {
51
- stat = fs.statSync(dirPath);
52
- } catch (err) {
53
- file_spinner.fail(`${chalk.red(dirPath)}不存在!`);
54
- return;
55
- }
56
-
57
- if (!stat.isDirectory()) {
58
- file_spinner.fail(`${chalk.red(dirPath)}不是一个文件夹!`);
59
- return;
60
- } else {
61
- let filePaths = [];
62
- file_spinner.succeed(`开始检索${chalk.red(dirPath)}`);
63
-
64
- getAllFilePaths(translateConfig, dirPath, filePaths);
65
-
66
- // log.success(`共找到${chalk.red(filePaths.length)}个要翻译的文件`);
67
- if (filePaths.length) {
68
- file_spinner.succeed(
69
- `共找到${chalk.red(filePaths.length)}个要翻译的文件`
70
- );
71
- file_spinner.start();
72
- await execWorkerSync(filePaths, 0);
73
- file_spinner.stop();
74
- } else {
75
- // log.success(`Exit`);
76
- file_spinner.warn(
77
- `共找到${chalk.red(filePaths.length)}个要翻译的文件`
78
- );
79
- }
80
- }
81
- } else {
82
- file_spinner.succeed(`正在翻译${chalk.yellowBright(filePath)}`);
83
- file_spinner.start();
84
- let file_content = await readAndTranslateFileContent(filePath);
85
- let fileName = path.basename(filePath);
86
- let dirPath = path.dirname(filePath);
87
- let newFileName =
88
- fileName.split(".")[0] +
89
- `-${option.language}.` +
90
- fileName.split(".")[1];
91
- let newFilePath = dirPath + "/" + newFileName;
92
- writeFileContent(newFilePath, file_content, (spinner, isOk) => {
93
- if (isOk) {
94
- spinner.succeed("翻译结束");
95
- } else {
96
- spinner.fail("翻译失败!");
97
- }
98
-
99
- file_spinner.stop();
100
- });
101
- }
102
- },
103
- };
104
-
105
- /**
106
- * 递归处理i18n配置对象
107
- * @param config i18n配置js 一般为langs文件下的js文件
108
- * @description 把js对象处理成 [ { keys: ['common', 'title'], value: '要翻译的值'} ] 每个要翻译的中文为一个item keys表示他在对象里的位置
109
- */
110
- function parseConfigs(config) {
111
- let words = [];
112
-
113
- parseConfig(config, null);
114
- function parseConfig(config, curItem) {
115
- let keys = Object.keys(config);
116
- keys.forEach((key) => {
117
- let item = {
118
- keys: curItem ? curItem.keys.concat([key]) : [key],
119
- value: config[key],
120
- };
121
- // 对象的value为string时则为要翻译的值
122
- if (typeof item.value === "string") {
123
- words.push(item);
124
- } else {
125
- parseConfig(item.value, item);
126
- }
127
- });
128
- }
129
- return words;
130
- }
131
-
132
- /**
133
- * 把所有要翻译的词分组 每秒有查询次数限制
134
- * @param words 处理好的数据
135
- * @param limitLength 每秒查几个词
136
- * @returns {*[]} 处理后的二维数组
137
- */
138
- function limitWords(words, limitLength = 7) {
139
- let wordsLimit = [];
140
- if (words.length < limitLength) {
141
- return [words];
142
- } else {
143
- for (let i = 0; i < words.length; i += limitLength) {
144
- wordsLimit.push(words.slice(i, i + limitLength));
145
- }
146
- return wordsLimit;
147
- }
148
- }
149
-
150
- /**
151
- * 调用翻译功能
152
- * @param limitedWords 分组后的word数据
153
- * @param cb 全部翻译结束后的回调函数
154
- */
155
- function startTranslate(limitedWords, cb) {
156
- let curIndex = 0;
157
- let timer = null;
158
- timer = setInterval(() => {
159
- if (curIndex >= limitedWords.length) {
160
- clearInterval(timer);
161
- cb && cb();
162
- } else {
163
- limitedWords[curIndex].forEach(async (word) => {
164
- let res = await translate({
165
- query: word.value,
166
- from: "zh",
167
- to: "en",
168
- }).catch((err) => {
169
- console.log(err);
170
- });
171
-
172
- let translate_result = res?.trans_result
173
- ? res?.trans_result[0].dst
174
- : word.value;
175
- if (res?.error_code || !res) {
176
- const spinner = ora();
177
- spinner.warn(`翻译[${word.value}]时出错:` + JSON.stringify(res))
178
- }
179
- word.value = translate_result;
180
- });
181
- curIndex++;
182
- }
183
- }, 1000);
184
- }
185
-
186
- /**
187
- * 组装翻译后的数据结构
188
- * @param words
189
- * @param obj
190
- */
191
- function setTranslatedObj(words, obj) {
192
- words.forEach((item) => {
193
- item.keys.forEach((key, index) => {
194
- if (index === 0 && item.keys.length > 1) {
195
- if (!obj[key]) obj[key] = {};
196
- } else if (index < item.keys.length - 1) {
197
- // a.b.c
198
- let _key = item.keys.slice(0, index + 1).join(".");
199
- let flag = false;
200
- eval(`flag = !!!obj.${_key}`);
201
- if (flag) eval(`obj.${_key} = {}`);
202
- } else {
203
- let _key = item.keys.slice(0, index + 1).join(".");
204
- eval(`obj.${_key} = "${item.value}"`);
205
- }
206
- });
207
- });
208
- }
209
- function unquoteKeys(json) {
210
- return json.replace(/"(\\[^]|[^\\"])*"\s*:?/g, function (match) {
211
- if (/:$/.test(match)) {
212
- return match.replace(/^"|"(?=\s*:$)/g, "");
213
- } else {
214
- return match;
215
- }
216
- });
217
- }
218
- /**
219
- * 读取并翻译文本内容
220
- * @param filePath 文件地址
221
- * @param cb 翻译后的回调
222
- * @return 翻译后的文本
223
- */
224
- function readAndTranslateFileContent(filePath, cb = () => {}) {
225
- return new Promise((resolve, reject) => {
226
- fs.readFile(filePath, { encoding: "utf8" }, (err, data) => {
227
- if (err) {
228
- // log.error("读取文件失败");
229
- reject();
230
- } else {
231
- let jsonObj;
232
- let fileData = data.toString();
233
- let startIndex = fileData.indexOf("{");
234
- let endIndex = fileData.lastIndexOf("}");
235
- let jsonStr = fileData.slice(
236
- startIndex,
237
- endIndex === fileData.length ? endIndex : endIndex + 1
238
- );
239
- try {
240
- // 当成js执行
241
- eval("jsonObj = " + jsonStr);
242
- } catch (err) {
243
- jsonObj = null;
244
- // log.error("文件解析失败");
245
- reject();
246
- }
247
-
248
- if (jsonObj) {
249
- let obj = {};
250
- let words = parseConfigs(jsonObj);
251
- let limitedWords = limitWords(words, 7);
252
- // log.on(`正在翻译${chalk.yellow(filePath)}`);
253
- startTranslate(limitedWords, () => {
254
- let words_result = limitedWords.flat(1);
255
- setTranslatedObj(words_result, obj);
256
- let file_result =
257
- `export default ` + unquoteKeys(JSON.stringify(obj, null, 2));
258
- resolve(file_result);
259
- });
260
- }
261
- }
262
- });
263
- });
264
- }
265
-
266
- /**
267
- * 获取所有需要处理的文件路径+目标路径
268
- * @param dirPath 从指定的目录地址开始查找
269
- * @param filePaths 一个空数组,用来接收结果
270
- */
271
- function getAllFilePaths(translateConfig, dirPath, filePaths) {
272
- let files = fs.readdirSync(dirPath);
273
- files.forEach((file) => {
274
- let filePath = path.join(dirPath, file);
275
- let stats = fs.statSync(filePath);
276
- // 是否是文件夹
277
- let isDir = stats.isDirectory();
278
- if (isDir) {
279
- if (file === translateConfig.sourceDirName) {
280
- // 找到目标文件夹, 获取所有文件
281
- let files = fs.readdirSync(filePath);
282
- files.forEach((file) => {
283
- let jsPath = path.join(filePath, file);
284
- let targetPath = path.join(dirPath, translateConfig.targetDirName);
285
- filePaths.push({
286
- sourcePath: jsPath,
287
- targetPath,
288
- });
289
- });
290
- } else if (!translateConfig.ignoreFiles.includes(file)) {
291
- getAllFilePaths(translateConfig, filePath, filePaths);
292
- }
293
- }
294
- });
295
- }
296
-
297
- /**
298
- * 同步执行所有翻译操作
299
- * 因为每秒请求数有限制, 异步请求会超过最大并发数
300
- * @param files 所有要翻译的文件
301
- * @param index 当前进行到的index
302
- */
303
- async function execWorkerSync(files, index = 0) {
304
- let fileItem = files[index];
305
- let file_content = await readAndTranslateFileContent(fileItem.sourcePath);
306
- let fileName = path.basename(fileItem.sourcePath);
307
- let newFilePath = fileItem.targetPath + "/" + fileName;
308
- let exist = fs.existsSync(fileItem.targetPath);
309
- // 自动创建不存在的目录
310
- if (!exist) {
311
- try {
312
- // log.on(`创建文件夹${chalk.yellow(fileItem.targetPath)}`);
313
- fs.mkdirSync(fileItem.targetPath);
314
- } catch (error) {
315
- // log.error(`创建文件夹${chalk.red(fileItem.targetPath)}失败`);
316
- process.exit(1);
317
- }
318
- }
319
- writeFileContent(newFilePath, file_content, async (spinner, isOk) => {
320
- if (isOk) {
321
- spinner.succeed(`${newFilePath}已翻译`);
322
- } else {
323
- spinner.fail(`${newFilePath}翻译失败`);
324
- }
325
- index++;
326
- if (index < files.length) {
327
- spinner.start();
328
- await execWorkerSync(files, index);
329
- spinner.stop();
330
- } else {
331
- spinner.stop();
332
- spinner.succeed("翻译完毕");
333
- }
334
- });
335
- }
336
-
337
- export { translateCmd };
package/src/config.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "translate": {
3
- "sourceDirName": "zh-CN",
4
- "targetDirName": "en-US",
5
- "ignoreFiles": [
6
- "node_modules"
7
- ],
8
- "account": {
9
- "appId": "",
10
- "key": ""
11
- }
12
- },
13
- "editableConfig": [
14
- "translate"
15
- ]
16
- }
package/src/index.js DELETED
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from "commander";
3
- import { registerCommand, initProgram } from "./command/index.js";
4
- import { translateCmd } from "./command/translate.js";
5
- import { configCmd } from "./command/config.js";
6
- import { setCmd } from "./command/set.js";
7
- import { tinyCmd } from "./command/tiny.js";
8
- import { picgoCmd } from "./command/picgo.js";
9
- import { checkUpdate, checkNodeVersion } from './utils/common.js'
10
- import { i18nCmd } from "./command/i18n.js";
11
-
12
- const program = new Command();
13
-
14
- initProgram(program, async () => {
15
-
16
- if (process.env.RUNTIME_ENV !== 'electron') {
17
- checkNodeVersion()
18
- await checkUpdate()
19
- }
20
-
21
- registerCommand(program, translateCmd);
22
-
23
- registerCommand(program, setCmd);
24
-
25
- registerCommand(program, configCmd);
26
-
27
- registerCommand(program, tinyCmd);
28
- registerCommand(program, picgoCmd);
29
- registerCommand(program, i18nCmd)
30
- program.parse(process.argv);
31
- });