ai-yuca 1.5.3 → 1.5.5

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.
package/dist/bin/cli.js CHANGED
@@ -56,17 +56,32 @@ const pullCrowdin_1 = require("../src/pullCrowdin");
56
56
  const sharp_1 = require("../src/sharp");
57
57
  const svgFix_1 = require("../src/svgFix");
58
58
  const gKeys_1 = require("../src/gKeys");
59
+ const font_1 = require("../src/font");
59
60
  const program = new commander_1.Command();
60
61
  // 设置版本和描述
61
62
  program
62
63
  .version(pkg.version, '-v, --version')
63
- .description(pkg.description);
64
+ .description(pkg.description)
65
+ // 当用户使用 --help 时,不仅列出命令名称,也列出其所有选项
66
+ .configureHelp({
67
+ sortSubcommands: true,
68
+ subcommandTerm: (cmd) => {
69
+ // 提取所有的选项
70
+ const options = cmd.options.map(opt => {
71
+ const flag = opt.flags.split(',')[0]; // 取短选项或长选项作为代表
72
+ return opt.mandatory ? `<${flag}>` : `[${flag}]`;
73
+ });
74
+ const optStr = options.length > 0 ? options.join(' ') : '';
75
+ // 组合:命令名 + 选项概览
76
+ return `${cmd.name()} ${optStr}`.replace(/\s+/g, ' ').trim();
77
+ }
78
+ });
64
79
  // 添加analyze命令
65
80
  program
66
81
  .command('analyze')
67
- .description('分析文本内容')
68
- .option('-f, --file <path>', '指定要分析的文件路径')
69
- .option('-t, --text <text>', '直接分析提供的文本')
82
+ .description('分析文本内容,统计字符数、单词数和行数,并提取关键词')
83
+ .option('-f, --file <path>', '指定要分析的本地文件路径')
84
+ .option('-t, --text <text>', '直接分析命令行提供的文本字符串')
70
85
  .action((options) => {
71
86
  (0, index_1.analyze)(options);
72
87
  });
@@ -74,10 +89,10 @@ program
74
89
  program
75
90
  .command('sharp')
76
91
  .description('压缩图片文件 (jpg, png, webp)')
77
- .requiredOption('-s, --source <path>', '资源文件夹')
78
- .requiredOption('-o, --output <path>', '导出的文件夹地址')
79
- .option('-q, --quality <number>', '压缩质量 (1-100)', '80')
80
- .option('-f, --force', '强制压缩,即使目标文件已存在')
92
+ .requiredOption('-s, --source <path>', '需要压缩的资源文件夹路径')
93
+ .requiredOption('-o, --output <path>', '压缩后的图片导出文件夹地址')
94
+ .option('-q, --quality <number>', '压缩质量,取值范围 1-100,默认为 80', '80')
95
+ .option('-f, --force', '强制压缩,即使目标文件已存在也会被覆盖')
81
96
  .action(async (options) => {
82
97
  await (0, sharp_1.compressImages)(options);
83
98
  });
@@ -85,7 +100,7 @@ program
85
100
  program
86
101
  .command('svg-fix')
87
102
  .description('修复SVG路径并去色 (生成到 fixed 子目录)')
88
- .option('-s, --source <path>', 'SVG文件目录路径 (默认为当前目录)', '.')
103
+ .option('-s, --source <path>', 'SVG文件目录路径,默认为当前执行命令的目录', '.')
89
104
  .action(async (options) => {
90
105
  await (0, svgFix_1.svgFix)(options);
91
106
  });
@@ -93,48 +108,61 @@ program
93
108
  program
94
109
  .command('gkeys')
95
110
  .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)')
111
+ .requiredOption('-s, --source <path>', '需要扫描的源文件夹路径,通常是 pages 或 src/pages')
112
+ .option('-r, --regex <regex>', '自定义提取 Key 的正则表达式,默认为 {#(.+?)#}')
113
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
99
114
  .action(async (options) => {
100
115
  await (0, gKeys_1.gKeys)(options);
101
116
  });
102
117
  // 添加 tinify 命令
103
118
  program
104
119
  .command('tinify')
105
- .description('使用 Tinify API 压缩图片 (jpg, png, webp)')
106
- .option('-s, --source <path>', '资源文件夹 (默认为当前目录)', '.')
107
- .option('-o, --output <path>', '导出的文件夹地址 (默认为 source/zip)', '')
108
- .option('-k, --key <string>', 'Tinify API Key (如果不提供则尝试从配置文件读取)')
109
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
120
+ .description('使用 Tinify API 压缩图片 (jpg, png, webp),支持压缩目录或单个文件')
121
+ .option('-s, --source <path>', '需要压缩的资源文件夹路径,默认为当前执行命令的目录', '.')
122
+ .option('-f, --file <path>', '指定单个文件进行压缩(优先级高于 source')
123
+ .option('-o, --output <path>', '压缩后的图片导出路径(目录或文件)。如果未提供,单个文件将覆盖原图,目录默认输出到 source/zip', '')
124
+ .option('-k, --key <string>', 'Tinify API Key。如果不提供,则尝试从 vs.config.json 配置文件中读取')
125
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
110
126
  .action(async (options) => {
111
127
  await (0, tinify_1.compressImagesWithTinify)(options);
112
128
  });
