ai-yuca 1.4.9 → 1.5.2

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,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run dev -- init --help 2>&1)",
5
+ "Bash(npm run dev -- sharp --help 2>&1)"
6
+ ]
7
+ }
8
+ }
package/dist/bin/cli.js CHANGED
@@ -33,12 +33,16 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  return result;
34
34
  };
35
35
  })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
36
39
  Object.defineProperty(exports, "__esModule", { value: true });
37
40
  const commander_1 = require("commander");
38
41
  const pkg = __importStar(require("../package.json"));
39
42
  const fs = __importStar(require("fs"));
40
43
  const path = __importStar(require("path"));
41
44
  const child_process_1 = require("child_process");
45
+ const inquirer_1 = __importDefault(require("inquirer"));
42
46
  const index_1 = require("../src/index");
43
47
  const upload_1 = require("../src/upload");
44
48
  const download_1 = require("../src/download");
@@ -50,6 +54,8 @@ const tinify_1 = require("../src/tinify");
50
54
  const zipAnim_1 = require("../src/zipAnim");
51
55
  const pullCrowdin_1 = require("../src/pullCrowdin");
52
56
  const sharp_1 = require("../src/sharp");
57
+ const svgFix_1 = require("../src/svgFix");
58
+ const gKeys_1 = require("../src/gKeys");
53
59
  const program = new commander_1.Command();
54
60
  // 设置版本和描述
55
61
  program
@@ -75,12 +81,30 @@ program
75
81
  .action(async (options) => {
76
82
  await (0, sharp_1.compressImages)(options);
77
83
  });
84
+ // 添加 svg-fix 命令
85
+ program
86
+ .command('svg-fix')
87
+ .description('修复SVG路径并去色 (生成到 fixed 子目录)')
88
+ .option('-s, --source <path>', 'SVG文件目录路径 (默认为当前目录)', '.')
89
+ .action(async (options) => {
90
+ await (0, svgFix_1.svgFix)(options);
91
+ });
92
+ // 添加 gkeys 命令
93
+ program
94
+ .command('gkeys')
95
+ .description('提取 pages 目录下的翻译 key 并生成 gkeys.json')
96
+ .requiredOption('-s, --source <path>', 'pages 文件夹路径')
97
+ .option('-r, --regex <regex>', '提取 Key 的正则表达式 (默认为 {#(.+?)#})')
98
+ .option('-o, --output <path>', '输出文件路径 (默认为 source/gkeys.json)')
99
+ .action(async (options) => {
100
+ await (0, gKeys_1.gKeys)(options);
101
+ });
78
102
  // 添加 tinify 命令
79
103
  program
80
104
  .command('tinify')
81
105
  .description('使用 Tinify API 压缩图片 (jpg, png, webp)')
82
- .requiredOption('-s, --source <path>', '资源文件夹')
83
- .requiredOption('-o, --output <path>', '导出的文件夹地址')
106
+ .option('-s, --source <path>', '资源文件夹 (默认为当前目录)', '.')
107
+ .option('-o, --output <path>', '导出的文件夹地址 (默认为 source/zip)', '')
84
108
  .option('-k, --key <string>', 'Tinify API Key (如果不提供则尝试从配置文件读取)')
85
109
  .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json)')
86
110
  .action(async (options) => {
@@ -344,62 +368,232 @@ program
344
368
  process.exit(1);
345
369
  }
346
370
  });
347
- // 添加init命令
371
+ // 添加 init 命令
348
372
  program
349
373
  .command('init')
350
- .description('在当前目录生成vs.config.json配置文件')
374
+ .description('在当前目录生成 vs.config.json 配置文件(交互模式)')
351
375
  .option('-f, --force', '强制覆盖已存在的配置文件')
