i18n-xy
Version:
自动提取React项目中的中文字符串并进行国际化的CLI工具
1,367 lines (1,351 loc) • 124 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/config/default.config.ts
var defaultConfig;
var init_default_config = __esm({
"src/config/default.config.ts"() {
"use strict";
defaultConfig = {
// 基础语言配置
locale: "zh-CN",
fallbackLocale: "en-US",
outputDir: "locales",
// tempDir: undefined, // 可选,不设置则直接修改源文件
// 文件处理配置
include: [
"src/**/*.{js,jsx,ts,tsx}",
"pages/**/*.{js,jsx,ts,tsx}",
"components/**/*.{js,jsx,ts,tsx}"
],
exclude: [
"node_modules/**",
"dist/**",
"build/**",
"**/*.test.{js,jsx,ts,tsx}",
"**/*.spec.{js,jsx,ts,tsx}"
],
// Key 生成配置
keyGeneration: {
maxChineseLength: 10,
// 最大汉字长度
hashLength: 6,
// 哈希长度
maxRetryCount: 5,
// 最大重试次数
reuseExistingKey: true,
// 默认重复使用相同文案的key
duplicateKeySuffix: "hash",
// 重复key后缀模式,默认hash
keyPrefix: "",
// key前缀,默认为空
separator: "_",
// 连接符,默认下划线
pinyinOptions: {
// 拼音转换选项
toneType: "none",
// 不带声调
type: "array"
// 返回数组格式
}
},
// 输出配置
output: {
prettyJson: true,
// 格式化 JSON 输出
localeFileName: "{locale}.json"
// 语言文件名格式
},
// 日志配置
logging: {
enabled: true,
// 默认启用日志
level: "normal"
// 默认正常级别
},
// 替换配置
replacement: {
functionName: "$t",
// 默认替换函数名
quoteType: "single",
// 默认使用单引号
autoImport: {
enabled: false,
// 默认不启用自动引入
insertPosition: "afterImports",
// 默认在import语句后插入
imports: {}
}
},
// 翻译配置
translation: {
enabled: false,
// 默认不启用翻译功能
provider: "baidu",
// 默认使用百度翻译
defaultSourceLang: "zh",
// 默认源语言:中文
defaultTargetLang: "en",
// 默认目标语言:英文
concurrency: 5,
// 默认并发数:5 (降低并发数避免API限制)
retryTimes: 3,
// 默认重试次数:3次(不包括第一次)
retryDelay: 1e3,
// 默认重试延迟:1秒
batchDelay: 500,
// 默认批次延迟:0.5秒
baidu: {
appid: "",
// 需要用户配置
key: ""
// 需要用户配置
}
}
};
}
});
// src/utils/fs.ts
import * as fs from "fs-extra";
import * as path from "path";
import fg from "fast-glob";
function readFile2(filePath, encoding = "utf-8") {
const absPath = path.resolve(process.cwd(), filePath);
return fs.readFile(absPath, encoding);
}
function writeFile(filePath, data) {
const absPath = path.resolve(process.cwd(), filePath);
return fs.outputFile(absPath, data);
}
function writeFileWithTempDir(filePath, data, tempDir) {
let targetPath;
if (tempDir) {
const relativePath = path.relative(process.cwd(), filePath);
targetPath = path.resolve(process.cwd(), tempDir, relativePath);
} else {
targetPath = path.resolve(process.cwd(), filePath);
}
return fs.outputFile(targetPath, data);
}
function ensureDir2(dirPath) {
const absPath = path.resolve(process.cwd(), dirPath);
return fs.ensureDir(absPath);
}
function findFiles(pattern, options = {}) {
return fg(pattern, { dot: true, ...options, cwd: process.cwd() });
}
function findTargetFiles(include, exclude = []) {
return fg(include, { ignore: exclude, dot: true, cwd: process.cwd() });
}
function fileExists(filePath) {
const absPath = path.resolve(process.cwd(), filePath);
return fs.existsSync(absPath);
}
function readJsonSync2(filePath) {
const absPath = path.resolve(process.cwd(), filePath);
return fs.readJsonSync(absPath);
}
async function readJson(filePath, defaultValue) {
try {
const content = await readFile2(filePath, "utf-8");
return JSON.parse(content);
} catch (error) {
if (defaultValue !== void 0) {
return defaultValue;
}
throw error;
}
}
function writeJson(filePath, data, pretty = true) {
const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
return writeFile(filePath, content);
}
var init_fs = __esm({
"src/utils/fs.ts"() {
"use strict";
}
});
// src/utils/logger.ts
var Logger;
var init_logger = __esm({
"src/utils/logger.ts"() {
"use strict";
init_config();
Logger = class {
static getLogLevel() {
var _a;
try {
const config = ConfigManager.get();
return ((_a = config.logging) == null ? void 0 : _a.level) ?? "normal";
} catch {
return "normal";
}
}
static shouldLog(level) {
const currentLevel = this.getLogLevel();
if (currentLevel === "minimal") {
return level === "minimal";
} else if (currentLevel === "normal") {
return level !== "verbose";
} else if (currentLevel === "verbose") {
return true;
}
return false;
}
static success(message, level = "normal") {
if (this.shouldLog(level)) {
console.log(`\u2705 ${message}`);
}
}
static info(message, level = "normal") {
if (this.shouldLog(level)) {
console.log(`\u2139\uFE0F ${message}`);
}
}
static warn(message, level = "normal") {
if (this.shouldLog(level)) {
console.warn(`\u26A0\uFE0F ${message}`);
}
}
static error(message, level = "normal") {
if (this.shouldLog(level)) {
console.error(`\u274C ${message}`);
}
}
static verbose(message) {
this.info(message, "verbose");
}
};
}
});
// src/config/index.ts
import { merge } from "lodash";
function validateConfig(config) {
var _a, _b, _c, _d;
const errors = [];
if (!config.include || !Array.isArray(config.include) || config.include.length === 0) {
errors.push("include \u5B57\u6BB5\u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4");
}
if (!config.outputDir || typeof config.outputDir !== "string") {
errors.push("outputDir \u5B57\u6BB5\u5FC5\u987B\u662F\u6709\u6548\u7684\u5B57\u7B26\u4E32\u8DEF\u5F84");
}
if (!config.locale || typeof config.locale !== "string") {
errors.push("locale \u5B57\u6BB5\u5FC5\u987B\u662F\u6709\u6548\u7684\u8BED\u8A00\u4EE3\u7801");
}
if ((_a = config.translation) == null ? void 0 : _a.enabled) {
if (!config.translation.provider) {
errors.push("\u542F\u7528\u7FFB\u8BD1\u65F6\u5FC5\u987B\u6307\u5B9A translation.provider");
}
if (config.translation.provider === "baidu") {
if (!((_b = config.translation.baidu) == null ? void 0 : _b.appid) || !((_c = config.translation.baidu) == null ? void 0 : _c.key)) {
errors.push("\u4F7F\u7528\u767E\u5EA6\u7FFB\u8BD1\u65F6\u5FC5\u987B\u914D\u7F6E translation.baidu.appid \u548C translation.baidu.key");
}
}
}
if (((_d = config.logging) == null ? void 0 : _d.level) && !["minimal", "normal", "verbose"].includes(config.logging.level)) {
errors.push("logging.level \u5FC5\u987B\u662F minimal\u3001normal \u6216 verbose \u4E4B\u4E00");
}
if (errors.length > 0) {
Logger.error("\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25:", "minimal");
errors.forEach((error) => Logger.error(` - ${error}`, "minimal"));
throw new Error(`\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25: ${errors.join("; ")}`);
}
}
function loadConfig(customConfigPath) {
let userConfig = {};
if (customConfigPath) {
if (fileExists(customConfigPath)) {
Logger.verbose(`\u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6: ${customConfigPath}`);
try {
userConfig = readJsonSync2(customConfigPath);
Logger.verbose("\u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u6210\u529F");
} catch (error) {
throw new Error(`\u914D\u7F6E\u6587\u4EF6\u89E3\u6790\u5931\u8D25: ${error}`);
}
} else {
Logger.warn(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${customConfigPath}\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E`);
}
}
const finalConfig = merge({}, defaultConfig, userConfig);
validateConfig(finalConfig);
Logger.verbose("\u914D\u7F6E\u9A8C\u8BC1\u901A\u8FC7");
return finalConfig;
}
var ConfigManagerClass, ConfigManager;
var init_config = __esm({
"src/config/index.ts"() {
"use strict";
init_default_config();
init_fs();
init_logger();
ConfigManagerClass = class {
config = null;
init(config) {
this.config = config;
}
get() {
if (!this.config) {
throw new Error("ConfigManager: config not initialized!");
}
return this.config;
}
};
ConfigManager = new ConfigManagerClass();
}
});
// src/translation/providers/baidu.ts
import baiduTranslateService from "baidu-translate-service";
var BaiduTranslationProvider;
var init_baidu = __esm({
"src/translation/providers/baidu.ts"() {
"use strict";
init_logger();
BaiduTranslationProvider = class {
name = "baidu";
config;
constructor(config) {
this.config = config;
Logger.verbose(`\u767E\u5EA6\u7FFB\u8BD1\u63D0\u4F9B\u8005\u521D\u59CB\u5316\u5B8C\u6210\uFF0Cappid: ${config.appid ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E"}`);
}
async translate(text, from = "auto", to = "en") {
if (!this.isConfigured()) {
throw new Error("\u767E\u5EA6\u7FFB\u8BD1\u670D\u52A1\u672A\u914D\u7F6E\uFF0C\u8BF7\u8BBE\u7F6E appid \u548C key");
}
try {
const mappedFrom = this.mapLanguageCode(from);
const mappedTo = this.mapLanguageCode(to);
Logger.verbose(`\u53D1\u8D77\u767E\u5EA6\u7FFB\u8BD1\u8BF7\u6C42: ${mappedFrom} -> ${mappedTo}, \u6587\u672C\u957F\u5EA6: ${text.length}`);
const response = await baiduTranslateService({
appid: this.config.appid,
key: this.config.key,
q: text,
from: mappedFrom,
to: mappedTo
});
if (response.error_code) {
throw new Error(`\u767E\u5EA6\u7FFB\u8BD1API\u9519\u8BEF: ${response.error_code} - ${response.error_msg}`);
}
if (!response.trans_result || response.trans_result.length === 0) {
throw new Error("\u7FFB\u8BD1\u7ED3\u679C\u4E3A\u7A7A");
}
const translatedText = response.trans_result.map((item) => item.dst).join("\n");
Logger.verbose(`\u7FFB\u8BD1\u6210\u529F: "${text}" -> "${translatedText}"`);
return {
originalText: text,
translatedText,
sourceLanguage: response.from,
targetLanguage: response.to,
provider: this.name
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.error(`\u767E\u5EA6\u7FFB\u8BD1\u5931\u8D25: ${errorMessage}`);
throw error;
}
}
isConfigured() {
return !!(this.config.appid && this.config.key);
}
getSupportedLanguages() {
return [
"auto",
"zh",
"en",
"yue",
"wyw",
"jp",
"kor",
"fra",
"spa",
"th",
"ara",
"ru",
"pt",
"de",
"it",
"el",
"nl",
"pl",
"bul",
"est",
"dan",
"fin",
"cs",
"rom",
"slo",
"swe",
"hu",
"cht",
"vie"
];
}
/**
* 映射语言代码到百度翻译API支持的格式
*/
mapLanguageCode(lang) {
const langMap = {
"zh-CN": "zh",
"zh-TW": "cht",
"en-US": "en",
ja: "jp",
ko: "kor",
fr: "fra",
es: "spa",
ar: "ara",
ru: "ru",
pt: "pt",
de: "de",
it: "it",
nl: "nl",
pl: "pl",
sv: "swe",
vi: "vie"
};
return langMap[lang] ?? lang;
}
};
}
});
// src/translation/queue.ts
var TranslationQueue;
var init_queue = __esm({
"src/translation/queue.ts"() {
"use strict";
init_logger();
TranslationQueue = class {
concurrency;
tasks = [];
completedResults = /* @__PURE__ */ new Map();
failedTasks = /* @__PURE__ */ new Map();
stats = {
total: 0,
pending: 0,
completed: 0,
failed: 0,
inProgress: 0
};
batchDelay = 0;
// 批次间延迟(毫秒)
isApiLimited = false;
// API限制状态标志
apiLimitResetTime = 0;
// API限制重置时间
constructor(concurrency = 10) {
this.concurrency = concurrency;
}
/**
* 添加任务到队列
*/
addTask(task) {
task.currentRetry = 0;
this.tasks.push(task);
this.stats.total++;
this.stats.pending++;
}
/**
* 设置批次间延迟时间(毫秒)
*/
setBatchDelay(delay) {
this.batchDelay = delay;
}
/**
* 执行所有任务,返回结果映射
*/
async executeAll() {
Logger.info(`\u5F00\u59CB\u6267\u884C\u7FFB\u8BD1\u961F\u5217\uFF0C\u5E76\u53D1\u6570: ${this.concurrency}\uFF0C\u4EFB\u52A1\u603B\u6570: ${this.stats.total}`);
this.completedResults.clear();
this.failedTasks.clear();
const workers = [];
for (let i = 0; i < Math.min(this.concurrency, this.tasks.length); i++) {
workers.push(this.worker());
}
await Promise.all(workers);
return this.completedResults;
}
/**
* 工作线程,负责执行任务队列中的任务
*/
async worker() {
while (this.tasks.length > 0) {
if (this.isApiLimited) {
const now = Date.now();
if (now < this.apiLimitResetTime) {
const waitTime = this.apiLimitResetTime - now;
Logger.warn(`API\u901F\u7387\u9650\u5236\u4E2D\uFF0C\u7B49\u5F85 ${Math.ceil(waitTime / 1e3)} \u79D2\u540E\u7EE7\u7EED...`);
await this.delay(waitTime);
this.isApiLimited = false;
}
}
const task = this.tasks.shift();
if (!task) continue;
this.stats.pending--;
this.stats.inProgress++;
try {
const result = await task.execute();
this.completedResults.set(task.id, result);
this.stats.completed++;
this.stats.inProgress--;
Logger.verbose(`\u4EFB\u52A1 ${task.id} \u6267\u884C\u6210\u529F`);
if (this.batchDelay > 0) {
await this.delay(this.batchDelay);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (this.isApiLimitError(errorMessage)) {
this.handleApiLimitError();
this.tasks.unshift(task);
this.stats.pending++;
} else {
Logger.error(`\u4EFB\u52A1 ${task.id} \u6267\u884C\u5931\u8D25: ${errorMessage}`);
if ((task.currentRetry ?? 0) < task.maxRetries) {
task.currentRetry = (task.currentRetry ?? 0) + 1;
Logger.warn(
`\u4EFB\u52A1 ${task.id} \u6267\u884C\u5931\u8D25\uFF0C\u51C6\u5907\u7B2C ${task.currentRetry} \u6B21\u91CD\u8BD5: ${errorMessage}`
);
await this.delay(task.retryDelay);
this.tasks.push(task);
this.stats.pending++;
} else {
const finalError = error instanceof Error ? error : new Error(String(error));
this.failedTasks.set(task.id, finalError);
this.stats.failed++;
}
}
this.stats.inProgress--;
}
}
}
/**
* 检测是否为API限制错误
*/
isApiLimitError(errorMessage) {
return errorMessage.includes("API\u9519\u8BEF: 54003") || errorMessage.includes("Invalid Access Limit") || errorMessage.includes("rate limit") || errorMessage.includes("too many requests");
}
/**
* 处理API限制错误
*/
handleApiLimitError() {
this.isApiLimited = true;
const cooldownTime = 60 * 1e3;
this.apiLimitResetTime = Date.now() + cooldownTime;
Logger.warn(`\u68C0\u6D4B\u5230API\u901F\u7387\u9650\u5236\uFF0C\u5C06\u6682\u505C ${cooldownTime / 1e3} \u79D2\u540E\u7EE7\u7EED`);
}
/**
* 获取已完成任务的结果
*/
getCompletedResults() {
return this.completedResults;
}
/**
* 获取失败任务的错误信息
*/
getFailedTasks() {
return this.failedTasks;
}
/**
* 获取队列统计信息
*/
getStats() {
return { ...this.stats };
}
/**
* Promise延迟等待
*/
delay(ms) {
return new Promise((resolve2) => setTimeout(resolve2, ms));
}
};
}
});
// src/translation/cache.ts
var TranslationCache;
var init_cache = __esm({
"src/translation/cache.ts"() {
"use strict";
init_logger();
TranslationCache = class _TranslationCache {
static instance;
cache = /* @__PURE__ */ new Map();
constructor() {
}
/**
* 获取翻译缓存管理器实例
*/
static getInstance() {
if (!_TranslationCache.instance) {
_TranslationCache.instance = new _TranslationCache();
Logger.verbose("\u521D\u59CB\u5316\u7FFB\u8BD1\u5185\u5B58\u7F13\u5B58");
}
return _TranslationCache.instance;
}
/**
* 从缓存中获取翻译结果
*/
get(text, from, to, provider) {
const key = this.generateCacheKey(text, from, to, provider);
const cached = this.cache.get(key);
if (cached) {
Logger.verbose(`\u7F13\u5B58\u547D\u4E2D: "${text}" (${from} -> ${to})`);
return cached;
}
return null;
}
/**
* 将翻译结果存入缓存
*/
set(text, translatedText, from, to, provider) {
const key = this.generateCacheKey(text, from, to, provider);
this.cache.set(key, {
originalText: text,
translatedText,
sourceLanguage: from,
targetLanguage: to,
provider,
timestamp: Date.now()
});
}
/**
* 清空缓存
*/
clear() {
this.cache.clear();
Logger.verbose("\u7FFB\u8BD1\u7F13\u5B58\u5DF2\u6E05\u7A7A");
}
/**
* 获取缓存大小
*/
size() {
return this.cache.size;
}
/**
* 生成缓存键
*/
generateCacheKey(text, from, to, provider) {
return `${provider}:${from}:${to}:${text}`;
}
};
}
});
// src/translation/manager.ts
import * as path3 from "path";
var TranslationManager;
var init_manager = __esm({
"src/translation/manager.ts"() {
"use strict";
init_baidu();
init_logger();
init_queue();
init_fs();
init_cache();
TranslationManager = class {
providers = /* @__PURE__ */ new Map();
config;
cache;
constructor(config) {
this.config = config;
Logger.verbose(`\u521D\u59CB\u5316\u7FFB\u8BD1\u7BA1\u7406\u5668\uFF0C\u63D0\u4F9B\u8005: ${config.provider}\uFF0C\u5E76\u53D1\u6570: ${config.concurrency}`);
this.cache = TranslationCache.getInstance();
this.initializeProviders();
}
initializeProviders() {
var _a, _b;
if (((_a = this.config.baidu) == null ? void 0 : _a.appid) && ((_b = this.config.baidu) == null ? void 0 : _b.key)) {
const baiduProvider = new BaiduTranslationProvider({
appid: this.config.baidu.appid,
key: this.config.baidu.key
});
this.providers.set("baidu", baiduProvider);
Logger.verbose("\u767E\u5EA6\u7FFB\u8BD1\u670D\u52A1\u5DF2\u521D\u59CB\u5316");
}
}
/**
* 翻译文本
*/
async translate(text, from = this.config.defaultSourceLang ?? "auto", to = this.config.defaultTargetLang ?? "en") {
if (!this.config.enabled) {
throw new Error("\u7FFB\u8BD1\u670D\u52A1\u672A\u542F\u7528");
}
const provider = this.providers.get(this.config.provider);
if (!provider) {
throw new Error(`\u7FFB\u8BD1\u670D\u52A1\u63D0\u4F9B\u8005 '${this.config.provider}' \u672A\u627E\u5230\u6216\u672A\u914D\u7F6E`);
}
if (!provider.isConfigured()) {
throw new Error(`\u7FFB\u8BD1\u670D\u52A1\u63D0\u4F9B\u8005 '${this.config.provider}' \u914D\u7F6E\u4E0D\u5B8C\u6574`);
}
const providerName = provider.name ?? this.config.provider;
const cachedResult = this.cache.get(text, from, to, providerName);
if (cachedResult) {
Logger.info(`[\u7F13\u5B58] \u7FFB\u8BD1: "${text}" -> "${cachedResult.translatedText}"`);
return {
originalText: cachedResult.originalText,
translatedText: cachedResult.translatedText,
sourceLanguage: cachedResult.sourceLanguage,
targetLanguage: cachedResult.targetLanguage,
provider: cachedResult.provider
};
}
Logger.verbose(`\u4F7F\u7528 ${providerName} \u7FFB\u8BD1: "${text}" (${from} -> ${to})`);
try {
const result = await provider.translate(text, from, to);
Logger.info(`\u7FFB\u8BD1\u5B8C\u6210: "${text}" -> "${result.translatedText}"`);
this.cache.set(text, result.translatedText, from, to, providerName);
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.error(`\u7FFB\u8BD1\u5931\u8D25: ${errorMessage}`);
throw error;
}
}
/**
* 批量翻译文本数组
*/
async translateBatch(texts, from = this.config.defaultSourceLang ?? "auto", to = this.config.defaultTargetLang ?? "en") {
if (!this.config.enabled) {
throw new Error("\u7FFB\u8BD1\u670D\u52A1\u672A\u542F\u7528");
}
const concurrency = Math.min(this.config.concurrency ?? 5, 5);
const retryTimes = this.config.retryTimes ?? 3;
const retryDelay = Math.max(this.config.retryDelay ?? 1e3, 1e3);
const batchDelay = Math.max(this.config.batchDelay ?? 500, 500);
Logger.info(
`\u5F00\u59CB\u6279\u91CF\u7FFB\u8BD1\uFF0C\u6587\u672C\u6570\u91CF: ${texts.length}\uFF0C\u5E76\u53D1\u6570: ${concurrency}\uFF0C\u91CD\u8BD5\u6B21\u6570: ${retryTimes}`,
"normal"
);
Logger.verbose(`\u7FFB\u8BD1\u65B9\u5411: ${from} -> ${to}`);
const providerName = this.config.provider;
const textsToTranslate = [];
const cachedResults = /* @__PURE__ */ new Map();
for (let i = 0; i < texts.length; i++) {
const text = texts[i];
if (!text) continue;
const cachedResult = this.cache.get(text, from || "auto", to || "en", providerName);
if (cachedResult) {
Logger.verbose(`[\u7F13\u5B58] \u6587\u672C ${i + 1}/${texts.length}: "${text}"`);
cachedResults.set(`translate_${i}`, {
originalText: cachedResult.originalText,
translatedText: cachedResult.translatedText,
sourceLanguage: cachedResult.sourceLanguage,
targetLanguage: cachedResult.targetLanguage,
provider: cachedResult.provider
});
} else {
textsToTranslate.push(text);
}
}
const queue = new TranslationQueue(concurrency);
queue.setBatchDelay(batchDelay);
for (let i = 0; i < texts.length; i++) {
if (!cachedResults.has(`translate_${i}`)) {
const text = texts[i];
if (!text) continue;
queue.addTask({
id: `translate_${i}`,
execute: () => this.translate(text, from || "auto", to || "en"),
maxRetries: retryTimes,
retryDelay
});
}
}
Logger.info(`\u9700\u8981\u7FFB\u8BD1\u7684\u6587\u672C: ${textsToTranslate.length}\uFF0C\u4ECE\u7F13\u5B58\u52A0\u8F7D: ${cachedResults.size}`);
if (textsToTranslate.length === 0) {
Logger.info(`\u6240\u6709\u6587\u672C\u90FD\u4ECE\u7F13\u5B58\u52A0\u8F7D\uFF0C\u8DF3\u8FC7API\u8C03\u7528`);
const results2 = [];
for (let i = 0; i < texts.length; i++) {
const result = cachedResults.get(`translate_${i}`);
if (result) {
results2.push(result);
}
}
return results2;
}
const completedResults = await queue.executeAll();
const results = [];
const failedTasks = queue.getFailedTasks();
for (let i = 0; i < texts.length; i++) {
const taskId = `translate_${i}`;
if (cachedResults.has(taskId)) {
const result2 = cachedResults.get(taskId);
if (result2) {
results.push(result2);
}
continue;
}
const result = completedResults.get(taskId);
if (result) {
results.push(result);
} else {
const error = failedTasks.get(taskId);
const errorMessage = (error == null ? void 0 : error.message) ?? "\u672A\u77E5\u9519\u8BEF";
const currentText = texts[i];
if (currentText) {
Logger.error(`\u6587\u672C "${currentText}" \u7FFB\u8BD1\u5931\u8D25: ${errorMessage}`);
results.push({
originalText: currentText,
translatedText: currentText,
// 失败时返回原文
sourceLanguage: from || "auto",
targetLanguage: to || "en",
provider: this.config.provider
});
}
}
}
const stats = queue.getStats();
Logger.info(
`\u6279\u91CF\u7FFB\u8BD1\u5B8C\u6210\uFF0C\u6210\u529F: ${stats.completed}\uFF0C\u5931\u8D25: ${stats.failed}\uFF0C\u603B\u8BA1: ${texts.length}\uFF0C\u4F7F\u7528\u7F13\u5B58: ${cachedResults.size}`
);
return results;
}
/**
* 翻译语言文件(JSON)
* 此方法整合了原来的translateJsonFile和translateLanguageFiles
* 支持增量翻译和实时写入结果
*/
async translateLanguageFile(sourcePath, targetLocale, from = this.config.defaultSourceLang ?? "auto", to = this.config.defaultTargetLang ?? "en", incrementalMode = true) {
if (!fileExists(sourcePath)) {
throw new Error(`\u6E90\u8BED\u8A00\u6587\u4EF6\u4E0D\u5B58\u5728: ${sourcePath}`);
}
Logger.info(`\u{1F4D6} \u8BFB\u53D6JSON\u6587\u4EF6: ${sourcePath}`);
const sourceContent = await readJson(sourcePath);
const totalSourceItems = Object.keys(sourceContent).length;
const outputPath = path3.join(path3.dirname(sourcePath), `${targetLocale}.json`);
let existingTranslations = {};
let skippedCount = 0;
if (incrementalMode && fileExists(outputPath)) {
Logger.info(`\u589E\u91CF\u7FFB\u8BD1\u6A21\u5F0F: \u52A0\u8F7D\u5DF2\u6709\u7684\u7FFB\u8BD1\u6587\u4EF6 ${outputPath}`);
try {
existingTranslations = await readJson(outputPath);
Logger.info(`\u5DF2\u52A0\u8F7D ${Object.keys(existingTranslations).length} \u4E2A\u5DF2\u7FFB\u8BD1\u7684\u9879`);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.warn(`\u8BFB\u53D6\u5DF2\u6709\u7FFB\u8BD1\u6587\u4EF6\u5931\u8D25: ${errorMessage}\uFF0C\u5C06\u521B\u5EFA\u65B0\u6587\u4EF6`);
existingTranslations = {};
}
}
const keysToTranslate = [];
const textsToTranslate = [];
const finalTranslations = { ...existingTranslations };
for (const key in sourceContent) {
const value = sourceContent[key];
if (typeof value === "string") {
if (incrementalMode && key in existingTranslations && existingTranslations[key]) {
Logger.verbose(`\u8DF3\u8FC7\u5DF2\u7FFB\u8BD1\u7684\u952E: ${key}`);
skippedCount++;
continue;
}
keysToTranslate.push(key);
textsToTranslate.push(value);
} else {
finalTranslations[key] = value;
}
}
const needTranslateCount = textsToTranslate.length;
if (needTranslateCount === 0) {
Logger.info(`\u6CA1\u6709\u9700\u8981\u7FFB\u8BD1\u7684\u65B0\u6587\u672C\uFF0C\u4FDD\u7559\u6240\u6709\u5DF2\u7FFB\u8BD1\u5185\u5BB9`);
await writeJson(outputPath, finalTranslations, true);
return {
outputPath,
totalCount: totalSourceItems,
successCount: 0,
// 没有新翻译,成功数为0
skippedCount
// 返回跳过的数量
};
}
Logger.info(`\u{1F504} \u5F00\u59CB\u7FFB\u8BD1 ${needTranslateCount} \u4E2A\u6587\u672C\u6761\u76EE...`);
const results = await this.translateBatch(textsToTranslate, from, to);
let successCount = 0;
const realTimeWriteInterval = 50;
for (let i = 0; i < results.length; i++) {
const key = keysToTranslate[i];
const result = results[i];
if (key && typeof key === "string" && result && result.translatedText !== result.originalText) {
finalTranslations[key] = result.translatedText;
successCount++;
} else if (key && typeof key === "string") {
finalTranslations[key] = sourceContent[key];
}
if ((i + 1) % realTimeWriteInterval === 0 || i === results.length - 1) {
try {
await writeJson(outputPath, finalTranslations, true);
Logger.verbose(`\u5B9E\u65F6\u4FDD\u5B58\u7FFB\u8BD1\u7ED3\u679C\u5230 ${outputPath}\uFF0C\u5DF2\u5904\u7406 ${i + 1}/${results.length}`);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.warn(`\u5B9E\u65F6\u4FDD\u5B58\u7FFB\u8BD1\u7ED3\u679C\u5931\u8D25: ${errorMessage}`);
}
}
}
await writeJson(outputPath, finalTranslations, true);
Logger.verbose(`\u7FFB\u8BD1\u5B8C\u6210\uFF0C\u7ED3\u679C\u4FDD\u5B58\u5230: ${outputPath}`);
return {
outputPath,
totalCount: totalSourceItems,
successCount,
skippedCount
};
}
/**
* 检查服务是否可用
*/
isAvailable() {
if (!this.config.enabled) {
return false;
}
const provider = this.providers.get(this.config.provider);
return provider ? provider.isConfigured() : false;
}
/**
* 获取当前提供者支持的语言
*/
getSupportedLanguages() {
const provider = this.providers.get(this.config.provider);
return provider ? provider.getSupportedLanguages() : [];
}
/**
* 获取可用的翻译服务提供者列表
*/
getAvailableProviders() {
return Array.from(this.providers.keys()).filter((name) => {
const provider = this.providers.get(name);
return (provider == null ? void 0 : provider.isConfigured()) ?? false;
});
}
/**
* 获取缓存状态
*/
getCacheStatus() {
return {
size: this.cache.size()
};
}
/**
* 清除缓存
*/
clearCache() {
this.cache.clear();
Logger.info("\u7FFB\u8BD1\u7F13\u5B58\u5DF2\u6E05\u7A7A");
}
};
}
});
// src/translation/cli.ts
var cli_exports = {};
__export(cli_exports, {
translateCommand: () => translateCommand
});
import * as path4 from "path";
async function translateCommand(options) {
var _a, _b, _c, _d, _e;
const config = loadConfig(options.config);
ConfigManager.init(config);
if (!((_a = config.translation) == null ? void 0 : _a.enabled)) {
Logger.info("\u7FFB\u8BD1\u529F\u80FD\u672A\u542F\u7528\uFF0C\u8BF7\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u8BBE\u7F6E translation.enabled = true", "normal");
return;
}
const translationManager = new TranslationManager({
enabled: config.translation.enabled,
provider: config.translation.provider ?? "baidu",
defaultSourceLang: config.translation.defaultSourceLang ?? "zh",
defaultTargetLang: config.translation.defaultTargetLang ?? "en",
concurrency: config.translation.concurrency ?? 5,
retryTimes: config.translation.retryTimes ?? 3,
retryDelay: config.translation.retryDelay ?? 1e3,
batchDelay: config.translation.batchDelay ?? 500,
baidu: ((_b = config.translation.baidu) == null ? void 0 : _b.appid) && ((_c = config.translation.baidu) == null ? void 0 : _c.key) ? {
appid: config.translation.baidu.appid,
key: config.translation.baidu.key
} : void 0,
custom: ((_d = config.translation.custom) == null ? void 0 : _d.endpoint) && ((_e = config.translation.custom) == null ? void 0 : _e.apiKey) ? {
endpoint: config.translation.custom.endpoint,
apiKey: config.translation.custom.apiKey
} : void 0
});
if (!translationManager.isAvailable()) {
Logger.error("\u7FFB\u8BD1\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u914D\u7F6E", "minimal");
Logger.info("\u767E\u5EA6\u7FFB\u8BD1\u9700\u8981\u914D\u7F6E appid \u548C key", "normal");
return;
}
const defaultSourceLang = config.translation.defaultSourceLang ?? "zh";
const defaultTargetLang = config.translation.defaultTargetLang ?? config.fallbackLocale ?? "en";
const from = options.from ?? defaultSourceLang;
const to = options.to ?? defaultTargetLang;
const incrementalMode = options.incremental !== false;
try {
if (options.test && options.input) {
await handleTranslateTest(translationManager, options.input, from, to);
} else if (options.json) {
await handleTranslateJsonFile(translationManager, options.json, from, to, incrementalMode);
} else if (options.batch) {
await handleTranslateBatchFiles(translationManager, from, to, incrementalMode);
} else if (options.input) {
await handleTranslateInput(translationManager, options.input, from, to);
} else {
const outputDir = config.outputDir ?? "locales";
const sourceLocale = config.locale ?? "zh-CN";
const targetLocale = config.fallbackLocale ?? "en-US";
Logger.info(
`\u672A\u6307\u5B9A\u7FFB\u8BD1\u5185\u5BB9\uFF0C\u5C06\u4ECE ${outputDir} \u76EE\u5F55\u8BFB\u53D6 ${sourceLocale}.json \u6587\u4EF6\u5E76\u7FFB\u8BD1\u6210 ${targetLocale}`,
"normal"
);
const sourceLang = sourceLocale.split("-")[0] || "zh";
const targetLang = targetLocale.split("-")[0] || "en";
try {
const sourcePath = path4.join(outputDir, `${sourceLocale}.json`);
if (!fileExists(sourcePath)) {
throw new Error(`\u6E90\u8BED\u8A00\u6587\u4EF6\u4E0D\u5B58\u5728: ${sourcePath}\uFF0C\u8BF7\u5148\u8FD0\u884C\u751F\u6210\u547D\u4EE4\u521B\u5EFA\u6E90\u8BED\u8A00\u6587\u4EF6`);
}
Logger.info(`\u{1F4D6} \u4ECE\u6E90\u8BED\u8A00\u6587\u4EF6\u8BFB\u53D6: ${sourcePath}`);
const { outputPath, totalCount, successCount, skippedCount } = await translationManager.translateLanguageFile(
sourcePath,
targetLocale,
sourceLang,
targetLang,
incrementalMode
);
Logger.success(`\u2705 \u7FFB\u8BD1\u5B8C\u6210\uFF0C\u7ED3\u679C\u4FDD\u5B58\u5230: ${outputPath}`, "normal");
if (skippedCount > 0) {
if (successCount > 0) {
Logger.info(
`\u{1F4CA} \u6210\u529F\u7FFB\u8BD1: ${successCount}\u9879\u65B0\u5185\u5BB9\uFF0C\u8DF3\u8FC7\u5DF2\u7FFB\u8BD1\u9879: ${skippedCount}\u9879\uFF0C\u5171${totalCount}\u9879`,
"normal"
);
} else {
Logger.info(
`\u{1F4CA} \u65E0\u65B0\u5185\u5BB9\u9700\u8981\u7FFB\u8BD1\uFF0C\u5DF2\u6709\u7FFB\u8BD1\u9879: ${skippedCount}\u9879\uFF0C\u5171${totalCount}\u9879`,
"normal"
);
}
} else {
Logger.info(`\u{1F4CA} \u6210\u529F\u7FFB\u8BD1: ${successCount}/${totalCount}\u9879`, "normal");
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.error(`\u7FFB\u8BD1\u5931\u8D25: ${errorMessage}`, "minimal");
showUsageHelp();
}
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.error(`\u7FFB\u8BD1\u5931\u8D25: ${errorMessage}`, "minimal");
process.exit(1);
}
}
async function handleTranslateTest(manager, text, from, to) {
Logger.info(`\u6D4B\u8BD5\u7FFB\u8BD1\u6A21\u5F0F (${from} -> ${to})`, "normal");
Logger.info(`\u539F\u6587: ${text}`, "normal");
const result = await manager.translate(text, from, to);
Logger.success(`\u8BD1\u6587: ${result.translatedText}`, "normal");
Logger.info(`\u63D0\u4F9B\u8005: ${result.provider}`, "normal");
}
async function handleTranslateJsonFile(manager, jsonPath, from, to, incremental = true) {
const targetLocale = ConfigManager.get().fallbackLocale || "en-US";
const { outputPath, totalCount, successCount, skippedCount } = await manager.translateLanguageFile(jsonPath, targetLocale, from, to, incremental);
Logger.success(`\u2705 \u7FFB\u8BD1\u5B8C\u6210\uFF0C\u7ED3\u679C\u4FDD\u5B58\u5230: ${outputPath}`, "normal");
if (skippedCount > 0) {
if (successCount > 0) {
Logger.info(
`\u{1F4CA} \u6210\u529F\u7FFB\u8BD1: ${successCount}\u9879\u65B0\u5185\u5BB9\uFF0C\u8DF3\u8FC7\u5DF2\u7FFB\u8BD1\u9879: ${skippedCount}\u9879\uFF0C\u5171${totalCount}\u9879`,
"normal"
);
} else {
Logger.info(
`\u{1F4CA} \u65E0\u65B0\u5185\u5BB9\u9700\u8981\u7FFB\u8BD1\uFF0C\u5DF2\u6709\u7FFB\u8BD1\u9879: ${skippedCount}\u9879\uFF0C\u5171${totalCount}\u9879`,
"normal"
);
}
} else {
Logger.info(`\u{1F4CA} \u6210\u529F\u7FFB\u8BD1: ${successCount}/${totalCount}\u9879`, "normal");
}
}
async function handleTranslateBatchFiles(manager, from, to, incremental = true) {
const config = ConfigManager.get();
const outputDir = config.outputDir ?? "./locales";
const sourceLocale = config.locale ?? from;
const targetLocale = config.fallbackLocale ?? "en-US";
const sourcePath = path4.join(outputDir, `${sourceLocale}.json`);
if (!fileExists(sourcePath)) {
throw new Error(`\u6E90\u8BED\u8A00\u6587\u4EF6\u4E0D\u5B58\u5728: ${sourcePath}\uFF0C\u8BF7\u5148\u8FD0\u884C\u751F\u6210\u547D\u4EE4\u521B\u5EFA\u6E90\u8BED\u8A00\u6587\u4EF6`);
}
Logger.info(`\u{1F4D6} \u4ECE\u6E90\u8BED\u8A00\u6587\u4EF6\u8BFB\u53D6: ${sourcePath}`);
const { outputPath, totalCount, successCount, skippedCount } = await manager.translateLanguageFile(sourcePath, targetLocale, from, to, incremental);
Logger.success(`\u2705 \u6279\u91CF\u7FFB\u8BD1\u5B8C\u6210\uFF0C\u7ED3\u679C\u4FDD\u5B58\u5230: ${outputPath}`, "normal");
if (skippedCount > 0) {
if (successCount > 0) {
Logger.info(
`\u{1F4CA} \u6210\u529F\u7FFB\u8BD1: ${successCount}\u9879\u65B0\u5185\u5BB9\uFF0C\u8DF3\u8FC7\u5DF2\u7FFB\u8BD1\u9879: ${skippedCount}\u9879\uFF0C\u5171${totalCount}\u9879`,
"normal"
);
} else {
Logger.info(
`\u{1F4CA} \u65E0\u65B0\u5185\u5BB9\u9700\u8981\u7FFB\u8BD1\uFF0C\u5DF2\u6709\u7FFB\u8BD1\u9879: ${skippedCount}\u9879\uFF0C\u5171${totalCount}\u9879`,
"normal"
);
}
} else {
Logger.info(`\u{1F4CA} \u6210\u529F\u7FFB\u8BD1: ${successCount}/${totalCount}\u9879`, "normal");
}
}
async function handleTranslateInput(manager, input, from, to) {
let text = input;
if (fileExists(input)) {
try {
text = await readFile2(input, "utf-8");
Logger.info(`\u4ECE\u6587\u4EF6\u8BFB\u53D6\u5185\u5BB9: ${input}`);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${errorMessage}`);
}
}
Logger.info(`\u6B63\u5728\u7FFB\u8BD1 (${from} -> ${to})...`, "normal");
const result = await manager.translate(text, from, to);
Logger.info("\u7FFB\u8BD1\u7ED3\u679C:", "normal");
Logger.info(`\u539F\u6587 (${result.sourceLanguage}): ${result.originalText}`, "normal");
Logger.success(`\u8BD1\u6587 (${result.targetLanguage}): ${result.translatedText}`, "normal");
Logger.info(`\u63D0\u4F9B\u8005: ${result.provider}`, "verbose");
}
function showUsageHelp() {
Logger.error("\u8BF7\u6307\u5B9A\u7FFB\u8BD1\u5185\u5BB9\uFF1A", "minimal");
Logger.info(" \u4F7F\u7528 -i \u6307\u5B9A\u6587\u672C\u6216\u6587\u4EF6\u8DEF\u5F84", "normal");
Logger.info(" \u4F7F\u7528 -j \u6307\u5B9AJSON\u6587\u4EF6\u8DEF\u5F84", "normal");
Logger.info(" \u4F7F\u7528 --batch \u6279\u91CF\u7FFB\u8BD1\u8BED\u8A00\u6587\u4EF6", "normal");
Logger.info(' \u4F7F\u7528 --test -i "\u6587\u672C" \u6D4B\u8BD5\u7FFB\u8BD1', "normal");
Logger.info(" \u4F7F\u7528 --incremental=false \u7981\u7528\u589E\u91CF\u7FFB\u8BD1\u6A21\u5F0F", "normal");
}
var init_cli = __esm({
"src/translation/cli.ts"() {
"use strict";
init_config();
init_manager();
init_fs();
init_logger();
}
});
// src/cli.ts
init_config();
import { Command } from "commander";
// src/ast/index.ts
init_fs();
init_config();
import { parse as parse2 } from "@babel/parser";
import * as t2 from "@babel/types";
// src/gen-key-value.ts
init_fs();
init_config();
init_logger();
import { pinyin } from "pinyin-pro";
import * as path2 from "path";
import stringHash from "string-hash";
var keyValueCache = {};
var valueKeyCache = {};
var outputFilePath = "";
async function initI18nCache() {
var _a;
const config = ConfigManager.get();
const outputDir = config.outputDir ?? "locales";
await ensureDir2(outputDir);
const localeFileName = ((_a = config.output) == null ? void 0 : _a.localeFileName) ?? "{locale}.json";
const fileName = localeFileName.replace("{locale}", config.locale ?? "zh-CN");
outputFilePath = path2.join(outputDir, fileName);
keyValueCache = await readJson(outputFilePath, {});
valueKeyCache = {};
for (const [key, value] of Object.entries(keyValueCache)) {
valueKeyCache[value] = key;
}
const existingKeyCount = Object.keys(keyValueCache).length;
if (existingKeyCount > 0) {
Logger.verbose(`\u52A0\u8F7D\u4E86 ${existingKeyCount} \u4E2A\u5DF2\u5B58\u5728\u7684\u7FFB\u8BD1\u952E`);
}
await writeJson(outputFilePath, keyValueCache);
}
function toShortHash(text) {
var _a;
const config = ConfigManager.get();
const hashLength = ((_a = config.keyGeneration) == null ? void 0 : _a.hashLength) ?? 6;
return stringHash(text).toString(36).slice(0, hashLength);
}
function generateUniqueKey(baseKey, text) {
var _a, _b;
const config = ConfigManager.get();
const separator = ((_a = config.keyGeneration) == null ? void 0 : _a.separator) ?? "_";
const maxRetry = ((_b = config.keyGeneration) == null ? void 0 : _b.maxRetryCount) ?? 5;
if (!keyValueCache[baseKey]) {
return baseKey;
}
let finalKey = baseKey + separator + toShortHash(text);
let tryCount = 0;
while (keyValueCache[finalKey] && tryCount < maxRetry) {
finalKey = baseKey + separator + toShortHash(text + Math.random().toString());
tryCount++;
}
if (tryCount >= maxRetry) {
Logger.warn(`\u751F\u6210\u552F\u4E00key\u5931\u8D25\uFF0C\u8FBE\u5230\u6700\u5927\u91CD\u8BD5\u6B21\u6570: ${baseKey}`);
finalKey = baseKey + separator + Date.now().toString(36);
}
return finalKey;
}
function createI18nKey(text) {
var _a, _b, _c, _d, _e, _f;
const config = ConfigManager.get();
const reuseExistingKey = ((_a = config.keyGeneration) == null ? void 0 : _a.reuseExistingKey) ?? true;
if (reuseExistingKey && valueKeyCache[text]) {
Logger.verbose(`\u91CD\u590D\u4F7F\u7528\u5DF2\u5B58\u5728\u6587\u6848\u7684key: "${valueKeyCache[text]}" for "${text}"`);
return valueKeyCache[text];
}
const maxLength = ((_b = config.keyGeneration) == null ? void 0 : _b.maxChineseLength) ?? 10;
const han = (((_c = text.match(/[\u4e00-\u9fa5]+/g)) == null ? void 0 : _c.join("")) ?? "").slice(0, maxLength);
if (!han) {
return "";
}
const pinyinOptions = ((_d = config.keyGeneration) == null ? void 0 : _d.pinyinOptions) ?? { toneType: "none", type: "array" };
const separator = ((_e = config.keyGeneration) == null ? void 0 : _e.separator) ?? "_";
const keyPrefix = ((_f = config.keyGeneration) == null ? void 0 : _f.keyPrefix) ?? "";
const baseKey = (pinyin(han, {
toneType: pinyinOptions.toneType ?? "none",
type: "array"
}) ?? []).join(separator);
if (!baseKey) {
return "";
}
const keyWithPrefix = keyPrefix ? `${keyPrefix}${separator}${baseKey}` : baseKey;
const finalKey = generateUniqueKey(keyWithPrefix, text);
keyValueCache[finalKey] = text;
valueKeyCache[text] = finalKey;
return finalKey;
}
async function flushI18nCache() {
var _a;
if (outputFilePath) {
const config = ConfigManager.get();
const prettyJson = ((_a = config.output) == null ? void 0 : _a.prettyJson) ?? true;
await writeJson(outputFilePath, keyValueCache, prettyJson);
const keyCount = Object.keys(keyValueCache).length;
Logger.info(`\u5DF2\u4FDD\u5B58 ${keyCount} \u4E2A\u7FFB\u8BD1\u952E\u5230 ${outputFilePath}`, "normal");
}
}
// src/ast/index.ts
init_logger();
// src/utils/import-manager.ts
init_logger();
import { parse } from "@babel/parser";
import * as t from "@babel/types";
// src/utils/pattern.ts
init_logger();
import micromatch from "micromatch";
function matchPattern(pattern, filePath) {
try {
return micromatch.isMatch(filePath, pattern);
} catch (error) {
Logger.warn(`\u6A21\u5F0F\u5339\u914D\u51FA\u9519 - pattern: ${pattern}, filePath: ${filePath}, error: ${error}`);
return false;
}
}
function findMatchingImport(filePath, imports = {}) {
var _a;
Logger.verbose(`findMatchingImport: ${filePath}`);
const patterns = Object.keys(imports).sort((a, b) => {
const scoreA = getPatternSpecificity(a);
const scoreB = getPatternSpecificity(b);
return scoreB - scoreA;
});
for (const pattern of patterns) {
if (matchPattern(pattern, filePath)) {
Logger.verbose(`\u5339\u914D\u5230\u6A21\u5F0F: ${pattern} -> ${filePath}`);
return ((_a = imports[pattern]) == null ? void 0 : _a.importStatement) || null;
}
}
Logger.verbose(`\u672A\u627E\u5230\u5339\u914D\u7684import\u914D\u7F6E: ${filePath}`);
return null;
}
function getPatternSpecificity(pattern) {
var _a, _b, _c;
let score = 0;
if (!pattern.includes("*")) {
score += 100;
}
if (pattern.includes(".{") || pattern.match(/\.\w+$/)) {
score += 50;
}
score += (((_a = pattern.match(/\//g)) == null ? void 0 : _a.length) || 0) * 10;
score -= (((_b = pattern.match(/\*\*/g)) == null ? void 0 : _b.length) || 0) * 20;
score -= (((_c = pattern.match(/(?<!\*)\*(?!\*)/g)) == null ? void 0 : _c.length) || 0) * 5;
return score;
}
// src/utils/import-manager.ts
var generate = __require("@babel/generator").default;
function hasExistingImport(ast, importStatement) {
var _a;
try {
const importAst = parse(importStatement, {
sourceType: "module",
plugins: ["jsx", "typescript"]
});
if (!((_a = importAst.program) == null ? void 0 : _a.body) || importAst.program.body.length === 0) {
return false;
}
const newStatements = importAst.program.body;
const existingStatements = ast.program.body;
for (const newStmt of newStatements) {
let found = false;
for (const existingStmt of existingStatements) {
const newCode = generate(newStmt, { minified: true }).code;
const existingCode = generate(existingStmt, { minified: true }).code;
if (newCode === existingCode) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
} catch (error) {
Logger.verbose(`\u68C0\u67E5import\u91CD\u590D\u65F6\u51FA\u9519: ${error}`);
return false;
}
}
function addImportToAST(ast, importStatement, insertPosition = "afterImports") {
var _a;
try {
if (hasExistingImport(ast, importStatement)) {
Logger.verbose("Import\u8BED\u53E5\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u63D2\u5165");
return;
}
const importAst = parse(importStatement, {
sourceType: "module",
plugins: ["jsx", "typescript"]
});
if (((_a = importAst.program) == null ? void 0 : _a.body) && importAst.program.body.length > 0) {
let insertIndex = 0;
if (insertPosition === "afterImports") {
for (let i = 0; i < ast.program.body.length; i++) {
if (t.isImportDeclaration(ast.program.body[i])) {
insertIndex = i + 1;
} else {
break;
}
}
} else {
insertIndex = 0;
}
ast.program.body.splice(insertIndex, 0, ...importAst.program.body);
Logger.verbose(`Import\u8BED\u53E5\u5DF2\u63D2\u5165\u5230\u4F4D\u7F6E: ${insertPosition}`);
}
} catch (error) {
Logger.warn(`\u65E0\u6CD5\u89E3\u6790import\u8BED\u53E5: ${importStatement}`);
Logger.verbose(`\u9519\u8BEF\u8BE6\u60C5: ${error}`);
}
}
function addEmpty