113
129
  // 添加 zip-anim 命令
114
130
  program
115
131
  .command('zip-anim')
116
- .description('提取JSON文件中的base64位图,压缩后替换原位置')
117
- .requiredOption('-s, --source <path>', 'JSON文件路径 (相对路径)')
118
- .option('-k, --key <string>', 'Tinify API Key (如果不提供则尝试从配置文件读取)')
119
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
132
+ .description('提取 AE 导出的 JSON 动画文件中的 base64 位图,使用 Tinify 压缩后替换回原文件')
133
+ .requiredOption('-s, --source <path>', 'JSON文件路径 (支持相对路径)')
134
+ .option('-k, --key <string>', 'Tinify API Key。如果不提供,则尝试从 vs.config.json 配置文件中读取')
135
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
120
136
  .action(async (options) => {
121
137
  await (0, zipAnim_1.zipAnim)(options);
122
138
  });
139
+ // 添加 font 命令
140
+ program
141
+ .command('font')
142
+ .description('将 SVG 图标目录转换为字体库,支持从 vs.config.json 读取输入输出目录')
143
+ .option('-s, --source <path>', 'SVG 图标输入目录。未传时读取 vs.config.json 中的 font.source')
144
+ .option('-o, --output <path>', '字体库输出目录。未传时读取 vs.config.json 中的 font.output')
145
+ .option('-n, --name <string>', '字体名称。未传时读取 vs.config.json 中的 font.name,默认 iconfont')
146
+ .option('-p, --prefix <string>', '字体 class 前缀。未传时读取 vs.config.json 中的 font.prefix,默认与字体名称一致')
147
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
148
+ .action(async (options) => {
149
+ await (0, font_1.generateFontLibrary)(options);
150
+ });
123
151
  // 添加deploy命令
124
152
  program
125
153
  .command('deploy')
