@eevee-ta/cli 0.1.0-beta.1
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/README.md +492 -0
- package/assets/skills/eevee-mcp/SKILL.md +127 -0
- package/assets/skills/eevee-mcp/reference.md +105 -0
- package/assets/skills/eevee-migration/SKILL.md +181 -0
- package/assets/skills/eevee-project-setup/SKILL.md +266 -0
- package/dist/src/bin/cli.js +237 -0
- package/dist/src/bin/cli.js.map +1 -0
- package/dist/src/commands/business-command.js +340 -0
- package/dist/src/commands/business-command.js.map +1 -0
- package/dist/src/commands/config-command.js +71 -0
- package/dist/src/commands/config-command.js.map +1 -0
- package/dist/src/commands/intl-command.js +131 -0
- package/dist/src/commands/intl-command.js.map +1 -0
- package/dist/src/commands/lint-command.js +105 -0
- package/dist/src/commands/lint-command.js.map +1 -0
- package/dist/src/commands/mcp-command.js +167 -0
- package/dist/src/commands/mcp-command.js.map +1 -0
- package/dist/src/commands/skill-command.js +116 -0
- package/dist/src/commands/skill-command.js.map +1 -0
- package/dist/src/lib/args.js +77 -0
- package/dist/src/lib/args.js.map +1 -0
- package/dist/src/lib/business-catalog.js +445 -0
- package/dist/src/lib/business-catalog.js.map +1 -0
- package/dist/src/lib/config.js +69 -0
- package/dist/src/lib/config.js.map +1 -0
- package/dist/src/lib/intl-catalog.js +58 -0
- package/dist/src/lib/intl-catalog.js.map +1 -0
- package/dist/src/lib/intl-config.js +125 -0
- package/dist/src/lib/intl-config.js.map +1 -0
- package/dist/src/lib/intl-downloader.js +217 -0
- package/dist/src/lib/intl-downloader.js.map +1 -0
- package/dist/src/lib/intl-serializers/csv.js +25 -0
- package/dist/src/lib/intl-serializers/csv.js.map +1 -0
- package/dist/src/lib/intl-serializers/custom.js +23 -0
- package/dist/src/lib/intl-serializers/custom.js.map +1 -0
- package/dist/src/lib/intl-serializers/index.js +22 -0
- package/dist/src/lib/intl-serializers/index.js.map +1 -0
- package/dist/src/lib/intl-serializers/js.js +10 -0
- package/dist/src/lib/intl-serializers/js.js.map +1 -0
- package/dist/src/lib/intl-serializers/json.js +7 -0
- package/dist/src/lib/intl-serializers/json.js.map +1 -0
- package/dist/src/lib/intl-serializers/properties.js +43 -0
- package/dist/src/lib/intl-serializers/properties.js.map +1 -0
- package/dist/src/lib/intl-serializers/ts.js +16 -0
- package/dist/src/lib/intl-serializers/ts.js.map +1 -0
- package/dist/src/lib/intl-serializers/xml.js +21 -0
- package/dist/src/lib/intl-serializers/xml.js.map +1 -0
- package/dist/src/lib/json.js +40 -0
- package/dist/src/lib/json.js.map +1 -0
- package/dist/src/lib/lint-config.js +180 -0
- package/dist/src/lib/lint-config.js.map +1 -0
- package/dist/src/lib/lint-java.js +226 -0
- package/dist/src/lib/lint-java.js.map +1 -0
- package/dist/src/lib/lint-types.js +2 -0
- package/dist/src/lib/lint-types.js.map +1 -0
- package/dist/src/lib/lint-web.js +183 -0
- package/dist/src/lib/lint-web.js.map +1 -0
- package/dist/src/lib/lint.js +192 -0
- package/dist/src/lib/lint.js.map +1 -0
- package/dist/src/lib/mcp-client.js +185 -0
- package/dist/src/lib/mcp-client.js.map +1 -0
- package/dist/src/lib/output.js +34 -0
- package/dist/src/lib/output.js.map +1 -0
- package/dist/src/lib/skill-installer.js +123 -0
- package/dist/src/lib/skill-installer.js.map +1 -0
- package/dist/src/lib/tool-docs.js +66 -0
- package/dist/src/lib/tool-docs.js.map +1 -0
- package/dist/src/lib/types.js +2 -0
- package/dist/src/lib/types.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rename, unlink } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export function configPath() {
|
|
5
|
+
return process.env.EEVEE_CLI_CONFIG || path.join(homedir(), '.eevee-cli', 'config.json');
|
|
6
|
+
}
|
|
7
|
+
export async function loadConfigFile() {
|
|
8
|
+
try {
|
|
9
|
+
const raw = JSON.parse(await readFile(configPath(), 'utf8'));
|
|
10
|
+
return raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === 'ENOENT')
|
|
14
|
+
return {};
|
|
15
|
+
throw new Error(`无法读取配置文件 ${configPath()}: ${error.message}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function saveConfigFile(input) {
|
|
19
|
+
if ('url' in input)
|
|
20
|
+
validateUrl(input.url);
|
|
21
|
+
const current = await loadConfigFile();
|
|
22
|
+
const next = { ...current, ...input };
|
|
23
|
+
const dir = path.dirname(configPath());
|
|
24
|
+
await mkdir(dir, { recursive: true });
|
|
25
|
+
const temporary = `${configPath()}.tmp-${process.pid}`;
|
|
26
|
+
try {
|
|
27
|
+
await writeFile(temporary, JSON.stringify(next, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
28
|
+
await rename(temporary, configPath());
|
|
29
|
+
await chmod(configPath(), 0o600);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
await unlink(temporary).catch(() => undefined);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
import { writeFile } from 'node:fs/promises';
|
|
36
|
+
export async function resolveRuntimeConfig(overrides) {
|
|
37
|
+
const file = await loadConfigFile();
|
|
38
|
+
const url = overrides.url ?? process.env.EEVEE_MCP_URL ?? file.url ?? 'http://10.1.0.168:15000/mcp';
|
|
39
|
+
const token = overrides.token ?? process.env.EEVEE_TOKEN ?? file.token;
|
|
40
|
+
validateUrl(url);
|
|
41
|
+
return { url, token };
|
|
42
|
+
}
|
|
43
|
+
export function validateUrl(value) {
|
|
44
|
+
if (!value)
|
|
45
|
+
throw new Error('服务器地址不能为空');
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = new URL(value);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error(`服务器地址无效: ${value}`);
|
|
52
|
+
}
|
|
53
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
54
|
+
throw new Error('服务器地址必须以 http:// 或 https:// 开头');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function requireToken(token) {
|
|
58
|
+
if (!token)
|
|
59
|
+
throw new Error('缺少用户 token。请执行 `eevee config set --token <token>`,或临时使用 --token / EEVEE_TOKEN');
|
|
60
|
+
return token;
|
|
61
|
+
}
|
|
62
|
+
export function maskToken(token) {
|
|
63
|
+
if (!token)
|
|
64
|
+
return '(未设置)';
|
|
65
|
+
if (token.length <= 10)
|
|
66
|
+
return '********';
|
|
67
|
+
return `${token.slice(0, 6)}...${token.slice(-4)}`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CliError } from './args.js';
|
|
4
|
+
export async function readIntlCatalog(_root, configFile) {
|
|
5
|
+
let raw;
|
|
6
|
+
try {
|
|
7
|
+
raw = JSON.parse(await readFile(configFile, 'utf8'));
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
throw new CliError(`Intl 数据配置解析失败 (${configFile}): ${error.message}`);
|
|
11
|
+
}
|
|
12
|
+
const source = raw;
|
|
13
|
+
if (!source || !Array.isArray(source.outPaths)) {
|
|
14
|
+
throw new CliError(`Intl 数据配置格式不正确,缺少 outPaths 数组: ${configFile}`);
|
|
15
|
+
}
|
|
16
|
+
const catalog = { allTerms: new Map(), tagTerms: new Map() };
|
|
17
|
+
for (const output of source.outPaths) {
|
|
18
|
+
if (!output?.path || typeof output.path !== 'string')
|
|
19
|
+
continue;
|
|
20
|
+
// outPaths 相对于 intl 配置文件所在目录解析;这样 .eevee/intl.json 可以直接指向 .eevee/locales。
|
|
21
|
+
const directory = path.resolve(path.dirname(configFile), output.path);
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const match = /^zh-CN(?:-([^.]+))?\.json$/i.exec(entry.name);
|
|
31
|
+
if (!match || !entry.isFile())
|
|
32
|
+
continue;
|
|
33
|
+
const tag = match[1] ?? 'default';
|
|
34
|
+
const file = path.join(directory, entry.name);
|
|
35
|
+
let terms;
|
|
36
|
+
try {
|
|
37
|
+
terms = JSON.parse(await readFile(file, 'utf8'));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new CliError(`Intl 词条文件解析失败 (${file}): ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
if (!terms || typeof terms !== 'object' || Array.isArray(terms)) {
|
|
43
|
+
throw new CliError(`Intl 词条文件必须是对象: ${file}`);
|
|
44
|
+
}
|
|
45
|
+
const tagMap = catalog.tagTerms.get(tag) ?? new Map();
|
|
46
|
+
for (const key of Object.keys(terms)) {
|
|
47
|
+
tagMap.set(key, true);
|
|
48
|
+
catalog.allTerms.set(key, true);
|
|
49
|
+
}
|
|
50
|
+
catalog.tagTerms.set(tag, tagMap);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (catalog.allTerms.size === 0) {
|
|
54
|
+
throw new CliError(`未读取到任何 zh-CN*.json 词条; 请检查 Intl 数据配置: ${configFile}`);
|
|
55
|
+
}
|
|
56
|
+
return catalog;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=intl-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intl-catalog.js","sourceRoot":"","sources":["../../../src/lib/intl-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CliError } from './args.js';
|
|
4
|
+
const SUPPORTED_FORMATS = new Set(['json', 'ts', 'js', 'xml', 'csv', 'properties', 'custom']);
|
|
5
|
+
/** 从 cwd 向上查找 package.json,返回绝对路径;未找到返回 undefined */
|
|
6
|
+
export async function findPackageJson(startDir) {
|
|
7
|
+
let current = path.resolve(startDir);
|
|
8
|
+
while (true) {
|
|
9
|
+
const candidate = path.join(current, 'package.json');
|
|
10
|
+
try {
|
|
11
|
+
await readFile(candidate, 'utf8');
|
|
12
|
+
return candidate;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
const parent = path.dirname(current);
|
|
16
|
+
if (parent === current)
|
|
17
|
+
return undefined;
|
|
18
|
+
current = parent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** 定位并加载 intl 配置文件;不传路径时从 searchDir 向上查找 package.json */
|
|
23
|
+
export async function loadIntlConfig(explicitPath, searchDir = process.cwd()) {
|
|
24
|
+
let configPath;
|
|
25
|
+
let baseDir;
|
|
26
|
+
if (explicitPath) {
|
|
27
|
+
configPath = path.resolve(explicitPath);
|
|
28
|
+
baseDir = path.dirname(configPath);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const pkgPath = await findPackageJson(searchDir);
|
|
32
|
+
if (!pkgPath)
|
|
33
|
+
throw new CliError('未找到 package.json,请确认当前目录在一个 Node.js 项目中');
|
|
34
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
|
|
35
|
+
const eeveeField = pkg.eevee;
|
|
36
|
+
const intlPath = eeveeField?.intl;
|
|
37
|
+
if (typeof intlPath !== 'string' || !intlPath.trim()) {
|
|
38
|
+
throw new CliError('package.json 中未配置 eevee.intl 字段');
|
|
39
|
+
}
|
|
40
|
+
baseDir = path.dirname(pkgPath);
|
|
41
|
+
configPath = path.resolve(baseDir, intlPath);
|
|
42
|
+
}
|
|
43
|
+
let raw;
|
|
44
|
+
try {
|
|
45
|
+
raw = JSON.parse(await readFile(configPath, 'utf8'));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error.code === 'ENOENT') {
|
|
49
|
+
throw new CliError(`配置文件不存在: ${configPath}`);
|
|
50
|
+
}
|
|
51
|
+
throw new CliError(`配置文件解析失败: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
if (!Array.isArray(raw)) {
|
|
54
|
+
throw new CliError('配置文件根结构必须是数组');
|
|
55
|
+
}
|
|
56
|
+
if (raw.length === 0) {
|
|
57
|
+
throw new CliError('配置文件数组为空,至少需要一个输出目标');
|
|
58
|
+
}
|
|
59
|
+
const targets = raw.map((item, index) => validateTarget(item, index + 1));
|
|
60
|
+
return { baseDir, targets };
|
|
61
|
+
}
|
|
62
|
+
function validateTarget(item, index) {
|
|
63
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
64
|
+
throw new CliError(`配置第 ${index} 项必须是对象`);
|
|
65
|
+
}
|
|
66
|
+
const obj = item;
|
|
67
|
+
if (typeof obj.outPath !== 'string' || !obj.outPath.trim()) {
|
|
68
|
+
throw new CliError(`配置第 ${index} 项缺少必填字段 outPath`);
|
|
69
|
+
}
|
|
70
|
+
if (!obj.languages || typeof obj.languages !== 'object' || Array.isArray(obj.languages)) {
|
|
71
|
+
throw new CliError(`配置第 ${index} 项缺少必填字段 languages`);
|
|
72
|
+
}
|
|
73
|
+
const languages = obj.languages;
|
|
74
|
+
if (Object.keys(languages).length === 0) {
|
|
75
|
+
throw new CliError(`配置第 ${index} 项 languages 不能为空`);
|
|
76
|
+
}
|
|
77
|
+
for (const [k, v] of Object.entries(languages)) {
|
|
78
|
+
if (typeof v !== 'string' || !v.trim()) {
|
|
79
|
+
throw new CliError(`配置第 ${index} 项 languages["${k}"] 必须是非空字符串`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(obj.projects) || obj.projects.length === 0) {
|
|
83
|
+
throw new CliError(`配置第 ${index} 项缺少必填字段 projects(非空数组)`);
|
|
84
|
+
}
|
|
85
|
+
const format = typeof obj.format === 'string' ? obj.format : 'json';
|
|
86
|
+
if (!SUPPORTED_FORMATS.has(format)) {
|
|
87
|
+
throw new CliError(`配置第 ${index} 项 format "${format}" 不支持;可用格式: ${[...SUPPORTED_FORMATS].join(', ')}`);
|
|
88
|
+
}
|
|
89
|
+
const projects = obj.projects.map((p, pi) => validateProject(p, index, pi + 1));
|
|
90
|
+
const result = {
|
|
91
|
+
outPath: obj.outPath.trim(),
|
|
92
|
+
splitByTag: obj.splitByTag === true,
|
|
93
|
+
languages: Object.fromEntries(Object.entries(languages).map(([k, v]) => [k, String(v)])),
|
|
94
|
+
format,
|
|
95
|
+
projects,
|
|
96
|
+
};
|
|
97
|
+
if (obj.options && typeof obj.options === 'object' && !Array.isArray(obj.options)) {
|
|
98
|
+
result.options = obj.options;
|
|
99
|
+
}
|
|
100
|
+
if (format === 'custom') {
|
|
101
|
+
const custom = result.options?.custom;
|
|
102
|
+
if (!custom?.extension || !custom?.entryTemplate) {
|
|
103
|
+
throw new CliError(`配置第 ${index} 项 format 为 custom 时,options.custom.extension 和 options.custom.entryTemplate 必填`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
function validateProject(item, targetIndex, projectIndex) {
|
|
109
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
110
|
+
throw new CliError(`配置第 ${targetIndex} 项 projects[${projectIndex}] 必须是对象`);
|
|
111
|
+
}
|
|
112
|
+
const obj = item;
|
|
113
|
+
if (typeof obj.projectId !== 'number' || !Number.isInteger(obj.projectId) || obj.projectId <= 0) {
|
|
114
|
+
throw new CliError(`配置第 ${targetIndex} 项 projects[${projectIndex}].projectId 必须是正整数`);
|
|
115
|
+
}
|
|
116
|
+
const result = { projectId: obj.projectId };
|
|
117
|
+
if (Array.isArray(obj.versions)) {
|
|
118
|
+
result.versions = obj.versions.map(Number).filter((n) => Number.isInteger(n) && n > 0);
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(obj.tags)) {
|
|
121
|
+
result.tags = obj.tags.map(Number).filter((n) => Number.isInteger(n) && n > 0);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=intl-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intl-config.js","sourceRoot":"","sources":["../../../src/lib/intl-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { CliError } from './args.js';
|
|
4
|
+
import { resolveRuntimeConfig, requireToken } from './config.js';
|
|
5
|
+
import { getSerializer, serializeCsv } from './intl-serializers/index.js';
|
|
6
|
+
// ---------- HTTP ----------
|
|
7
|
+
function baseUrlFromMcpUrl(mcpUrl) {
|
|
8
|
+
const url = new URL(mcpUrl);
|
|
9
|
+
url.pathname = url.pathname.replace(/\/mcp\/?$/, '');
|
|
10
|
+
return url.origin + (url.pathname === '/' ? '' : url.pathname);
|
|
11
|
+
}
|
|
12
|
+
async function apiGet(base, token, apiPath, query) {
|
|
13
|
+
const url = new URL(apiPath, base + '/');
|
|
14
|
+
if (query) {
|
|
15
|
+
for (const [k, v] of Object.entries(query)) {
|
|
16
|
+
if (v !== undefined && v !== '')
|
|
17
|
+
url.searchParams.set(k, v);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const response = await fetch(url, {
|
|
21
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
22
|
+
signal: AbortSignal.timeout(30_000),
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const body = await response.text().catch(() => '');
|
|
26
|
+
throw new CliError(`API 请求失败 ${response.status}: ${url.pathname} ${body.slice(0, 200)}`);
|
|
27
|
+
}
|
|
28
|
+
const json = await response.json();
|
|
29
|
+
return json.data;
|
|
30
|
+
}
|
|
31
|
+
async function fetchTagMap(base, token, projectId) {
|
|
32
|
+
const groups = await apiGet(base, token, `/api/v1/projects/${projectId}/tag-groups`);
|
|
33
|
+
const map = new Map();
|
|
34
|
+
for (const group of groups) {
|
|
35
|
+
for (const tag of group.tags ?? []) {
|
|
36
|
+
map.set(tag.id, { name: tag.name, groupName: group.name });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return map;
|
|
40
|
+
}
|
|
41
|
+
async function fetchTerms(base, token, projectId, versionId, tagId) {
|
|
42
|
+
const terms = [];
|
|
43
|
+
let page = 1;
|
|
44
|
+
const pageSize = 200;
|
|
45
|
+
while (true) {
|
|
46
|
+
const query = {
|
|
47
|
+
ref_locale: 'zh-CN',
|
|
48
|
+
page: String(page),
|
|
49
|
+
page_size: String(pageSize),
|
|
50
|
+
};
|
|
51
|
+
if (tagId !== undefined)
|
|
52
|
+
query.tag_ids = String(tagId);
|
|
53
|
+
if (versionId !== undefined)
|
|
54
|
+
query.version_id = String(versionId);
|
|
55
|
+
const data = await apiGet(base, token, `/api/v1/projects/${projectId}/terms/page`, query);
|
|
56
|
+
for (const item of data.items ?? []) {
|
|
57
|
+
const translations = new Map();
|
|
58
|
+
for (const t of item.translations ?? []) {
|
|
59
|
+
translations.set(t.locale, t.content);
|
|
60
|
+
}
|
|
61
|
+
terms.push({
|
|
62
|
+
name: item.name,
|
|
63
|
+
tagIds: new Set((item.tags ?? []).map((t) => t.tag_id)),
|
|
64
|
+
translations,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (terms.length >= data.total || (data.items ?? []).length === 0)
|
|
68
|
+
break;
|
|
69
|
+
page++;
|
|
70
|
+
}
|
|
71
|
+
return terms;
|
|
72
|
+
}
|
|
73
|
+
// ---------- 核心下载 ----------
|
|
74
|
+
export async function downloadIntl(config) {
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
const runtime = await resolveRuntimeConfig({});
|
|
77
|
+
requireToken(runtime.token);
|
|
78
|
+
const base = baseUrlFromMcpUrl(runtime.url);
|
|
79
|
+
const token = runtime.token;
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const target of config.targets) {
|
|
82
|
+
const result = await processTarget(base, token, config.baseDir, target);
|
|
83
|
+
results.push(result);
|
|
84
|
+
}
|
|
85
|
+
const totalTerms = results.reduce((sum, r) => sum + r.termCount, 0);
|
|
86
|
+
return { targets: results, totalTerms, duration: Date.now() - start };
|
|
87
|
+
}
|
|
88
|
+
async function processTarget(base, token, baseDir, target) {
|
|
89
|
+
const outDir = path.resolve(baseDir, target.outPath);
|
|
90
|
+
await mkdir(outDir, { recursive: true });
|
|
91
|
+
const format = target.format ?? 'json';
|
|
92
|
+
const serializer = format === 'csv' ? null : getSerializer(format);
|
|
93
|
+
// 拉取所有词条(按项目分组合并)
|
|
94
|
+
const allTerms = new Map();
|
|
95
|
+
for (const project of target.projects) {
|
|
96
|
+
const tagMap = await fetchTagMap(base, token, project.projectId);
|
|
97
|
+
const versionIds = project.versions?.length ? project.versions : [undefined];
|
|
98
|
+
// tags: [] 表示项目级全量;未配置 tags 时保持原语义,展开为平台上的全部标签。
|
|
99
|
+
// 平台项目没有标签时也降级为项目级全量,避免 Java 等后端项目无法同步。
|
|
100
|
+
if (Array.isArray(project.tags) && project.tags.length === 0) {
|
|
101
|
+
for (const versionId of versionIds) {
|
|
102
|
+
const terms = await fetchTerms(base, token, project.projectId, versionId);
|
|
103
|
+
for (const term of terms) {
|
|
104
|
+
allTerms.set(term.name, term); // 后面的覆盖前面的
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const tagIds = project.tags?.length ? project.tags : [...tagMap.keys()];
|
|
110
|
+
if (tagIds.length === 0) {
|
|
111
|
+
for (const versionId of versionIds) {
|
|
112
|
+
const terms = await fetchTerms(base, token, project.projectId, versionId);
|
|
113
|
+
for (const term of terms) {
|
|
114
|
+
allTerms.set(term.name, term); // 后面的覆盖前面的
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
for (const versionId of versionIds) {
|
|
120
|
+
for (const tagId of tagIds) {
|
|
121
|
+
const terms = await fetchTerms(base, token, project.projectId, versionId, tagId);
|
|
122
|
+
for (const term of terms) {
|
|
123
|
+
allTerms.set(term.name, term); // 后面的覆盖前面的
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const files = [];
|
|
131
|
+
const outputLocales = Object.values(target.languages);
|
|
132
|
+
if (format === 'csv') {
|
|
133
|
+
// CSV 模式:单文件输出所有语言列
|
|
134
|
+
const data = new Map();
|
|
135
|
+
for (const [key, term] of allTerms) {
|
|
136
|
+
const locales = {};
|
|
137
|
+
for (const [platformLocale, outputLocale] of Object.entries(target.languages)) {
|
|
138
|
+
locales[outputLocale] = term.translations.get(platformLocale) ?? term.name;
|
|
139
|
+
}
|
|
140
|
+
data.set(key, locales);
|
|
141
|
+
}
|
|
142
|
+
const content = serializeCsv(data, outputLocales, target.options);
|
|
143
|
+
const filePath = path.join(outDir, `intl.csv`);
|
|
144
|
+
await writeFile(filePath, content, 'utf8');
|
|
145
|
+
files.push({ path: path.relative(baseDir, filePath), terms: data.size });
|
|
146
|
+
}
|
|
147
|
+
else if (target.splitByTag) {
|
|
148
|
+
// 分包模式:每个标签独立输出
|
|
149
|
+
const tagFileCounts = new Map();
|
|
150
|
+
for (const term of allTerms.values()) {
|
|
151
|
+
for (const tagId of term.tagIds) {
|
|
152
|
+
tagFileCounts.set(tagId, (tagFileCounts.get(tagId) ?? 0) + 1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (const [platformLocale, outputLocale] of Object.entries(target.languages)) {
|
|
156
|
+
for (const tagId of tagFileCounts.keys()) {
|
|
157
|
+
const tagInfo = await getTagInfo(base, token, target.projects, tagId);
|
|
158
|
+
const entries = {};
|
|
159
|
+
for (const term of allTerms.values()) {
|
|
160
|
+
if (!term.tagIds.has(tagId))
|
|
161
|
+
continue;
|
|
162
|
+
entries[term.name] = term.translations.get(platformLocale) ?? term.name;
|
|
163
|
+
}
|
|
164
|
+
const ext = format === 'custom' ? (target.options?.custom?.extension ?? '.txt') : serializer.extension;
|
|
165
|
+
const fileName = `${outputLocale}-${tagInfo.safeName}${ext}`;
|
|
166
|
+
const filePath = path.join(outDir, fileName);
|
|
167
|
+
await writeFile(filePath, serializer.serialize(entries, target.options), 'utf8');
|
|
168
|
+
files.push({ path: path.relative(baseDir, filePath), terms: Object.keys(entries).length });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// 合并模式:每个语言一个文件
|
|
174
|
+
for (const [platformLocale, outputLocale] of Object.entries(target.languages)) {
|
|
175
|
+
const entries = {};
|
|
176
|
+
for (const [key, term] of allTerms) {
|
|
177
|
+
entries[key] = term.translations.get(platformLocale) ?? term.name;
|
|
178
|
+
}
|
|
179
|
+
const ext = format === 'custom' ? (target.options?.custom?.extension ?? '.txt') : serializer.extension;
|
|
180
|
+
const fileName = `${outputLocale}${ext}`;
|
|
181
|
+
const filePath = path.join(outDir, fileName);
|
|
182
|
+
await writeFile(filePath, serializer.serialize(entries, target.options), 'utf8');
|
|
183
|
+
files.push({ path: path.relative(baseDir, filePath), terms: Object.keys(entries).length });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
outPath: target.outPath,
|
|
188
|
+
format: target.format ?? 'json',
|
|
189
|
+
files,
|
|
190
|
+
termCount: allTerms.size,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// tagId → 安全文件名(tagName,若重名加 groupName)
|
|
194
|
+
async function getTagInfo(base, token, projects, tagId) {
|
|
195
|
+
for (const project of projects) {
|
|
196
|
+
const tagMap = await fetchTagMapCached(base, token, project.projectId);
|
|
197
|
+
const info = tagMap.get(tagId);
|
|
198
|
+
if (info) {
|
|
199
|
+
return { safeName: sanitizeFileName(info.name) };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { safeName: String(tagId) };
|
|
203
|
+
}
|
|
204
|
+
// 简单缓存避免重复调用 tag-groups
|
|
205
|
+
const tagCache = new Map();
|
|
206
|
+
async function fetchTagMapCached(base, token, projectId) {
|
|
207
|
+
let cached = tagCache.get(projectId);
|
|
208
|
+
if (!cached) {
|
|
209
|
+
cached = await fetchTagMap(base, token, projectId);
|
|
210
|
+
tagCache.set(projectId, cached);
|
|
211
|
+
}
|
|
212
|
+
return cached;
|
|
213
|
+
}
|
|
214
|
+
function sanitizeFileName(name) {
|
|
215
|
+
return name.replace(/[/\\:*?"<>|]/g, '_').trim() || String(name);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=intl-downloader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intl-downloader.js","sourceRoot":"","sources":["../../../src/lib/intl-downloader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function escapeCsvField(value, delimiter) {
|
|
2
|
+
if (value.includes(delimiter) || value.includes('"') || value.includes('\n') || value.includes('\r')) {
|
|
3
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
4
|
+
}
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export function serializeCsv(data, outputLocales, options) {
|
|
8
|
+
const csv = options?.csv ?? {};
|
|
9
|
+
const delimiter = csv.delimiter ?? ',';
|
|
10
|
+
const header = csv.header !== false;
|
|
11
|
+
const columns = csv.columns ?? ['key', ...outputLocales];
|
|
12
|
+
const rows = [];
|
|
13
|
+
if (header)
|
|
14
|
+
rows.push(columns.map((c) => escapeCsvField(c, delimiter)).join(delimiter));
|
|
15
|
+
for (const [key, locales] of data) {
|
|
16
|
+
const row = columns.map((col) => {
|
|
17
|
+
if (col === 'key')
|
|
18
|
+
return escapeCsvField(key, delimiter);
|
|
19
|
+
return escapeCsvField(locales[col] ?? '', delimiter);
|
|
20
|
+
});
|
|
21
|
+
rows.push(row.join(delimiter));
|
|
22
|
+
}
|
|
23
|
+
return rows.join('\n') + '\n';
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../../../../src/lib/intl-serializers/csv.ts"],"names":[],"mappings":"AAKA,SAAS,cAAc,CAAC,KAAa,EAAE,SAAiB;IACtD,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrG,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,IAA0B,EAC1B,aAAuB,EACvB,OAAqB;IAErB,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC;IACvC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC;IACpC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,GAAG,aAAa,CAAC,CAAC;IAEzD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACxF,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YAC9B,IAAI,GAAG,KAAK,KAAK;gBAAE,OAAO,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACzD,OAAO,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class CustomSerializer {
|
|
2
|
+
get extension() {
|
|
3
|
+
// extension 从 options 中读取,这里返回空字符串;实际值由 downloader 侧解析
|
|
4
|
+
return '';
|
|
5
|
+
}
|
|
6
|
+
serialize(entries, options) {
|
|
7
|
+
const custom = options?.custom;
|
|
8
|
+
if (!custom?.entryTemplate)
|
|
9
|
+
throw new Error('custom 格式需要 options.custom.entryTemplate');
|
|
10
|
+
const delimiter = custom.delimiter ?? '\n';
|
|
11
|
+
const body = Object.entries(entries)
|
|
12
|
+
.map(([key, value]) => custom.entryTemplate.replace(/\{key\}/g, key).replace(/\{value\}/g, value))
|
|
13
|
+
.join(delimiter);
|
|
14
|
+
const parts = [];
|
|
15
|
+
if (custom.header)
|
|
16
|
+
parts.push(custom.header);
|
|
17
|
+
parts.push(body);
|
|
18
|
+
if (custom.footer)
|
|
19
|
+
parts.push(custom.footer);
|
|
20
|
+
return parts.join(delimiter) + '\n';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../../src/lib/intl-serializers/custom.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,gBAAgB;IAC3B,IAAI,SAAS;QACX,uDAAuD;QACvD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,SAAS,CAAC,OAA+B,EAAE,OAAqB;QAC9D,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAExF,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CACpB,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAC3E;aACA,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,MAAM,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7C,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { JsonSerializer } from './json.js';
|
|
2
|
+
import { TsSerializer } from './ts.js';
|
|
3
|
+
import { JsSerializer } from './js.js';
|
|
4
|
+
import { XmlSerializer } from './xml.js';
|
|
5
|
+
import { PropertiesSerializer } from './properties.js';
|
|
6
|
+
import { CustomSerializer } from './custom.js';
|
|
7
|
+
export { serializeCsv } from './csv.js';
|
|
8
|
+
const REGISTRY = new Map([
|
|
9
|
+
['json', () => new JsonSerializer()],
|
|
10
|
+
['ts', () => new TsSerializer()],
|
|
11
|
+
['js', () => new JsSerializer()],
|
|
12
|
+
['xml', () => new XmlSerializer()],
|
|
13
|
+
['properties', () => new PropertiesSerializer()],
|
|
14
|
+
['custom', () => new CustomSerializer()],
|
|
15
|
+
]);
|
|
16
|
+
export function getSerializer(format) {
|
|
17
|
+
const factory = REGISTRY.get(format);
|
|
18
|
+
if (!factory)
|
|
19
|
+
throw new Error(`不支持的输出格式: ${format}`);
|
|
20
|
+
return factory();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/intl-serializers/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAA6B,MAAM,UAAU,CAAC;AAEnE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAA+B;IACrD,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,cAAc,EAAE,CAAC;IACpC,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;IAChC,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;IAChC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,aAAa,EAAE,CAAC;IAClC,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,oBAAoB,EAAE,CAAC;IAChD,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,gBAAgB,EAAE,CAAC;CACzC,CAAC,CAAC;AAEH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,aAAa,MAAM,EAAE,CAAC,CAAC;IACrD,OAAO,OAAO,EAAE,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class JsSerializer {
|
|
2
|
+
extension = '.js';
|
|
3
|
+
serialize(entries) {
|
|
4
|
+
const body = Object.entries(entries)
|
|
5
|
+
.map(([k, v]) => ` ${JSON.stringify(k)}: ${JSON.stringify(v)},`)
|
|
6
|
+
.join('\n');
|
|
7
|
+
return `'use strict';\n\nmodule.exports = {\n${body}\n};\n`;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=js.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js.js","sourceRoot":"","sources":["../../../../src/lib/intl-serializers/js.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,YAAY;IACd,SAAS,GAAG,KAAK,CAAC;IAE3B,SAAS,CAAC,OAA+B;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;aAChE,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,wCAAwC,IAAI,QAAQ,CAAC;IAC9D,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../../../src/lib/intl-serializers/json.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,cAAc;IAChB,SAAS,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,OAA+B;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACjD,CAAC;CACF"}
|