UNPKG

7.27 kBJavaScriptView Raw
1#!/usr/bin/env node
2(async function () {
3 "use script";
4 process.on("uncaughtException", (err) => {
5 console.error("got an error: %s", err);
6 process.exitCode = 1;
7 });
8
9 const updateNotifier = require("update-notifier");
10 const whatTime = require("what-time");
11 const minimatch = require("minimatch");
12 const async = require("async");
13 const fs = require("fs");
14 const path = require("path");
15 const listMd = require("./src/util/readmd.js");
16 const meow = require("meow");
17
18 const mergeConfig = require("./src/config/mergeConfig.js");
19
20 const {
21 g,
22 y,
23 yow,
24 m,
25 b,
26 r,
27 relaPath,
28 insert_flg,
29 } = require("./src/util/util.js");
30
31 // Cli cmd
32 const cli = meow(`
33Usage
34 $ translateMds [folder/file name] [options]
35
36Example
37 $ translateMds md/
38 $ translateMds -T 'inlineCode,linkReference,link,heading' readme.md
39
40 ${b("[options]")}
41 ${g("-a API")} : default < baidu > ${y("{google|baidu|youdao}")}
42 ${g("-f from ")} : default < auto detect >
43 ${g("-t to ")} : default < zh >
44 ${g("-N num ")} : default < 1 > ${y("{async number}")}
45 ${g("-R rewrite")} : default < false > ${y(
46 "{yes/no rewrite translate file}"
47 )}
48
49🌟${m("[high user options]")}❤️
50
51 ${g("-D debug")}
52 ${g("-C cache")} : default: false ${y("cache in disk")}
53 ${g("-G google.com")} : default: false ${y(
54 "{ cn => com with Google api }"
55 )}
56 ${g("-F force")} : default: false ${y(
57 "{ If, translate result is no 100%, force wirte md file }"
58 )}
59 ${g("-M match")} : default [ ". ", "! "//...] ${y(
60 "{match this str, merge translate result }"
61 )}
62 ${g("-S skips")} : default ["... ", "etc. ", "i.e. "] ${y(
63 "{match this str will, skip merge translate result }"
64 )}
65 ${g("-T types")} : default ["html", "code"] ${y(
66 "{pass the md AST type}"
67 )}
68 ${g("--timewait ")} : default < 80 > ${y(
69 "{each fetch api wait time}"
70 )}
71 ${g("--values [path]")} : default: false ${y(
72 "{write the original of wait for translate file}"
73 )} ${r("[single file]")}
74 ${g("--translate [path]")} : default: false ${y(
75 "{use this file translate}"
76 )} ${r("[single file]")}
77 ${g("--text-glob [pattern]")} : default: false ${y(
78 "{text must be match, then be transalte}"
79 )}
80 ${g("--no-disk")} : default: false ${y(
81 "{do not use cached Result}"
82 )}
83 ${g("--cache-name [filename]")}: default: "translateMds" ${y(
84 "named the cache file"
85 )}
86 ${g("--glob [pattern]")} : default: false ${y(
87 "{file must be match, then be transalte}"
88 )}
89 ${g("--ignore [relative file/folder]")} : default: false ${y(
90 "{ignore files/folders string, split with `,` }"
91 )}
92
93`);
94
95 updateNotifier({ pkg: cli.pkg }).notify();
96
97 // Fix write file Path is absoulte
98 const dir = cli.input[0];
99 if (!dir) {
100 console.error(g("--> v" + cli.pkg.version), cli.help);
101 process.exit(1);
102 }
103
104 // Merge config
105 const {
106 debug,
107 tranFr,
108 tranTo,
109 api,
110 rewrite,
111 asyncNum,
112 Force,
113 ignores,
114 glob,
115 Cache,
116 } = mergeConfig(cli);
117
118 const translateMds = require("./src/translateMds.js");
119
120 const {
121 loggerStart,
122 loggerText,
123 loggerStop,
124 oneOra,
125 } = require("./src/config/loggerConfig.js"); // Winston config
126
127 // after workOptions ready
128 const { writeDataToFile } = require("./src/util/writeDataToFile.js");
129
130 console.log(b(`> ${yow(`Cache:${Cache}`)} Starting 翻译`) + r(dir));
131
132 // Get floder markdown files Array
133 const getList = await listMd(path.resolve(process.cwd(), dir), {
134 deep: "all",
135 });
136
137 console.log(b(`总文件数 ${getList.length}, 有些文件会跳过`));
138
139 let Done = 0;
140 const noDone = [];
141 let showAsyncnum = 0;
142
143 loggerStart("translate running ... >> ");
144 async.mapLimit(getList, asyncNum, runTranslate, (err, IsTranslateS) => {
145 loggerStop();
146 if (noDone.length > 0) {
147 process.exitCode = 1;
148 }
149 if (err) {
150 throw err;
151 }
152
153 Done++;
154 if (IsTranslateS.every((x) => Boolean(x))) {
155 oneOra("All Done");
156 } else {
157 if (debug !== "debug") {
158 oneOra(
159 `Some No Done , ${yow("use")} cli-option${r(" { -D } ")} find the Err`
160 );
161 }
162 if (!Force) {
163 oneOra(
164 `Or ${yow("use")} cli-option${r(
165 " { -F } "
166 )} Force put the translate Result`
167 );
168 }
169 if (debug === "debug" || Force) {
170 oneOra(
171 `[${g("DEBUG")}:${debug === "debug"}|${g("Force")}:${Force}] mode`
172 );
173 }
174 }
175 oneOra(`time:${whatTime(process.uptime())}`);
176 });
177
178 /**
179 * @description async Translate filename value , Return true or false
180 * @param {String} value
181 * @returns {Boolean}
182 */
183
184 async function runTranslate(value) {
185 const rePath = relaPath(value);
186 loggerText(`++++ <😊 > ${rePath}`);
187
188 let State = true;
189 Done++;
190
191 const localDone = Done;
192
193 // Filter same file
194 if (value.endsWith(`.${tranTo}.md`) || !value.endsWith(".md")) {
195 loggerText(b(`- 翻译的 - 或者 不是 md 文件的 ${g(rePath)}`));
196 return State;
197 }
198 if (value.match(/\.[a-zA-Z]{2}\.md+/)) {
199 // TOGO country short name
200 loggerText(b(`- 有后缀为 *.国家简写.md ${g(rePath)}`));
201 return State;
202 }
203 if (!rewrite && fs.existsSync(insert_flg(value, `.${tranTo}`, 3))) {
204 loggerText(b(`已翻译, 不覆盖 ${g(rePath)}`));
205 return State;
206 }
207 if (glob && glob.some((g) => !minimatch(value, g, { matchBase: true }))) {
208 loggerText(b(`glob, no match ${g(rePath)}`));
209 return State;
210 }
211 if (
212 ignores &&
213 ignores.some((ignore) => value.includes(path.resolve(ignore)))
214 ) {
215 loggerText(b(`ignore, ${g(rePath)}`));
216 return State;
217 }
218
219 loggerText(`1. do 第${localDone}文件 ${rePath}`);
220
221 // Open async num
222 showAsyncnum++;
223 const startTime = new Date().getTime();
224
225 const _translateMds = await translateMds(
226 [value, api, tranFr, tranTo],
227 debug,
228 true
229 );
230
231 // Succeed / force wirte data
232 if (_translateMds.every((x) => !x.error && x.text) || Force) {
233 // Translate no ok
234 const _tranData = _translateMds.map((x) => x.text); // Single file translate data
235
236 await writeDataToFile(_tranData, value).then((text) => loggerText(text));
237 }
238
239 let Err;
240 for (const _t of _translateMds) {
241 if (_t.error) {
242 Err = _t.error;
243 break;
244 }
245 }
246
247 const endtime = new Date().getTime() - startTime;
248 const humanTime = whatTime(endtime / 1000);
249
250 if (State && !Err) {
251 oneOra(
252 `已搞定 第 ${localDone} 文件 - 并发${b(showAsyncnum)} -- ${b(
253 humanTime
254 )} - ${rePath}`
255 );
256 } else {
257 State = false; // Translate no ok
258 if (!State) {
259 // Write data no ok | translate no ok
260 noDone.push(value); // If process exit code
261 oneOra(
262 `没完成 第 ${localDone} 文件 - 并发${b(showAsyncnum)} -- ${b(
263 humanTime
264 )} - ${rePath} \n ${Err}`,
265 "fail"
266 );
267 }
268 }
269
270 showAsyncnum--;
271
272 return State;
273 }
274
275 process.on("exit", (_) => {
276 loggerStop();
277 });
278})();