126
- .description('部署文件到指定环境')
127
- .requiredOption('-e, --env <environment>', '部署环境(如:dev、test、production')
128
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
129
- .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径(可选,不提供则使用应用默认凭证)')
130
- .option('-s, --source <path>', '自定义源路径(覆盖配置文件中的uploadPath')
131
- .option('-d, --destination <path>', '自定义目标路径(覆盖配置文件中的目标路径)')
132
- .option('--no-recursive', '禁用递归上传目录中的文件')
133
- .option('--no-compression', '禁用GZIP压缩')
134
- .option('--no-cache', '禁用文件缓存功能')
135
- .option('--cache-file <path>', '指定缓存文件路径(默认为 .cdn.cache.[branch].json')
136
- .option('--show-config', '仅显示配置信息,不执行部署')
137
- .option('-f, --force', '强制执行,跳过交互式确认')
154
+ .description('一键部署文件到指定环境 (如 dev、test、production)')
155
+ .requiredOption('-e, --env <environment>', '部署目标环境名称,例如:dev、test、production')
156
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
157
+ .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径。如果不提供,则尝试使用默认凭证')
158
+ .option('-s, --source <path>', '自定义源路径,该选项会覆盖配置文件中的 uploadPath')
159
+ .option('-d, --destination <path>', '自定义目标路径,该选项会覆盖配置文件中的 destination')
160
+ .option('--no-recursive', '禁用递归上传,仅上传目录第一层的文件')
161
+ .option('--no-compression', '禁用 GZIP 压缩,默认会对文本和样式文件启用')
162
+ .option('--no-cache', '禁用文件缓存检查,强制上传所有文件')
163
+ .option('--cache-file <path>', '指定缓存文件路径,默认为 .cdn.cache.[branch].json')
164
+ .option('--show-config', '仅打印读取到的配置信息,不执行真实的部署操作')
165
+ .option('-f, --force', '强制执行部署,跳过终端交互式确认提示')
138
166
  .action(async (options) => {
139
167
  try {
140
168
  const result = await (0, deploy_1.deployFiles)(options);
@@ -162,13 +190,13 @@ program
162
190
  // 添加upload命令
163
191
  program
164
192
  .command('upload')
165
- .description('上传文件到GCP存储桶')
166
- .option('-s, --source <paths...>', '指定要上传的文件或目录路径(支持多个)')
167
- .option('-b, --bucket <name>', 'GCP存储桶名称')
168
- .option('-d, --destination <path>', '目标路径(存储桶中的路径)')
169
- .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径(可选,不提供则使用应用默认凭证)')
170
- .option('-r, --recursive', '递归上传目录中的文件', false)
171
- .option('-c, --no-compression', '禁用GZIP压缩(默认对js、css、json、html、woff文件启用压缩)')
193
+ .description('上传文件或目录到 GCP (Google Cloud Storage) 存储桶')
194
+ .option('-s, --source <paths...>', '指定要上传的本地文件或目录路径,支持指定多个')
195
+ .option('-b, --bucket <name>', '目标 GCP 存储桶的名称')
196
+ .option('-d, --destination <path>', '上传到存储桶中的目标前缀路径')
197
+ .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径。可选,不提供则使用应用默认凭证')
198
+ .option('-r, --recursive', '如果源是目录,是否递归上传其中的所有文件', false)
199
+ .option('-c, --no-compression', '禁用 GZIP 压缩(默认会对 js、css、json、html、woff 等文件启用压缩)')
172
200
  .action(async (options) => {
173
201
  try {
174
202
  const { source, bucket, destination, keyFile, recursive, compression = true } = options;
@@ -252,12 +280,12 @@ program
252
280
  // 添加download命令
253
281
  program
254
282
  .command('download')
255
- .description('从GCP存储桶下载文件或文件夹')
256
- .option('-s, --source <path>', '指定要下载的GCP存储桶中的文件或目录路径')
257
- .option('-b, --bucket <name>', 'GCP存储桶名称')
258
- .option('-d, --destination <path>', '本地目标路径(下载到本地的路径)')
259
- .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径(可选,不提供则使用应用默认凭证)')
260
- .option('-r, --recursive', '递归下载目录中的文件', false)
283
+ .description('从 GCP 存储桶下载文件或整个文件夹到本地')
284
+ .option('-s, --source <path>', '指定要下载的 GCP 存储桶中的源文件或目录路径')
285
+ .option('-b, --bucket <name>', 'GCP 存储桶名称')
286
+ .option('-d, --destination <path>', '下载后保存的本地目标目录路径')
287
+ .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径。可选,不提供则使用应用默认凭证')
288
+ .option('-r, --recursive', '是否递归下载整个目录中的所有文件', false)
261
289
  .action(async (options) => {
262
290
  try {
263
291
  const { source, bucket, destination, keyFile, recursive } = options;
@@ -303,16 +331,16 @@ program
303
331
  // 添加基于配置文件的上传命令
304
332
  program
305
333
  .command('upload-config')
306
- .description('基于vs.config.json配置文件上传文件到GCP存储桶')
307
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
308
- .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径(可选,不提供则使用应用默认凭证)')
309
- .option('-s, --source <path>', '自定义源路径(覆盖配置文件中的uploadPath')
310
- .option('-d, --destination <path>', '自定义目标路径(覆盖配置文件中的目标路径)')
311
- .option('--no-recursive', '禁用递归上传目录中的文件')
312
- .option('--no-compression', '禁用GZIP压缩')
313
- .option('--no-cache', '禁用文件缓存功能')
314
- .option('--cache-file <path>', '指定缓存文件路径(默认为 .cdn.cache.[branch].json')
315
- .option('--show-config', '仅显示配置信息,不执行上传')
334
+ .description('基于 vs.config.json 配置文件自动上传文件到 GCP 存储桶')
335
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
336
+ .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径。可选,不提供则使用应用默认凭证')
337
+ .option('-s, --source <path>', '自定义源路径,该选项会覆盖配置文件中的 uploadPath')
338
+ .option('-d, --destination <path>', '自定义目标路径,该选项会覆盖配置文件中的 destination')
339
+ .option('--no-recursive', '禁用递归上传,仅上传目录第一层的文件')
340
+ .option('--no-compression', '禁用 GZIP 压缩功能')
341
+ .option('--no-cache', '禁用文件缓存检查功能,强制全量上传')
342
+ .option('--cache-file <path>', '指定缓存文件路径,默认为 .cdn.cache.[branch].json')
343
+ .option('--show-config', '仅打印解析后的配置信息,不执行真实的上传操作')
316
344
  .action(async (options) => {
317
345
  try {
318
346
  const { config: configPath, keyFile, source: customSourcePath, destination: customDestination, recursive = true, compression = true, cache = true, cacheFile, showConfig = false } = options;
@@ -371,10 +399,10 @@ program
371
399
  // 添加 init 命令
372
400
  program
373
401
  .command('init')
374
- .description('在当前目录生成 vs.config.json 配置文件(交互模式)')
375
- .option('-f, --force', '强制覆盖已存在的配置文件')
402
+ .description('在当前项目目录下以交互模式生成并初始化 vs.config.json 配置文件')
403
+ .option('-f, --force', '如果配置文件已存在,则强制覆盖且跳过确认提示')
376
404
  .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;
405
+ 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, _10, _11, _12, _13, _14;
378
406
  try {
379
407
  const configPath = path.join(process.cwd(), 'vs.config.json');
380
408
  let existingConfig = null;
@@ -482,6 +510,34 @@ program
482
510
  default: ((_k = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.apiKeys) === null || _k === void 0 ? void 0 : _k.tinify) || ''
483
511
  }
484
512
  ]);
513
+ // Font 配置
514
+ console.log('\n🔤 Font 配置');
515
+ const fontConfig = await inquirer_1.default.prompt([
516
+ {
517
+ type: 'input',
518
+ name: 'source',
519
+ message: 'SVG 图标输入目录 (font.source):',
520
+ default: ((_l = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.font) === null || _l === void 0 ? void 0 : _l.source) || 'test/svgs'
521
+ },
522
+ {
523
+ type: 'input',
524
+ name: 'output',
525
+ message: '字体库输出目录 (font.output):',
526
+ default: ((_m = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.font) === null || _m === void 0 ? void 0 : _m.output) || 'test/font-dist'
527
+ },
528
+ {
529
+ type: 'input',
530
+ name: 'name',
531
+ message: '字体名称 (font.name,可选):',
532
+ default: ((_o = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.font) === null || _o === void 0 ? void 0 : _o.name) || 'iconfont'
533
+ },
534
+ {
535
+ type: 'input',
536
+ name: 'prefix',
537
+ message: '字体前缀 (font.prefix,可选,默认使用字体名称):',
538
+ default: ((_p = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.font) === null || _p === void 0 ? void 0 : _p.prefix) || ((_q = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.font) === null || _q === void 0 ? void 0 : _q.name) || 'iconfont'
539
+ }
540
+ ]);
485
541
  // Crowdin 配置
486
542
  console.log('\n🌍 Crowdin 配置');
487
543
  const crowdinConfig = await inquirer_1.default.prompt([
@@ -489,63 +545,63 @@ program
489
545
  type: 'input',
490
546
  name: 'project',
491
547
  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'
548
+ default: ((_r = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _r === void 0 ? void 0 : _r.project) || 'fed-project-name'
493
549
  },
494
550
  {
495
551
  type: 'input',
496
552
  name: 'workDir',
497
553
  message: '工作目录 (workDir):',
498
- default: ((_m = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _m === void 0 ? void 0 : _m.workDir) || 'src'
554
+ default: ((_s = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _s === void 0 ? void 0 : _s.workDir) || 'src'
499
555
  },
500
556
  {
501
557
  type: 'input',
502
558
  name: 'keysDir',
503
559
  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'
560
+ default: ((_t = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _t === void 0 ? void 0 : _t.keysDir) || 'src/_i18n'
505
561
  },
506
562
  {
507
563
  type: 'input',
508
564
  name: 'reg',
509
565
  message: '正则表达式模式 (reg):',
510
- default: ((_p = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _p === void 0 ? void 0 : _p.reg) || '{#(.+?)#}'
566
+ default: ((_u = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _u === void 0 ? void 0 : _u.reg) || '{#(.+?)#}'
511
567
  },
512
568
  {
513
569
  type: 'input',
514
570
  name: 'prefix',
515
571
  message: 'Crowdin 前缀 (prefix):',
516
- default: ((_q = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _q === void 0 ? void 0 : _q.prefix) || 'fed'
572
+ default: ((_v = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _v === void 0 ? void 0 : _v.prefix) || 'fed'
517
573
  },
518
574
  {
519
575
  type: 'input',
520
576
  name: 'FromIni',
521
577
  message: '来源初始化 (FromIni):',
522
- default: ((_r = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _r === void 0 ? void 0 : _r.FromIni) || 'mall'
578
+ default: ((_w = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _w === void 0 ? void 0 : _w.FromIni) || 'mall'
523
579
  },
524
580
  {
525
581
  type: 'input',
526
582
  name: 'Region',
527
583
  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'
584
+ default: ((_x = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _x === void 0 ? void 0 : _x.Region) || 'us-east-1'
529
585
  },
530
586
  {
531
587
  type: 'input',
532
588
  name: 'HostName',
533
589
  message: 'Crowdin Host (可选):',
534
- default: ((_t = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _t === void 0 ? void 0 : _t.HostName) || ''
590
+ default: ((_y = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _y === void 0 ? void 0 : _y.HostName) || ''
535
591
  },
536
592
  {
537
593
  type: 'checkbox',
538
594
  name: 'langMap',
539
595
  message: '选择支持的语言 (空格选择,回车确认):',
540
596
  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 }
597
+ { name: 'zh-cn', value: 'zh-cn', checked: ((_0 = (_z = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _z === void 0 ? void 0 : _z.langMap) === null || _0 === void 0 ? void 0 : _0.includes('zh-cn')) || false },
598
+ { name: 'en-us', value: 'en-us', checked: ((_2 = (_1 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _1 === void 0 ? void 0 : _1.langMap) === null || _2 === void 0 ? void 0 : _2.includes('en-us')) || false },
599
+ { name: 'de-de', value: 'de-de', checked: ((_4 = (_3 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _3 === void 0 ? void 0 : _3.langMap) === null || _4 === void 0 ? void 0 : _4.includes('de-de')) || false },
600
+ { name: 'pt-pt', value: 'pt-pt', checked: ((_6 = (_5 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _5 === void 0 ? void 0 : _5.langMap) === null || _6 === void 0 ? void 0 : _6.includes('pt-pt')) || false },
601
+ { name: 'fr-fr', value: 'fr-fr', checked: ((_8 = (_7 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _7 === void 0 ? void 0 : _7.langMap) === null || _8 === void 0 ? void 0 : _8.includes('fr-fr')) || false },
602
+ { name: 'es-es', value: 'es-es', checked: ((_10 = (_9 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _9 === void 0 ? void 0 : _9.langMap) === null || _10 === void 0 ? void 0 : _10.includes('es-es')) || false },
603
+ { name: 'it-it', value: 'it-it', checked: ((_12 = (_11 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _11 === void 0 ? void 0 : _11.langMap) === null || _12 === void 0 ? void 0 : _12.includes('it-it')) || false },
604
+ { name: 'ru-ru', value: 'ru-ru', checked: ((_14 = (_13 = existingConfig === null || existingConfig === void 0 ? void 0 : existingConfig.crowdin) === null || _13 === void 0 ? void 0 : _13.langMap) === null || _14 === void 0 ? void 0 : _14.includes('ru-ru')) || false }
549
605
  ]
550
606
  }
551
607
  ]);
@@ -570,6 +626,12 @@ program
570
626
  apiKeys: {
571
627
  tinify: apiKeysConfig.tinify
572
628
  },
629
+ font: {
630
+ source: fontConfig.source,
631
+ output: fontConfig.output,
632
+ name: fontConfig.name,
633
+ prefix: fontConfig.prefix
634
+ },
573
635
  crowdin: {
574
636
  project: crowdinConfig.project,
575
637
  langMap: crowdinConfig.langMap.length > 0 ? crowdinConfig.langMap : ['zh-cn', 'en-us'],
@@ -590,6 +652,7 @@ program
590
652
  console.log(` 📤 Upload: ${finalConfig.upload.uploadPath} -> ${finalConfig.upload.s3Static}`);
591
653
  console.log(` 🚀 Deploy: ${finalConfig.deploy.baseUrl.join(', ')}`);
592
654
  console.log(` ☁️ AWS: ${finalConfig.aws.Bucket} (${finalConfig.aws.Region})`);
655
+ console.log(` 🔤 Font: ${finalConfig.font.source} -> ${finalConfig.font.output} (${finalConfig.font.name}, prefix: ${finalConfig.font.prefix})`);
593
656
  console.log(` 🌍 Crowdin: ${finalConfig.crowdin.project} [${finalConfig.crowdin.langMap.join(', ')}]`);
594
657
  }
595
658
  catch (err) {
@@ -600,28 +663,28 @@ program
600
663
  // 添加 transKey 命令
601
664
  program
602
665
  .command('transKey')
603
- .description('提取项目中的翻译key并生成源文件')
604
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
666
+ .description('从项目代码中提取包含特定格式的翻译 key,并生成多语言的基础源文件')
667
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
605
668
  .action(async (options) => {
606
669
  await (0, transKey_1.transKey)(options);
607
670
  });
608
671
  // 添加 useTrans 命令
609
672
  program
610
673
  .command('useTrans')
611
- .description(' un-trans.json 中的翻译合并到 source 目录中')
612
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
674
+ .description('将已翻译好的 un-trans.json 文件内容合并或替换到源多语言配置目录中')
675
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
613
676
  .action(async (options) => {
614
677
  await (0, useTrans_1.useTrans)(options);
615
678
  });
616
679
  // 添加pull-crowdin命令
617
680
  program
618
681
  .command('cr-pull')
619
- .description('从GCP下载Crowdin相关的JSON资源文件')
620
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
621
- .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径(可选,不提供则使用应用默认凭证)')
622
- .option('-r, --region <region>', 'GCP区域(默认为ap-southeast-1')
623
- .option('--exclude <pattern>', '排除包含指定模式的文件')
624
- .option('--aws', '使用AWS凭证而非GCP凭证')
682
+ .description('从 GCP 或 AWS 存储桶下载 Crowdin 翻译同步相关的 JSON 资源文件')
683
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
684
+ .option('-k, --key-file <path>', 'GCP服务账号密钥文件路径。可选,不提供则尝试使用默认凭证')
685
+ .option('-r, --region <region>', '云服务提供商的存储区域节点,默认为 ap-southeast-1')
686
+ .option('--exclude <pattern>', '排除包含指定正则或字符串模式的资源文件')
687
+ .option('--aws', '使用 AWS 的凭证和存储服务,而不是默认的 GCP 服务')
625
688
  .action(async (options) => {
626
689
  try {
627
690
  const { config: configPath, keyFile, region, exclude, aws = false } = options;
@@ -642,8 +705,8 @@ program
642
705
  // 添加crowdin-push命令
643
706
  program
644
707
  .command('cr-push')
645
- .description('推送本地keys文件到Crowdin项目')
646
- .option('-c, --config <path>', '指定配置文件路径(默认为项目根目录下的vs.config.json')
708
+ .description('读取本地生成的 keys 翻译源文件,并将它们推送到对应的 Crowdin 项目和存储桶')
709
+ .option('-c, --config <path>', '指定配置文件路径,默认为项目根目录下的 vs.config.json')
647
710
  .action(async (options) => {
648
711
  try {
649
712
  const { config: configPath } = options;
@@ -713,8 +776,8 @@ program
713
776
  program
714
777
  .command('lg')
715
778
  .description('执行 JumpServer 登录脚本')
716
- .requiredOption('-s, --server <ip>', '目标IP/关键字')
717
- .option('-u, --user <username>', 'JumpServer用户名', 'zhengjingpei')
779
+ .requiredOption('-s, --server <ip>', '目标IP或关键字,例如:192.168.1.1 或 web-server')
780
+ .option('-u, --user <username>', 'JumpServer用户名,默认为 zhengjingpei', 'zhengjingpei')
718
781
  .action(async (options) => {
719
782
  try {
720
783
  // 生产环境路径(编译后)
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-yuca",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "一个用AI生成的开发辅助工具",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -42,6 +42,7 @@
42
42
  "mime-types": "^3.0.1",
43
43
  "sharp": "^0.34.5",
44
44
  "svgo": "^4.0.1",
45
+ "svgtofont": "^6.5.1",
45
46
  "tinify": "^1.8.2"
46
47
  },
47
48
  "devDependencies": {
@@ -6,6 +6,12 @@ export interface VSConfig {
6
6
  uploadPath: string;
7
7
  s3Static: string;
8
8
  };
9
+ font?: {
10
+ source: string;
11
+ output: string;
12
+ name?: string;
13
+ prefix?: string;
14
+ };
9
15
  aws: {
10
16
  Bucket: string;
11
17
  prefix: string;
@@ -0,0 +1,2 @@
1
+ import { FontCommandOptions } from './types/font';
2
+ export declare function generateFontLibrary(options: FontCommandOptions): Promise<void>;
@@ -0,0 +1,127 @@
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.generateFontLibrary = generateFontLibrary;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const child_process_1 = require("child_process");
43
+ const util_1 = require("util");
44
+ const chalk_1 = __importDefault(require("chalk"));
45
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
46
+ function loadFontConfig(configPath) {
47
+ if (!fs.existsSync(configPath)) {
48
+ return null;
49
+ }
50
+ try {
51
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
52
+ }
53
+ catch (error) {
54
+ throw new Error(`读取配置文件失败: ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ }
57
+ async function generateFontLibrary(options) {
58
+ var _a, _b, _c, _d;
59
+ const configPath = options.config || path.join(process.cwd(), 'vs.config.json');
60
+ const config = loadFontConfig(configPath);
61
+ const source = options.source || ((_a = config === null || config === void 0 ? void 0 : config.font) === null || _a === void 0 ? void 0 : _a.source);
62
+ const output = options.output || ((_b = config === null || config === void 0 ? void 0 : config.font) === null || _b === void 0 ? void 0 : _b.output);
63
+ const fontName = options.name || ((_c = config === null || config === void 0 ? void 0 : config.font) === null || _c === void 0 ? void 0 : _c.name) || 'iconfont';
64
+ const classNamePrefix = options.prefix || ((_d = config === null || config === void 0 ? void 0 : config.font) === null || _d === void 0 ? void 0 : _d.prefix) || fontName;
65
+ if (!source || !output) {
66
+ console.error(chalk_1.default.red('错误: 请通过命令参数或 vs.config.json 中的 font.source/font.output 提供输入和输出目录。'));
67
+ process.exit(1);
68
+ }
69
+ const sourceAbs = path.resolve(process.cwd(), source);
70
+ const outputAbs = path.resolve(process.cwd(), output);
71
+ if (!fs.existsSync(sourceAbs)) {
72
+ console.error(chalk_1.default.red(`错误: SVG 源目录不存在: ${sourceAbs}`));
73
+ process.exit(1);
74
+ }
75
+ if (!fs.existsSync(outputAbs)) {
76
+ fs.mkdirSync(outputAbs, { recursive: true });
77
+ }
78
+ const svgToFontOptions = {
79
+ src: sourceAbs,
80
+ dist: outputAbs,
81
+ fontName,
82
+ classNamePrefix,
83
+ css: true,
84
+ outSVGReact: true,
85
+ outSVGReactNative: false,
86
+ outSVGVue: true,
87
+ outSVGPath: true,
88
+ svgicons2svgfont: {
89
+ fontHeight: 1000,
90
+ normalize: true,
91
+ },
92
+ };
93
+ const args = [
94
+ '--input-type=module',
95
+ '--eval',
96
+ [
97
+ "import svgtofont from 'svgtofont';",
98
+ 'const options = JSON.parse(process.env.AI_YUCA_FONT_OPTIONS || "{}");',
99
+ 'await svgtofont(options);',
100
+ ].join(' ')
101
+ ];
102
+ console.log(chalk_1.default.blue(`SVG 源目录: ${sourceAbs}`));
103
+ console.log(chalk_1.default.blue(`字体输出目录: ${outputAbs}`));
104
+ console.log(chalk_1.default.blue(`字体名称: ${fontName}`));
105
+ console.log(chalk_1.default.blue(`字体前缀: ${classNamePrefix}`));
106
+ try {
107
+ const { stdout, stderr } = await execFileAsync(process.execPath, args, {
108
+ cwd: process.cwd(),
109
+ env: {
110
+ ...process.env,
111
+ AI_YUCA_FONT_OPTIONS: JSON.stringify(svgToFontOptions),
112
+ },
113
+ });
114
+ if (stdout.trim()) {
115
+ console.log(stdout.trim());
116
+ }
117
+ if (stderr.trim()) {
118
+ console.log(stderr.trim());
119
+ }
120
+ console.log(chalk_1.default.green('字体库生成完成。'));
121
+ }
122
+ catch (error) {
123
+ const message = error instanceof Error ? error.message : String(error);
124
+ console.error(chalk_1.default.red(`生成字体库失败: ${message}`));
125
+ process.exit(1);
126
+ }
127
+ }
@@ -16,3 +16,4 @@ export { createStorageClient, uploadFile, uploadFiles } from './upload';
16
16
  export { downloadFiles } from './download';
17
17
  export { uploadFilesWithConfig, getConfigSummary } from './uploadWithConfig';
18
18
  export { loadConfig, getBucketName, getUploadDestination, getUploadSourcePath } from './config';
19
+ export { generateFontLibrary } from './font';
package/dist/src/index.js CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.getUploadSourcePath = exports.getUploadDestination = exports.getBucketName = exports.loadConfig = exports.getConfigSummary = exports.uploadFilesWithConfig = exports.downloadFiles = exports.uploadFiles = exports.uploadFile = exports.createStorageClient = void 0;
36
+ exports.generateFontLibrary = exports.getUploadSourcePath = exports.getUploadDestination = exports.getBucketName = exports.loadConfig = exports.getConfigSummary = exports.uploadFilesWithConfig = exports.downloadFiles = exports.uploadFiles = exports.uploadFile = exports.createStorageClient = void 0;
37
37
  exports.analyze = analyze;
38
38
  exports.analyzeContent = analyzeContent;
39
39
  /**
@@ -124,3 +124,5 @@ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function (
124
124
  Object.defineProperty(exports, "getBucketName", { enumerable: true, get: function () { return config_1.getBucketName; } });
125
125
  Object.defineProperty(exports, "getUploadDestination", { enumerable: true, get: function () { return config_1.getUploadDestination; } });
126
126
  Object.defineProperty(exports, "getUploadSourcePath", { enumerable: true, get: function () { return config_1.getUploadSourcePath; } });
127
+ var font_1 = require("./font");
128
+ Object.defineProperty(exports, "generateFontLibrary", { enumerable: true, get: function () { return font_1.generateFontLibrary; } });
@@ -44,14 +44,7 @@ const os = __importStar(require("os"));
44
44
  const fast_glob_1 = __importDefault(require("fast-glob"));
45
45
  const chalk_1 = __importDefault(require("chalk"));
46
46
  async function compressImagesWithTinify(options) {
47
- let { source, output, config, key } = options;
48
- // 处理默认参数
49
- if (!source) {
50
- source = '.';
51
- }
52
- if (!output) {
53
- output = path.join(source, 'zip');
54
- }
47
+ let { source, output, config, key, file: singleFile } = options;
55
48
  let apiKey = key;
56
49
  // 如果命令行没有提供key,尝试从配置文件读取
57
50
  if (!apiKey) {
@@ -120,6 +113,51 @@ async function compressImagesWithTinify(options) {
120
113
  }
121
114
  // 设置 API Key
122
115
  tinify_1.default.key = apiKey;
116
+ if (singleFile) {
117
+ const fileAbs = path.resolve(process.cwd(), singleFile);
118
+ if (!fs.existsSync(fileAbs)) {
119
+ console.error(chalk_1.default.red(`文件不存在: ${fileAbs}`));
120
+ return;
121
+ }
122
+ let outputAbs = fileAbs;
123
+ if (output) {
124
+ outputAbs = path.resolve(process.cwd(), output);
125
+ // 如果输出的是目录,则保留原文件名
126
+ if (!path.extname(outputAbs)) {
127
+ if (!fs.existsSync(outputAbs)) {
128
+ fs.mkdirSync(outputAbs, { recursive: true });
129
+ }
130
+ outputAbs = path.join(outputAbs, path.basename(fileAbs));
131
+ }
132
+ else {
133
+ const outputDir = path.dirname(outputAbs);
134
+ if (!fs.existsSync(outputDir)) {
135
+ fs.mkdirSync(outputDir, { recursive: true });
136
+ }
137
+ }
138
+ }
139
+ console.log(chalk_1.default.blue(`开始压缩单个文件: ${fileAbs}`));
140
+ try {
141
+ const srcStats = fs.statSync(fileAbs);
142
+ const sourceFile = tinify_1.default.fromFile(fileAbs);
143
+ await sourceFile.toFile(outputAbs);
144
+ const destStats = fs.statSync(outputAbs);
145
+ const savedBytes = srcStats.size - destStats.size;
146
+ const savedPercent = (savedBytes / srcStats.size * 100).toFixed(2);
147
+ console.log(chalk_1.default.green(`✓ 压缩成功: ${(srcStats.size / 1024).toFixed(2)}KB -> ${(destStats.size / 1024).toFixed(2)}KB (节省 ${savedPercent}%)`));
148
+ }
149
+ catch (err) {
150
+ console.error(chalk_1.default.red(`✗ 压缩失败: ${err.message}`));
151
+ }
152
+ return;
153
+ }
154
+ // 处理默认参数
155
+ if (!source) {
156
+ source = '.';
157
+ }
158
+ if (!output) {
159
+ output = path.join(source, 'zip');
160
+ }
123
161
  const sourceAbs = path.resolve(process.cwd(), source);
124
162
  const outputAbs = path.resolve(process.cwd(), output);
125
163
  if (!fs.existsSync(sourceAbs)) {
@@ -146,10 +184,16 @@ async function compressImagesWithTinify(options) {
146
184
  console.log(chalk_1.default.blue(`找到 ${files.length} 个图片文件,开始压缩...`));
147
185
  let successCount = 0;
148
186
  let failCount = 0;
187
+ let skipCount = 0;
149
188
  for (const file of files) {
150
189
  const sourcePath = path.join(sourceAbs, file);
151
190
  const outputPath = path.join(outputAbs, file);
152
191
  const outputDir = path.dirname(outputPath);
192
+ if (fs.existsSync(outputPath)) {
193
+ console.log(chalk_1.default.gray(`跳过已压缩文件: ${file}`));
194
+ skipCount++;
195
+ continue;
196
+ }
153
197
  if (!fs.existsSync(outputDir)) {
154
198
  fs.mkdirSync(outputDir, { recursive: true });
155
199
  }
@@ -176,5 +220,5 @@ async function compressImagesWithTinify(options) {
176
220
  failCount++;
177
221
  }
178
222
  }
179
- console.log(chalk_1.default.blue(`\n压缩完成! 成功: ${successCount}, 失败: ${failCount}`));
223
+ console.log(chalk_1.default.blue(`\n压缩完成! 成功: ${successCount}, 跳过: ${skipCount}, 失败: ${failCount}`));
180
224
  }
@@ -0,0 +1,7 @@
1
+ export interface FontCommandOptions {
2
+ source?: string;
3
+ output?: string;
4
+ config?: string;
5
+ name?: string;
6
+ prefix?: string;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -13,3 +13,4 @@ export * from './tinify';
13
13
  export * from './zipAnim';
14
14
  export * from './svgFix';
15
15
  export * from './gKeys';
16
+ export * from './font';
@@ -37,3 +37,4 @@ __exportStar(require("./tinify"), exports);
37
37
  __exportStar(require("./zipAnim"), exports);
38
38
  __exportStar(require("./svgFix"), exports);
39
39
  __exportStar(require("./gKeys"), exports);
40
+ __exportStar(require("./font"), exports);
@@ -1,5 +1,6 @@
1
1
  export interface TinifyCommandOptions {
2
- source: string;
2
+ source?: string;
3
+ file?: string;
3
4
  output: string;
4
5
  config?: string;
5
6
  key?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-yuca",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "一个用AI生成的开发辅助工具",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -42,6 +42,7 @@
42
42
  "mime-types": "^3.0.1",
43
43
  "sharp": "^0.34.5",
44
44
  "svgo": "^4.0.1",
45
+ "svgtofont": "^6.5.1",
45
46
  "tinify": "^1.8.2"
46
47
  },
47
48
  "devDependencies": {
package/vs.config.json CHANGED
@@ -25,6 +25,12 @@
25
25
  "apiKeys": {
26
26
  "tinify": "jMvkVbDc3B6BXWwCFXQSsVGSg9FWrvhZ"
27
27
  },
28
+ "font": {
29
+ "source": "test/svgs",
30
+ "output": "test/font-dist",
31
+ "name": "ai-icons",
32
+ "prefix": ""
33
+ },
28
34
  "crowdin": {
29
35
  "project": "fed-buy-buy-buy-home",
30
36
  "langMap": [
@@ -45,4 +51,4 @@
45
51
  "FromIni": "mall",
46
52
  "HostName": "https://cdn.alvinclub.ca"
47
53
  }
48
- }
54
+ }