352
376
  .action(async (options) => {
377
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9;
353
378
  try {
354
379
  const configPath = path.join(process.cwd(), 'vs.config.json');
380
+ let existingConfig = null;
355
381
  // 检查文件是否已存在
356
- if (fs.existsSync(configPath) && !options.force) {
357
- console.log('vs.config.json 文件已存在。使用 --force 选项强制覆盖。');
358
- return;
382
+ if (fs.existsSync(configPath)) {
383
+ if (!options.force) {
384
+ console.log('⚠️ vs.config.json 文件已存在');
385
+ const { action } = await inquirer_1.default.prompt([
386
+ {
387
+ type: 'list',
388
+ name: 'action',
389
+ message: '请选择操作:',
390
+ choices: [
391
+ { name: '基于现有配置修改', value: 'modify' },
392
+ { name: '完全重新配置', value: 'recreate' },
393
+ { name: '取消', value: 'cancel' }
394
+ ]
395
+ }
396
+ ]);
397
+ if (action === 'cancel') {
398
+ return;
399
+ }
400
+ if (action === 'modify') {
401
+ // 读取现有配置
402
+ const content = fs.readFileSync(configPath, 'utf8');
403
+ existingConfig = JSON.parse(content);
404
+ console.log('✓ 已加载现有配置,将基于此进行修改\n');
405
+ }
406
+ }
359
407
  }
360
- // 默认配置内容
361
- const defaultConfig = {
362
- "upload": {
363
- "uploadPath": "out",
364
- "s3Static": "static/projectName"
408
+ console.log('📝 开始配置 vs.config.json\n');
409
+ // Upload 配置
410
+ console.log('📤 Upload 配置');
411
+ const uploadConfig = await inquirer_1.default.prompt([
412
+ {
413
+ type: 'input',
414
+ name: 'uploadPath',
415
+ message: '上传源路径 (uploadPath):',
416
+ default: ((_a = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.upload) === null || _a === void 0 ? void 0 : _a.uploadPath) || 'out'
417
+ },
418
+ {
419
+ type: 'input',
420
+ name: 's3Static',
421
+ message: 'S3 静态资源路径 (s3Static):',
422
+ default: ((_b = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.upload) === null || _b === void 0 ? void 0 : _b.s3Static) || 'static/projectName'
423
+ }
424
+ ]);
425
+ // Deploy 配置
426
+ console.log('\n🚀 Deploy 配置');
427
+ const deployConfig = await inquirer_1.default.prompt([
428
+ {
429
+ type: 'input',
430
+ name: 'host',
431
+ message: '生产环境 Host (host):',
432
+ default: ((_c = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.deploy) === null || _c === void 0 ? void 0 : _c.host) || ''
433
+ },
434
+ {
435
+ type: 'input',
436
+ name: 'testHost',
437
+ message: '测试环境 Host (testHost):',
438
+ default: ((_d = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.deploy) === null || _d === void 0 ? void 0 : _d.testHost) || ''
439
+ },
440
+ {
441
+ type: 'input',
442
+ name: 'baseUrl',
443
+ message: '输入需要部署的 baseUrl (多个以逗号隔开):',
444
+ default: ((_e = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.deploy) === null || _e === void 0 ? void 0 : _e.baseUrl) ? existingConfig.deploy.baseUrl.join(',') : '/'
445
+ }
446
+ ]);
447
+ // AWS 配置
448
+ console.log('\n☁️ AWS 配置');
449
+ const awsConfig = await inquirer_1.default.prompt([
450
+ {
451
+ type: 'input',
452
+ name: 'Bucket',
453
+ message: 'S3 Bucket 名称:',
454
+ default: ((_f = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.aws) === null || _f === void 0 ? void 0 : _f.Bucket) || 'cdn'
455
+ },
456
+ {
457
+ type: 'input',
458
+ name: 'prefix',
459
+ message: 'S3 路径前缀 (prefix):',
460
+ default: ((_g = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.aws) === null || _g === void 0 ? void 0 : _g.prefix) || 'fed'
461
+ },
462
+ {
463
+ type: 'input',
464
+ name: 'Region',
465
+ message: 'AWS 区域 (Region):',
466
+ default: ((_h = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.aws) === null || _h === void 0 ? void 0 : _h.Region) || 'us-east-1'
467
+ },
468
+ {
469
+ type: 'input',
470
+ name: 'HostName',
471
+ message: 'Host 名称 (可选):',
472
+ default: ((_j = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.aws) === null || _j === void 0 ? void 0 : _j.HostName) || ''
473
+ }
474
+ ]);
475
+ // API Keys 配置
476
+ console.log('\n🔑 API Keys 配置');
477
+ const apiKeysConfig = await inquirer_1.default.prompt([
478
+ {
479
+ type: 'input',
480
+ name: 'tinify',
481
+ message: 'Tinify API Key (可选):',
482
+ default: ((_k = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.apiKeys) === null || _k === void 0 ? void 0 : _k.tinify) || ''
483
+ }
484
+ ]);
485
+ // Crowdin 配置
486
+ console.log('\n🌍 Crowdin 配置');
487
+ const crowdinConfig = await inquirer_1.default.prompt([
488
+ {
489
+ type: 'input',
490
+ name: 'project',
491
+ message: 'Crowdin 项目名称:',
492
+ default: ((_l = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _l === void 0 ? void 0 : _l.project) || 'fed-project-name'
493
+ },
494
+ {
495
+ type: 'input',
496
+ name: 'workDir',
497
+ message: '工作目录 (workDir):',
498
+ default: ((_m = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _m === void 0 ? void 0 : _m.workDir) || 'src'
499
+ },
500
+ {
501
+ type: 'input',
502
+ name: 'keysDir',
503
+ message: 'Keys 目录 (keysDir):',
504
+ default: ((_o = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _o === void 0 ? void 0 : _o.keysDir) || 'src/_i18n'
505
+ },
506
+ {
507
+ type: 'input',
508
+ name: 'reg',
509
+ message: '正则表达式模式 (reg):',
510
+ default: ((_p = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _p === void 0 ? void 0 : _p.reg) || '{#(.+?)#}'
511
+ },
512
+ {
513
+ type: 'input',
514
+ name: 'prefix',
515
+ message: 'Crowdin 前缀 (prefix):',
516
+ default: ((_q = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _q === void 0 ? void 0 : _q.prefix) || 'fed'
517
+ },
518
+ {
519
+ type: 'input',
520
+ name: 'FromIni',
521
+ message: '来源初始化 (FromIni):',
522
+ default: ((_r = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _r === void 0 ? void 0 : _r.FromIni) || 'mall'
523
+ },
524
+ {
525
+ type: 'input',
526
+ name: 'Region',
527
+ message: 'Crowdin 区域 (Region):',
528
+ default: ((_s = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _s === void 0 ? void 0 : _s.Region) || 'us-east-1'
529
+ },
530
+ {
531
+ type: 'input',
532
+ name: 'HostName',
533
+ message: 'Crowdin Host (可选):',
534
+ default: ((_t = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _t === void 0 ? void 0 : _t.HostName) || ''
535
+ },
536
+ {
537
+ type: 'checkbox',
538
+ name: 'langMap',
539
+ message: '选择支持的语言 (空格选择,回车确认):',
540
+ choices: [
541
+ { name: 'zh-cn', value: 'zh-cn', checked: ((_v = (_u = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _u === void 0 ? void 0 : _u.langMap) === null || _v === void 0 ? void 0 : _v.includes('zh-cn')) || false },
542
+ { name: 'en-us', value: 'en-us', checked: ((_x = (_w = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _w === void 0 ? void 0 : _w.langMap) === null || _x === void 0 ? void 0 : _x.includes('en-us')) || false },
543
+ { name: 'de-de', value: 'de-de', checked: ((_z = (_y = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _y === void 0 ? void 0 : _y.langMap) === null || _z === void 0 ? void 0 : _z.includes('de-de')) || false },
544
+ { name: 'pt-pt', value: 'pt-pt', checked: ((_1 = (_0 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _0 === void 0 ? void 0 : _0.langMap) === null || _1 === void 0 ? void 0 : _1.includes('pt-pt')) || false },
545
+ { name: 'fr-fr', value: 'fr-fr', checked: ((_3 = (_2 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _2 === void 0 ? void 0 : _2.langMap) === null || _3 === void 0 ? void 0 : _3.includes('fr-fr')) || false },
546
+ { name: 'es-es', value: 'es-es', checked: ((_5 = (_4 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _4 === void 0 ? void 0 : _4.langMap) === null || _5 === void 0 ? void 0 : _5.includes('es-es')) || false },
547
+ { name: 'it-it', value: 'it-it', checked: ((_7 = (_6 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _6 === void 0 ? void 0 : _6.langMap) === null || _7 === void 0 ? void 0 : _7.includes('it-it')) || false },
548
+ { name: 'ru-ru', value: 'ru-ru', checked: ((_9 = (_8 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _8 === void 0 ? void 0 : _8.langMap) === null || _9 === void 0 ? void 0 : _9.includes('ru-ru')) || false }
549
+ ]
550
+ }
551
+ ]);
552
+ // 构建最终配置
553
+ const finalConfig = {
554
+ upload: {
555
+ uploadPath: uploadConfig.uploadPath,
556
+ s3Static: uploadConfig.s3Static
365
557
  },
366
- "deploy": {
367
- "baseUrlExample": "baseUrl: ['/', 'en-us/', 'zh-hk/', 'test/']",
368
- "baseUrl": [
369
- "/",
370
- "en-us/",
371
- ],
372
- "host": "",
373
- "testHost": ""
558
+ deploy: {
559
+ baseUrlExample: "baseUrl: ['/', 'en-us/', 'zh-hk/', 'test/']",
560
+ baseUrl: deployConfig.baseUrl ? deployConfig.baseUrl.split(',').map((s) => s.trim()).filter((s) => s.length > 0) : ['/'],
561
+ host: deployConfig.host,
562
+ testHost: deployConfig.testHost
374
563
  },
375
- "aws": {
376
- "Bucket": "cdn",
377
- "prefix": "fed",
378
- "Region": "us-east-1",
379
- "HostName": ""
564
+ aws: {
565
+ Bucket: awsConfig.Bucket,
566
+ prefix: awsConfig.prefix,
567
+ Region: awsConfig.Region,
568
+ HostName: awsConfig.HostName
380
569
  },
381
- "apiKeys": {
382
- "tinify": ""
570
+ apiKeys: {
571
+ tinify: apiKeysConfig.tinify
383
572
  },
384
- "crowdin": {
385
- "project": "fed-project-name",
386
- "langMap": ["zh-cn", "en-us", "de-de", "pt-pt", "fr-fr", "es-es", "it-it", "ru-ru"],
387
- "workDir": "src",
388
- "reg": "{#(.+?)#}",
389
- "keysDir": "src/_i18n",
390
- "prefix": "fed",
391
- "Region": "us-east-1",
392
- "FromIni": "mall",
393
- "HostName": ""
573
+ crowdin: {
574
+ project: crowdinConfig.project,
575
+ langMap: crowdinConfig.langMap.length > 0 ? crowdinConfig.langMap : ['zh-cn', 'en-us'],
576
+ workDir: crowdinConfig.workDir,
577
+ reg: crowdinConfig.reg,
578
+ keysDir: crowdinConfig.keysDir,
579
+ prefix: crowdinConfig.prefix,
580
+ Region: crowdinConfig.Region,
581
+ FromIni: crowdinConfig.FromIni,
582
+ HostName: crowdinConfig.HostName
394
583
  }
395
584
  };
396
585
  // 写入配置文件
397
- fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), 'utf8');
398
- console.log(' vs.config.json 配置文件已生成');
399
- console.log('请根据您的项目需求修改配置文件中的相关参数。');
586
+ fs.writeFileSync(configPath, JSON.stringify(finalConfig, null, 2), 'utf8');
587
+ console.log('\n✅ vs.config.json 配置文件已生成');
588
+ console.log(`📁 路径:${configPath}`);
589
+ console.log('\n配置摘要:');
590
+ console.log(` 📤 Upload: ${finalConfig.upload.uploadPath} -> ${finalConfig.upload.s3Static}`);
591
+ console.log(` 🚀 Deploy: ${finalConfig.deploy.baseUrl.join(', ')}`);
592
+ console.log(` ☁️ AWS: ${finalConfig.aws.Bucket} (${finalConfig.aws.Region})`);
593
+ console.log(` 🌍 Crowdin: ${finalConfig.crowdin.project} [${finalConfig.crowdin.langMap.join(', ')}]`);
400
594
  }
401
595
  catch (err) {
402
- console.error(`生成配置文件失败: ${err instanceof Error ? err.message : String(err)}`);
596
+ console.error(`生成配置文件失败:${err instanceof Error ? err.message : String(err)}`);
403
597
  process.exit(1);
404
598
  }
405
599
  });
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-yuca",
3
- "version": "1.4.9",
3
+ "version": "1.5.2",
4
4
  "description": "一个用AI生成的开发辅助工具",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -13,9 +13,10 @@
13
13
  "scripts": {
14
14
  "build": "tsc",
15
15
  "test": "mocha -r ts-node/register 'test/**/*.ts'",
16
- "start": "ts-node src/index.ts",
16
+ "start": "node ./dist/bin/cli.js",
17
17
  "lint": "eslint . --ext .ts",
18
18
  "dev": "ts-node bin/cli.ts",
19
+ "dev:watch": "tsc --watch",
19
20
  "prepublishOnly": "npm run build"
20
21
  },
21
22
  "keywords": [
@@ -40,6 +41,7 @@
40
41
  "md5-file": "^5.0.0",
41
42
  "mime-types": "^3.0.1",
42
43
  "sharp": "^0.34.5",
44
+ "svgo": "^4.0.1",
43
45
  "tinify": "^1.8.2"
44
46
  },
45
47
  "devDependencies": {
@@ -0,0 +1,2 @@
1
+ import { GKeysCommandOptions } from './types/gKeys';
2
+ export declare function gKeys(options: GKeysCommandOptions): Promise<void>;
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.gKeys = gKeys;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const fast_glob_1 = __importDefault(require("fast-glob"));
43
+ async function gKeys(options) {
44
+ const { source, regex, config: configPath } = options;
45
+ if (!source) {
46
+ console.error('❌ 请提供源文件夹路径 (-s)');
47
+ return;
48
+ }
49
+ const sourceAbs = path.resolve(process.cwd(), source);
50
+ if (!fs.existsSync(sourceAbs)) {
51
+ console.error(`❌ 源路径不存在: ${sourceAbs}`);
52
+ return;
53
+ }
54
+ // 读取配置文件
55
+ const configFile = configPath || path.join(process.cwd(), 'vs.config.json');
56
+ if (!fs.existsSync(configFile)) {
57
+ console.error(`❌ 配置文件不存在: ${configFile}`);
58
+ return;
59
+ }
60
+ let langFilePath = '';
61
+ let keysDirAbs = '';
62
+ try {
63
+ const configData = JSON.parse(fs.readFileSync(configFile, 'utf8'));
64
+ if (configData.crowdin && configData.crowdin.keysDir) {
65
+ keysDirAbs = path.resolve(process.cwd(), configData.crowdin.keysDir);
66
+ langFilePath = path.join(keysDirAbs, 'lang.ts');
67
+ }
68
+ else {
69
+ console.warn('⚠️ 配置文件中未找到 crowdin.keysDir,将跳过生成 lang.ts');
70
+ }
71
+ }
72
+ catch (e) {
73
+ console.error(`❌ 读取配置文件失败: ${e}`);
74
+ return;
75
+ }
76
+ // 默认正则匹配 {#...#}
77
+ const regexPattern = regex ? new RegExp(regex, 'g') : /\{#(.+?)#\}/g;
78
+ console.log(`🚀 开始扫描 Pages: ${sourceAbs}`);
79
+ console.log(`🔍 正则表达式: ${regexPattern}`);
80
+ // 1. 找到所有 .tsx 入口文件
81
+ const entryFiles = await (0, fast_glob_1.default)('**/*.tsx', {
82
+ cwd: sourceAbs,
83
+ absolute: true
84
+ });
85
+ if (entryFiles.length === 0) {
86
+ console.log('⚠️ 未找到 .tsx 入口文件');
87
+ return;
88
+ }
89
+ console.log(`📄 找到 ${entryFiles.length} 个页面入口文件`);
90
+ // 用于收集所有的 pageKey 定义,以便生成 lang.ts
91
+ const pageKeyDefinitions = {};
92
+ let processedCount = 0;
93
+ let skippedCount = 0;
94
+ for (const entryFile of entryFiles) {
95
+ try {
96
+ // 生成基于文件路径的驼峰命名 Key
97
+ const relativePath = path.relative(sourceAbs, entryFile);
98
+ const pathWithoutExt = relativePath.replace(/\.[^/.]+$/, "");
99
+ const camelCaseKey = pathWithoutExt.split(path.sep).map(part => {
100
+ const parts = part.split(/[-_]/);
101
+ const camelParts = parts.map(p => {
102
+ if (!p)
103
+ return '';
104
+ if (p.startsWith('['))
105
+ return p;
106
+ return p.charAt(0).toUpperCase() + p.slice(1);
107
+ });
108
+ return camelParts.join('').replace(/[\[\]]/g, '');
109
+ }).join('');
110
+ const keys = new Set();
111
+ const processedFiles = new Set();
112
+ // 递归处理文件,传入 keysDirAbs 用于忽略
113
+ await processFile(entryFile, regexPattern, keys, processedFiles, sourceAbs, keysDirAbs);
114
+ if (keys.size > 0) {
115
+ const sortedKeys = Array.from(keys).sort();
116
+ // 收集结果
117
+ pageKeyDefinitions[camelCaseKey] = sortedKeys;
118
+ console.log(` ✅ 提取到 ${keys.size} 个 Key`);
119
+ }
120
+ else {
121
+ console.log(` ⚠️ 未提取到 Key`);
122
+ pageKeyDefinitions[camelCaseKey] = [];
123
+ }
124
+ processedCount++;
125
+ }
126
+ catch (err) {
127
+ console.error(`❌ 处理文件出错: ${entryFile}`, err);
128
+ }
129
+ }
130
+ console.log(`\n----------------------------------------`);
131
+ console.log(`处理完成: ${processedCount} 个, 跳过: ${skippedCount} 个 (未找到 PAGE_KEY)`);
132
+ // 生成 lang.ts
133
+ if (langFilePath && Object.keys(pageKeyDefinitions).length > 0) {
134
+ generateLangFile(langFilePath, pageKeyDefinitions);
135
+ }
136
+ else if (Object.keys(pageKeyDefinitions).length === 0) {
137
+ console.log(`⚠️ 没有提取到任何 Key,跳过生成 lang.ts`);
138
+ }
139
+ }
140
+ function generateLangFile(filePath, data) {
141
+ let content = '// Auto generated by ai-yuca gkeys\n\n';
142
+ for (const [key, keys] of Object.entries(data)) {
143
+ content += `export const ${key}Keys = ${JSON.stringify(keys)};\n`;
144
+ }
145
+ // 确保目录存在
146
+ const dir = path.dirname(filePath);
147
+ if (!fs.existsSync(dir)) {
148
+ fs.mkdirSync(dir, { recursive: true });
149
+ }
150
+ fs.writeFileSync(filePath, content, 'utf8');
151
+ console.log(`\n🎉 已生成全局语言文件: ${filePath}`);
152
+ }
153
+ async function processFile(filePath, regex, keys, processedFiles, rootContext, ignoreDir // 需要忽略的目录路径 (config.crowdin.keysDir)
154
+ ) {
155
+ // 忽略 keysDir 目录下的文件,防止循环引用
156
+ if (ignoreDir && path.resolve(filePath).startsWith(ignoreDir)) {
157
+ return;
158
+ }
159
+ if (processedFiles.has(filePath))
160
+ return;
161
+ processedFiles.add(filePath);
162
+ if (!fs.existsSync(filePath))
163
+ return;
164
+ let content = fs.readFileSync(filePath, 'utf8');
165
+ // 不再需要剔除 PAGE_KEY 定义,因为我们不再往回写,而且正则提取的是 {#...#},
166
+ // 只要 lang.ts 里的定义不包含 {#...#} 且我们忽略了 lang.ts 文件,就不会有问题。
167
+ // 1. 提取当前文件中的 Key
168
+ let match;
169
+ regex.lastIndex = 0;
170
+ while ((match = regex.exec(content)) !== null) {
171
+ if (match[0]) {
172
+ keys.add(match[0]);
173
+ }
174
+ }
175
+ // 2. 分析 import 引用,递归处理
176
+ const importRegex = /import\s+(?:(?:[\w{}\s,*]+)\s+from\s+)?['"]([^'"]+)['"]/g;
177
+ let importMatch;
178
+ while ((importMatch = importRegex.exec(content)) !== null) {
179
+ let importPath = importMatch[1];
180
+ let targetPath = '';
181
+ // 处理 @ 别名
182
+ if (importPath.startsWith('@/')) {
183
+ // 假设 @ 指向 src 目录
184
+ // 我们需要找到项目根目录下的 src
185
+ // rootContext 是 sourceAbs,通常是 pages 目录
186
+ // 假设项目结构是 root/src/pages
187
+ // 所以 rootContext 往上两级可能是 root?或者 rootContext 本身就在 src 下?
188
+ // 更稳妥的方式是查找 tsconfig.json 或 jsconfig.json 中的 paths 配置
189
+ // 但为了简单,我们假设 @ -> process.cwd() + /src
190
+ targetPath = path.join(process.cwd(), 'src', importPath.substring(2));
191
+ }
192
+ else if (importPath.startsWith('.')) {
193
+ // 解析相对路径
194
+ const currentDir = path.dirname(filePath);
195
+ targetPath = path.resolve(currentDir, importPath);
196
+ }
197
+ else {
198
+ // 忽略非相对路径 (node_modules)
199
+ continue;
200
+ }
201
+ // 忽略样式/图片文件
202
+ if (importPath.match(/\.(css|less|scss|sass|png|jpg|jpeg|gif|svg)$/))
203
+ continue;
204
+ // 尝试添加扩展名
205
+ const extensions = ['.tsx', '.ts', '.jsx', '.js'];
206
+ let resolvedPath = '';
207
+ // 1. 尝试直接加扩展名
208
+ for (const ext of extensions) {
209
+ if (fs.existsSync(targetPath + ext)) {
210
+ resolvedPath = targetPath + ext;
211
+ break;
212
+ }
213
+ }
214
+ // 2. 尝试作为目录找 index
215
+ if (!resolvedPath) {
216
+ for (const ext of extensions) {
217
+ const indexPath = path.join(targetPath, `index${ext}`);
218
+ if (fs.existsSync(indexPath)) {
219
+ resolvedPath = indexPath;
220
+ break;
221
+ }
222
+ }
223
+ }
224
+ // 3. 如果原本就有扩展名
225
+ if (!resolvedPath && fs.existsSync(targetPath)) {
226
+ const stat = fs.statSync(targetPath);
227
+ if (stat.isFile()) {
228
+ resolvedPath = targetPath;
229
+ }
230
+ }
231
+ if (resolvedPath) {
232
+ await processFile(resolvedPath, regex, keys, processedFiles, rootContext, ignoreDir);
233
+ }
234
+ }
235
+ }
@@ -44,10 +44,14 @@ expect "Opt>"
44
44
  # 发送目标IP/关键字
45
45
  send "'"${TARGET}"'\r"
46
46
  # 处理后续交互场景
47
+ # 替换 expect 中的中文匹配行
47
48
  expect {
48
- "复用SSH连接" { send "\r"; interact } # 复用连接时自动回车
49
- "Last login:" { interact } # 直接登录成功
50
- "Opt>" { interact } # 回到菜单(手动选择)
49
+ "SSH connection reuse" { # 英文关键字
50
+ send "\r"
51
+ interact
52
+ }
53
+ "Last login:" { interact }
54
+ "Opt>" { interact }
51
55
  }
52
56
  # 兜底保持交互
53
57
  interact
@@ -0,0 +1,2 @@
1
+ import { SvgFixCommandOptions } from './types';
2
+ export declare function svgFix(options: SvgFixCommandOptions): Promise<void>;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.svgFix = svgFix;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const fast_glob_1 = __importDefault(require("fast-glob"));
43
+ const svgo_1 = require("svgo");
44
+ async function svgFix(options) {
45
+ const sourcePath = path.resolve(process.cwd(), options.source || '.');
46
+ const destinationPath = path.join(sourcePath, 'fixed');
47
+ if (!fs.existsSync(sourcePath)) {
48
+ console.error(`❌ 源路径不存在: ${sourcePath}`);
49
+ return;
50
+ }
51
+ console.log(`🚀 开始处理 SVG 文件...`);
52
+ console.log(`📂 源目录: ${sourcePath}`);
53
+ console.log(`📂 输出目录: ${destinationPath}`);
54
+ try {
55
+ // 1. 查找所有SVG文件
56
+ const svgFiles = await (0, fast_glob_1.default)('**/*.svg', {
57
+ cwd: sourcePath,
58
+ absolute: true,
59
+ ignore: ['**/fixed/**'] // 避免重复处理输出目录
60
+ });
61
+ if (svgFiles.length === 0) {
62
+ console.log('⚠️ 未找到 SVG 文件');
63
+ return;
64
+ }
65
+ console.log(`🔄 找到 ${svgFiles.length} 个 SVG 文件,开始处理...`);
66
+ let processedCount = 0;
67
+ let failedCount = 0;
68
+ for (const file of svgFiles) {
69
+ try {
70
+ const content = fs.readFileSync(file, 'utf8');
71
+ // 计算输出路径,保持原有目录结构
72
+ const relativePath = path.relative(sourcePath, file);
73
+ const outputPath = path.join(destinationPath, relativePath);
74
+ const outputDir = path.dirname(outputPath);
75
+ // 确保子目录存在
76
+ if (!fs.existsSync(outputDir)) {
77
+ fs.mkdirSync(outputDir, { recursive: true });
78
+ }
79
+ // 使用 SVGO 进行优化和去色
80
+ const result = (0, svgo_1.optimize)(content, {
81
+ path: file,
82
+ multipass: true, // 多次遍历以获得更好效果
83
+ plugins: [
84
+ 'preset-default', // 包含 mergePaths, convertStyleToAttrs 等默认优化
85
+ 'removeDimensions', // 移除 width/height,方便 CSS 控制
86
+ {
87
+ name: 'removeAttrs',
88
+ params: {
89
+ attrs: '(fill|stroke|style)' // 移除颜色相关属性
90
+ }
91
+ },
92
+ {
93
+ name: 'addAttributesToSVGElement',
94
+ params: {
95
+ attributes: [
96
+ { fill: 'currentColor' } // 设置默认填充色为当前字体颜色
97
+ ]
98
+ }
99
+ }
100
+ ]
101
+ });
102
+ fs.writeFileSync(outputPath, result.data, 'utf8');
103
+ processedCount++;
104
+ }
105
+ catch (err) {
106
+ console.error(`✗ 处理出错: ${path.relative(sourcePath, file)}`, err);
107
+ failedCount++;
108
+ }
109
+ }
110
+ console.log(`\n✅ 处理完成: ${processedCount} 个文件`);
111
+ if (failedCount > 0) {
112
+ console.log(`❌ 失败: ${failedCount} 个文件`);
113
+ }
114
+ console.log(`🎉 输出目录: ${destinationPath}`);
115
+ }
116
+ catch (err) {
117
+ console.error(`❌ 执行失败: ${err instanceof Error ? err.message : String(err)}`);
118
+ process.exit(1);
119
+ }
120
+ }
@@ -40,13 +40,17 @@ exports.compressImagesWithTinify = compressImagesWithTinify;
40
40
  const tinify_1 = __importDefault(require("tinify"));
41
41
  const fs = __importStar(require("fs"));
42
42
  const path = __importStar(require("path"));
43
+ const os = __importStar(require("os"));
43
44
  const fast_glob_1 = __importDefault(require("fast-glob"));
44
45
  const chalk_1 = __importDefault(require("chalk"));
45
46
  async function compressImagesWithTinify(options) {
46
- const { source, output, config, key } = options;
47
- if (!source || !output) {
48
- console.error(chalk_1.default.red('错误: 必须提供源文件夹路径 (-s) 和导出文件夹路径 (-o)'));
49
- process.exit(1);
47
+ let { source, output, config, key } = options;
48
+ // 处理默认参数
49
+ if (!source) {
50
+ source = '.';
51
+ }
52
+ if (!output) {
53
+ output = path.join(source, 'zip');
50
54
  }
51
55
  let apiKey = key;
52
56
  // 如果命令行没有提供key,尝试从配置文件读取
@@ -73,6 +77,42 @@ async function compressImagesWithTinify(options) {
73
77
  console.error(chalk_1.default.red(`错误: 配置文件不存在: ${configPath}`));
74
78
  process.exit(1);
75
79
  }
80
+ // 如果还没找到,尝试从用户主目录下的 .yuca/.env 获取
81
+ if (!apiKey) {
82
+ try {
83
+ const homeDir = os.homedir();
84
+ const envPath = path.join(homeDir, '.yuca', '.env');
85
+ if (fs.existsSync(envPath)) {
86
+ // console.log(chalk.gray(`尝试读取 ${envPath}...`));
87
+ const envContent = fs.readFileSync(envPath, 'utf8');
88
+ const lines = envContent.split('\n');
89
+ for (const line of lines) {
90
+ const trimmedLine = line.trim();
91
+ // 跳过注释和空行
92
+ if (!trimmedLine || trimmedLine.startsWith('#'))
93
+ continue;
94
+ // 解析 KEY=VALUE
95
+ const parts = trimmedLine.split('=');
96
+ if (parts.length >= 2) {
97
+ const key = parts[0].trim();
98
+ // 获取值部分(可能包含=号,所以重新组合)
99
+ const value = parts.slice(1).join('=').trim();
100
+ // 移除可能的引号
101
+ const cleanValue = value.replace(/^['"]|['"]$/g, '');
102
+ // 检查可能的键名: tinify, TINIFY_KEY, TINIFY_API_KEY, apiKeys.tinify
103
+ if (['tinify', 'TINIFY_KEY', 'TINIFY_API_KEY', 'apiKeys.tinify'].includes(key)) {
104
+ apiKey = cleanValue;
105
+ // console.log(chalk.green(`成功从 ${envPath} 获取到 API Key`));
106
+ break;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ catch (e) {
113
+ console.warn(chalk_1.default.yellow(`警告: 读取用户环境配置失败: ${e}`));
114
+ }
115
+ }
76
116
  }
77
117
  if (!apiKey) {
78
118
  console.error(chalk_1.default.red('错误: 未提供 API Key。请通过 -k 参数或配置文件 (apiKeys.tinify) 提供。'));
@@ -93,8 +133,12 @@ async function compressImagesWithTinify(options) {
93
133
  console.log(chalk_1.default.blue(`源目录: ${sourceAbs}`));
94
134
  console.log(chalk_1.default.blue(`导出目录: ${outputAbs}`));
95
135
  // 查找图片文件
136
+ // 排除输出目录,防止循环压缩
96
137
  const patterns = ['**/*.{jpg,jpeg,png,webp}'];
97
- const files = await (0, fast_glob_1.default)(patterns, { cwd: sourceAbs });
138
+ const files = await (0, fast_glob_1.default)(patterns, {
139
+ cwd: sourceAbs,
140
+ ignore: [path.relative(sourceAbs, outputAbs) + '/**']
141
+ });
98
142
  if (files.length === 0) {
99
143
  console.log(chalk_1.default.yellow('未找到任何图片文件。'));
100
144
  return;
@@ -171,55 +171,23 @@ async function transKey(options) {
171
171
  console.log(`✅ 生成 ${fileName}: 总 Key 数 ${sortedKeys.length}, 已翻译 ${matchedCount}`);
172
172
  }
173
173
  // 5. 生成未翻译词条文件 (un-trans.json)
174
- // 根据需求 "un-trans.json 存放在 source 中"
175
174
  const untransPath = path.join(outputDir, 'un-trans.json');
176
- let existingUntransData = {};
177
- // 读取 un-trans.json 的存量数据
178
- if (fs.existsSync(untransPath)) {
179
- try {
180
- existingUntransData = JSON.parse(fs.readFileSync(untransPath, 'utf8'));
181
- }
182
- catch (e) {
183
- console.warn(`无法解析 un-trans.json: ${e}`);
184
- }
185
- }
186
- // 合并新旧未翻译数据(保留旧数据)
187
- // 逻辑:以新的 untranslatedKeys 为主,如果旧数据里有该语言该key且有值(虽然理论上未翻译文件里的值应该是空的,但也许用户手动填了?),
188
- // 还是保留空值,因为既然在 source 里没值,说明还是未翻译。
189
- // 但用户说 "每次执行这个操作前这个文件的存量数据",意思是把新生成的未翻译数据和旧的合并?
190
- // 通常 un-trans.json 只是一个报告文件。
191
- // 如果用户想保留 un-trans.json 里的数据,可能是想看累计的未翻译?
192
- // 但如果某个 key 在本次运行中已经翻译了(在 oldData 中有值),它就不应该出现在 un-trans.json 中了。
193
- // 所以,un-trans.json 应该只包含当前真正未翻译的 key。
194
- // "存量数据" 可能指的是:如果 un-trans.json 里有一些 key 是本次扫描没扫到的(比如代码删了),是否要保留?
195
- // 或者,用户只是想把新发现的未翻译 key 合并进去?
196
- // 根据需求 "每次执行这个操作前这个文件的存量数据" -> 可能是指读取它,然后做某种合并。
197
- // 但最合理的逻辑是:un-trans.json 应该反映当前的未翻译状态。
198
- // 如果 key 在代码中存在且未翻译 -> 写入 un-trans.json
199
- // 如果 key 在代码中不存在 -> 不写入
200
- // 如果 key 已翻译 -> 不写入
201
- // 但既然提到了 "存量数据",我将采用以下策略:
202
- // 1. 读取旧 un-trans.json
203
- // 2. 将本次检测到的未翻译 key 合并进去 (如果旧文件里有额外的内容,保留之?不,这会导致废弃 key 堆积)
204
- // 让我们假设 "存量数据" 是指保留那些在本次扫描中依然未翻译的条目,或者仅仅是做一次全量覆盖?
205
- // 如果是全量覆盖,那就不需要读取存量数据了。
206
- // 既然明确要求 "每次执行这个操作前这个文件的存量数据",那我就把旧数据读出来,
207
- // 然后把本次的未翻译数据合并进去(覆盖旧的)。
208
- // 修正:按照 "un-trans.json" 的通常用途,它应该是“当前未翻译词条的快照”。
209
- // 但为了满足 "保留存量数据" 的字面意思,我会把旧数据 merge 进去。
210
- // 比如:旧数据有 { "en": { "A": "" } },本次扫描发现 { "en": { "B": "" } }
211
- // 结果是 { "en": { "A": "", "B": "" } }
212
- // 风险:如果 "A" 已经在代码中删除了,它还会留在 un-trans.json 中。
213
- const finalUntransData = { ...existingUntransData };
175
+ let finalUntransData = {};
176
+ // 只需要判断文件是否存在,如果不存在则创建(实际上这里我们每次都会重新生成内容)
177
+ // 如果存在,我们不需要读取它的旧值来做逻辑判断,而是直接覆盖写入最新的未翻译条目
178
+ // 或者,如果用户的意图是保留旧的 un-trans.json 中的某些手动编辑过的值(虽然通常它是空的)?
179
+ // 根据最新指示 "transKey 不需要读取旧值",我们直接忽略读取步骤。
180
+ // 初始化结构
214
181
  for (const lang of langMap) {
215
- if (!finalUntransData[lang]) {
216
- finalUntransData[lang] = {};
217
- }
218
- // 合并当前未翻译的
182
+ finalUntransData[lang] = {};
183
+ // 写入当前扫描到的未翻译 key
219
184
  for (const key in untranslatedKeys[lang]) {
220
185
  finalUntransData[lang][key] = "";
221
186
  }
222
187
  }
188
+ // 如果文件不存在,或者即使存在,我们也覆盖它(因为不需要读取旧值)
189
+ // 但为了稳妥起见,如果文件不存在,我们肯定要创建。
190
+ // fs.writeFileSync 会自动创建文件(如果目录存在,目录在上面已经创建了)
223
191
  fs.writeFileSync(untransPath, JSON.stringify(finalUntransData, null, 2), 'utf8');
224
192
  console.log(`\n📋 未翻译词条已导出至: ${untransPath}`);
225
193
  console.log('\n所有文件生成完毕!');
@@ -0,0 +1,6 @@
1
+ export interface GKeysCommandOptions {
2
+ source: string;
3
+ regex?: string;
4
+ output?: string;
5
+ config?: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -11,3 +11,5 @@ export * from './pullCrowdin';
11
11
  export * from './sharp';
12
12
  export * from './tinify';
13
13
  export * from './zipAnim';
14
+ export * from './svgFix';
15
+ export * from './gKeys';
@@ -35,3 +35,5 @@ __exportStar(require("./sharp"), exports);
35
35
  // 导出所有tinify相关类型
36
36
  __exportStar(require("./tinify"), exports);
37
37
  __exportStar(require("./zipAnim"), exports);
38
+ __exportStar(require("./svgFix"), exports);
39
+ __exportStar(require("./gKeys"), exports);
@@ -0,0 +1,3 @@
1
+ export interface SvgFixCommandOptions {
2
+ source: string;
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-yuca",
3
- "version": "1.4.9",
3
+ "version": "1.5.2",
4
4
  "description": "一个用AI生成的开发辅助工具",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -13,9 +13,10 @@
13
13
  "scripts": {
14
14
  "build": "tsc",
15
15
  "test": "mocha -r ts-node/register 'test/**/*.ts'",
16
- "start": "ts-node src/index.ts",
16
+ "start": "node ./dist/bin/cli.js",
17
17
  "lint": "eslint . --ext .ts",
18
18
  "dev": "ts-node bin/cli.ts",
19
+ "dev:watch": "tsc --watch",
19
20
  "prepublishOnly": "npm run build"
20
21
  },
21
22
  "keywords": [
@@ -40,6 +41,7 @@
40
41
  "md5-file": "^5.0.0",
41
42
  "mime-types": "^3.0.1",
42
43
  "sharp": "^0.34.5",
44
+ "svgo": "^4.0.1",
43
45
  "tinify": "^1.8.2"
44
46
  },
45
47
  "devDependencies": {
package/vs.config.json CHANGED
@@ -7,7 +7,11 @@
7
7
  "baseUrlExample": "baseUrl: ['/', 'en-us/', 'zh-hk/', 'test/']",
8
8
  "baseUrl": [
9
9
  "/",
10
- "en-us/"
10
+ "en-us/",
11
+ "zh-hk/",
12
+ "zh-cn/",
13
+ "de-de/",
14
+ "test/"
11
15
  ],
12
16
  "host": "https://www.china2u.xyz",
13
17
  "testHost": "https://fp.decom.valleysound.xyz"
@@ -16,11 +20,10 @@
16
20
  "Bucket": "decom-cdn-test",
17
21
  "prefix": "fed",
18
22
  "Region": "us-east-1",
19
- "FileKey": "/Users/yucang/Desktop/sodium-burner-472205-g2-65cae6a8923d.json",
20
23
  "HostName": "https://cdn.cn2u.xyz"
21
24
  },
22
25
  "apiKeys": {
23
- "tinify": "jMvkVbDc3B6BXWwCFXQSsVGSg9FWrvhZ"
26
+ "tinify": "jMvkVbDc3B6BXWwCFXQSsVGSg9FWrvhZ"
24
27
  },
25
28
  "crowdin": {
26
29
  "project": "fed-buy-buy-buy-home",
@@ -37,7 +40,6 @@
37
40
  "workDir": ".",
38
41
  "reg": "{#(.+?)#}",
39
42
  "keysDir": "i18n",
40
- "Bucket": "decom-cdn-test",
41
43
  "prefix": "fed",
42
44
  "Region": "us-east-1",
43
45
  "FromIni": "mall",