ai-yuca 1.5.2 → 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 +152 -89
- package/dist/package.json +2 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/font.d.ts +2 -0
- package/dist/src/font.js +127 -0
- package/dist/src/gKeys.js +87 -26
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -1
- package/dist/src/tinify.js +53 -9
- package/dist/src/types/font.d.ts +7 -0
- package/dist/src/types/font.js +2 -0
- package/dist/src/types/index.d.ts +1 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/types/tinify.d.ts +2 -1
- package/package.json +2 -1
- package/vs.config.json +7 -1
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>', '
|
|
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>', '
|
|
98
|
-
.option('-
|
|
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('-
|
|
108
|
-
.option('-
|
|
109
|
-
.option('-
|
|
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
|
|
117
|
-
.requiredOption('-s, --source <path>', 'JSON文件路径 (
|
|
118
|
-
.option('-k, --key <string>', 'Tinify API Key
|
|
119
|
-
.option('-c, --config <path>', '
|
|
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>', '
|
|
128
|
-
.option('-c, --config <path>', '
|
|
129
|
-
.option('-k, --key-file <path>', 'GCP
|
|
130
|
-
.option('-s, --source <path>', '
|
|
131
|
-
.option('-d, --destination <path>', '
|
|
132
|
-
.option('--no-recursive', '
|
|
133
|
-
.option('--no-compression', '禁用GZIP
|
|
134
|
-
.option('--no-cache', '
|
|
135
|
-
.option('--cache-file <path>', '
|
|
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('
|
|
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', '
|
|
171
|
-
.option('-c, --no-compression', '禁用GZIP
|
|
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', '
|
|
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
|
|
307
|
-
.option('-c, --config <path>', '
|
|
308
|
-
.option('-k, --key-file <path>', 'GCP
|
|
309
|
-
.option('-s, --source <path>', '
|
|
310
|
-
.option('-d, --destination <path>', '
|
|
311
|
-
.option('--no-recursive', '
|
|
312
|
-
.option('--no-compression', '禁用GZIP
|
|
313
|
-
.option('--no-cache', '
|
|
314
|
-
.option('--cache-file <path>', '
|
|
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('
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
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: ((
|
|
542
|
-
{ name: 'en-us', value: 'en-us', checked: ((
|
|
543
|
-
{ name: 'de-de', value: 'de-de', checked: ((
|
|
544
|
-
{ name: 'pt-pt', value: 'pt-pt', checked: ((
|
|
545
|
-
{ name: 'fr-fr', value: 'fr-fr', checked: ((
|
|
546
|
-
{ name: 'es-es', value: 'es-es', checked: ((
|
|
547
|
-
{ name: 'it-it', value: 'it-it', checked: ((
|
|
548
|
-
{ name: 'ru-ru', value: 'ru-ru', checked: ((
|
|
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('
|
|
604
|
-
.option('-c, --config <path>', '
|
|
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('
|
|
612
|
-
.option('-c, --config <path>', '
|
|
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
|
|
620
|
-
.option('-c, --config <path>', '
|
|
621
|
-
.option('-k, --key-file <path>', 'GCP
|
|
622
|
-
.option('-r, --region <region>', '
|
|
623
|
-
.option('--exclude <pattern>', '
|
|
624
|
-
.option('--aws', '使用AWS
|
|
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('
|
|
646
|
-
.option('-c, --config <path>', '
|
|
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
|
|
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
|
+
"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/dist/src/config.d.ts
CHANGED
package/dist/src/font.js
ADDED
|
@@ -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
|
+
}
|
package/dist/src/gKeys.js
CHANGED
|
@@ -40,6 +40,52 @@ exports.gKeys = gKeys;
|
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
43
|
+
// 解析 tsconfig.json 中的 paths
|
|
44
|
+
function getTsconfigPaths(cwd) {
|
|
45
|
+
try {
|
|
46
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
47
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
48
|
+
// 简单移除注释和尾随逗号再解析
|
|
49
|
+
const content = fs.readFileSync(tsconfigPath, 'utf8');
|
|
50
|
+
const cleanContent = content
|
|
51
|
+
.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '')
|
|
52
|
+
.replace(/,\s*([\]}])/g, '$1');
|
|
53
|
+
const tsconfig = JSON.parse(cleanContent);
|
|
54
|
+
if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
|
|
55
|
+
return {
|
|
56
|
+
paths: tsconfig.compilerOptions.paths,
|
|
57
|
+
baseUrl: tsconfig.compilerOptions.baseUrl || '.'
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
// 忽略解析错误
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// 匹配路径别名
|
|
68
|
+
function resolveAliasPath(importPath, config, cwd) {
|
|
69
|
+
if (config.paths) {
|
|
70
|
+
for (const [aliasPattern, targetPaths] of Object.entries(config.paths)) {
|
|
71
|
+
if (!targetPaths || targetPaths.length === 0)
|
|
72
|
+
continue;
|
|
73
|
+
// 把 alias 结尾的 /* 去掉
|
|
74
|
+
const aliasBase = aliasPattern.replace(/\/\*$/, '');
|
|
75
|
+
// 如果 importPath 等于 aliasBase 或者以 aliasBase/ 开头
|
|
76
|
+
if (importPath === aliasBase || importPath.startsWith(aliasBase + '/')) {
|
|
77
|
+
const targetBase = targetPaths[0].replace(/\/\*$/, '');
|
|
78
|
+
const relativePath = importPath.substring(aliasBase.length);
|
|
79
|
+
return path.resolve(cwd, config.baseUrl, targetBase, relativePath.replace(/^\//, ''));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// 尝试直接基于 baseUrl 解析(非相对路径)
|
|
84
|
+
if (!importPath.startsWith('.') && !importPath.startsWith('/')) {
|
|
85
|
+
return path.resolve(cwd, config.baseUrl, importPath);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
43
89
|
async function gKeys(options) {
|
|
44
90
|
const { source, regex, config: configPath } = options;
|
|
45
91
|
if (!source) {
|
|
@@ -75,6 +121,7 @@ async function gKeys(options) {
|
|
|
75
121
|
}
|
|
76
122
|
// 默认正则匹配 {#...#}
|
|
77
123
|
const regexPattern = regex ? new RegExp(regex, 'g') : /\{#(.+?)#\}/g;
|
|
124
|
+
const tsconfigPaths = getTsconfigPaths(process.cwd());
|
|
78
125
|
console.log(`🚀 开始扫描 Pages: ${sourceAbs}`);
|
|
79
126
|
console.log(`🔍 正则表达式: ${regexPattern}`);
|
|
80
127
|
// 1. 找到所有 .tsx 入口文件
|
|
@@ -110,7 +157,7 @@ async function gKeys(options) {
|
|
|
110
157
|
const keys = new Set();
|
|
111
158
|
const processedFiles = new Set();
|
|
112
159
|
// 递归处理文件,传入 keysDirAbs 用于忽略
|
|
113
|
-
await processFile(entryFile, regexPattern, keys, processedFiles, sourceAbs, keysDirAbs);
|
|
160
|
+
await processFile(entryFile, regexPattern, keys, processedFiles, sourceAbs, keysDirAbs, tsconfigPaths);
|
|
114
161
|
if (keys.size > 0) {
|
|
115
162
|
const sortedKeys = Array.from(keys).sort();
|
|
116
163
|
// 收集结果
|
|
@@ -150,8 +197,8 @@ function generateLangFile(filePath, data) {
|
|
|
150
197
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
151
198
|
console.log(`\n🎉 已生成全局语言文件: ${filePath}`);
|
|
152
199
|
}
|
|
153
|
-
async function processFile(filePath, regex, keys, processedFiles, rootContext, ignoreDir // 需要忽略的目录路径 (config.crowdin.keysDir)
|
|
154
|
-
) {
|
|
200
|
+
async function processFile(filePath, regex, keys, processedFiles, rootContext, ignoreDir, // 需要忽略的目录路径 (config.crowdin.keysDir)
|
|
201
|
+
tsconfigPaths) {
|
|
155
202
|
// 忽略 keysDir 目录下的文件,防止循环引用
|
|
156
203
|
if (ignoreDir && path.resolve(filePath).startsWith(ignoreDir)) {
|
|
157
204
|
return;
|
|
@@ -173,30 +220,44 @@ async function processFile(filePath, regex, keys, processedFiles, rootContext, i
|
|
|
173
220
|
}
|
|
174
221
|
}
|
|
175
222
|
// 2. 分析 import 引用,递归处理
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
223
|
+
const importPaths = new Set();
|
|
224
|
+
const regexes = [
|
|
225
|
+
/import\s+[^'"]*?from\s+['"]([^'"]+)['"]/g,
|
|
226
|
+
/export\s+[^'"]*?from\s+['"]([^'"]+)['"]/g,
|
|
227
|
+
/import\s+['"]([^'"]+)['"]/g,
|
|
228
|
+
/import\s*\(\s*[^'"]*?['"]([^'"]+)['"][^)]*?\)/g,
|
|
229
|
+
/require\s*\(\s*[^'"]*?['"]([^'"]+)['"][^)]*?\)/g
|
|
230
|
+
];
|
|
231
|
+
for (const rx of regexes) {
|
|
232
|
+
let match;
|
|
233
|
+
rx.lastIndex = 0;
|
|
234
|
+
while ((match = rx.exec(content)) !== null) {
|
|
235
|
+
if (match[1]) {
|
|
236
|
+
importPaths.add(match[1]);
|
|
237
|
+
}
|
|
191
238
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
239
|
+
}
|
|
240
|
+
for (const importPath of importPaths) {
|
|
241
|
+
let targetPath = '';
|
|
242
|
+
// 处理路径别名或相对路径
|
|
243
|
+
if (tsconfigPaths) {
|
|
244
|
+
const resolvedAlias = resolveAliasPath(importPath, tsconfigPaths, process.cwd());
|
|
245
|
+
if (resolvedAlias) {
|
|
246
|
+
targetPath = resolvedAlias;
|
|
247
|
+
}
|
|
196
248
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
249
|
+
if (!targetPath) {
|
|
250
|
+
if (importPath.startsWith('@/')) {
|
|
251
|
+
targetPath = path.join(process.cwd(), 'src', importPath.substring(2));
|
|
252
|
+
}
|
|
253
|
+
else if (importPath.startsWith('.')) {
|
|
254
|
+
const currentDir = path.dirname(filePath);
|
|
255
|
+
targetPath = path.resolve(currentDir, importPath);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// 忽略非相对路径 (node_modules)
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
200
261
|
}
|
|
201
262
|
// 忽略样式/图片文件
|
|
202
263
|
if (importPath.match(/\.(css|less|scss|sass|png|jpg|jpeg|gif|svg)$/))
|
|
@@ -229,7 +290,7 @@ async function processFile(filePath, regex, keys, processedFiles, rootContext, i
|
|
|
229
290
|
}
|
|
230
291
|
}
|
|
231
292
|
if (resolvedPath) {
|
|
232
|
-
await processFile(resolvedPath, regex, keys, processedFiles, rootContext, ignoreDir);
|
|
293
|
+
await processFile(resolvedPath, regex, keys, processedFiles, rootContext, ignoreDir, tsconfigPaths);
|
|
233
294
|
}
|
|
234
295
|
}
|
|
235
296
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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; } });
|
package/dist/src/tinify.js
CHANGED
|
@@ -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
|
}
|
package/dist/src/types/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-yuca",
|
|
3
|
-
"version": "1.5.
|
|
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
|
+
}
|