i18-fe-automator-beta 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.
@@ -0,0 +1,2312 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import inquirer from 'inquirer';
4
+ import { program } from 'commander';
5
+ import request from 'request';
6
+ import { glob } from 'glob';
7
+ import md5 from 'js-md5';
8
+ import CryptoJS from 'crypto-js';
9
+ import ora from 'ora';
10
+ import chalk from 'chalk';
11
+ import xlsx from 'node-xlsx';
12
+ import { fileURLToPath } from 'url';
13
+ import path from 'path';
14
+ import querystring from 'querystring';
15
+ import crypto from 'crypto';
16
+ import { v5 } from 'uuid';
17
+ import { ESLint } from 'eslint';
18
+ import * as espree from 'espree';
19
+ import { createRequire } from 'node:module';
20
+ import stripAnsi from 'strip-ansi';
21
+ import 'shelljs';
22
+ import process$1 from 'child_process';
23
+ import _ from 'lodash';
24
+ import { nanoid } from 'nanoid';
25
+ import request$1 from 'request-promise';
26
+ import 'os';
27
+ import 'console';
28
+
29
+ function Loading() {
30
+ this.spinner = ora();
31
+ }
32
+ var Loading$1 = new Loading().spinner;
33
+
34
+ //加密
35
+ function encrypt(word, keyStr) {
36
+ keyStr = keyStr ? keyStr : "abcdefgabcdefg12";
37
+ var key = CryptoJS.enc.Utf8.parse(keyStr); //Latin1 w8m31+Yy/Nw6thPsMpO5fg==
38
+ var srcs = CryptoJS.enc.Utf8.parse(word);
39
+ var encrypted = CryptoJS.AES.encrypt(srcs, key, {
40
+ mode: CryptoJS.mode.ECB,
41
+ padding: CryptoJS.pad.Pkcs7,
42
+ });
43
+ return encrypted.toString();
44
+ }
45
+
46
+ function login(data) {
47
+ data.password = encrypt(md5(data.password));
48
+ const env=data.env;
49
+ Loading$1.start(`${env}: 登录中...`);
50
+ return new Promise((resolve, reject) => {
51
+ request(
52
+ {
53
+ url: `https://${
54
+ env === "pro" ? "" : `${env}-`
55
+ }hxjf.hongxinshop.com/sys/login`,
56
+ method: "POST",
57
+ json: true,
58
+ body: data,
59
+ },
60
+ function (error, response, body) {
61
+ const res = body;
62
+ if (!error && res.code == 200) {
63
+ Loading$1.succeed(`${env}: 登录成功`);
64
+ const access_token = res.data.access_token;
65
+ resolve(access_token);
66
+ } else {
67
+ Loading$1.fail(`${env}: 登录失败`);
68
+ console.log(chalk.red(error || res.msg));
69
+ }
70
+ }
71
+ );
72
+ });
73
+ }
74
+
75
+ function importExcel({ uploadFilePath, token, env }) {
76
+ Loading$1.start(`${env}: 导入文件中...`);
77
+ const stream = fs.createReadStream(uploadFilePath);
78
+ return new Promise((resolve, reject) => {
79
+ request(
80
+ {
81
+ url: `https://${
82
+ env === "pro" ? "" : `${env}-`
83
+ }hxjf.hongxinshop.com/vue-api/api-globalization/api/i18n/importLangExcel`,
84
+ method: "POST",
85
+ headers: {
86
+ contentType: "multipart/form-data",
87
+ Authorization: `Bearer ${token}`,
88
+ "x-request-vaildate": "open",
89
+ },
90
+ formData: {
91
+ file: stream,
92
+ },
93
+ },
94
+ function (error, response, body) {
95
+ const res = JSON.parse(body);
96
+ if (!error && res.code == 200) {
97
+ Loading$1.succeed(`${env}: 导入文件成功`);
98
+ resolve(true);
99
+ } else {
100
+ Loading$1.fail(`${env}: 导入文件失败`);
101
+ console.log(chalk.red(error || JSON.stringify(res.data)));
102
+ }
103
+ }
104
+ );
105
+ });
106
+ }
107
+ function readExcel({ uploadFilePath }) {
108
+ return new Promise((resolve, reject) => {
109
+ // 读取文件
110
+ const workSheetsFromFile = xlsx.parse(uploadFilePath);
111
+ const data = workSheetsFromFile[0].data;
112
+ // 获取第一行
113
+ const titleList = data[0];
114
+ // 获取项目所在的列
115
+ const projectIndex = titleList.indexOf("项目");
116
+ if (projectIndex === -1) {
117
+ console.log(chalk.red("请检查文件是否包含项目列"));
118
+ return;
119
+ }
120
+ // 获取数据
121
+ const list = data.slice(1);
122
+ const set = new Set();
123
+ list.forEach((item) => {
124
+ if (item[projectIndex]) {
125
+ set.add(item[projectIndex]);
126
+ }
127
+ });
128
+ // 判断set是否为空
129
+ if (set.size === 0) {
130
+ console.log(chalk.red("请检查文件是否包含项目数据"));
131
+ return;
132
+ }
133
+ resolve([...set]);
134
+ });
135
+ }
136
+
137
+ function uploadExcel({ data, token, env }) {
138
+ Loading$1.start(`${env}: 导入项目 ${data.projectList.join()}`);
139
+ return new Promise((resolve, reject) => {
140
+ request(
141
+ {
142
+ url: `https://${
143
+ env === "pro" ? "" : `${env}-`
144
+ }hxjf.hongxinshop.com/vue-api/api-globalization/api/i18n/uploadI18nLangForFront`,
145
+ method: "POST",
146
+ json: true,
147
+ headers: {
148
+ Authorization: `Bearer ${token}`,
149
+ "x-request-vaildate": "open",
150
+ },
151
+ body: data,
152
+ },
153
+ function (error, response, body) {
154
+ const res = body;
155
+ if (!error && res.code == 200) {
156
+ Loading$1.succeed(`${env}: 导入项目成功`);
157
+ resolve(true);
158
+ } else {
159
+ Loading$1.fail(`${env}: 导入项目失败`);
160
+ console.log(chalk.red(error || res.msg || res.message));
161
+ }
162
+ }
163
+ );
164
+ });
165
+ }
166
+
167
+ // 获取命令行所在的目录
168
+ function getRunCliPath({ directory = false, root = false } = {}) {
169
+ const __filename = fileURLToPath(import.meta.url);
170
+ const __dirname = path.dirname(__filename);
171
+ if (root) {
172
+ return path.resolve(__dirname, "../");
173
+ }
174
+ return directory ? __dirname : __filename;
175
+ }
176
+
177
+ // 获取package.json
178
+ function getPackageJson() {
179
+ const packageJson = JSON.parse(
180
+ fs.readFileSync(path.resolve(getRunCliPath({ root: true }), "../package.json"))
181
+ );
182
+ return packageJson;
183
+ }
184
+ // 判断是否存在该路径
185
+ function isExistPath(path) {
186
+ return fs.existsSync(path);
187
+ }
188
+
189
+ // 写入缓存
190
+ function writeCache(key, value) {
191
+ fs.writeFileSync(getRunCliPath({ root: true }) + "/.cache/" + key, value);
192
+ }
193
+ // 读取缓存
194
+ function readCache(key) {
195
+ return fs.readFileSync(getRunCliPath({ root: true }) + "/.cache/" + key);
196
+ }
197
+
198
+ const sleep = (time) =>
199
+ new Promise((resolve = 2000) => {
200
+ setTimeout(() => resolve, time);
201
+ });
202
+ async function upload$1() {
203
+ const SECRET_NAME = "sass";
204
+ const SECRET_NAME_PRO = "sass_pro";
205
+ let cacheSecret = {};
206
+ let cacheSecretPro = {};
207
+ if (!isExistPath(getRunCliPath({ root: true }) + "/.cache")) {
208
+ fs.mkdirSync(getRunCliPath({ root: true }) + "/.cache");
209
+ }
210
+ const cacheFilePath =
211
+ getRunCliPath({ root: true }) + "/.cache/" + SECRET_NAME;
212
+ const cacheFilePathPro =
213
+ getRunCliPath({ root: true }) + "/.cache/" + SECRET_NAME_PRO;
214
+ if (isExistPath(cacheFilePath)) {
215
+ cacheSecret = JSON.parse(readCache(SECRET_NAME));
216
+ }
217
+ if (isExistPath(cacheFilePathPro)) {
218
+ cacheSecretPro = JSON.parse(readCache(SECRET_NAME_PRO));
219
+ }
220
+ // 是否上传
221
+ inquirer
222
+ .prompt([
223
+ {
224
+ message: "是否上传到sass平台",
225
+ name: "upload",
226
+ type: "confirm",
227
+ default: true,
228
+ },
229
+ {
230
+ // 选择环境
231
+ message: "请选择环境",
232
+ name: "env",
233
+ type: "rawlist",
234
+ choices: [
235
+ {
236
+ name: "mit",
237
+ value: "mit",
238
+ },
239
+ {
240
+ name: "sit",
241
+ value: "sit",
242
+ },
243
+ {
244
+ name: "uat",
245
+ value: "uat",
246
+ },
247
+ {
248
+ name: "pro",
249
+ value: "pro",
250
+ },
251
+ // {
252
+ // name: "一键发布pro(mit->sit->uat->pro)",
253
+ // value: "topro",
254
+ // },
255
+ {
256
+ name: "一键发布uat(mit->sit->uat)",
257
+ value: "to-mit,sit,uat",
258
+ },
259
+ {
260
+ name: "一键发布sit(mit->sit)",
261
+ value: "to-mit,sit",
262
+ },
263
+ ],
264
+ when: function (res) {
265
+ return res.upload;
266
+ },
267
+ validate: function (val) {
268
+ if (val.length > 0) {
269
+ return true;
270
+ }
271
+ return "请选择环境";
272
+ },
273
+ },
274
+ {
275
+ message: "请输入域账号",
276
+ name: "username",
277
+ // 当upload为true时,展示
278
+ when: function (res) {
279
+ return res.upload && res.env !== "pro";
280
+ },
281
+ default: cacheSecret.username,
282
+ // 必填
283
+ validate: function (val) {
284
+ if (val) {
285
+ return true;
286
+ }
287
+ return "请输入域账号";
288
+ },
289
+ },
290
+ {
291
+ message: "请输入域密码",
292
+ name: "password",
293
+ default: cacheSecret.password,
294
+ when: function (res) {
295
+ return res.upload && res.env !== "pro";
296
+ },
297
+ // 必填
298
+ validate: function (val) {
299
+ if (val) {
300
+ return true;
301
+ }
302
+ return "请输入域密码";
303
+ },
304
+ },
305
+ {
306
+ message: "请输入生产域账号",
307
+ name: "username",
308
+ // 当upload为true时,展示
309
+ when: function (res) {
310
+ return res.upload && res.env === "pro";
311
+ },
312
+ default: cacheSecretPro.username,
313
+ // 必填
314
+ validate: function (val) {
315
+ if (val) {
316
+ return true;
317
+ }
318
+ return "请输入生产域账号";
319
+ },
320
+ },
321
+ {
322
+ message: "请输入生产域密码",
323
+ name: "password",
324
+ default: cacheSecretPro.password,
325
+ when: function (res) {
326
+ return res.upload && res.env === "pro";
327
+ },
328
+ // 必填
329
+ validate: function (val) {
330
+ if (val) {
331
+ return true;
332
+ }
333
+ return "请输入生产域密码";
334
+ },
335
+ },
336
+ ])
337
+ .then(async (res) => {
338
+ const { upload, env, username, password } = res;
339
+ let envList = [];
340
+ if (env.indexOf("to") > -1) {
341
+ envList = env.replace("to-", "").split(",");
342
+ } else {
343
+ envList = [env];
344
+ }
345
+ if (upload) {
346
+ writeCache(
347
+ env === "pro" ? "sass_pro" : "sass",
348
+ JSON.stringify({
349
+ username,
350
+ password,
351
+ })
352
+ );
353
+ // 上传逻辑
354
+ // 1.获取上传文件路径
355
+ const choices = fs
356
+ .readdirSync("./")
357
+ .filter((item) => item.indexOf(".xlsx") > -1);
358
+ // 2. 选择上传文件
359
+ inquirer
360
+ .prompt([
361
+ {
362
+ type: "rawlist",
363
+ name: "excelFileName",
364
+ message: "请选择要上传的excel文件",
365
+ choices,
366
+ validate: function (val) {
367
+ if (val) {
368
+ return true;
369
+ }
370
+ return "请选择项目";
371
+ },
372
+ },
373
+ ])
374
+ .then(async (res) => {
375
+ const { excelFileName } = res;
376
+ // 2.读取excel
377
+ const uploadFilePath = "./" + excelFileName;
378
+ const projectList = await readExcel({ uploadFilePath });
379
+ for (const env of envList) {
380
+ // 1.登录
381
+ const token = await login({
382
+ env,
383
+ username,
384
+ password,
385
+ });
386
+ // 2.导入excel
387
+ await importExcel({
388
+ uploadFilePath,
389
+ token,
390
+ env,
391
+ });
392
+ sleep(2000);
393
+ // 3.上传
394
+ await uploadExcel({
395
+ data: {
396
+ projectList,
397
+ },
398
+ token,
399
+ env,
400
+ });
401
+ console.log(`🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀${env}成功`);
402
+ }
403
+ });
404
+ }
405
+ });
406
+ }
407
+
408
+ // 获取应用list
409
+ function getApplicationList({ appName, env, token }) {
410
+ return new Promise((resolve, reject) => {
411
+ request(
412
+ {
413
+ url: `https://${
414
+ env === "pro" ? "" : `${env}-`
415
+ }hxjf.hongxinshop.com/api-u/api/saas/app/nameList`,
416
+ method: "POST",
417
+ json: true,
418
+ body: { name: appName, pageNo: 1, pageSize: 50 },
419
+ headers: {
420
+ Authorization: `Bearer ${token}`,
421
+ "x-request-vaildate": "open",
422
+ },
423
+ },
424
+ function (error, response, body) {
425
+ const res = body;
426
+ if (!error && res.code == 200) {
427
+ resolve(
428
+ res.data.map((item) => {
429
+ // return {
430
+ // name: item.name,
431
+ // value: item.id,
432
+ // };
433
+ return {
434
+ name: item.name,
435
+ value: item.name,
436
+ id: item.id,
437
+ code: item.code,
438
+ };
439
+ })
440
+ );
441
+ } else {
442
+ console.log(chalk.red(error || JSON.stringify(res.data)));
443
+ }
444
+ }
445
+ );
446
+ });
447
+ }
448
+ // 获取应用按钮权限配置
449
+ function getApplicationButtonConfig({ appId, env, token }) {
450
+ Loading$1.start(`获取按钮权限配置中...`);
451
+ return new Promise((resolve, reject) => {
452
+ request(
453
+ {
454
+ url: `https://${
455
+ env === "pro" ? "" : `${env}-`
456
+ }hxjf.hongxinshop.com/api-u/api/saas/permission/list`,
457
+ method: "POST",
458
+ json: true,
459
+ body: { id: appId },
460
+ headers: {
461
+ Authorization: `Bearer ${token}`,
462
+ "x-request-vaildate": "open",
463
+ },
464
+ },
465
+ function (error, response, body) {
466
+ const res = body;
467
+ if (!error && res.code == 200) {
468
+ Loading$1.succeed(`获取${env}环境按钮权限配置成功`);
469
+ resolve(res.data);
470
+ } else {
471
+ Loading$1.fail(`获取${env}环境按钮权限配置失败`);
472
+ console.log(chalk.red(error || JSON.stringify(res.data)));
473
+ }
474
+ }
475
+ );
476
+ });
477
+ }
478
+ // 删除应用按钮权限
479
+ function deleteApplicationButtonConfig({ data, env, token }) {
480
+ return new Promise((resolve, reject) => {
481
+ request(
482
+ {
483
+ url: `https://${
484
+ env === "pro" ? "" : `${env}-`
485
+ }hxjf.hongxinshop.com/api-u/api/saas/permission/del`,
486
+ method: "POST",
487
+ json: true,
488
+ body: data,
489
+ headers: {
490
+ Authorization: `Bearer ${token}`,
491
+ "x-request-vaildate": "open",
492
+ },
493
+ },
494
+ function (error, response, body) {
495
+ const res = body;
496
+ if (!error && res.code == 200) {
497
+ resolve(res.data);
498
+ } else {
499
+ const errorData = error || JSON.stringify(res.data);
500
+ console.log(chalk.red(errorData));
501
+ reject(errorData);
502
+ }
503
+ }
504
+ );
505
+ });
506
+ }
507
+ // 新增应用按钮权限
508
+ function addApplicationButtonConfig({ data, env, token }) {
509
+ Loading$1.start(`新增按钮权限 ${data.code} 中...`);
510
+ return new Promise((resolve, reject) => {
511
+ request(
512
+ {
513
+ url: `https://${
514
+ env === "pro" ? "" : `${env}-`
515
+ }hxjf.hongxinshop.com/api-u/api/saas/permission/add`,
516
+ method: "POST",
517
+ json: true,
518
+ body: data,
519
+ headers: {
520
+ Authorization: `Bearer ${token}`,
521
+ "x-request-vaildate": "open",
522
+ },
523
+ },
524
+ function (error, response, body) {
525
+ const res = body;
526
+ if (!error && res.code == 200) {
527
+ Loading$1.succeed(`${data.code}`);
528
+ resolve(true);
529
+ } else {
530
+ Loading$1.fail(`${data.code}`);
531
+ console.log(chalk.red(error || JSON.stringify(res.data)));
532
+ }
533
+ }
534
+ );
535
+ });
536
+ }
537
+ async function syncSassConfig() {
538
+ inquirer
539
+ .prompt([
540
+ {
541
+ message: `使用须知:
542
+ ${chalk.green(`
543
+ 1.此命令会${chalk.red('强制覆盖')}[from]->[to]环境配置,${chalk.red('不会合并')}!!! \n
544
+ 2.请确保同步前已经在目标环境创建了应用 \n
545
+ 3.从mit->sit/uat,建议先同步sit->mit以保证mit环境的配置是最新的
546
+ `)}
547
+ `,
548
+ name: "notice",
549
+ type: "confirm",
550
+ default: false,
551
+ },
552
+ {
553
+ message: "请选择从哪个环境同步配置",
554
+ name: "from",
555
+ type: "list",
556
+ choices: ["mit", "sit", "uat"],
557
+ when: (answers) => answers.notice,
558
+ },
559
+ {
560
+ message: "请选择同步至哪个环境",
561
+ name: "to",
562
+ type: "list",
563
+ when: (answers) => answers.notice,
564
+ choices: (answers) => {
565
+ const list = ["mit", "sit", "uat"];
566
+ return list.filter((item) => item !== answers.from);
567
+ },
568
+ },
569
+ ])
570
+ .then(async (answers) => {
571
+ const { from, to,notice } = answers;
572
+ if(!notice){
573
+ return
574
+ }
575
+ const token = await login({
576
+ env: from,
577
+ username: "superAdmin",
578
+ password: "admin1",
579
+ });
580
+ const { inputAppName } = await inquirer.prompt([
581
+ {
582
+ message: "请输入应用名称关键字",
583
+ name: "inputAppName",
584
+ type: "input",
585
+ validate: function (value) {
586
+ if (value) {
587
+ return true;
588
+ }
589
+ return "请输入应用名称";
590
+ },
591
+ },
592
+ ]);
593
+ const applicationList = await getApplicationList({
594
+ env: from,
595
+ token,
596
+ appName: inputAppName,
597
+ });
598
+ const { appName } = await inquirer.prompt([
599
+ {
600
+ message: "请选择应用名称",
601
+ name: "appName",
602
+ type: "rawlist",
603
+ choices: applicationList,
604
+ },
605
+ ]);
606
+ const appId = applicationList.find((item) => item.name === appName).id;
607
+ // let appConfig = await getApplicationConfig({ appId, env: from, token });
608
+ const fromButtonList = await getApplicationButtonConfig({
609
+ appId,
610
+ env: from,
611
+ token,
612
+ });
613
+ if (!fromButtonList.length) {
614
+ return console.log(chalk.red("按钮权限配置为空,无法同步"));
615
+ }
616
+ console.log(`开始同步到${to}🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀`);
617
+ const toToken = await login({
618
+ env: to,
619
+ username: "superAdmin",
620
+ password: "admin1",
621
+ });
622
+ const toApplicationList = await getApplicationList({
623
+ env: to,
624
+ token: toToken,
625
+ appName,
626
+ });
627
+ if (!toApplicationList.length) {
628
+ return console.log(chalk.red(`${to}环境不存在该应用`));
629
+ }
630
+ const toAppId = toApplicationList[0].id;
631
+ const toButtonList = await getApplicationButtonConfig({
632
+ appId: toAppId,
633
+ env: to,
634
+ token: toToken,
635
+ });
636
+ try {
637
+ if (toButtonList.length) {
638
+ const deleteButtonPromiseList = toButtonList.map((item) => {
639
+ return deleteApplicationButtonConfig({
640
+ data: {
641
+ id: item.id,
642
+ ver: item.ver,
643
+ },
644
+ env: to,
645
+ token: toToken,
646
+ });
647
+ });
648
+ Loading$1.start(`删除${to}环境按钮权限配置中...`);
649
+ await Promise.all(deleteButtonPromiseList);
650
+ Loading$1.succeed(`删除${to}环境按钮权限配置成功`);
651
+ }
652
+ } catch (error) {
653
+ console.log(chalk.red(`删除${to}环境按钮权限配置失败`));
654
+ } finally {
655
+ for (const item of fromButtonList) {
656
+ const { code, name, visitConf } = item;
657
+ await addApplicationButtonConfig({
658
+ env: to,
659
+ token: toToken,
660
+ data: {
661
+ code1: code.split(":")[0],
662
+ code,
663
+ name,
664
+ visitConf,
665
+ ascriptionApp: toAppId,
666
+ },
667
+ });
668
+ }
669
+ console.log(chalk.green("同步成功"));
670
+ }
671
+ });
672
+ }
673
+
674
+ /**
675
+ * @fileoverview 不要单独中文
676
+ * @author ypf
677
+ */
678
+ //------------------------------------------------------------------------------
679
+ // Rule Definition
680
+ //------------------------------------------------------------------------------
681
+
682
+ /** @type {import('eslint').Rule.RuleModule} */
683
+
684
+ // 判断字符串是否是中文
685
+ const isChinese = (str) => {
686
+ return /[\u4e00-\u9fa5]+/.test(str);
687
+ };
688
+ //去除特殊字符,包含空格
689
+ function trimSpecial(string = "", formatter) {
690
+ // const pattern =
691
+ // /[`~!@#$^\-&*()=|{}':;',\\\[\]\.<>\/?~!@#¥……&*()——|{}【】';:""'。,、?\s]/g;
692
+ // return string.replace(pattern, "");
693
+ // console.log(string);
694
+ // 获取开头空白符的位置
695
+ const startIdx = string.search(/\S/) - 1;
696
+ // 获取结尾空白符的位置
697
+ const endIdx = string.search(/\S\s*$/) + 1;
698
+ // 获取开头和结尾的字符串
699
+ const startStr = string.slice(0, startIdx + 1);
700
+ const endStr = string.slice(endIdx);
701
+ // 获取中间的字符串
702
+ const middle = string.slice(startIdx + 1, endIdx);
703
+ // 取出中间字符串的换行符
704
+ const middleStr = middle.replace(/\n/g, "");
705
+ return startStr + formatter(middleStr) + endStr;
706
+ }
707
+ // 判断当前节点是否已经翻译过
708
+ function isTranslate(node) {
709
+ if (
710
+ node.parent?.parent?.parent?.type === "CallExpression" &&
711
+ node.parent?.parent?.parent?.callee?.name === "$hxt"
712
+ ) {
713
+ return true;
714
+ }
715
+ // console不翻译
716
+ if (
717
+ node.parent.type === "CallExpression" &&
718
+ node.parent.callee?.object?.name === "console"
719
+ ) {
720
+ return true;
721
+ }
722
+
723
+ return false;
724
+ }
725
+
726
+ // 空key
727
+ const emptyKeyRules = (context) => {
728
+ return {
729
+ CallExpression(node) {
730
+ if (node.callee.name === "$hxt") {
731
+ const properties = node.arguments[0]?.properties || [];
732
+ // 如果属性是key且值为空
733
+ const result = properties.some((item) => {
734
+ // 去除key空格
735
+ const key = item.key.name.replace(/\s/g, "");
736
+ if (key === "key") {
737
+ // 去除value空格
738
+ // value是模版字符串
739
+ let value = "";
740
+ if (item.value.type === "TemplateLiteral") {
741
+ value = item.value.quasis[0].value.raw.replace(/\s/g, "");
742
+ } else if (item.value.type === "Literal") {
743
+ value = item.value.value.replace(/\s/g, "");
744
+ }
745
+ if (value === "") {
746
+ return true;
747
+ }
748
+ }
749
+ });
750
+ if (result) {
751
+ context.report({
752
+ node: node,
753
+ messageId: "noSingleChinese",
754
+ data: {
755
+ raw: chalk.green("key为空"),
756
+ },
757
+ fix: (fixer) => {},
758
+ });
759
+ }
760
+ }
761
+ },
762
+ };
763
+ };
764
+ var noSingleChineseRule = {
765
+ meta: {
766
+ type: "suggestion", // `problem`, `suggestion`, or `layout`
767
+ docs: {
768
+ description: "不要单独中文",
769
+ recommended: false,
770
+ url: null, // URL to the documentation page for this rule
771
+ },
772
+ fixable: "code", // Or `code` or `whitespace`
773
+ schema: [], // Add a schema if the rule has options
774
+ messages: {
775
+ noSingleChinese: "不要单独中文: {{raw}}",
776
+ }, // Add messageId and message
777
+ },
778
+ create(context) {
779
+ const sourceCode = context.sourceCode;
780
+ context.filename;
781
+ // console.log(filename,999999)
782
+ return context.parserServices.defineTemplateBodyVisitor(
783
+ // Event handlers for <template>.
784
+ {
785
+ // 纯字符串,如 测试
786
+ VText(node) {
787
+ // 如果是中文,替换为 {{ $hxt({key:'',desc:'中文'})}}
788
+ if (!isTranslate(node)) {
789
+ if (isChinese(node.value)) {
790
+ context.report({
791
+ node,
792
+ messageId: "noSingleChinese",
793
+ data: {
794
+ raw: chalk.green(node.value),
795
+ },
796
+ // message: `VText`,
797
+ fix: (fixer) => {
798
+ return fixer.replaceText(
799
+ node,
800
+ trimSpecial(node.value, (middle) => {
801
+ return "{{ $hxt({key:'',desc:'" + middle + "'})}}";
802
+ })
803
+ );
804
+ },
805
+ });
806
+ }
807
+ }
808
+ },
809
+ // 纯字符串,如 {{ mini ? '测试' : `开启` }}中的测试
810
+ // 纯字符串,如 {{test('测试')}}中的测试
811
+ Literal(node) {
812
+ if (!isTranslate(node)) {
813
+ if (isChinese(node.value)) {
814
+ context.report({
815
+ node,
816
+ messageId: "noSingleChinese",
817
+ data: {
818
+ raw: chalk.green(node.value),
819
+ },
820
+ // message: `Literal`,
821
+ fix: (fixer) => {
822
+ // console.log(1111, node);
823
+ return fixer.replaceText(
824
+ node,
825
+ trimSpecial(node.value, (middle) => {
826
+ return "$hxt({key:'',desc:'" + middle + "'})";
827
+ })
828
+ );
829
+ },
830
+ });
831
+ }
832
+ }
833
+ },
834
+ // 模版字符串,
835
+ // 如{{ mini ? '测试' : `开启` }}中的开启
836
+ // 如{{ mini ? `${a}测试` : "开启" }}中的${a}测试
837
+ // 如{{`111`}}
838
+ // 如<p :a="`${a}册书`">
839
+ TemplateLiteral(node) {
840
+ if (!isTranslate(node)) {
841
+ const textSource = sourceCode.getText(node);
842
+ let text = textSource;
843
+ // 删除$符号
844
+ text = text.replace(/\${/g, "{");
845
+ if (isChinese(text)) {
846
+ // 获取expressions的文本
847
+ const expressionsText = node.expressions.map((item) => {
848
+ return sourceCode.getText(item);
849
+ });
850
+ let expressionStr = "";
851
+ if (expressionsText.length) {
852
+ expressionsText.forEach((item, index) => {
853
+ const key = `slot${index + 1}`;
854
+ text = text.replace(item, `${key}`);
855
+ expressionStr += `${key}:${item},`;
856
+ });
857
+ // 删除最后一个逗号
858
+ expressionStr = `{${expressionStr.slice(0, -1)}}`;
859
+ }
860
+ if (!isChinese(text)) {
861
+ return;
862
+ }
863
+ context.report({
864
+ node,
865
+ data: {
866
+ raw: chalk.green(textSource),
867
+ },
868
+ messageId: "noSingleChinese",
869
+ // message: `TemplateLiteral`,
870
+ fix: (fixer) => {
871
+ return fixer.replaceText(
872
+ node,
873
+ trimSpecial(text, (middle) => {
874
+ return `$hxt({key:'',desc:${middle}}${
875
+ expressionStr ? `,${expressionStr}` : ""
876
+ })`;
877
+ })
878
+ );
879
+ },
880
+ });
881
+ }
882
+ }
883
+ },
884
+ // 如<p title="1">
885
+ VLiteral(node) {
886
+ if (!isTranslate(node)) {
887
+ if (isChinese(node.value)) {
888
+ context.report({
889
+ node,
890
+ messageId: "noSingleChinese",
891
+ // message: `VLiteral`,
892
+ data: {
893
+ raw: chalk.green(node.value),
894
+ },
895
+
896
+ fix: (fixer) => {
897
+ // 父节点需要改为冒号方式
898
+ const parentNode = node.parent;
899
+ if (parentNode.type === "VAttribute") {
900
+ const key = parentNode.key;
901
+ return fixer.replaceText(
902
+ parentNode,
903
+ ":" +
904
+ key.name +
905
+ "=" +
906
+ '"' +
907
+ trimSpecial(node.value, (middle) => {
908
+ return "$hxt({key:'',desc:'" + middle + "'})";
909
+ }) +
910
+ '"'
911
+ );
912
+ }
913
+ },
914
+ });
915
+ }
916
+ }
917
+ },
918
+ // 如 <p v-permission="测试">中的测试
919
+ Identifier(node) {
920
+ if (
921
+ !isTranslate(node) &&
922
+ node.parent.type === "VExpressionContainer"
923
+ ) {
924
+ if (isChinese(node.name)) {
925
+ context.report({
926
+ node,
927
+ messageId: "noSingleChinese",
928
+ // message: `Identifier`,
929
+ data: {
930
+ raw: chalk.green(node.name),
931
+ },
932
+ fix: (fixer) => {
933
+ return fixer.replaceText(
934
+ node,
935
+ trimSpecial(node.name, (middle) => {
936
+ return "$hxt({key:'',desc:'" + middle + "'})";
937
+ })
938
+ );
939
+ },
940
+ });
941
+ }
942
+ }
943
+ },
944
+ ...emptyKeyRules(context),
945
+ },
946
+ // Event handlers for <script> or scripts. (optional)
947
+ {
948
+ JSXText(node) {
949
+ if (!isTranslate(node)) {
950
+ if (isChinese(node.value)) {
951
+ context.report({
952
+ node,
953
+ messageId: "noSingleChinese",
954
+ data: {
955
+ raw: chalk.green(node.value),
956
+ },
957
+ fix: (fixer) => {
958
+ return fixer.replaceText(
959
+ node,
960
+ trimSpecial(node.value, (middle) => {
961
+ return "{ $hxt({key:'',desc:'" + middle + "'})}";
962
+ })
963
+ );
964
+ },
965
+ });
966
+ }
967
+ }
968
+ },
969
+ TemplateLiteral(node) {
970
+ if (!isTranslate(node)) {
971
+ const textSource = sourceCode.getText(node);
972
+ let text = textSource;
973
+ // 删除$符号
974
+ text = text.replace(/\${/g, "{");
975
+ if (isChinese(text)) {
976
+ // 获取expressions的文本
977
+ const expressionsText = node.expressions.map((item) => {
978
+ return sourceCode.getText(item);
979
+ });
980
+ // console.log(expressionsText,111)
981
+ let expressionStr = "";
982
+ if (expressionsText.length) {
983
+ expressionsText.forEach((item, index) => {
984
+ const key = `slot${index + 1}`;
985
+ text = text.replace(item, `${key}`);
986
+ expressionStr += `${key}:${item},`;
987
+ });
988
+ // 删除最后一个逗号
989
+ expressionStr = `{${expressionStr.slice(0, -1)}}`;
990
+ }
991
+ if (!isChinese(text)) {
992
+ return;
993
+ }
994
+ context.report({
995
+ node,
996
+ messageId: "noSingleChinese",
997
+ data: {
998
+ raw: chalk.green(textSource),
999
+ },
1000
+ // message: `TemplateLiteral`,
1001
+ fix: (fixer) => {
1002
+ // return console.log(text,expressionStr)
1003
+ // trimSpecial(text, (middle) => {
1004
+ // return `$hxt({key:'',desc:${middle}}${
1005
+ // expressionStr ? `,${expressionStr}` : ""
1006
+ // })`;
1007
+ // })
1008
+ // return
1009
+ return fixer.replaceText(
1010
+ node,
1011
+ trimSpecial(text, (middle) => {
1012
+ return `$hxt({key:'',desc:${middle}}${
1013
+ expressionStr ? `,${expressionStr}` : ""
1014
+ })`;
1015
+ })
1016
+ );
1017
+ },
1018
+ });
1019
+ }
1020
+ }
1021
+ },
1022
+ Literal(node) {
1023
+ // 如果正则忽略
1024
+ if (node.regex) return;
1025
+ if (!isTranslate(node)) {
1026
+ if (isChinese(node.value)) {
1027
+ // 增加{}包裹 render(){return <p title={'测试'} title="测试" title={`${1}测试`}>内容 {`测试`}</p>} 中的内容
1028
+ if (node.parent.type === "JSXAttribute") {
1029
+ context.report({
1030
+ node,
1031
+ messageId: "noSingleChinese",
1032
+ data: {
1033
+ raw: chalk.green(node.value),
1034
+ },
1035
+ fix: (fixer) => {
1036
+ return fixer.replaceText(
1037
+ node,
1038
+ trimSpecial(node.value, (middle) => {
1039
+ return "{$hxt({key:'',desc:'" + middle + "'})}";
1040
+ })
1041
+ );
1042
+ },
1043
+ });
1044
+ } else {
1045
+ context.report({
1046
+ node,
1047
+ messageId: "noSingleChinese",
1048
+ data: {
1049
+ raw: chalk.green(node.value),
1050
+ },
1051
+ fix: (fixer) => {
1052
+ return fixer.replaceText(
1053
+ node,
1054
+ trimSpecial(node.value, (middle) => {
1055
+ return "$hxt({key:'',desc:'" + middle + "'})";
1056
+ })
1057
+ );
1058
+ },
1059
+ });
1060
+ }
1061
+ }
1062
+ }
1063
+ },
1064
+ ...emptyKeyRules(context),
1065
+ }
1066
+ );
1067
+ },
1068
+ };
1069
+
1070
+ /**
1071
+ * @fileoverview 不能单独金额
1072
+ * @author ypf
1073
+ */
1074
+
1075
+ //------------------------------------------------------------------------------
1076
+ // Rule Definition
1077
+ //------------------------------------------------------------------------------
1078
+
1079
+ /** @type {import('eslint').Rule.RuleModule} */
1080
+ const inputTagNameList = [
1081
+ "el-input",
1082
+ "ElInput",
1083
+ "hx-input",
1084
+ "HxInput",
1085
+ "van-field",
1086
+ "VanField",
1087
+ "hxmb-field",
1088
+ "HxmbField",
1089
+ ]; // input标签
1090
+ const formTagNameList = ["hx-form", "HxForm", "HxmForm", "hxmb-form"]; // form标签
1091
+ // 匹配金额名
1092
+ const currencyList = [
1093
+ // "currency",
1094
+ "total",
1095
+ "price",
1096
+ "amount",
1097
+ ];
1098
+ // 价格后缀过滤
1099
+ const notIncludeCurrencySuffixList = [
1100
+ "code",
1101
+ "type",
1102
+ "no",
1103
+ "status",
1104
+ "name",
1105
+ "sort",
1106
+ "source",
1107
+ "qty",
1108
+ ];
1109
+ // 关键字属性
1110
+ const keyPropList = ["prop", "name", "id", "key"];
1111
+ const hasCurrencyName = (str) => {
1112
+ return currencyList.some((item) => {
1113
+ str = str.toLowerCase();
1114
+ const suffix = str.split(item)[1]?.toLowerCase();
1115
+ if (str.includes(item)) {
1116
+ if(suffix){
1117
+ return !notIncludeCurrencySuffixList.some((item) =>
1118
+ suffix.includes(item)
1119
+ );
1120
+ }
1121
+ return true
1122
+ }
1123
+ return false;
1124
+ });
1125
+ };
1126
+ var noSingleCurrencyRule = {
1127
+ meta: {
1128
+ type: "suggestion", // `problem`, `suggestion`, or `layout`
1129
+ docs: {
1130
+ description: "不能单独金额",
1131
+ recommended: false,
1132
+ url: null, // URL to the documentation page for this rule
1133
+ },
1134
+ fixable: null, // Or `code` or `whitespace`
1135
+ schema: [], // Add a schema if the rule has options
1136
+ messages: {
1137
+ noSingleCurrency: "不要单独币种: {{raw}}",
1138
+ }, // Add messageId and message
1139
+ },
1140
+
1141
+ create(context) {
1142
+ const sourceCode = context.sourceCode;
1143
+ // 获取路径
1144
+ const filename = context.filename;
1145
+ return context.parserServices.defineTemplateBodyVisitor(
1146
+ // Event handlers for <template>.
1147
+ {
1148
+ // 模版标签, 如<el-input >
1149
+ VElement(node) {
1150
+ const tagName = node.rawName;
1151
+ if (inputTagNameList.indexOf(tagName) > -1) {
1152
+ const attrs = node.startTag.attributes;
1153
+ // 遍历找到v-model 属性
1154
+ attrs.some((attr) => {
1155
+ const attrTextSource = sourceCode.getText(attr);
1156
+ if (attr.key?.name?.name === "model") {
1157
+ const textSource = sourceCode.getText(attr.value);
1158
+ // 如果包含currency、price、amount,则报错
1159
+ if (hasCurrencyName(textSource)) {
1160
+ context.report({
1161
+ node,
1162
+ data: {
1163
+ raw:
1164
+ chalk.red(`Template|<${tagName}> `) +
1165
+ chalk.green(attrTextSource),
1166
+ },
1167
+ messageId: "noSingleCurrency",
1168
+ // message: `TemplateLiteral`,
1169
+ fix: (fixer) => {},
1170
+ });
1171
+ }
1172
+ }
1173
+ return false;
1174
+ });
1175
+ }
1176
+ },
1177
+ },
1178
+ // Event handlers for <script> or scripts. (optional)
1179
+ {
1180
+ // jsx 标签,如 render() { return <el-input /> } 中的<el-input />
1181
+ // <el-form formItemList=[]>中的formItemList
1182
+ JSXElement(node) {
1183
+ // console.log(node,'node')
1184
+ const tagName = node?.openingElement?.name?.name;
1185
+ // input
1186
+ if (inputTagNameList.indexOf(tagName) > -1) {
1187
+ // 遍历找到vModel 属性
1188
+ const attrs = node.openingElement.attributes;
1189
+ attrs.some((attr) => {
1190
+ const attrTextSource = sourceCode.getText(attr);
1191
+ // console.log(attrTextSource,'attrTextSource')
1192
+ if (attr.name.name === "vModel") {
1193
+ const textSource = sourceCode.getText(attr.value);
1194
+ // 如果包含币种
1195
+ if (hasCurrencyName(textSource)) {
1196
+ context.report({
1197
+ node,
1198
+ data: {
1199
+ raw:
1200
+ chalk.red(`JSX|<${tagName}> `) +
1201
+ chalk.green(attrTextSource),
1202
+ },
1203
+ messageId: "noSingleCurrency",
1204
+ // message: `TemplateLiteral`,
1205
+ fix: (fixer) => {},
1206
+ });
1207
+ }
1208
+ }
1209
+ });
1210
+ }
1211
+ // form
1212
+ if (formTagNameList.indexOf(tagName)) {
1213
+ const attrs = node.openingElement.attributes;
1214
+ // 遍历找到formItemList 属性
1215
+ attrs.some((attr) => {
1216
+ sourceCode.getText(attr);
1217
+ // 是formItemList
1218
+ if (attr?.name?.name === "formItemList") {
1219
+ // 且值是数组
1220
+ if (attr?.value?.expression?.type === "ArrayExpression") {
1221
+ // 遍历数组
1222
+ attr.value.expression.elements.forEach((item) => {
1223
+ (item.properties || []).some((item) => {
1224
+ const attrTextSource = sourceCode.getText(item);
1225
+ if (keyPropList.indexOf(item.key.name) > -1) {
1226
+ const textSource = sourceCode.getText(item.value);
1227
+ if (item?.value?.type === "CallExpression") return;
1228
+ // 如果包含币种
1229
+ if (hasCurrencyName(textSource)) {
1230
+ context.report({
1231
+ node,
1232
+ data: {
1233
+ raw:
1234
+ chalk.red(`JSX|${tagName}|${attr.name.name} `) +
1235
+ chalk.green(`${attrTextSource}`),
1236
+ },
1237
+ messageId: "noSingleCurrency",
1238
+ // message: `TemplateLiteral`,
1239
+ fix: (fixer) => {},
1240
+ });
1241
+ }
1242
+ }
1243
+ });
1244
+ });
1245
+ }
1246
+ }
1247
+ });
1248
+ }
1249
+ },
1250
+ // [{prop:'price'}]
1251
+ ArrayExpression(node) {
1252
+ // 不检测路由文件
1253
+ if (filename.includes("src/router")) return;
1254
+ // 是数组且不是jsx属性上的数组(上面已经检测过了,否则会出现2条错误)
1255
+ if (node.parent?.parent?.type === "JSXAttribute") return;
1256
+ (node.elements || []).forEach((item) => {
1257
+ // 数组里面是对象
1258
+ if (item?.type === "ObjectExpression") {
1259
+ (item.properties || []).forEach((item) => {
1260
+ const attrTextSource = sourceCode.getText(item);
1261
+ if (keyPropList.indexOf(item?.key?.name) > -1) {
1262
+ if (item?.value?.type === "CallExpression") return;
1263
+ const textSource = sourceCode.getText(item.value);
1264
+ // 如果包含币种
1265
+ if (hasCurrencyName(textSource)) {
1266
+ context.report({
1267
+ node,
1268
+ data: {
1269
+ raw:
1270
+ chalk.red(`JSX|[{}] `) +
1271
+ chalk.green(`${attrTextSource}`),
1272
+ },
1273
+ messageId: "noSingleCurrency",
1274
+ });
1275
+ }
1276
+ }
1277
+ });
1278
+ }
1279
+ });
1280
+ },
1281
+ }
1282
+ );
1283
+ },
1284
+ };
1285
+
1286
+ /**
1287
+ * @fileoverview 不能单独金额
1288
+ * @author ypf
1289
+ */
1290
+
1291
+ //------------------------------------------------------------------------------
1292
+ // Rule Definition
1293
+ //------------------------------------------------------------------------------
1294
+
1295
+ /** @type {import('eslint').Rule.RuleModule} */
1296
+ const tagNameList = ["hx-search-list-page", "HxSearchListPage"];
1297
+ const attrNameList = ["custom-column-module", "customColumnModule"];
1298
+ const moduleArr = new Set();
1299
+ const getModuleArr = () => [...moduleArr];
1300
+
1301
+ var noSingleCustomColumnModule = {
1302
+ meta: {
1303
+ type: "suggestion", // `problem`, `suggestion`, or `layout`
1304
+ docs: {
1305
+ description: "不能单独自定义列",
1306
+ recommended: false,
1307
+ url: null, // URL to the documentation page for this rule
1308
+ },
1309
+ fixable: null, // Or `code` or `whitespace`
1310
+ schema: [], // Add a schema if the rule has options
1311
+ messages: {}, // Add messageId and message
1312
+ },
1313
+
1314
+ create(context) {
1315
+ const sourceCode = context.sourceCode;
1316
+ // 获取路径
1317
+ const fullPathName = context.filename;
1318
+ const cwd = context.cwd;
1319
+ const getPath = (loc = {}) => {
1320
+ const location = `${loc.start?.line}:${loc.start?.column} `;
1321
+ return location + fullPathName.replace(cwd, "");
1322
+ };
1323
+ return context.parserServices.defineTemplateBodyVisitor(
1324
+ // Event handlers for <template>.
1325
+ {
1326
+ // 模版标签,
1327
+ VElement(node) {
1328
+ const tagName = node.rawName;
1329
+ if (tagNameList.indexOf(tagName) > -1) {
1330
+ const attrs = node.startTag.attributes;
1331
+ attrs.some((attr) => {
1332
+ sourceCode.getText(attr);
1333
+ if (
1334
+ attrNameList.includes(attr.key?.argument?.rawName) &&
1335
+ attr.value?.expression?.type === "Literal"
1336
+ ) {
1337
+ const value = attr.value.expression.value;
1338
+ moduleArr.add({
1339
+ module: value,
1340
+ path: getPath(attr.loc),
1341
+ });
1342
+ } else if (
1343
+ attrNameList.includes(attr.key?.rawName) &&
1344
+ attr.value.type === "VLiteral"
1345
+ ) {
1346
+ const value = attr.value.value;
1347
+ moduleArr.add({
1348
+ module: +value,
1349
+ path: getPath(attr.loc),
1350
+ });
1351
+ } else if (
1352
+ attrNameList.includes(attr.key?.argument?.rawName) ||
1353
+ attrNameList.includes(attr.key?.rawName)
1354
+ ) {
1355
+ moduleArr.add({
1356
+ module: sourceCode.getText(attr.value.expression),
1357
+ path: getPath(attr.loc),
1358
+ isDynamics: true, // 变量
1359
+ });
1360
+ }
1361
+ return false;
1362
+ });
1363
+ }
1364
+ },
1365
+ },
1366
+ // Event handlers for <script> or scripts. (optional)
1367
+ {
1368
+ JSXElement(node) {
1369
+ const tagName = node?.openingElement?.name?.name;
1370
+ if (tagNameList.indexOf(tagName) > -1) {
1371
+ // 遍历找到customColumnModule 属性
1372
+ const attrs = node.openingElement.attributes;
1373
+ attrs.some((attr) => {
1374
+ sourceCode.getText(attr);
1375
+ if (attrNameList.includes(attr?.name?.name)) {
1376
+ if (attr.value?.expression?.type === "Literal") {
1377
+ const value = attr.value.expression.value;
1378
+ moduleArr.add({
1379
+ module: value,
1380
+ path: getPath(attr.loc),
1381
+ });
1382
+ } else {
1383
+ moduleArr.add({
1384
+ module: sourceCode.getText(attr.value.expression),
1385
+ path: getPath(attr.loc),
1386
+ isDynamics: true, // 变量
1387
+ });
1388
+ }
1389
+ }
1390
+ });
1391
+ }
1392
+ },
1393
+ }
1394
+ );
1395
+ },
1396
+ };
1397
+
1398
+ /**
1399
+ * @fileoverview 国际化
1400
+ * @author ypf
1401
+ */
1402
+ const require = createRequire(import.meta.url);
1403
+ const VueESlintParserPath = require.resolve('vue-eslint-parser');
1404
+ const meta = {
1405
+ name: "eslint-plugin-i18n",
1406
+ version: "1.0.0",
1407
+ };
1408
+ var i18n = {
1409
+ rules: {
1410
+ "no-single-chinese": noSingleChineseRule,
1411
+ "no-single-currency": noSingleCurrencyRule,
1412
+ "no-single-customColumnModule": noSingleCustomColumnModule,
1413
+ },
1414
+ processors: {},
1415
+ configs: {
1416
+ recommended: {
1417
+ // 插件
1418
+ plugins: [
1419
+ "i18n", // 可以省略eslint-plugin-
1420
+ ],
1421
+ rules: {
1422
+ "i18n/no-single-chinese": "warn",
1423
+ "i18n/no-single-currency": "warn",
1424
+ "i18n/no-single-customColumnModule": "warn",
1425
+ },
1426
+ // parser: "vue-eslint-parser",
1427
+ parser: VueESlintParserPath, // 文档中是只能字符串
1428
+ parserOptions: {
1429
+ ecmaVersion: "latest", // 指定你想要使用的 ECMAScript 版本
1430
+ sourceType: "module", // 支持脚本类型为模块,否则不支持import/export
1431
+ ecmaFeatures: {
1432
+ jsx: true,
1433
+ },
1434
+ parser: {
1435
+ js: espree,
1436
+ jsx: espree, // 支持jsx语法
1437
+ // ts: tsParser,
1438
+ // tsx: tsParser,
1439
+ },
1440
+ },
1441
+ },
1442
+ },
1443
+ };
1444
+
1445
+ // https://www.npmjs.com/package/text-table?activeTab=readme
1446
+
1447
+ function table (rows_, opts) {
1448
+ if (!opts) opts = {};
1449
+ var hsep = opts.hsep === undefined ? " " : opts.hsep;
1450
+ var align = opts.align || [];
1451
+ var stringLength =
1452
+ opts.stringLength ||
1453
+ function (s) {
1454
+ return String(s).length;
1455
+ };
1456
+ var dotsizes = reduce(
1457
+ rows_,
1458
+ function (acc, row) {
1459
+ forEach(row, function (c, ix) {
1460
+ var n = dotindex(c);
1461
+ if (!acc[ix] || n > acc[ix]) acc[ix] = n;
1462
+ });
1463
+ return acc;
1464
+ },
1465
+ []
1466
+ );
1467
+
1468
+ var rows = map(rows_, function (row) {
1469
+ return map(row, function (c_, ix) {
1470
+ var c = String(c_);
1471
+ if (align[ix] === ".") {
1472
+ var index = dotindex(c);
1473
+ var size =
1474
+ dotsizes[ix] + (/\./.test(c) ? 1 : 2) - (stringLength(c) - index);
1475
+ return c + Array(size).join(" ");
1476
+ } else return c;
1477
+ });
1478
+ });
1479
+
1480
+ var sizes = reduce(
1481
+ rows,
1482
+ function (acc, row) {
1483
+ forEach(row, function (c, ix) {
1484
+ var n = stringLength(c);
1485
+ if (!acc[ix] || n > acc[ix]) acc[ix] = n;
1486
+ });
1487
+ return acc;
1488
+ },
1489
+ []
1490
+ );
1491
+
1492
+ return map(rows, function (row) {
1493
+ return map(row, function (c, ix) {
1494
+ var n = sizes[ix] - stringLength(c) || 0;
1495
+ var s = Array(Math.max(n + 1, 1)).join(" ");
1496
+ if (align[ix] === "r" || align[ix] === ".") {
1497
+ return s + c;
1498
+ }
1499
+ if (align[ix] === "c") {
1500
+ return (
1501
+ Array(Math.ceil(n / 2 + 1)).join(" ") +
1502
+ c +
1503
+ Array(Math.floor(n / 2 + 1)).join(" ")
1504
+ );
1505
+ }
1506
+
1507
+ return c + s;
1508
+ })
1509
+ .join(hsep)
1510
+ .replace(/\s+$/, "");
1511
+ }).join("\n");
1512
+ }
1513
+
1514
+ function dotindex(c) {
1515
+ var m = /\.[^.]*$/.exec(c);
1516
+ return m ? m.index + 1 : c.length;
1517
+ }
1518
+
1519
+ function reduce(xs, f, init) {
1520
+ if (xs.reduce) return xs.reduce(f, init);
1521
+ var i = 0;
1522
+ var acc = arguments.length >= 3 ? init : xs[i++];
1523
+ for (; i < xs.length; i++) {
1524
+ f(acc, xs[i], i);
1525
+ }
1526
+ return acc;
1527
+ }
1528
+
1529
+ function forEach(xs, f) {
1530
+ if (xs.forEach) return xs.forEach(f);
1531
+ for (var i = 0; i < xs.length; i++) {
1532
+ f.call(xs, xs[i], i);
1533
+ }
1534
+ }
1535
+
1536
+ function map(xs, f) {
1537
+ if (xs.map) return xs.map(f);
1538
+ var res = [];
1539
+ for (var i = 0; i < xs.length; i++) {
1540
+ res.push(f.call(xs, xs[i], i));
1541
+ }
1542
+ return res;
1543
+ }
1544
+
1545
+ //------------------------------------------------------------------------------
1546
+
1547
+ /**
1548
+ * Given a word and a count, append an s if count is not one.
1549
+ * @param {string} word A word in its singular form.
1550
+ * @param {int} count A number controlling whether word should be pluralized.
1551
+ * @returns {string} The original word with an s on the end if count is not one.
1552
+ */
1553
+ function pluralize(word, count) {
1554
+ return (count === 1 ? word : `${word}s`);
1555
+ }
1556
+
1557
+ //------------------------------------------------------------------------------
1558
+ // Public Interface
1559
+ //------------------------------------------------------------------------------
1560
+
1561
+ function stylish(results) {
1562
+
1563
+ let output = "\n",
1564
+ errorCount = 0,
1565
+ warningCount = 0,
1566
+ fixableErrorCount = 0,
1567
+ fixableWarningCount = 0,
1568
+ summaryColor = "yellow";
1569
+ results.forEach(result => {
1570
+ const messages = result.messages;
1571
+
1572
+ if (messages.length === 0) {
1573
+ return;
1574
+ }
1575
+
1576
+ errorCount += result.errorCount;
1577
+ warningCount += result.warningCount;
1578
+ fixableErrorCount += result.fixableErrorCount;
1579
+ fixableWarningCount += result.fixableWarningCount;
1580
+
1581
+ output += `${chalk.underline(result.filePath)}\n`;
1582
+
1583
+ output += `${table(
1584
+ messages.map(message => {
1585
+ let messageType;
1586
+
1587
+ if (message.fatal || message.severity === 2) {
1588
+ messageType = chalk.red("error");
1589
+ summaryColor = "red";
1590
+ } else {
1591
+ messageType = chalk.yellow("warning");
1592
+ }
1593
+
1594
+ return [
1595
+ "",
1596
+ message.line || 0,
1597
+ message.column || 0,
1598
+ messageType,
1599
+ message.message.replace(/([^ ])\.$/u, "$1"),
1600
+ chalk.dim(message.ruleId || "")
1601
+ ];
1602
+ }),
1603
+ {
1604
+ align: ["", "r", "l"],
1605
+ stringLength(str) {
1606
+ return stripAnsi(str).length;
1607
+ }
1608
+ }
1609
+ ).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
1610
+ });
1611
+
1612
+ const total = errorCount + warningCount;
1613
+
1614
+ if (total > 0) {
1615
+ output += chalk[summaryColor].bold([
1616
+ "\u2716 ", total, pluralize(" problem", total),
1617
+ " (", errorCount, pluralize(" error", errorCount), ", ",
1618
+ warningCount, pluralize(" warning", warningCount), ")\n"
1619
+ ].join(""));
1620
+
1621
+ if (fixableErrorCount > 0 || fixableWarningCount > 0) {
1622
+ output += chalk[summaryColor].bold([
1623
+ " ", fixableErrorCount, pluralize(" error", fixableErrorCount), " and ",
1624
+ fixableWarningCount, pluralize(" warning", fixableWarningCount),
1625
+ " potentially fixable with the `--fix` option.\n"
1626
+ ].join(""));
1627
+ }
1628
+ }
1629
+
1630
+ // Resets output color, for prevent change on top level
1631
+ return total > 0 ? chalk.reset(output) : "";
1632
+ }
1633
+
1634
+ async function lint$1(patterns, fix, rule, hook = {}) {
1635
+ const rules$1 = {
1636
+ "i18n/no-single-chinese": rule ? "off" : "warn",
1637
+ };
1638
+ rules[0].choices.forEach((item) => {
1639
+ rules$1[`i18n/no-single-${item.value}`] = "off";
1640
+ if (item.children) {
1641
+ item.children.forEach((child) => {
1642
+ child.choices.forEach((item) => {
1643
+ rules$1[`i18n/no-single-${item.value}`] = "off";
1644
+ });
1645
+ });
1646
+ }
1647
+ });
1648
+ if (rule) {
1649
+ rules$1[`i18n/no-single-${rule}`] = "warn";
1650
+ }
1651
+ const eslint = new ESLint({
1652
+ useEslintrc: false, // 不检测其他规则,只检测i18n规则, 不设置为false,则会检测项目中的.eslintrc.js文件规则
1653
+ // 只检测js,jsx,vue文件
1654
+ extensions: [".js", ".jsx", ".vue"],
1655
+ fix: fix || false, // 是否自动修复
1656
+ overrideConfigFile: null,
1657
+ plugins: { [meta.name]: i18n }, // 必须写,下面的extends才能识别, 和配置文件那种不一样,配置文件那种只写extends就行,不用写plugins
1658
+ overrideConfig: {
1659
+ extends: [
1660
+ "plugin:i18n/recommended", // 表示使用test插件的推荐规则, plugin:后不能有空格,否则不生效
1661
+ ],
1662
+ // 此处增加rules是为了动态判断覆盖extends中的rule级别
1663
+ rules: rules$1,
1664
+ },
1665
+ });
1666
+ Loading$1.start(`检测中...`);
1667
+ try {
1668
+ // 检查文件
1669
+ const results = await eslint.lintFiles(patterns);
1670
+ hook.lintFilesAfter && (await hook.lintFilesAfter(results,Loading$1));
1671
+ if (!fix) {
1672
+ // 判断是否有匹配到错误
1673
+ const hasErrors = results.some(
1674
+ (result) => result.messages && result.messages.length
1675
+ );
1676
+ if (!hasErrors) {
1677
+ return Loading$1.succeed(`没有要修复的错误`);
1678
+ }
1679
+ Loading$1.succeed(`检测完成`);
1680
+ // 如果没有fix,则直接输出错误
1681
+ return console.log(stylish(results));
1682
+ }
1683
+ // 判断是否有匹配到错误
1684
+ const hasErrors1 = results.some(
1685
+ (result) => result.output || result.warningCount
1686
+ );
1687
+ if (!hasErrors1) {
1688
+ return Loading$1.succeed(`没有要修复的错误`);
1689
+ }
1690
+ // 输出回原文件
1691
+ await ESLint.outputFixes(results);
1692
+ Loading$1.succeed(`替换空key完成`);
1693
+ inquirer
1694
+ .prompt([
1695
+ {
1696
+ message: `是否开始翻译key`,
1697
+ name: "isTransKey",
1698
+ type: "confirm",
1699
+ default: true,
1700
+ },
1701
+ ])
1702
+ .then(({ isTransKey }) => {
1703
+ if (isTransKey) {
1704
+ console.log(chalk.green(`开始准备翻译...`));
1705
+ process$1.execSync(`fe-it-alpha --fix -i ${patterns} `, {
1706
+ stdio: "inherit", // 打印子进程的输出到父进程
1707
+ });
1708
+ }
1709
+ });
1710
+ } catch (error) {
1711
+ Loading$1.fail(`检测失败`);
1712
+ console.error(chalk.red(error));
1713
+ }
1714
+ }
1715
+
1716
+ const condition = {
1717
+ currency: hasCurrencyName,
1718
+ };
1719
+ async function customColumnModule (lintc, fix, rule, parentRule) {
1720
+ const { way } = await inquirer.prompt([
1721
+ {
1722
+ message: "请选择检测自定义列方式",
1723
+ name: "way",
1724
+ type: "list",
1725
+ choices: [
1726
+ {
1727
+ name: "自动检测HxSearchListPage的自定义列ID",
1728
+ value: "normal",
1729
+ },
1730
+ {
1731
+ name: "自定义输入",
1732
+ value: "input",
1733
+ },
1734
+ ],
1735
+ },
1736
+ ]);
1737
+ if (way === "normal") {
1738
+ lint$1(lintc, fix, "customColumnModule", {
1739
+ lintFilesAfter: (results, Loading) => {
1740
+ const arr = getModuleArr();
1741
+ const dynamicsArr = arr.filter((item) => item.isDynamics);
1742
+ const normalArr = arr.filter((item) => !item.isDynamics);
1743
+
1744
+ return new Promise((resolve) => {
1745
+ Loading.succeed(`检测完成,${arr.length?'':'未'}匹配到规则`);
1746
+ if (arr.length) {
1747
+ getColumn(normalArr, dynamicsArr, parentRule);
1748
+ }
1749
+ });
1750
+ },
1751
+ });
1752
+ } else if (way === "input") {
1753
+ const { customColumnId } = await inquirer.prompt([
1754
+ {
1755
+ message: "请输入自定义列ID,格式为xx,xx,xx",
1756
+ name: "customColumnId",
1757
+ type: "input",
1758
+ },
1759
+ ]);
1760
+ const moduleArr = customColumnId.split(",").map((item) => {
1761
+ return {
1762
+ module: item,
1763
+ path: "NA",
1764
+ };
1765
+ });
1766
+ getColumn(moduleArr, [], parentRule);
1767
+ }
1768
+ }
1769
+
1770
+ async function getColumn(moduleArr = [], dynamicsArr = [],parentRule) {
1771
+ const token = await login({
1772
+ env: "sit",
1773
+ username: "superAdmin",
1774
+ password: "admin1",
1775
+ });
1776
+ const requestArr = moduleArr.map(({ module, path }) =>
1777
+ request$1({
1778
+ url: `http://sit-hxjf.hongxinshop.com/api-item/api/customizedColumns/find`,
1779
+ method: "POST",
1780
+ json: true,
1781
+ body: { module },
1782
+ headers: {
1783
+ Authorization: `Bearer ${token}`,
1784
+ "x-request-vaildate": "open",
1785
+ },
1786
+ })
1787
+ );
1788
+ Promise.allSettled(requestArr).then(async (res) => {
1789
+ const tableData = [];
1790
+ res.forEach(({ value, status }, index) => {
1791
+ const { path, module } = moduleArr[index];
1792
+ if (status === "fulfilled") {
1793
+ const { data } = value;
1794
+ const { showColumns, hiddenColumns } = data;
1795
+ let fieldArr = [];
1796
+ showColumns.concat(hiddenColumns).forEach((item) => {
1797
+ if (condition[parentRule](item.showField)) {
1798
+ fieldArr.push(`${item.showField} ${item.showName}`);
1799
+ }
1800
+ });
1801
+ if (fieldArr.length) {
1802
+ fieldArr.forEach((item, index) => {
1803
+ let obj = {
1804
+ module,
1805
+ field: item,
1806
+ path,
1807
+ };
1808
+ if (index !== 0) {
1809
+ // obj.module = module;
1810
+ // obj.path = path;
1811
+ delete obj.module;
1812
+ delete obj.path;
1813
+ }
1814
+ tableData.push(obj);
1815
+ });
1816
+ } else {
1817
+ tableData.push({
1818
+ module,
1819
+ field: "NA",
1820
+ path,
1821
+ });
1822
+ }
1823
+ } else {
1824
+ tableData.push({
1825
+ module,
1826
+ field: "error",
1827
+ path,
1828
+ });
1829
+ }
1830
+ });
1831
+ // 合并动态模块
1832
+ dynamicsArr.forEach((item) => {
1833
+ tableData.push({
1834
+ module: item.module,
1835
+ field: "error",
1836
+ path: item.path,
1837
+ });
1838
+ });
1839
+ // 排序, field为NA和error的排在后面
1840
+ tableData.sort((a, b) => {
1841
+ if (["NA", "error"].includes(b.field)) {
1842
+ return -1;
1843
+ } else {
1844
+ return 1;
1845
+ }
1846
+ });
1847
+ const successTableData = []; // 成功的数据
1848
+ const naTableData = [];
1849
+ const noNaTableData = [];
1850
+ const errorTableData = []; // 失败的数据
1851
+ tableData.forEach((item) => {
1852
+ if (item.field === "error") {
1853
+ errorTableData.push(item);
1854
+ } else {
1855
+ if (item.field === "NA") {
1856
+ naTableData.push(item);
1857
+ } else {
1858
+ noNaTableData.push(item);
1859
+ }
1860
+ successTableData.push(item);
1861
+ }
1862
+ });
1863
+
1864
+ console.log(
1865
+ chalk.green(
1866
+ `共${tableData.length}个moduleId, 成功${
1867
+ successTableData.length
1868
+ }个(规则命中${noNaTableData.length}个, ${chalk.yellow(
1869
+ `未命中${naTableData.length}个`
1870
+ )}),`
1871
+ ) + chalk.red(`失败${errorTableData.length}个`)
1872
+ );
1873
+ if (noNaTableData.length) {
1874
+ console.log(chalk.green(`成功命中规则`));
1875
+ console.table(noNaTableData);
1876
+ }
1877
+ if (errorTableData.length) {
1878
+ console.log(chalk.red(`失败,需手动处理或重试`));
1879
+ console.table(errorTableData);
1880
+ }
1881
+ if (naTableData.length) {
1882
+ console.log(chalk.yellow(`成功但未命中规则,无需处理`));
1883
+ console.table(naTableData);
1884
+ }
1885
+ });
1886
+ }
1887
+
1888
+ // import packageJson from "../package.json" assert { type: "json" };
1889
+ const { version } = getPackageJson();
1890
+ // const spinner = ora();
1891
+ program.version(version);
1892
+ program.option("-i, --input <type>", "翻译空key");
1893
+ // command('merge')
1894
+ // .option("-i, --input <type>" , "翻译空key")
1895
+ // .option("-m, --merge" , "输出文件").action((options) => {
1896
+ // console.log('merge', options)
1897
+ // })
1898
+ // 上传翻译
1899
+ program.option("-u, --upload", "上传翻译包");
1900
+ // 同步sass应用权限配置
1901
+ program.option("-sass, --sass", "同步sass按钮权限配置");
1902
+ // 检测未$hxt中文
1903
+ program.option("-lint, --lint <patterns>", "检测未$hxt的中文");
1904
+ // 检测配置项
1905
+ program.option("-lintc, --lintc <patterns>", "检测指定配置规则");
1906
+ // 检测未$hxt中文自动修复
1907
+ program.option("--fix", "修复lint检出的错误");
1908
+ program.parse(process.argv);
1909
+
1910
+ // 判断命令参数
1911
+ const { input, sass, upload, lint, lintc, fix } = program.opts();
1912
+ const rules = [
1913
+ {
1914
+ message: "请选择检查规则",
1915
+ name: "rule",
1916
+ type: "list",
1917
+ choices: [
1918
+ {
1919
+ name: "币种",
1920
+ value: "currency",
1921
+ children: [
1922
+ {
1923
+ message: "请选择场景",
1924
+ name: "scene",
1925
+ type: "list",
1926
+ choices: [
1927
+ {
1928
+ name: "匹配属性名含有price、total、amount金额字段",
1929
+ value: "currency",
1930
+ },
1931
+ {
1932
+ name: "自定义列",
1933
+ value: "customColumnModule",
1934
+ },
1935
+ ],
1936
+ },
1937
+ ],
1938
+ },
1939
+ ],
1940
+ },
1941
+ ];
1942
+ if (sass) {
1943
+ syncSassConfig();
1944
+ } else if (upload) {
1945
+ upload$1();
1946
+ } else if (lint) {
1947
+ lint$1(lint, fix);
1948
+ } else if (lintc) {
1949
+ inquirer.prompt(rules).then(async ({ rule }) => {
1950
+ const parentRule =rule;
1951
+ if (rule === "currency") {
1952
+ const ruleChoice = rules.find((item) => item.name === "rule");
1953
+ const currencySubChoice = ruleChoice.choices.find(
1954
+ (item) => item.value === "currency"
1955
+ ).children;
1956
+ const { scene } = await inquirer.prompt(currencySubChoice);
1957
+ rule = scene;
1958
+ if(rule==='customColumnModule'){
1959
+ return customColumnModule(lintc,fix,rule,parentRule)
1960
+ }
1961
+ }
1962
+ lint$1(lintc, fix, rule);
1963
+ });
1964
+ } else {
1965
+ let projectName = "请在package.json中配置name字段(项目名称)";
1966
+ const projectPath = "./package.json";
1967
+ const isExist = isExistPath(projectPath);
1968
+ if (isExist) {
1969
+ const str = fs.readFileSync(projectPath, "utf-8").toString();
1970
+ projectName = JSON.parse(str).name;
1971
+ }
1972
+ inquirer
1973
+ .prompt([
1974
+ {
1975
+ message: `请确认package.json中项目名称是否是${projectName}`,
1976
+ name: "isProjectName",
1977
+ type: "confirm",
1978
+ default: true,
1979
+ },
1980
+ ])
1981
+ .then((res) => {
1982
+ if (res.isProjectName) {
1983
+ inquirer
1984
+ .prompt([
1985
+ {
1986
+ message: "请选择翻译接口",
1987
+ name: "type",
1988
+ type: "list",
1989
+ choices: [
1990
+ {
1991
+ name: "百度翻译",
1992
+ value: "baidu",
1993
+ },
1994
+ ],
1995
+ },
1996
+ ])
1997
+ .then((res) => {
1998
+ const { type } = res;
1999
+ let cacheSecret = {};
2000
+ if (!isExistPath(getRunCliPath({ root: true }) + "/.cache")) {
2001
+ fs.mkdirSync(getRunCliPath({ root: true }) + "/.cache");
2002
+ }
2003
+ const cacheFilePath =
2004
+ getRunCliPath({ root: true }) + "/.cache/" + res.type;
2005
+ if (isExistPath(cacheFilePath)) {
2006
+ const cache = JSON.parse(readCache(res.type));
2007
+ cacheSecret = cache;
2008
+ }
2009
+ const question = [
2010
+ {
2011
+ message: "请输入百度翻译的appid",
2012
+ name: "appid",
2013
+ default: cacheSecret.appid,
2014
+ // 必填
2015
+ validate: function (val) {
2016
+ if (val) {
2017
+ return true;
2018
+ }
2019
+ return "请输入百度翻译的appid";
2020
+ },
2021
+ },
2022
+ {
2023
+ message: "请输入百度翻译的key",
2024
+ name: "key",
2025
+ default: cacheSecret.key,
2026
+ // 必填
2027
+ validate: function (val) {
2028
+ if (val) {
2029
+ return true;
2030
+ }
2031
+ return "请输入百度翻译的key";
2032
+ },
2033
+ },
2034
+ ];
2035
+ inquirer.prompt(question).then((res) => {
2036
+ writeCache(
2037
+ type,
2038
+ JSON.stringify({
2039
+ type,
2040
+ ...res,
2041
+ })
2042
+ );
2043
+ const { appid, key } = res;
2044
+ const isLintFix = fix;
2045
+ translateUtil(appid, key, projectName, isLintFix);
2046
+ });
2047
+ });
2048
+ } else {
2049
+ console.log(
2050
+ chalk.red("请在package.json中配置正确的name字段(项目名称)")
2051
+ );
2052
+ }
2053
+ });
2054
+ }
2055
+
2056
+ function translateUtil(appid, key, projectName, isLintFix) {
2057
+ const { input } = program.opts();
2058
+ let src = [input];
2059
+ const isSrcDirectory = fs.statSync(input).isDirectory();
2060
+ if (isSrcDirectory) {
2061
+ const directory = input + "/**/*.{js,vue,jsx,ts,tsx}";
2062
+ src = glob.sync(directory, {
2063
+ ignore: directory + `/**/node_modules/**`,
2064
+ });
2065
+ }
2066
+ let allArr = []; // 所有文本组成的数组
2067
+ let groupArr = []; // 分组后的数组
2068
+ let newAllArr = []; // 分组后的数组还原成所有数组
2069
+ // 循环遍历文件下的文件
2070
+ function readFolder(isOk) {
2071
+ // spinner.start();
2072
+ src.forEach((item, index) => {
2073
+ const fPath = item;
2074
+ let file = fs.readFileSync(fPath, "utf-8").toString();
2075
+ // 提取file里所有以$hxt({ key: '', desc:为开头 以')}结尾的字符串
2076
+ const reg =
2077
+ /\$hxt\([\r\n\s]*\{[\r\n\s]*key[\r\n\s]*:[\r\n\s]*['|"][\r\n\s]*['|"],[\r\n\s]*desc[\r\n\s]*:[\r\n\s]*['|"|`](.*?)['|"|`],*[\r\n\s]*\}(\)|,[\r\n\s]*\{)/g;
2078
+ file = file.replace(reg, function (match, p1, p2) {
2079
+ if (isOk) {
2080
+ const reg1 = /key[\r\n\s]*:[\r\n\s]*('|")[\r\n\s]*('|")/g;
2081
+ let key = newAllArr.find(
2082
+ (item) => item.path === fPath && item.ZH_CN === p1
2083
+ ).key;
2084
+ // 去除{ slotxx }
2085
+ key = key.replace(/\{[\r\n\s]*slot\d[\r\n\s]*\}/g, "");
2086
+ // 去除空格
2087
+ key = key.replace(/\s/g, "");
2088
+ return match.replace(reg1, `key: '${key}'`);
2089
+ } else {
2090
+ allArr.push({
2091
+ path: fPath,
2092
+ ZH_CN: p1,
2093
+ EN: "",
2094
+ key: "",
2095
+ });
2096
+ }
2097
+ });
2098
+ if (isOk) {
2099
+ fs.writeFileSync(fPath, file, "utf-8");
2100
+ }
2101
+ });
2102
+ if (isOk) {
2103
+ // spinner.stop();
2104
+ return;
2105
+ }
2106
+ // 数组分割,如果str长度大于2000,就分割成多个数组
2107
+ let str = "";
2108
+ let cutArr = [];
2109
+ console.log(chalk.green(`共有${allArr.length}条数据需要翻译`));
2110
+ allArr.forEach((item, index) => {
2111
+ if (str.length > 2000) {
2112
+ groupArr.push(cutArr);
2113
+ str = "";
2114
+ cutArr = [];
2115
+ }
2116
+ str += item.ZH_CN + "\n";
2117
+ cutArr.push(item);
2118
+ });
2119
+ if (cutArr.length > 0) {
2120
+ Array.prototype.push.apply(groupArr, [cutArr]);
2121
+ } else {
2122
+ groupArr = allArr;
2123
+ }
2124
+ console.log(chalk.green(`共有${groupArr.length}组数据需要翻译`));
2125
+ if(!groupArr.length) return
2126
+ const task = groupArr.map(async (item, index) => {
2127
+ let str = "";
2128
+ item.forEach((item1, index1) => {
2129
+ // 去除换行空格
2130
+ const ZH_CN = item1.ZH_CN.replace(/\n/g, "");
2131
+ str += ZH_CN + "\n";
2132
+ });
2133
+ return translate(item, str, index + 1);
2134
+ });
2135
+ Promise.allSettled(task).then(async (res) => {
2136
+ console.log(chalk.green("任务执行完毕"));
2137
+ groupArr.forEach((item, index) => {
2138
+ Array.prototype.push.apply(newAllArr, item);
2139
+ });
2140
+ readFolder(true);
2141
+
2142
+ let exportData = [];
2143
+ // key中有debug排后面
2144
+ const sortNewAllArr = _.sortBy(newAllArr, function (item) {
2145
+ return item.key.indexOf("debug") > -1;
2146
+ });
2147
+ sortNewAllArr.forEach((item, index) => {
2148
+ const rowArr = [
2149
+ "GIT", // 系统
2150
+ projectName, // 项目
2151
+ "frontInit", // 模块(路由)
2152
+ "", // 编码
2153
+ 1, // 所属(1:前端 2:后端)
2154
+ 1, // 数据类型(1:static,2:enum,3:json)
2155
+ "", // 中文值
2156
+ "", // 英文
2157
+ "", // 泰文(TH)
2158
+ "", // 越南(VI)
2159
+ "", // 土耳其(TR)
2160
+ ];
2161
+ const cacheIndex = {
2162
+ ZH_CN: 6,
2163
+ EN: 7,
2164
+ key: 3,
2165
+ };
2166
+ Object.entries(item).forEach(([key, value], index1) => {
2167
+ const insertIndex = cacheIndex[key];
2168
+ rowArr[insertIndex] = value;
2169
+ });
2170
+ exportData.push(rowArr);
2171
+ });
2172
+ const { isMerge } = await inquirer.prompt([
2173
+ {
2174
+ message: `是否合并至export.xlsx,默认${chalk.red("否,直接覆盖")}`,
2175
+ name: "isMerge",
2176
+ type: "confirm",
2177
+ default: false,
2178
+ },
2179
+ ]);
2180
+ try {
2181
+ if (isMerge) {
2182
+ const str = xlsx.parse(fs.readFileSync("./export.xlsx"));
2183
+ if (str[0].data.length) {
2184
+ exportData = str[0].data.concat(exportData);
2185
+ } else {
2186
+ throw new Error();
2187
+ }
2188
+ } else {
2189
+ throw new Error();
2190
+ }
2191
+ } catch (error) {
2192
+ // 第一行插入标题
2193
+ exportData.unshift([
2194
+ "系统",
2195
+ "项目",
2196
+ "模块",
2197
+ "编码",
2198
+ "所属(1:前端 2:后端)",
2199
+ "数据类型(1:static,2:enum,3:json)",
2200
+ "中文值",
2201
+ "英文",
2202
+ "泰文(TH)",
2203
+ "越南(VI)",
2204
+ "土耳其(TR)",
2205
+ ]);
2206
+ }
2207
+ const buffer = xlsx.build([{ data: exportData }]);
2208
+ fs.writeFileSync(`./export.xlsx`, buffer, "binary");
2209
+ console.log(chalk.green(`${isMerge?"合并":"覆盖"}文件成功`));
2210
+ });
2211
+ }
2212
+
2213
+ // 百度翻译接口
2214
+ async function translate(item, str, groupIndex) {
2215
+ const q = str;
2216
+ const salt = Math.random();
2217
+ const from = "zh";
2218
+ const to = "en";
2219
+ const sign = md5(appid + q + salt + key);
2220
+ const query = querystring.stringify({
2221
+ q,
2222
+ appid,
2223
+ salt,
2224
+ from,
2225
+ to,
2226
+ sign,
2227
+ });
2228
+ const url = "http://api.fanyi.baidu.com/api/trans/vip/translate?" + query;
2229
+ return new Promise((resolve, reject) => {
2230
+ // 自动翻译延时,必须大于 1000 ms,否则调用百度翻译 API 会失败
2231
+ throttle(() => {
2232
+ console.log(chalk.green(`正在翻译第${groupIndex}组数据`));
2233
+ return request(url, function (_error, response, body) {
2234
+ const resBody = JSON.parse(body);
2235
+ if (resBody.trans_result) {
2236
+ console.log(`ok`);
2237
+ let result = resBody.trans_result || [];
2238
+ item.forEach((item1, index1) => {
2239
+ const id = v5(item1.path, v5.URL).slice(0, 6);
2240
+ if (result[index1] && result[index1].src === item1.ZH_CN) {
2241
+ // 删除str中的单引号或双引号或斜杠或逗号
2242
+ let str = result[index1].dst
2243
+ .replace(/\'/g, "")
2244
+ .replace(/\"/g, "")
2245
+ .replace(/\//g, "")
2246
+ .replace(/\,/g, "");
2247
+ // 每个单词首首字母转换为大写
2248
+ const s = str.split(" ");
2249
+ let key = s
2250
+ .map((item, index) => {
2251
+ return item[0].toUpperCase() + item.slice(1);
2252
+ })
2253
+ .join("");
2254
+ // 去除key中的标点符号
2255
+ key = key.replace(/[^a-zA-Z0-9{}]/g, "").slice(0, 73);
2256
+ let EN = result[index1].dst;
2257
+ EN = EN[0].toUpperCase() + EN.slice(1);
2258
+ item1.EN = EN;
2259
+ if (isLintFix) {
2260
+ item1.key = nanoid().replace("_", "a");
2261
+ // item1.key = id + "-" + key;
2262
+ } else {
2263
+ item1.key = id + "-" + key;
2264
+ }
2265
+ }
2266
+ });
2267
+ resolve(true);
2268
+ } else {
2269
+ item.forEach((item1, index1) => {
2270
+ const id = v5(item1.path, v5.URL).slice(0, 6);
2271
+ item1.key = id + "-" + "debug";
2272
+ });
2273
+ console.log(chalk.red(`error: ${body}`));
2274
+ resolve(true);
2275
+ }
2276
+ });
2277
+ });
2278
+ });
2279
+ }
2280
+ // md5加密
2281
+ function md5(str) {
2282
+ const md5 = crypto.createHash("md5");
2283
+ md5.update(str);
2284
+ return md5.digest("hex");
2285
+ }
2286
+
2287
+ // 节流函数
2288
+ const delay = 2000;
2289
+ const throttle = (function (delay = 1500) {
2290
+ const wait = [];
2291
+ let canCall = true;
2292
+ return function throttle(callback) {
2293
+ if (!canCall) {
2294
+ if (callback) wait.push(callback);
2295
+ return;
2296
+ }
2297
+
2298
+ callback();
2299
+ canCall = false;
2300
+ setTimeout(() => {
2301
+ canCall = true;
2302
+ if (wait.length) {
2303
+ throttle(wait.shift());
2304
+ }
2305
+ }, delay);
2306
+ };
2307
+ })(delay);
2308
+ // 开始方法
2309
+ readFolder();
2310
+ }
2311
+
2312
+ export { rules };