@ai-i18n/eslint-plugin 1.0.0-alpha.7 → 1.0.0-alpha.9
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 +53 -7
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +246 -84
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -31,6 +31,28 @@ export default [
|
|
|
31
31
|
只有 `*-auto-import` preset 会声明 Runtime 全局。所有 preset 都声明只读的
|
|
32
32
|
`defineI18nMessages` 编译宏。
|
|
33
33
|
|
|
34
|
+
希望在自动导入模式中禁止残留的同名显式 import 时,可以按需启用:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
export default [
|
|
38
|
+
...aiI18n.configs['vue-auto-import'],
|
|
39
|
+
{
|
|
40
|
+
rules: {
|
|
41
|
+
'ai-i18n/no-redundant-auto-import': [
|
|
42
|
+
'warn',
|
|
43
|
+
{ autoImport: ['t', 'tRef', 'useI18n'] },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
React 使用 `['t', 'useI18n']`;Vanilla 使用
|
|
51
|
+
`['t', 'setLang', 'getLang', 'getLangs', 'getLangLoadState', 'subscribe']`。该规则不在
|
|
52
|
+
preset 中默认启用。它只检查来自 `virtual:ai-i18n` 的未改名值导入,保留改名导入、
|
|
53
|
+
namespace import、type import 和当前模式不会自动注入的 API,并支持 `eslint --fix`。
|
|
54
|
+
import 内部有注释时只报告,不自动修改。
|
|
55
|
+
|
|
34
56
|
## Vue SFC
|
|
35
57
|
|
|
36
58
|
`.vue` 是可选文件格式。Vue 项目应先通过 `eslint-plugin-vue` 配置
|
|
@@ -97,22 +119,42 @@ function SaveButton() {
|
|
|
97
119
|
跨文件或 `useMemo` / `useState` 等任意数据流,因此规则提示的是可以确定的常见问题,
|
|
98
120
|
不代表覆盖所有译文生命周期错误。两条规则都不提供自动修复。
|
|
99
121
|
|
|
100
|
-
|
|
122
|
+
五条规则可独立启用。四条静态分析规则无法启动时,同一文件只报告一次双语错误;官方 preset 由
|
|
101
123
|
`t-static-args` 优先报告,避免次级规则重复提示。
|
|
102
124
|
|
|
103
125
|
规则与 Vite 共用静态参数语义,包括从 `useI18n()` 获得的对象成员调用
|
|
104
126
|
`i18n.t()`、`i18n['t']()`、省略式 `t('source', undefined)` 和 tagged template。
|
|
127
|
+
整棵可静态求值的纯文案对象或数组可以直接传给 `t()` 或 Vue `tRef()`,不要求
|
|
128
|
+
`defineI18nMessages()` 或 `as const`;规则会按去重后的字符串叶子计算静态候选数。
|
|
105
129
|
Vue 模板必须在 `<script setup>` 中绑定 `useI18n()` 返回的 `t`;自动导入只省略 import,
|
|
106
130
|
不会自动合成 Hook。`vue-auto-import` 会把裸 template-only `t()` 作为 error;模板中已
|
|
107
131
|
绑定到 Runtime 顶层 `t` 的调用则由 `no-unsubscribed-t` warning。
|
|
108
132
|
在 template 或 JSX/TSX 渲染期间调用 `tRef()` 会重复创建 `computed`,同一规则会提示在
|
|
109
133
|
Vue setup 中只创建一次并使用返回的 Ref。
|
|
110
134
|
|
|
111
|
-
|
|
135
|
+
对象或数组的成员级引用只有在根集合由 `defineI18nMessages()` 标记后才属于推荐写法。
|
|
136
|
+
动态生成的树、非普通对象以及带第二参数的整树调用会报错。字符串拼接、
|
|
112
137
|
逻辑表达式、`let` 文案、普通集合成员、`const tr = t`、命名空间调用、二次 Hook 解构、
|
|
113
138
|
`useI18n().t()` 与 `require()` 都会报错。
|
|
114
139
|
|
|
115
|
-
|
|
140
|
+
## tsconfig 自动发现与路径别名
|
|
141
|
+
|
|
142
|
+
静态分析规则默认从当前文件向上寻找最近的 `tsconfig.json`,解析 `extends`,递归读取
|
|
143
|
+
`references`,再按 importer 是否满足各项目的 `files`、`include`、`exclude` 选择实际
|
|
144
|
+
配置。因此常见的 `@/*` TypeScript path alias 不需要额外选项。Vue 文件必须由项目显式
|
|
145
|
+
包含,例如 `include: ['src/**/*.ts', 'src/**/*.vue']`。
|
|
146
|
+
|
|
147
|
+
`tsconfigPath` 是自动发现入口的可选覆盖项,适用于非标准配置名或希望固定从某个 solution
|
|
148
|
+
config 开始解析的项目。相对路径按 ESLint 进程的工作目录解析;指向带 `references` 的根
|
|
149
|
+
配置后,仍会执行相同的递归与项目选择逻辑。
|
|
150
|
+
|
|
151
|
+
TypeScript 6 的编译器仍执行已有的 `baseUrl` 解析,但会报告弃用诊断;TypeScript 7 将
|
|
152
|
+
不再支持该选项。插件兼容 TypeScript 5/6 的 `baseUrl + paths`;新项目推荐省略
|
|
153
|
+
`baseUrl`,写成 `"paths": { "@/*": ["./src/*"] }`。若旧项目还依赖 `baseUrl` 的未匹配
|
|
154
|
+
bare import 查找,则用 `"*": ["./src/*"]` 显式保留。只存在于 Vite `resolve.alias`、
|
|
155
|
+
未同步到 tsconfig `paths` 的 alias 不属于 ESLint 的 TypeScript 配置。
|
|
156
|
+
|
|
157
|
+
需要覆盖自动发现入口时,可以显式配置规则:
|
|
116
158
|
|
|
117
159
|
```js
|
|
118
160
|
import aiI18n from '@ai-i18n/eslint-plugin';
|
|
@@ -133,21 +175,21 @@ export default [
|
|
|
133
175
|
'warn',
|
|
134
176
|
{
|
|
135
177
|
autoImport: ['t', 'tRef', 'useI18n'],
|
|
136
|
-
tsconfigPath: './tsconfig.json',
|
|
178
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
137
179
|
},
|
|
138
180
|
],
|
|
139
181
|
'ai-i18n/no-unsubscribed-t': [
|
|
140
182
|
'warn',
|
|
141
183
|
{
|
|
142
184
|
autoImport: ['t', 'tRef', 'useI18n'],
|
|
143
|
-
tsconfigPath: './tsconfig.json',
|
|
185
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
144
186
|
},
|
|
145
187
|
],
|
|
146
188
|
'ai-i18n/static-candidate-limit': [
|
|
147
189
|
'warn',
|
|
148
190
|
{
|
|
149
191
|
autoImport: ['t', 'tRef', 'useI18n'],
|
|
150
|
-
tsconfigPath: './tsconfig.json',
|
|
192
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
151
193
|
maxStaticCandidates: 2_000,
|
|
152
194
|
},
|
|
153
195
|
],
|
|
@@ -155,9 +197,13 @@ export default [
|
|
|
155
197
|
'error',
|
|
156
198
|
{
|
|
157
199
|
autoImport: ['t', 'tRef', 'useI18n'],
|
|
158
|
-
tsconfigPath: './tsconfig.json',
|
|
200
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
159
201
|
},
|
|
160
202
|
],
|
|
203
|
+
'ai-i18n/no-redundant-auto-import': [
|
|
204
|
+
'warn',
|
|
205
|
+
{ autoImport: ['t', 'tRef', 'useI18n'] },
|
|
206
|
+
],
|
|
161
207
|
},
|
|
162
208
|
},
|
|
163
209
|
];
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { ESLint, Rule } from "eslint";
|
|
|
2
2
|
//#region src/rules/no-eager-translation.d.ts
|
|
3
3
|
declare const noEagerTranslation: Rule.RuleModule;
|
|
4
4
|
//#endregion
|
|
5
|
+
//#region src/rules/no-redundant-auto-import.d.ts
|
|
6
|
+
declare const noRedundantAutoImport: Rule.RuleModule;
|
|
7
|
+
//#endregion
|
|
5
8
|
//#region src/rules/no-unsubscribed-t.d.ts
|
|
6
9
|
declare const noUnsubscribedT: Rule.RuleModule;
|
|
7
10
|
//#endregion
|
|
@@ -14,5 +17,5 @@ declare const tStaticArgs: Rule.RuleModule;
|
|
|
14
17
|
//#region src/index.d.ts
|
|
15
18
|
declare const plugin: ESLint.Plugin;
|
|
16
19
|
//#endregion
|
|
17
|
-
export { plugin as default, noEagerTranslation, noUnsubscribedT, staticCandidateLimit, tStaticArgs };
|
|
20
|
+
export { plugin as default, noEagerTranslation, noRedundantAutoImport, noUnsubscribedT, staticCandidateLimit, tStaticArgs };
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/rules/no-eager-translation.ts","../src/rules/no-unsubscribed-t.ts","../src/rules/static-candidate-limit.ts","../src/rules/t-static-args.ts","../src/index.ts"],"mappings":";;cAea,oBAAoB,KAAK;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/rules/no-eager-translation.ts","../src/rules/no-redundant-auto-import.ts","../src/rules/no-unsubscribed-t.ts","../src/rules/static-candidate-limit.ts","../src/rules/t-static-args.ts","../src/index.ts"],"mappings":";;cAea,oBAAoB,KAAK;;;cCiBzB,uBAAuB,KAAK;;;cCV5B,iBAAiB,KAAK;;;cCTtB,sBAAsB,KAAK;;;cC6C3B,aAAa,KAAK;;;cC9CzB,QAAQ,OAAO"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,168 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { AI_I18N_VIRTUAL_MODULE_ID, Analyzer, diagnosticMessage, extractMessages, findInvalidDefineI18nMessagesReferences, findTranslationCalls, findUnboundCalls, validateRecommendedUsage } from "@ai-i18n/analyzer";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { createFilesMatcher, createPathsMatcher, parseTsconfig } from "get-tsconfig";
|
|
6
|
+
import picomatch from "picomatch";
|
|
5
7
|
import { analyzeVueSource } from "@ai-i18n/analyzer/vue";
|
|
8
|
+
//#region src/tsconfig-projects.ts
|
|
9
|
+
const PROJECT_CACHE_TTL_MS = 250;
|
|
10
|
+
const MAX_PROJECT_CACHE_ENTRIES = 1e3;
|
|
11
|
+
const READ_FILE_CACHE_PREFIX = "readFileSync:";
|
|
12
|
+
const READ_FILE_CACHE_SUFFIX = ":utf8";
|
|
13
|
+
const projectGraphCache = /* @__PURE__ */ new Map();
|
|
14
|
+
const discoveryCache = /* @__PURE__ */ new Map();
|
|
15
|
+
function createTsconfigResolver(tsconfigPath) {
|
|
16
|
+
const explicitRoot = tsconfigPath ? path.normalize(path.resolve(tsconfigPath)) : void 0;
|
|
17
|
+
return (specifier, importer) => {
|
|
18
|
+
const rootConfig = explicitRoot ?? findNearestTsconfig(importer);
|
|
19
|
+
if (!rootConfig) return [];
|
|
20
|
+
return selectProject(loadProjectGraph(rootConfig), importer)?.pathsMatcher?.(specifier) ?? [];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function findNearestTsconfig(importer) {
|
|
24
|
+
let directory = path.dirname(path.resolve(importer));
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const cached = discoveryCache.get(directory);
|
|
27
|
+
if (cached && cached.expiresAt > now) return cached.tsconfigPath;
|
|
28
|
+
const visited = [];
|
|
29
|
+
for (;;) {
|
|
30
|
+
visited.push(directory);
|
|
31
|
+
const candidate = path.join(directory, "tsconfig.json");
|
|
32
|
+
if (isFile$1(candidate)) {
|
|
33
|
+
cacheDiscovery(visited, candidate, now);
|
|
34
|
+
return candidate;
|
|
35
|
+
}
|
|
36
|
+
const parent = path.dirname(directory);
|
|
37
|
+
if (parent === directory) {
|
|
38
|
+
cacheDiscovery(visited, null, now);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
directory = parent;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function cacheDiscovery(directories, tsconfigPath, now) {
|
|
45
|
+
if (discoveryCache.size >= MAX_PROJECT_CACHE_ENTRIES) discoveryCache.clear();
|
|
46
|
+
for (const directory of directories) discoveryCache.set(directory, {
|
|
47
|
+
expiresAt: now + PROJECT_CACHE_TTL_MS,
|
|
48
|
+
tsconfigPath
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function loadProjectGraph(rootConfig) {
|
|
52
|
+
const cached = projectGraphCache.get(rootConfig);
|
|
53
|
+
if (cached && projectGraphIsFresh(cached)) return cached.projects;
|
|
54
|
+
const projects = [];
|
|
55
|
+
const stamps = /* @__PURE__ */ new Map();
|
|
56
|
+
visitProject(rootConfig, projects, stamps, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set());
|
|
57
|
+
if (projectGraphCache.size >= MAX_PROJECT_CACHE_ENTRIES) projectGraphCache.clear();
|
|
58
|
+
projectGraphCache.set(rootConfig, {
|
|
59
|
+
expiresAt: Date.now() + PROJECT_CACHE_TTL_MS,
|
|
60
|
+
projects,
|
|
61
|
+
stamps
|
|
62
|
+
});
|
|
63
|
+
return projects;
|
|
64
|
+
}
|
|
65
|
+
function visitProject(filename, projects, stamps, parsingCache, visited) {
|
|
66
|
+
filename = path.normalize(path.resolve(filename));
|
|
67
|
+
if (visited.has(filename)) return;
|
|
68
|
+
visited.add(filename);
|
|
69
|
+
stamps.set(filename, readFileStamp(filename));
|
|
70
|
+
let config;
|
|
71
|
+
try {
|
|
72
|
+
config = {
|
|
73
|
+
path: filename,
|
|
74
|
+
config: parseTsconfig(filename, parsingCache)
|
|
75
|
+
};
|
|
76
|
+
captureParsedConfigStamps(parsingCache, stamps);
|
|
77
|
+
} catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
for (const reference of config.config.references ?? []) {
|
|
81
|
+
const referenced = resolveReference(filename, reference.path);
|
|
82
|
+
if (referenced) visitProject(referenced, projects, stamps, parsingCache, visited);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
projects.push({
|
|
86
|
+
config,
|
|
87
|
+
filesMatcher: createFilesMatcher(config),
|
|
88
|
+
pathsMatcher: createCompatiblePathsMatcher(config)
|
|
89
|
+
});
|
|
90
|
+
} catch {}
|
|
91
|
+
}
|
|
92
|
+
function captureParsedConfigStamps(parsingCache, stamps) {
|
|
93
|
+
for (const key of parsingCache.keys()) {
|
|
94
|
+
if (!key.startsWith(READ_FILE_CACHE_PREFIX) || !key.endsWith(READ_FILE_CACHE_SUFFIX)) continue;
|
|
95
|
+
const filename = key.slice(13, -5);
|
|
96
|
+
stamps.set(filename, readFileStamp(filename));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function createCompatiblePathsMatcher(config) {
|
|
100
|
+
const compilerOptions = config.config.compilerOptions;
|
|
101
|
+
const paths = compilerOptions?.paths;
|
|
102
|
+
if (!paths || compilerOptions.baseUrl) return createPathsMatcher(config);
|
|
103
|
+
const compatiblePaths = Object.fromEntries(Object.entries(paths).map(([pattern, targets]) => [pattern, targets.map((target) => path.isAbsolute(target) || target.startsWith(".") ? target : `./${target}`)]));
|
|
104
|
+
return createPathsMatcher({
|
|
105
|
+
path: config.path,
|
|
106
|
+
config: {
|
|
107
|
+
...config.config,
|
|
108
|
+
compilerOptions: {
|
|
109
|
+
...compilerOptions,
|
|
110
|
+
paths: compatiblePaths
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function resolveReference(ownerConfig, referencePath) {
|
|
116
|
+
const candidate = path.resolve(path.dirname(ownerConfig), referencePath);
|
|
117
|
+
return [
|
|
118
|
+
candidate,
|
|
119
|
+
candidate.endsWith(".json") ? candidate : `${candidate}.json`,
|
|
120
|
+
path.join(candidate, "tsconfig.json")
|
|
121
|
+
].find(isFile$1) ?? null;
|
|
122
|
+
}
|
|
123
|
+
function selectProject(projects, importer) {
|
|
124
|
+
const absoluteImporter = path.resolve(importer);
|
|
125
|
+
return projects.find((project) => projectMatchesImporter(project, absoluteImporter));
|
|
126
|
+
}
|
|
127
|
+
function projectMatchesImporter(project, importer) {
|
|
128
|
+
if (path.extname(importer) !== ".vue") return Boolean(project.filesMatcher(importer));
|
|
129
|
+
return matchesExplicitVueInput(project.config, importer);
|
|
130
|
+
}
|
|
131
|
+
function matchesExplicitVueInput({ config, path: configPath }, importer) {
|
|
132
|
+
const directory = path.dirname(configPath);
|
|
133
|
+
const relative = normalizePath(path.relative(directory, importer));
|
|
134
|
+
if (relative.startsWith("../")) return false;
|
|
135
|
+
if (config.files?.some((file) => path.resolve(directory, file) === path.resolve(importer))) return true;
|
|
136
|
+
if (config.exclude?.some((pattern) => matchesExclude(relative, pattern))) return false;
|
|
137
|
+
return Boolean(config.include?.some((pattern) => pattern.includes(".vue") && picomatch.isMatch(relative, normalizePath(pattern), { dot: true })));
|
|
138
|
+
}
|
|
139
|
+
function matchesExclude(relative, pattern) {
|
|
140
|
+
pattern = normalizePath(pattern).replace(/\/+$/, "");
|
|
141
|
+
return picomatch.isMatch(relative, pattern, { dot: true }) || picomatch.isMatch(relative, `${pattern}/**`, { dot: true });
|
|
142
|
+
}
|
|
143
|
+
function projectGraphIsFresh(cached) {
|
|
144
|
+
if (cached.expiresAt <= Date.now()) return false;
|
|
145
|
+
for (const [filename, stamp] of cached.stamps) if (readFileStamp(filename) !== stamp) return false;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
function readFileStamp(filename) {
|
|
149
|
+
try {
|
|
150
|
+
const stat = fs.statSync(filename);
|
|
151
|
+
return stat.isFile() ? `${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}` : null;
|
|
152
|
+
} catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function isFile$1(filename) {
|
|
157
|
+
try {
|
|
158
|
+
return fs.statSync(filename).isFile();
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function normalizePath(filename) {
|
|
164
|
+
return filename.replaceAll("\\", "/");
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
6
167
|
//#region src/resolve-import.ts
|
|
7
168
|
const SOURCE_EXTENSIONS = [
|
|
8
169
|
"",
|
|
@@ -17,36 +178,16 @@ const SOURCE_EXTENSIONS = [
|
|
|
17
178
|
];
|
|
18
179
|
const DIRECTORY_STAMP_TTL_MS = 250;
|
|
19
180
|
const MAX_PROBE_CACHE_ENTRIES = 1e4;
|
|
20
|
-
const tsconfigCache = /* @__PURE__ */ new Map();
|
|
21
181
|
const directoryStampCache = /* @__PURE__ */ new Map();
|
|
22
182
|
const probeCache = /* @__PURE__ */ new Map();
|
|
23
183
|
function createImportResolver(tsconfigPath) {
|
|
24
|
-
const
|
|
25
|
-
let baseUrl = process.cwd();
|
|
26
|
-
if (tsconfigPath) {
|
|
27
|
-
const absoluteConfig = path.resolve(tsconfigPath);
|
|
28
|
-
const config = readTsConfig(absoluteConfig);
|
|
29
|
-
baseUrl = path.resolve(path.dirname(absoluteConfig), config.compilerOptions?.baseUrl ?? ".");
|
|
30
|
-
for (const [pattern, targets] of Object.entries(config.compilerOptions?.paths ?? {})) {
|
|
31
|
-
const star = pattern.indexOf("*");
|
|
32
|
-
aliases.push({
|
|
33
|
-
prefix: star < 0 ? pattern : pattern.slice(0, star),
|
|
34
|
-
suffix: star < 0 ? "" : pattern.slice(star + 1),
|
|
35
|
-
targets
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
184
|
+
const resolveTsconfig = createTsconfigResolver(tsconfigPath);
|
|
39
185
|
return (specifier, importer) => {
|
|
40
186
|
if (specifier === "virtual:ai-i18n") return specifier;
|
|
41
187
|
if (specifier.startsWith(".")) return probeSource(path.resolve(path.dirname(importer), specifier));
|
|
42
|
-
for (const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (const target of alias.targets) {
|
|
46
|
-
const candidate = target.includes("*") ? target.replace("*", value) : target;
|
|
47
|
-
const resolved = probeSource(path.resolve(baseUrl, candidate));
|
|
48
|
-
if (resolved) return resolved;
|
|
49
|
-
}
|
|
188
|
+
for (const candidate of resolveTsconfig(specifier, importer)) {
|
|
189
|
+
const resolved = probeSource(candidate);
|
|
190
|
+
if (resolved) return resolved;
|
|
50
191
|
}
|
|
51
192
|
return null;
|
|
52
193
|
};
|
|
@@ -85,28 +226,6 @@ function isFile(file) {
|
|
|
85
226
|
return false;
|
|
86
227
|
}
|
|
87
228
|
}
|
|
88
|
-
function readTsConfig(filename) {
|
|
89
|
-
const stamp = readFileStamp(filename);
|
|
90
|
-
const cached = tsconfigCache.get(filename);
|
|
91
|
-
if (cached?.stamp === stamp) return cached.value;
|
|
92
|
-
let value = {};
|
|
93
|
-
try {
|
|
94
|
-
value = JSON.parse(stripJsonComments(fs.readFileSync(filename, "utf8")));
|
|
95
|
-
} catch {}
|
|
96
|
-
tsconfigCache.set(filename, {
|
|
97
|
-
stamp,
|
|
98
|
-
value
|
|
99
|
-
});
|
|
100
|
-
return value;
|
|
101
|
-
}
|
|
102
|
-
function readFileStamp(filename) {
|
|
103
|
-
try {
|
|
104
|
-
const stat = fs.statSync(filename);
|
|
105
|
-
return stat.isFile() ? `${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}` : null;
|
|
106
|
-
} catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
229
|
function readDirectoryStamp(directory) {
|
|
111
230
|
const now = Date.now();
|
|
112
231
|
const cached = directoryStampCache.get(directory);
|
|
@@ -123,43 +242,6 @@ function readDirectoryStamp(directory) {
|
|
|
123
242
|
});
|
|
124
243
|
return value;
|
|
125
244
|
}
|
|
126
|
-
function stripJsonComments(source) {
|
|
127
|
-
let result = "";
|
|
128
|
-
let quote = "";
|
|
129
|
-
let escaped = false;
|
|
130
|
-
for (let index = 0; index < source.length; index += 1) {
|
|
131
|
-
const current = source[index];
|
|
132
|
-
const next = source[index + 1];
|
|
133
|
-
if (quote) {
|
|
134
|
-
result += current;
|
|
135
|
-
if (escaped) escaped = false;
|
|
136
|
-
else if (current === "\\") escaped = true;
|
|
137
|
-
else if (current === quote) quote = "";
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if (current === "\"" || current === "'") {
|
|
141
|
-
quote = current;
|
|
142
|
-
result += current;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (current === "/" && next === "/") {
|
|
146
|
-
while (index < source.length && source[index] !== "\n") index += 1;
|
|
147
|
-
result += "\n";
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
if (current === "/" && next === "*") {
|
|
151
|
-
index += 2;
|
|
152
|
-
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) {
|
|
153
|
-
if (source[index] === "\n") result += "\n";
|
|
154
|
-
index += 1;
|
|
155
|
-
}
|
|
156
|
-
index += 1;
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
result += current;
|
|
160
|
-
}
|
|
161
|
-
return result.replace(/,\s*([}\]])/g, "$1");
|
|
162
|
-
}
|
|
163
245
|
//#endregion
|
|
164
246
|
//#region src/analyze.ts
|
|
165
247
|
const POTENTIAL_TRANSLATION_RE = /virtual:ai-i18n|\b(?:t|tRef|useI18n|defineI18nMessages)\b/;
|
|
@@ -502,6 +584,85 @@ function storesTranslationResult(parent, child) {
|
|
|
502
584
|
}
|
|
503
585
|
}
|
|
504
586
|
//#endregion
|
|
587
|
+
//#region src/rules/no-redundant-auto-import.ts
|
|
588
|
+
const RUNTIME_MODULE = "virtual:ai-i18n";
|
|
589
|
+
const noRedundantAutoImport = {
|
|
590
|
+
meta: {
|
|
591
|
+
type: "suggestion",
|
|
592
|
+
docs: { description: "禁止显式导入已由 ai-i18n autoImport 注入的 Runtime API" },
|
|
593
|
+
fixable: "code",
|
|
594
|
+
schema: [{
|
|
595
|
+
type: "object",
|
|
596
|
+
properties: { autoImport: {
|
|
597
|
+
type: "array",
|
|
598
|
+
items: { enum: [
|
|
599
|
+
"t",
|
|
600
|
+
"tRef",
|
|
601
|
+
"useI18n",
|
|
602
|
+
"setLang",
|
|
603
|
+
"getLang",
|
|
604
|
+
"getLangs",
|
|
605
|
+
"getLangLoadState",
|
|
606
|
+
"subscribe"
|
|
607
|
+
] },
|
|
608
|
+
minItems: 1,
|
|
609
|
+
uniqueItems: true
|
|
610
|
+
} },
|
|
611
|
+
required: ["autoImport"],
|
|
612
|
+
additionalProperties: false
|
|
613
|
+
}],
|
|
614
|
+
messages: { redundantImport: diagnosticMessage("ai-i18n autoImport 已注入以下 API:{{names}}。请删除来自 virtual:ai-i18n 的冗余导入。", "ai-i18n autoImport injects these APIs: {{names}}. Remove the redundant imports from virtual:ai-i18n.") }
|
|
615
|
+
},
|
|
616
|
+
create(context) {
|
|
617
|
+
const enabled = new Set(context.options[0]?.autoImport ?? []);
|
|
618
|
+
return { ImportDeclaration(rawNode) {
|
|
619
|
+
const node = rawNode;
|
|
620
|
+
if (node.importKind === "type" || node.source.value !== RUNTIME_MODULE) return;
|
|
621
|
+
const named = node.specifiers.filter((specifier) => specifier.type === "ImportSpecifier");
|
|
622
|
+
const redundant = named.filter((specifier) => {
|
|
623
|
+
const imported = importedName(specifier);
|
|
624
|
+
return specifier.importKind !== "type" && imported !== null && imported === specifier.local.name && enabled.has(imported);
|
|
625
|
+
});
|
|
626
|
+
if (!redundant.length) return;
|
|
627
|
+
const names = redundant.map((specifier) => importedName(specifier)).filter((name) => name !== null);
|
|
628
|
+
const fix = createFix(context, node, named, redundant);
|
|
629
|
+
context.report({
|
|
630
|
+
node,
|
|
631
|
+
messageId: "redundantImport",
|
|
632
|
+
data: { names: names.join(", ") },
|
|
633
|
+
fix
|
|
634
|
+
});
|
|
635
|
+
} };
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
function importedName(specifier) {
|
|
639
|
+
if (specifier.imported.type === "Identifier" && "name" in specifier.imported) return specifier.imported.name;
|
|
640
|
+
return typeof specifier.imported.value === "string" ? specifier.imported.value : null;
|
|
641
|
+
}
|
|
642
|
+
function createFix(context, node, named, redundant) {
|
|
643
|
+
if (context.sourceCode.getCommentsInside(node).length) return void 0;
|
|
644
|
+
const redundantSet = new Set(redundant);
|
|
645
|
+
if (!node.specifiers.filter((specifier) => !redundantSet.has(specifier)).length) return (fixer) => fixer.remove(node);
|
|
646
|
+
const remainingNamed = named.filter((specifier) => !redundantSet.has(specifier));
|
|
647
|
+
const firstNamed = named[0];
|
|
648
|
+
const lastNamed = named.at(-1);
|
|
649
|
+
if (!firstNamed || !lastNamed) return void 0;
|
|
650
|
+
const openBrace = context.sourceCode.getTokenBefore(firstNamed);
|
|
651
|
+
const closeBrace = context.sourceCode.getTokenAfter(lastNamed);
|
|
652
|
+
if (openBrace?.value !== "{" || closeBrace?.value !== "}") return void 0;
|
|
653
|
+
if (remainingNamed.length) {
|
|
654
|
+
const [, openEnd] = context.sourceCode.getRange(openBrace);
|
|
655
|
+
const [closeStart] = context.sourceCode.getRange(closeBrace);
|
|
656
|
+
const replacement = remainingNamed.map((specifier) => context.sourceCode.getText(specifier)).join(", ");
|
|
657
|
+
return (fixer) => fixer.replaceTextRange([openEnd, closeStart], ` ${replacement} `);
|
|
658
|
+
}
|
|
659
|
+
const beforeOpen = context.sourceCode.getTokenBefore(openBrace);
|
|
660
|
+
if (beforeOpen?.value !== ",") return void 0;
|
|
661
|
+
const [commaStart] = context.sourceCode.getRange(beforeOpen);
|
|
662
|
+
const [, closeEnd] = context.sourceCode.getRange(closeBrace);
|
|
663
|
+
return (fixer) => fixer.removeRange([commaStart, closeEnd]);
|
|
664
|
+
}
|
|
665
|
+
//#endregion
|
|
505
666
|
//#region src/rules/no-unsubscribed-t.ts
|
|
506
667
|
const FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
507
668
|
"ArrowFunctionExpression",
|
|
@@ -834,6 +995,7 @@ const plugin = {
|
|
|
834
995
|
},
|
|
835
996
|
rules: {
|
|
836
997
|
"no-eager-translation": noEagerTranslation,
|
|
998
|
+
"no-redundant-auto-import": noRedundantAutoImport,
|
|
837
999
|
"no-unsubscribed-t": noUnsubscribedT,
|
|
838
1000
|
"static-candidate-limit": staticCandidateLimit,
|
|
839
1001
|
"t-static-args": tStaticArgs
|
|
@@ -928,6 +1090,6 @@ plugin.configs["react-auto-import"] = [{
|
|
|
928
1090
|
}
|
|
929
1091
|
}];
|
|
930
1092
|
//#endregion
|
|
931
|
-
export { plugin as default, noEagerTranslation, noUnsubscribedT, staticCandidateLimit, tStaticArgs };
|
|
1093
|
+
export { plugin as default, noEagerTranslation, noRedundantAutoImport, noUnsubscribedT, staticCandidateLimit, tStaticArgs };
|
|
932
1094
|
|
|
933
1095
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["FUNCTION_TYPES"],"sources":["../src/resolve-import.ts","../src/analyze.ts","../src/vue-sfc.ts","../src/rule-analysis.ts","../src/rules/translation-call.ts","../src/rules/no-eager-translation.ts","../src/rules/no-unsubscribed-t.ts","../src/rules/static-candidate-limit.ts","../src/rules/t-static-args.ts","../src/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst SOURCE_EXTENSIONS = [\n '',\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mts',\n '.cts',\n '.mjs',\n '.cjs',\n] as const;\nconst DIRECTORY_STAMP_TTL_MS = 250;\nconst MAX_PROBE_CACHE_ENTRIES = 10_000;\n\ninterface TsConfig {\n compilerOptions?: {\n baseUrl?: string;\n paths?: Record<string, string[]>;\n };\n}\n\ninterface Alias {\n prefix: string;\n suffix: string;\n targets: string[];\n}\n\ninterface CachedTsConfig {\n stamp: string | null;\n value: TsConfig;\n}\n\ninterface CachedDirectoryStamp {\n expiresAt: number;\n value: string | null;\n}\n\ninterface CachedProbe {\n candidateDirectory: string | null;\n parentDirectory: string | null;\n resolved: string | null;\n}\n\nconst tsconfigCache = new Map<string, CachedTsConfig>();\nconst directoryStampCache = new Map<string, CachedDirectoryStamp>();\nconst probeCache = new Map<string, CachedProbe>();\n\nexport function createImportResolver(tsconfigPath?: string) {\n const aliases: Alias[] = [];\n let baseUrl = process.cwd();\n if (tsconfigPath) {\n const absoluteConfig = path.resolve(tsconfigPath);\n const config = readTsConfig(absoluteConfig);\n baseUrl = path.resolve(\n path.dirname(absoluteConfig),\n config.compilerOptions?.baseUrl ?? '.',\n );\n for (const [pattern, targets] of Object.entries(\n config.compilerOptions?.paths ?? {},\n )) {\n const star = pattern.indexOf('*');\n aliases.push({\n prefix: star < 0 ? pattern : pattern.slice(0, star),\n suffix: star < 0 ? '' : pattern.slice(star + 1),\n targets,\n });\n }\n }\n\n return (specifier: string, importer: string): string | null => {\n if (specifier === 'virtual:ai-i18n') return specifier;\n if (specifier.startsWith('.')) {\n return probeSource(path.resolve(path.dirname(importer), specifier));\n }\n for (const alias of aliases) {\n if (\n !specifier.startsWith(alias.prefix) ||\n !specifier.endsWith(alias.suffix)\n ) {\n continue;\n }\n const value = specifier.slice(\n alias.prefix.length,\n specifier.length - alias.suffix.length || undefined,\n );\n for (const target of alias.targets) {\n const candidate = target.includes('*')\n ? target.replace('*', value)\n : target;\n const resolved = probeSource(path.resolve(baseUrl, candidate));\n if (resolved) return resolved;\n }\n }\n return null;\n };\n}\n\nfunction probeSource(candidate: string): string | null {\n candidate = path.normalize(candidate);\n const cached = probeCache.get(candidate);\n if (\n cached &&\n cached.parentDirectory === readDirectoryStamp(path.dirname(candidate)) &&\n cached.candidateDirectory === readDirectoryStamp(candidate)\n ) {\n return cached.resolved;\n }\n\n let resolved: string | null = null;\n for (const extension of SOURCE_EXTENSIONS) {\n const file = `${candidate}${extension}`;\n if (isFile(file)) {\n resolved = path.normalize(file);\n break;\n }\n }\n if (!resolved) {\n for (const extension of SOURCE_EXTENSIONS.slice(1)) {\n const file = path.join(candidate, `index${extension}`);\n if (isFile(file)) {\n resolved = path.normalize(file);\n break;\n }\n }\n }\n if (probeCache.size >= MAX_PROBE_CACHE_ENTRIES) probeCache.clear();\n probeCache.set(candidate, {\n parentDirectory: readDirectoryStamp(path.dirname(candidate)),\n candidateDirectory: readDirectoryStamp(candidate),\n resolved,\n });\n return resolved;\n}\n\nfunction isFile(file: string) {\n try {\n return fs.statSync(file).isFile();\n } catch {\n return false;\n }\n}\n\nfunction readTsConfig(filename: string): TsConfig {\n const stamp = readFileStamp(filename);\n const cached = tsconfigCache.get(filename);\n if (cached?.stamp === stamp) return cached.value;\n let value: TsConfig = {};\n try {\n value = JSON.parse(\n stripJsonComments(fs.readFileSync(filename, 'utf8')),\n ) as TsConfig;\n } catch {\n // 无效或不存在的 tsconfig 与未配置路径别名等价。\n }\n tsconfigCache.set(filename, { stamp, value });\n return value;\n}\n\nfunction readFileStamp(filename: string): string | null {\n try {\n const stat = fs.statSync(filename);\n return stat.isFile()\n ? `${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`\n : null;\n } catch {\n return null;\n }\n}\n\nfunction readDirectoryStamp(directory: string): string | null {\n const now = Date.now();\n const cached = directoryStampCache.get(directory);\n if (cached && cached.expiresAt > now) return cached.value;\n let value: string | null = null;\n try {\n const stat = fs.statSync(directory);\n if (stat.isDirectory()) {\n value = `${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`;\n }\n } catch {\n // 不存在的目录也要缓存,避免重复的失败探测。\n }\n if (directoryStampCache.size >= MAX_PROBE_CACHE_ENTRIES) {\n directoryStampCache.clear();\n }\n directoryStampCache.set(directory, {\n expiresAt: now + DIRECTORY_STAMP_TTL_MS,\n value,\n });\n return value;\n}\n\nfunction stripJsonComments(source: string): string {\n let result = '';\n let quote = '';\n let escaped = false;\n for (let index = 0; index < source.length; index += 1) {\n const current = source[index]!;\n const next = source[index + 1];\n if (quote) {\n result += current;\n if (escaped) escaped = false;\n else if (current === '\\\\') escaped = true;\n else if (current === quote) quote = '';\n continue;\n }\n if (current === '\"' || current === \"'\") {\n quote = current;\n result += current;\n continue;\n }\n if (current === '/' && next === '/') {\n while (index < source.length && source[index] !== '\\n') index += 1;\n result += '\\n';\n continue;\n }\n if (current === '/' && next === '*') {\n index += 2;\n while (\n index < source.length &&\n !(source[index] === '*' && source[index + 1] === '/')\n ) {\n if (source[index] === '\\n') result += '\\n';\n index += 1;\n }\n index += 1;\n continue;\n }\n result += current;\n }\n return result.replace(/,\\s*([}\\]])/g, '$1');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n AI_I18N_VIRTUAL_MODULE_ID,\n Analyzer,\n extractMessages,\n findInvalidDefineI18nMessagesReferences,\n findTranslationCalls,\n findUnboundCalls,\n validateRecommendedUsage,\n type Module,\n type ExtractWarningCode,\n type RecommendedUsageCode,\n type AnalysisLanguage,\n type TranslationCall,\n type TranslationHookBinding,\n} from '@ai-i18n/analyzer';\nimport { createImportResolver } from './resolve-import.js';\n\nexport interface StaticArgsWarning {\n code: ExtractWarningCode | RecommendedUsageCode;\n line: number;\n column: number;\n message: string;\n}\n\nexport interface StaticAnalysisResult {\n warnings: StaticArgsWarning[];\n translationCalls: TranslationCall[];\n}\n\nexport type AutoImportApi = 't' | 'tRef' | 'useI18n';\nexport type AutoImportOption = boolean | readonly AutoImportApi[];\n\ninterface AutoImportBindings {\n t: boolean;\n tRef: boolean;\n useI18n: boolean;\n}\n\nconst POTENTIAL_TRANSLATION_RE =\n /virtual:ai-i18n|\\b(?:t|tRef|useI18n|defineI18nMessages)\\b/;\n\nexport function analyzeStaticArgs(\n code: string,\n filename: string,\n tsconfigPath?: string,\n lang?: AnalysisLanguage,\n autoImport: AutoImportOption = false,\n maxStaticCandidates = Number.POSITIVE_INFINITY,\n): StaticArgsWarning[] {\n return analyzeStaticSource(\n code,\n filename,\n tsconfigPath,\n lang,\n autoImport,\n maxStaticCandidates,\n ).warnings;\n}\n\nexport function analyzeStaticSource(\n code: string,\n filename: string,\n tsconfigPath?: string,\n lang?: AnalysisLanguage,\n autoImport: AutoImportOption = false,\n maxStaticCandidates = Number.POSITIVE_INFINITY,\n): StaticAnalysisResult {\n if (!hasPotentialTranslationCandidate(code)) {\n return { warnings: [], translationCalls: [] };\n }\n const autoImports = normalizeAutoImports(autoImport);\n const resolve = createImportResolver(tsconfigPath);\n const analyzer = new Analyzer({ resolve });\n analyzer.addFile(\n AI_I18N_VIRTUAL_MODULE_ID,\n 'export function t(source) { return source } export function tRef(source) { return source }',\n );\n const entryPath = normalizeFilename(filename);\n const entry = analyzer.addFile(entryPath, code, lang ? { lang } : undefined);\n if (!hasTranslationCandidate(entry, autoImports)) {\n return { warnings: [], translationCalls: [] };\n }\n loadDependencies(analyzer, entry, resolve);\n analyzer.link();\n const hooks = translationHooks(autoImports);\n const extraction = extractMessages(\n entry,\n AI_I18N_VIRTUAL_MODULE_ID,\n hooks,\n autoImports.t || autoImports.tRef,\n maxStaticCandidates,\n ).warnings;\n const recommended = validateRecommendedUsage(\n entry,\n AI_I18N_VIRTUAL_MODULE_ID,\n hooks,\n autoImports.t || autoImports.tRef,\n );\n const warnings = recommended.length\n ? [\n ...extraction.filter((warning) => warning.code === 'parse-error'),\n ...extraction.filter(\n (warning) => warning.code === 'static-candidate-limit',\n ),\n ...recommended,\n ]\n : extraction;\n return {\n warnings: warnings.map(({ code: warningCode, line, column, message }) => ({\n code: warningCode,\n line,\n column,\n message,\n })),\n translationCalls: findTranslationCalls(\n entry,\n AI_I18N_VIRTUAL_MODULE_ID,\n hooks,\n autoImports.t || autoImports.tRef,\n ),\n };\n}\n\nexport function hasPotentialTranslationCandidate(code: string): boolean {\n return POTENTIAL_TRANSLATION_RE.test(code);\n}\n\nexport function normalizeAutoImports(\n autoImport: AutoImportOption | undefined,\n): AutoImportBindings {\n if (typeof autoImport === 'boolean') {\n // boolean 保留跨框架的历史语义;Vue-only tRef 由 Vue preset 显式声明。\n return { t: autoImport, tRef: false, useI18n: autoImport };\n }\n return {\n t: autoImport?.includes('t') ?? false,\n tRef: autoImport?.includes('tRef') ?? false,\n useI18n: autoImport?.includes('useI18n') ?? false,\n };\n}\n\nfunction hasTranslationCandidate(\n module: Module,\n autoImports: AutoImportBindings,\n): boolean {\n const unbound = ['defineI18nMessages'];\n if (autoImports.t) unbound.push('t');\n if (autoImports.tRef) unbound.push('tRef');\n if (autoImports.useI18n) unbound.push('useI18n');\n return (\n module.imports.some(\n (item) =>\n !item.typeOnly &&\n (item.specifier === AI_I18N_VIRTUAL_MODULE_ID ||\n item.name === 't' ||\n item.name === 'tRef' ||\n item.name === 'useI18n'),\n ) ||\n findInvalidDefineI18nMessagesReferences(module).length > 0 ||\n findUnboundCalls(module, new Set(unbound)).length > 0\n );\n}\n\nfunction translationHooks(\n autoImports: AutoImportBindings,\n): readonly TranslationHookBinding[] {\n return [\n {\n module: AI_I18N_VIRTUAL_MODULE_ID,\n hook: 'useI18n',\n property: 't',\n autoImport: autoImports.useI18n,\n },\n ];\n}\n\nfunction loadDependencies(\n analyzer: Analyzer,\n entry: Module,\n resolve: (specifier: string, importer: string) => string | null,\n) {\n const queue = [entry];\n const visited = new Set<string>();\n while (queue.length) {\n const module = queue.shift()!;\n if (visited.has(module.path)) continue;\n visited.add(module.path);\n for (const item of module.imports) {\n const resolved = resolve(item.specifier, module.path);\n if (!resolved || resolved === AI_I18N_VIRTUAL_MODULE_ID) continue;\n let dependency = analyzer.module(resolved);\n if (!dependency) {\n try {\n dependency = analyzer.addFile(\n resolved,\n fs.readFileSync(resolved, 'utf8'),\n );\n } catch {\n continue;\n }\n }\n queue.push(dependency);\n }\n }\n}\n\nfunction normalizeFilename(filename: string) {\n return filename.startsWith('<')\n ? path.resolve('eslint-input.ts')\n : path.resolve(filename);\n}\n","import { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { diagnosticMessage } from '@ai-i18n/analyzer';\nimport {\n analyzeVueSource,\n type VueAnalysisSource,\n type VueCompiler,\n} from '@ai-i18n/analyzer/vue';\n\ninterface VueParserServices {\n getDocumentFragment?: () => unknown;\n}\n\ntype Require = ReturnType<typeof createRequire>;\ntype NodeVueCompiler = VueCompiler & {\n registerTS?: (loadTypeScript: () => unknown) => void;\n};\n\nconst packageRequire = createRequire(import.meta.url);\n\nexport function createVueAnalysisSource(\n source: string,\n filename: string,\n parserServices: VueParserServices,\n): VueAnalysisSource {\n if (!parserServices.getDocumentFragment?.()) {\n throw new Error(\n diagnosticMessage(\n '检查 .vue 文件需要配置 vue-eslint-parser。',\n 'Configure vue-eslint-parser to lint .vue files.',\n ),\n );\n }\n\n try {\n // Vue preset 真正处理 SFC 时才加载编译器,不影响 React/Vanilla 项目。\n const compiler = loadVueCompiler(filename);\n return analyzeVueSource(source, filename, compiler);\n } catch (error) {\n if (isMissingVueCompiler(error)) {\n throw new Error(\n diagnosticMessage(\n '检查 .vue 文件需要安装 @vue/compiler-sfc。',\n 'Install @vue/compiler-sfc to lint .vue files.',\n ),\n );\n }\n throw error;\n }\n}\n\nfunction loadVueCompiler(filename: string): VueCompiler {\n const hostRequire = createRequire(path.resolve(filename));\n // 优先复用宿主 Vue 的 Node wrapper,它会为 imported types 注册 TypeScript。\n const compiler = (tryRequire(hostRequire, 'vue/compiler-sfc') ??\n tryRequire(packageRequire, 'vue/compiler-sfc') ??\n tryRequire(hostRequire, '@vue/compiler-sfc') ??\n packageRequire('@vue/compiler-sfc')) as NodeVueCompiler;\n compiler.registerTS?.(() => {\n const typescript =\n tryResolve(hostRequire, 'typescript') ??\n tryResolve(packageRequire, 'typescript');\n return packageRequire(typescript ?? 'typescript');\n });\n return compiler;\n}\n\nfunction tryRequire(require: Require, id: string): unknown {\n const resolved = tryResolve(require, id);\n return resolved ? require(resolved) : null;\n}\n\nfunction tryResolve(require: Require, id: string): string | null {\n try {\n return require.resolve(id);\n } catch (error) {\n if (isMissingVueCompiler(error)) return null;\n throw error;\n }\n}\n\nfunction isMissingVueCompiler(error: unknown): boolean {\n return (\n error instanceof Error &&\n 'code' in error &&\n (error.code === 'MODULE_NOT_FOUND' ||\n error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')\n );\n}\n","import type { Rule } from 'eslint';\nimport {\n analyzeStaticSource,\n hasPotentialTranslationCandidate,\n normalizeAutoImports,\n type AutoImportOption,\n type StaticAnalysisResult,\n type StaticArgsWarning,\n} from './analyze.js';\nimport { diagnosticMessage, type TranslationCall } from '@ai-i18n/analyzer';\nimport { createVueAnalysisSource } from './vue-sfc.js';\n\ninterface RuleAnalysisOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n}\n\ninterface CachedAnalysis {\n source: ReturnType<typeof createAnalysisSource>;\n analyses: Map<string, StaticAnalysisResult>;\n}\n\nconst DEFAULT_CANDIDATE_LIMIT = 1_000;\nconst cache = new WeakMap<object, CachedAnalysis>();\nconst reportedAnalysisFailures = new WeakSet<object>();\nconst EMPTY_ANALYSIS: StaticAnalysisResult = {\n warnings: [],\n translationCalls: [],\n};\n\nexport function analyzeRuleContext(\n context: Rule.RuleContext,\n options: RuleAnalysisOptions,\n maxStaticCandidates = Number.POSITIVE_INFINITY,\n): StaticArgsWarning[] {\n return analyzeRuleContextResult(context, options, maxStaticCandidates)\n .warnings;\n}\n\nexport function analyzeTranslationCalls(\n context: Rule.RuleContext,\n options: RuleAnalysisOptions,\n): TranslationCall[] {\n return analyzeRuleContextResult(context, options, DEFAULT_CANDIDATE_LIMIT)\n .translationCalls;\n}\n\nexport function reportAnalysisFailureOnce(\n context: Rule.RuleContext,\n node: Rule.Node,\n error: unknown,\n): void {\n if (reportedAnalysisFailures.has(context.sourceCode)) return;\n reportedAnalysisFailures.add(context.sourceCode);\n const detail = error instanceof Error ? error.message : String(error);\n context.report({\n node,\n message: diagnosticMessage(\n `静态分析失败:${detail}`,\n `Static analysis failed: ${detail}`,\n ),\n });\n}\n\nfunction analyzeRuleContextResult(\n context: Rule.RuleContext,\n options: RuleAnalysisOptions,\n maxStaticCandidates: number,\n): StaticAnalysisResult {\n if (!hasPotentialTranslationCandidate(context.sourceCode.text)) {\n return EMPTY_ANALYSIS;\n }\n const cached: CachedAnalysis = cache.get(context.sourceCode) ?? {\n source: createAnalysisSource(context),\n analyses: new Map<string, StaticAnalysisResult>(),\n };\n cache.set(context.sourceCode, cached);\n\n const analyze = (limit: number) => {\n const autoImports = normalizeAutoImports(options.autoImport);\n const key = JSON.stringify([\n options.tsconfigPath ?? null,\n autoImports.t,\n autoImports.tRef,\n autoImports.useI18n,\n limit,\n ]);\n const existing = cached.analyses.get(key);\n if (existing) return existing;\n const result = analyzeStaticSource(\n cached.source.code,\n context.filename,\n options.tsconfigPath,\n cached.source.lang,\n options.autoImport,\n limit,\n );\n const analysis = {\n warnings: result.warnings.map((warning) => ({\n ...warning,\n ...cached.source.mapLocation(warning),\n })),\n translationCalls: result.translationCalls.map((call) => ({\n ...call,\n ...cached.source.mapLocation(call),\n })),\n };\n cached.analyses.set(key, analysis);\n return analysis;\n };\n\n if (Number.isFinite(maxStaticCandidates)) {\n return analyze(maxStaticCandidates);\n }\n const preflight = analyze(DEFAULT_CANDIDATE_LIMIT);\n return preflight.warnings.some(\n (warning) => warning.code === 'static-candidate-limit',\n )\n ? analyze(Number.POSITIVE_INFINITY)\n : preflight;\n}\n\nfunction createAnalysisSource(context: Rule.RuleContext) {\n return context.filename.endsWith('.vue')\n ? createVueAnalysisSource(\n context.sourceCode.text,\n context.filename,\n context.sourceCode.parserServices,\n )\n : {\n code: context.sourceCode.text,\n lang: undefined,\n mapLocation: (location: { line: number; column: number }) => location,\n };\n}\n","import type { TranslationCall } from '@ai-i18n/analyzer';\nimport type { Rule } from 'eslint';\nimport type { AutoImportOption } from '../analyze.js';\nimport { analyzeTranslationCalls } from '../rule-analysis.js';\n\nexport interface TranslationRuleOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n}\n\nexport interface TranslationCandidate {\n kind: TranslationCall['kind'];\n node: Rule.Node;\n}\n\nexport interface MatchedTranslationCall extends TranslationCandidate {\n call: TranslationCall;\n}\n\nexport function matchTranslationCalls(\n context: Rule.RuleContext,\n options: TranslationRuleOptions,\n candidates: readonly TranslationCandidate[],\n): MatchedTranslationCall[] {\n const calls = new Map(\n analyzeTranslationCalls(context, options).map((call) => [\n locationKey(call.line, call.column, call.kind),\n call,\n ]),\n );\n return candidates.flatMap((candidate) => {\n const location = candidate.node.loc?.start;\n if (!location) return [];\n const call = calls.get(\n locationKey(location.line, location.column, candidate.kind),\n );\n return call ? [{ ...candidate, call }] : [];\n });\n}\n\nfunction locationKey(\n line: number,\n column: number,\n kind: TranslationCall['kind'],\n): string {\n return `${kind}:${line}:${column}`;\n}\n","import { diagnosticMessage } from '@ai-i18n/analyzer';\nimport type { Rule } from 'eslint';\nimport { reportAnalysisFailureOnce } from '../rule-analysis.js';\nimport {\n matchTranslationCalls,\n type TranslationCandidate,\n type TranslationRuleOptions,\n} from './translation-call.js';\n\nconst FUNCTION_TYPES = new Set([\n 'ArrowFunctionExpression',\n 'FunctionDeclaration',\n 'FunctionExpression',\n]);\n\nexport const noEagerTranslation: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: '警告不会随语言切换更新的提前求值翻译结果',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n eagerTranslation: diagnosticMessage(\n '初始化期间保存的 t() 结果不会随语言切换更新。请改为函数或 Getter,在使用时调用 t();Vue setup 中的响应式展示值可使用 tRef()。',\n 'A t() result stored during initialization will not update when the language changes. Evaluate it lazily in a function or getter; reactive display values in Vue setup can use tRef().',\n ),\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as TranslationRuleOptions;\n const isVueSfc = context.filename.toLowerCase().endsWith('.vue');\n const candidates: TranslationCandidate[] = [];\n return {\n CallExpression(node) {\n candidates.push({ kind: 'call', node });\n },\n TaggedTemplateExpression(node) {\n candidates.push({ kind: 'tagged-template', node });\n },\n 'Program:exit'(program) {\n let matches;\n try {\n matches = matchTranslationCalls(context, options, candidates);\n } catch (error) {\n reportAnalysisFailureOnce(context, program as Rule.Node, error);\n return;\n }\n for (const { call, node } of matches) {\n if (call.origin === 'vue-ref') continue;\n if (!storesOutsideFunction(node, context, isVueSfc)) continue;\n context.report({ node, messageId: 'eagerTranslation' });\n }\n },\n };\n },\n};\n\nfunction storesOutsideFunction(\n node: Rule.Node,\n context: Rule.RuleContext,\n isVueSfc: boolean,\n): boolean {\n let current = node as ParentNode;\n let storesResult = false;\n while (current.parent) {\n const parent = current.parent;\n if (FUNCTION_TYPES.has(parent.type)) {\n // 普通 setup() 与 <script setup> 一样只执行一次;其他函数仍视为延迟求值。\n const returnsExpression =\n parent.type === 'ArrowFunctionExpression' && parent.body === current;\n return (\n isVueComponentSetup(parent, context, isVueSfc) &&\n (storesResult || returnsExpression)\n );\n }\n if (storesTranslationResult(parent, current)) storesResult = true;\n if (parent.type === 'Program') return storesResult;\n current = parent;\n }\n return storesResult;\n}\n\nfunction isVueComponentSetup(\n node: ParentNode,\n context: Rule.RuleContext,\n isVueSfc: boolean,\n): boolean {\n const directOwner = node.parent;\n if (\n directOwner?.type === 'CallExpression' &&\n directOwner.arguments?.[0] === node\n ) {\n return isImportedDefineComponent(directOwner.callee, context);\n }\n\n const property = node.parent;\n if (\n property?.type !== 'Property' ||\n property.value !== node ||\n property.computed ||\n propertyName(property.key) !== 'setup'\n ) {\n return false;\n }\n\n const options = property.parent;\n if (options?.type !== 'ObjectExpression') return false;\n const owner = options.parent;\n if (\n isVueSfc &&\n owner?.type === 'ExportDefaultDeclaration' &&\n owner.declaration === options\n ) {\n return true;\n }\n return (\n owner?.type === 'CallExpression' &&\n owner.arguments?.[0] === options &&\n isImportedDefineComponent(owner.callee, context)\n );\n}\n\nfunction propertyName(node: unknown): string | undefined {\n const key = node as { name?: string; type?: string; value?: unknown };\n if (key?.type === 'Identifier') return key.name;\n return key?.type === 'Literal' && typeof key.value === 'string'\n ? key.value\n : undefined;\n}\n\nfunction isImportedDefineComponent(\n node: unknown,\n context: Rule.RuleContext,\n): boolean {\n const callee = node as {\n computed?: boolean;\n name?: string;\n object?: unknown;\n property?: unknown;\n type?: string;\n };\n if (callee?.type === 'Identifier') {\n return isVueImport(callee, context, 'ImportSpecifier');\n }\n return (\n callee?.type === 'MemberExpression' &&\n !callee.computed &&\n propertyName(callee.property) === 'defineComponent' &&\n isVueImport(callee.object, context, 'ImportNamespaceSpecifier')\n );\n}\n\nfunction isVueImport(\n node: unknown,\n context: Rule.RuleContext,\n specifierType: 'ImportNamespaceSpecifier' | 'ImportSpecifier',\n): boolean {\n const identifier = node as Rule.Node & { name?: string };\n if (identifier?.type !== 'Identifier' || !identifier.name) return false;\n\n let scope = context.sourceCode.getScope(identifier);\n while (true) {\n const variable = scope.set.get(identifier.name);\n if (!variable) {\n if (!scope.upper) return false;\n scope = scope.upper;\n continue;\n }\n return variable.defs.some((definition) => {\n if (\n definition.type !== 'ImportBinding' ||\n definition.node.type !== specifierType ||\n definition.parent.source.value !== 'vue'\n ) {\n return false;\n }\n return (\n specifierType === 'ImportNamespaceSpecifier' ||\n propertyName((definition.node as { imported?: unknown }).imported) ===\n 'defineComponent'\n );\n });\n }\n}\n\nfunction storesTranslationResult(\n parent: ParentNode,\n child: ParentNode,\n): boolean {\n if (\n (parent as { type: string }).type === 'AccessorProperty' &&\n parent.value === child\n ) {\n return true;\n }\n switch (parent.type) {\n case 'VariableDeclarator':\n return parent.init === child;\n case 'AssignmentExpression':\n case 'AssignmentPattern':\n return parent.right === child;\n case 'PropertyDefinition':\n return parent.value === child;\n case 'ReturnStatement':\n return parent.argument === child;\n case 'ExportDefaultDeclaration':\n return parent.declaration === child;\n default:\n return false;\n }\n}\n\ntype ParentNode = Rule.Node & {\n arguments?: unknown[];\n argument?: unknown;\n body?: unknown;\n callee?: unknown;\n computed?: boolean;\n parent: ParentNode | null;\n declaration?: unknown;\n init?: unknown;\n key?: unknown;\n right?: unknown;\n value?: unknown;\n};\n","import { diagnosticMessage } from '@ai-i18n/analyzer';\nimport type { Rule } from 'eslint';\nimport { reportAnalysisFailureOnce } from '../rule-analysis.js';\nimport {\n matchTranslationCalls,\n type TranslationCandidate,\n type TranslationRuleOptions,\n} from './translation-call.js';\n\nconst FUNCTION_TYPES = new Set([\n 'ArrowFunctionExpression',\n 'FunctionDeclaration',\n 'FunctionExpression',\n]);\nconst IMMEDIATE_CONSOLE_METHODS = new Set([\n 'debug',\n 'error',\n 'info',\n 'log',\n 'warn',\n]);\n\nexport const noUnsubscribedT: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: '检查组件渲染路径中的翻译 API 生命周期',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n unsubscribedT: diagnosticMessage(\n '组件渲染期间使用顶层 t 不会订阅语言状态,语言切换不会主动刷新,缓存的渲染结果也可能继续使用旧译文。请使用 useI18n() 返回的 t。',\n 'Top-level t does not subscribe the component to language updates, so language changes do not trigger a render and cached render results may remain stale. Use the t returned by useI18n().',\n ),\n renderTRef: diagnosticMessage(\n '不要在组件渲染或 template 中调用 tRef(),否则每次渲染都会创建新的 computed。请在 setup 中创建一次并使用返回的 Ref;渲染函数中请直接使用 useI18n() 返回的 t。',\n 'Do not call tRef() during component rendering or in a template because each render creates a new computed. Create it once in setup and use the returned Ref; in render functions, call the t returned by useI18n() directly.',\n ),\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as TranslationRuleOptions;\n const candidates: TranslationCandidate[] = [];\n const jsxOwners = new Set<ParentNode>();\n const templateNodes = new Set<Rule.Node>();\n const collectJsxOwner = (node: Rule.Node) => {\n const owner = nearestFunction(node);\n if (owner) jsxOwners.add(owner);\n };\n const scriptVisitor: Rule.RuleListener = {\n CallExpression(node) {\n candidates.push({ kind: 'call', node });\n },\n TaggedTemplateExpression(node) {\n candidates.push({ kind: 'tagged-template', node });\n },\n JSXElement: collectJsxOwner,\n JSXFragment: collectJsxOwner,\n 'Program:exit'(program) {\n let matches;\n try {\n matches = matchTranslationCalls(context, options, candidates);\n } catch (error) {\n reportAnalysisFailureOnce(context, program as Rule.Node, error);\n return;\n }\n for (const { call, node } of matches) {\n if (call.origin === 'vue-ref') {\n if (templateNodes.has(node)) {\n if (!isVueTemplateEventHandler(node)) {\n context.report({ node, messageId: 'renderTRef' });\n }\n continue;\n }\n const owner = nearestFunction(node);\n if (owner && jsxOwners.has(owner)) {\n context.report({ node, messageId: 'renderTRef' });\n }\n continue;\n }\n if (call.origin !== 'runtime') continue;\n if (templateNodes.has(node)) {\n if (!isVueTemplateEventHandler(node)) {\n context.report({ node, messageId: 'unsubscribedT' });\n }\n continue;\n }\n // ponytail: 先检查直接拥有 JSX 的最近函数;出现真实漏报后再引入调用图与框架数据流。\n const owner = nearestFunction(node);\n if (\n !owner ||\n !jsxOwners.has(owner) ||\n isImmediateConsoleEffect(node)\n ) {\n continue;\n }\n context.report({ node, messageId: 'unsubscribedT' });\n }\n },\n };\n const parserServices = context.sourceCode\n .parserServices as VueParserServices;\n if (!parserServices.defineTemplateBodyVisitor) return scriptVisitor;\n return parserServices.defineTemplateBodyVisitor(\n {\n CallExpression(node) {\n candidates.push({ kind: 'call', node });\n templateNodes.add(node);\n },\n TaggedTemplateExpression(node) {\n candidates.push({ kind: 'tagged-template', node });\n templateNodes.add(node);\n },\n },\n scriptVisitor,\n // 模板默认在 Program:exit 才遍历;提前到 Program,确保汇总前已收集候选。\n { templateBodyTriggerSelector: 'Program' },\n );\n },\n};\n\nfunction nearestFunction(node: Rule.Node): ParentNode | null {\n let current = node as ParentNode;\n while (current.parent) {\n current = current.parent;\n if (FUNCTION_TYPES.has(current.type)) return current;\n }\n return null;\n}\n\nfunction isImmediateConsoleEffect(node: Rule.Node): boolean {\n const call = (node as ParentNode).parent;\n if (\n call?.type !== 'CallExpression' ||\n call.parent?.type !== 'ExpressionStatement' ||\n !call.arguments.some((argument) => argument === node) ||\n call.callee.type !== 'MemberExpression' ||\n call.callee.object.type !== 'Identifier' ||\n call.callee.object.name !== 'console'\n ) {\n return false;\n }\n const property = call.callee.property;\n const method =\n !call.callee.computed && property.type === 'Identifier'\n ? property.name\n : call.callee.computed &&\n property.type === 'Literal' &&\n typeof property.value === 'string'\n ? property.value\n : null;\n return method !== null && IMMEDIATE_CONSOLE_METHODS.has(method);\n}\n\nfunction isVueTemplateEventHandler(node: Rule.Node): boolean {\n let current = node as unknown as TemplateParentNode;\n while (current.parent) {\n current = current.parent;\n if (current.type === 'VOnExpression') return true;\n }\n return false;\n}\n\ntype ParentNode = Rule.Node & {\n parent: ParentNode | null;\n};\n\ninterface TemplateParentNode {\n type: string;\n parent: TemplateParentNode | null;\n}\n\ninterface VueParserServices {\n defineTemplateBodyVisitor?(\n templateBodyVisitor: Record<string, (node: Rule.Node) => void>,\n scriptVisitor: Rule.RuleListener,\n options: { templateBodyTriggerSelector: 'Program' | 'Program:exit' },\n ): Rule.RuleListener;\n}\n","import type { Rule } from 'eslint';\nimport type { AutoImportOption } from '../analyze.js';\nimport {\n analyzeRuleContext,\n reportAnalysisFailureOnce,\n} from '../rule-analysis.js';\n\ninterface RuleOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n maxStaticCandidates?: number;\n}\n\nexport const staticCandidateLimit: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: '警告单个 t() 展开的静态候选数量过多',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n maxStaticCandidates: {\n type: 'integer',\n minimum: 1,\n maximum: Number.MAX_SAFE_INTEGER,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n candidateLimit: '{{reason}}',\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as RuleOptions;\n return {\n 'Program:exit'(node) {\n try {\n const warnings = analyzeRuleContext(\n context,\n options,\n options.maxStaticCandidates ?? 1_000,\n );\n for (const warning of warnings) {\n if (warning.code !== 'static-candidate-limit') continue;\n context.report({\n node,\n loc: {\n start: warning,\n end: { line: warning.line, column: warning.column + 1 },\n },\n messageId: 'candidateLimit',\n data: { reason: warning.message },\n });\n }\n } catch (error) {\n reportAnalysisFailureOnce(context, node as Rule.Node, error);\n }\n },\n };\n },\n};\n","import type { Rule } from 'eslint';\nimport { diagnosticMessage } from '@ai-i18n/analyzer';\nimport { normalizeAutoImports, type AutoImportOption } from '../analyze.js';\nimport {\n analyzeRuleContext,\n reportAnalysisFailureOnce,\n} from '../rule-analysis.js';\n\ninterface RuleOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n}\n\ninterface AstNode {\n type: string;\n name?: string;\n importKind?: string;\n range?: [number, number];\n argument?: AstNode;\n left?: AstNode;\n elements?: Array<AstNode | null>;\n properties?: AstNode[];\n value?: AstNode;\n id?: AstNode;\n declarations?: AstNode[];\n specifiers?: AstNode[];\n local?: AstNode;\n declaration?: AstNode | null;\n body?: AstNode[];\n parent?: AstNode | null;\n}\n\ninterface VueElement extends AstNode {\n startTag?: {\n attributes?: Array<{\n directive?: boolean;\n key?: { name?: string };\n }>;\n };\n}\n\ninterface VueDocument {\n children?: AstNode[];\n}\n\ninterface VueParserServices {\n defineTemplateBodyVisitor?: (\n templateVisitor: Record<string, (node: AstNode) => void>,\n scriptVisitor: Rule.RuleListener,\n ) => Rule.RuleListener;\n getDocumentFragment?: () => VueDocument | null;\n}\n\ninterface VueReference {\n id?: AstNode;\n variable?: unknown;\n}\n\nexport const tStaticArgs: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: '要求 virtual:ai-i18n 的 t() 参数可被静态提取',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n analysisFailed: '{{reason}}',\n dynamicArg: '{{reason}}',\n invalidUsage: '{{reason}}',\n templateNeedsI18n: '{{reason}}',\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as RuleOptions;\n const scriptVisitor: Rule.RuleListener = {\n 'Program:exit'(node) {\n let warnings;\n try {\n warnings = analyzeRuleContext(context, options);\n } catch (error) {\n reportAnalysisFailureOnce(context, node as Rule.Node, error);\n return;\n }\n for (const warning of warnings) {\n const analysisFailed = warning.code === 'parse-error';\n const invalidUsage =\n warning.code !== 'dynamic-argument' &&\n warning.code !== 'unresolved-argument' &&\n !analysisFailed;\n context.report({\n node,\n loc: {\n start: warning,\n end: { line: warning.line, column: warning.column + 1 },\n },\n messageId: analysisFailed\n ? 'analysisFailed'\n : invalidUsage\n ? 'invalidUsage'\n : 'dynamicArg',\n data: {\n reason: analysisFailed\n ? diagnosticMessage(\n `静态分析失败:${warning.message}`,\n `Static analysis failed: ${warning.message}`,\n )\n : invalidUsage\n ? warning.message\n : diagnosticMessage(\n 't() 的参数无法静态提取。source 请使用静态字符串,options 请使用只包含 comment 的静态对象。',\n 'The t() arguments cannot be statically extracted. Use a static string for source and a static object containing only comment for options.',\n ),\n },\n });\n }\n },\n };\n\n if (\n !context.filename.toLowerCase().endsWith('.vue') ||\n !normalizeAutoImports(options.autoImport).t\n ) {\n return scriptVisitor;\n }\n const services = context.sourceCode\n .parserServices as unknown as VueParserServices;\n const document = services.getDocumentFragment?.();\n const scripts = document?.children?.filter(isScriptElement) ?? [];\n const setupScript = scripts.find(isScriptSetup);\n // 普通 Options API 的模板可从组件实例获得 t,无法仅凭 SFC 静态确认来源。\n if ((!setupScript && scripts.length > 0) || !document) {\n return scriptVisitor;\n }\n if (\n setupScript &&\n hasTopLevelBinding(\n context.sourceCode.ast as unknown as AstNode,\n setupScript,\n 't',\n )\n ) {\n return scriptVisitor;\n }\n if (!services.defineTemplateBodyVisitor) return scriptVisitor;\n\n const reportTemplateT = (node: AstNode) => {\n const target =\n node.type === 'CallExpression'\n ? (node as AstNode & { callee?: AstNode }).callee\n : (node as AstNode & { tag?: AstNode }).tag;\n if (\n target?.type !== 'Identifier' ||\n target.name !== 't' ||\n isTemplateLocal(target)\n ) {\n return;\n }\n context.report({\n node: target as Rule.Node,\n messageId: 'templateNeedsI18n',\n data: {\n reason: diagnosticMessage(\n '模板中的裸 t 无法绑定到 ai-i18n,也不会订阅语言变化。请在 <script setup> 中声明 const { t } = useI18n()。',\n 'A bare t in the template is not bound to ai-i18n and does not subscribe to language changes. Declare const { t } = useI18n() in <script setup>.',\n ),\n },\n });\n };\n return services.defineTemplateBodyVisitor(\n {\n CallExpression: reportTemplateT,\n TaggedTemplateExpression: reportTemplateT,\n },\n scriptVisitor,\n );\n },\n};\n\nfunction isScriptElement(node: AstNode): node is VueElement {\n return node.type === 'VElement' && node.name === 'script';\n}\n\nfunction isScriptSetup(node: VueElement): boolean {\n return (\n node.startTag?.attributes?.some(\n (attribute) => !attribute.directive && attribute.key?.name === 'setup',\n ) ?? false\n );\n}\n\nfunction hasTopLevelBinding(\n program: AstNode,\n script: VueElement,\n name: string,\n): boolean {\n return (\n program.body?.some(\n (statement) =>\n isInside(statement, script) && statementDeclares(statement, name),\n ) ?? false\n );\n}\n\nfunction isInside(node: AstNode, container: AstNode): boolean {\n return Boolean(\n node.range &&\n container.range &&\n node.range[0] >= container.range[0] &&\n node.range[1] <= container.range[1],\n );\n}\n\nfunction statementDeclares(node: AstNode, name: string): boolean {\n switch (node.type) {\n case 'ImportDeclaration':\n return (\n node.importKind !== 'type' &&\n (node.specifiers?.some(\n (item) => item.importKind !== 'type' && item.local?.name === name,\n ) ??\n false)\n );\n case 'VariableDeclaration':\n return (\n node.declarations?.some((item) => patternDeclares(item.id, name)) ??\n false\n );\n case 'FunctionDeclaration':\n case 'ClassDeclaration':\n case 'TSEnumDeclaration':\n case 'TSImportEqualsDeclaration':\n return node.id?.name === name;\n case 'ExportNamedDeclaration':\n case 'ExportDefaultDeclaration':\n return node.declaration\n ? statementDeclares(node.declaration, name)\n : false;\n default:\n return false;\n }\n}\n\nfunction patternDeclares(node: AstNode | undefined, name: string): boolean {\n if (!node) return false;\n switch (node.type) {\n case 'Identifier':\n return node.name === name;\n case 'RestElement':\n return patternDeclares(node.argument, name);\n case 'AssignmentPattern':\n return patternDeclares(node.left, name);\n case 'ArrayPattern':\n return (\n node.elements?.some((item) =>\n patternDeclares(item ?? undefined, name),\n ) ?? false\n );\n case 'ObjectPattern':\n return (\n node.properties?.some((item) =>\n patternDeclares(\n item.type === 'RestElement' ? item.argument : item.value,\n name,\n ),\n ) ?? false\n );\n default:\n return false;\n }\n}\n\nfunction isTemplateLocal(node: AstNode): boolean {\n let parent = node.parent;\n while (parent && parent.type !== 'VExpressionContainer') {\n parent = parent.parent;\n }\n const references = (\n parent as (AstNode & { references?: VueReference[] }) | null | undefined\n )?.references;\n return (\n references?.some(\n (reference) =>\n reference.variable &&\n (reference.id === node ||\n (reference.id?.range?.[0] === node.range?.[0] &&\n reference.id?.range?.[1] === node.range?.[1])),\n ) ?? false\n );\n}\n","import { createRequire } from 'node:module';\nimport type { ESLint } from 'eslint';\nimport { noEagerTranslation } from './rules/no-eager-translation.js';\nimport { noUnsubscribedT } from './rules/no-unsubscribed-t.js';\nimport { staticCandidateLimit } from './rules/static-candidate-limit.js';\nimport { tStaticArgs } from './rules/t-static-args.js';\n\nconst { version } = createRequire(import.meta.url)('../package.json') as {\n version: string;\n};\n\nconst plugin: ESLint.Plugin = {\n meta: {\n name: '@ai-i18n/eslint-plugin',\n version,\n namespace: 'ai-i18n',\n },\n rules: {\n 'no-eager-translation': noEagerTranslation,\n 'no-unsubscribed-t': noUnsubscribedT,\n 'static-candidate-limit': staticCandidateLimit,\n 't-static-args': tStaticArgs,\n },\n configs: {},\n};\n\n// 规则集必须由使用者显式引入,不会修改宿主项目的 ESLint 配置。\nplugin.configs!.recommended = [\n {\n ignores: ['**/*.vue'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: { defineI18nMessages: 'readonly' },\n },\n rules: {\n 'ai-i18n/t-static-args': 'error',\n 'ai-i18n/no-eager-translation': 'warn',\n 'ai-i18n/no-unsubscribed-t': 'warn',\n 'ai-i18n/static-candidate-limit': 'warn',\n },\n },\n];\n\nplugin.configs!.vue = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,vue}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: { defineI18nMessages: 'readonly' },\n },\n rules: {\n 'ai-i18n/t-static-args': 'error',\n 'ai-i18n/no-eager-translation': 'warn',\n 'ai-i18n/no-unsubscribed-t': 'warn',\n 'ai-i18n/static-candidate-limit': 'warn',\n },\n },\n];\n\n// 自动导入 preset 必须与 Vite 各模式实际注入的 API 一一对应。\nplugin.configs!['vanilla-auto-import'] = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: {\n t: 'readonly',\n setLang: 'readonly',\n getLang: 'readonly',\n getLangs: 'readonly',\n getLangLoadState: 'readonly',\n subscribe: 'readonly',\n defineI18nMessages: 'readonly',\n },\n },\n rules: {\n 'ai-i18n/t-static-args': ['error', { autoImport: ['t'] }],\n 'ai-i18n/no-eager-translation': ['warn', { autoImport: ['t'] }],\n 'ai-i18n/static-candidate-limit': ['warn', { autoImport: ['t'] }],\n },\n },\n];\n\nplugin.configs!['vue-auto-import'] = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,vue}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: {\n t: 'readonly',\n tRef: 'readonly',\n useI18n: 'readonly',\n defineI18nMessages: 'readonly',\n },\n },\n rules: {\n 'ai-i18n/t-static-args': [\n 'error',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n 'ai-i18n/no-eager-translation': [\n 'warn',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n 'ai-i18n/no-unsubscribed-t': [\n 'warn',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n 'ai-i18n/static-candidate-limit': [\n 'warn',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n },\n },\n];\n\nplugin.configs!['react-auto-import'] = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: {\n t: 'readonly',\n useI18n: 'readonly',\n defineI18nMessages: 'readonly',\n },\n },\n rules: {\n 'ai-i18n/t-static-args': ['error', { autoImport: ['t', 'useI18n'] }],\n 'ai-i18n/no-eager-translation': [\n 'warn',\n { autoImport: ['t', 'useI18n'] },\n ],\n 'ai-i18n/no-unsubscribed-t': ['warn', { autoImport: ['t', 'useI18n'] }],\n 'ai-i18n/static-candidate-limit': [\n 'warn',\n { autoImport: ['t', 'useI18n'] },\n ],\n },\n },\n];\n\nexport {\n noEagerTranslation,\n noUnsubscribedT,\n staticCandidateLimit,\n tStaticArgs,\n};\nexport default plugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["isFile","FUNCTION_TYPES"],"sources":["../src/tsconfig-projects.ts","../src/resolve-import.ts","../src/analyze.ts","../src/vue-sfc.ts","../src/rule-analysis.ts","../src/rules/translation-call.ts","../src/rules/no-eager-translation.ts","../src/rules/no-redundant-auto-import.ts","../src/rules/no-unsubscribed-t.ts","../src/rules/static-candidate-limit.ts","../src/rules/t-static-args.ts","../src/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n createFilesMatcher,\n createPathsMatcher,\n parseTsconfig,\n type FileMatcher,\n type PathsMatcher,\n type TsConfigResult,\n} from 'get-tsconfig';\nimport picomatch from 'picomatch';\n\nconst PROJECT_CACHE_TTL_MS = 250;\nconst MAX_PROJECT_CACHE_ENTRIES = 1_000;\nconst READ_FILE_CACHE_PREFIX = 'readFileSync:';\nconst READ_FILE_CACHE_SUFFIX = ':utf8';\n\ninterface TsConfigProject {\n config: TsConfigResult;\n filesMatcher: FileMatcher;\n pathsMatcher: PathsMatcher | null;\n}\n\ninterface CachedProjectGraph {\n expiresAt: number;\n projects: TsConfigProject[];\n stamps: Map<string, string | null>;\n}\n\ninterface CachedDiscovery {\n expiresAt: number;\n tsconfigPath: string | null;\n}\n\nconst projectGraphCache = new Map<string, CachedProjectGraph>();\nconst discoveryCache = new Map<string, CachedDiscovery>();\n\nexport function createTsconfigResolver(tsconfigPath?: string) {\n const explicitRoot = tsconfigPath\n ? path.normalize(path.resolve(tsconfigPath))\n : undefined;\n\n return (specifier: string, importer: string): readonly string[] => {\n const rootConfig = explicitRoot ?? findNearestTsconfig(importer);\n if (!rootConfig) return [];\n const project = selectProject(loadProjectGraph(rootConfig), importer);\n return project?.pathsMatcher?.(specifier) ?? [];\n };\n}\n\nfunction findNearestTsconfig(importer: string): string | null {\n let directory = path.dirname(path.resolve(importer));\n const now = Date.now();\n const cached = discoveryCache.get(directory);\n if (cached && cached.expiresAt > now) return cached.tsconfigPath;\n const visited: string[] = [];\n\n for (;;) {\n visited.push(directory);\n const candidate = path.join(directory, 'tsconfig.json');\n if (isFile(candidate)) {\n cacheDiscovery(visited, candidate, now);\n return candidate;\n }\n const parent = path.dirname(directory);\n if (parent === directory) {\n cacheDiscovery(visited, null, now);\n return null;\n }\n directory = parent;\n }\n}\n\nfunction cacheDiscovery(\n directories: readonly string[],\n tsconfigPath: string | null,\n now: number,\n) {\n if (discoveryCache.size >= MAX_PROJECT_CACHE_ENTRIES) {\n discoveryCache.clear();\n }\n for (const directory of directories) {\n discoveryCache.set(directory, {\n expiresAt: now + PROJECT_CACHE_TTL_MS,\n tsconfigPath,\n });\n }\n}\n\nfunction loadProjectGraph(rootConfig: string): readonly TsConfigProject[] {\n const cached = projectGraphCache.get(rootConfig);\n if (cached && projectGraphIsFresh(cached)) return cached.projects;\n\n const projects: TsConfigProject[] = [];\n const stamps = new Map<string, string | null>();\n const parsingCache = new Map<string, string>();\n const visited = new Set<string>();\n visitProject(rootConfig, projects, stamps, parsingCache, visited);\n\n if (projectGraphCache.size >= MAX_PROJECT_CACHE_ENTRIES) {\n projectGraphCache.clear();\n }\n projectGraphCache.set(rootConfig, {\n expiresAt: Date.now() + PROJECT_CACHE_TTL_MS,\n projects,\n stamps,\n });\n return projects;\n}\n\nfunction visitProject(\n filename: string,\n projects: TsConfigProject[],\n stamps: Map<string, string | null>,\n parsingCache: Map<string, string>,\n visited: Set<string>,\n) {\n filename = path.normalize(path.resolve(filename));\n if (visited.has(filename)) return;\n visited.add(filename);\n stamps.set(filename, readFileStamp(filename));\n\n let config: TsConfigResult;\n try {\n config = {\n path: filename,\n config: parseTsconfig(filename, parsingCache),\n };\n captureParsedConfigStamps(parsingCache, stamps);\n } catch {\n return;\n }\n\n // references 按声明顺序优先于 solution config 本身,匹配具体项目后再回退根配置。\n for (const reference of config.config.references ?? []) {\n const referenced = resolveReference(filename, reference.path);\n if (referenced) {\n visitProject(referenced, projects, stamps, parsingCache, visited);\n }\n }\n\n try {\n projects.push({\n config,\n filesMatcher: createFilesMatcher(config),\n pathsMatcher: createCompatiblePathsMatcher(config),\n });\n } catch {\n // 无效的 include、exclude 或 paths 与当前项目不提供 alias 等价。\n }\n}\n\nfunction captureParsedConfigStamps(\n parsingCache: ReadonlyMap<string, unknown>,\n stamps: Map<string, string | null>,\n) {\n // get-tsconfig 的共享缓存会列出 extends 链读取过的文件,一并纳入失效判断。\n for (const key of parsingCache.keys()) {\n if (\n !key.startsWith(READ_FILE_CACHE_PREFIX) ||\n !key.endsWith(READ_FILE_CACHE_SUFFIX)\n ) {\n continue;\n }\n const filename = key.slice(\n READ_FILE_CACHE_PREFIX.length,\n -READ_FILE_CACHE_SUFFIX.length,\n );\n stamps.set(filename, readFileStamp(filename));\n }\n}\n\nfunction createCompatiblePathsMatcher(\n config: TsConfigResult,\n): PathsMatcher | null {\n const compilerOptions = config.config.compilerOptions;\n const paths = compilerOptions?.paths;\n if (!paths || compilerOptions.baseUrl) return createPathsMatcher(config);\n\n // TS 5.x/6.x 接受无 baseUrl 时不带 \"./\" 的 target;get-tsconfig 默认更严格。\n const compatiblePaths = Object.fromEntries(\n Object.entries(paths).map(([pattern, targets]) => [\n pattern,\n targets.map((target) =>\n path.isAbsolute(target) || target.startsWith('.')\n ? target\n : `./${target}`,\n ),\n ]),\n );\n return createPathsMatcher({\n path: config.path,\n config: {\n ...config.config,\n compilerOptions: {\n ...compilerOptions,\n paths: compatiblePaths,\n },\n },\n });\n}\n\nfunction resolveReference(\n ownerConfig: string,\n referencePath: string,\n): string | null {\n const candidate = path.resolve(path.dirname(ownerConfig), referencePath);\n const attempts = [\n candidate,\n candidate.endsWith('.json') ? candidate : `${candidate}.json`,\n path.join(candidate, 'tsconfig.json'),\n ];\n return attempts.find(isFile) ?? null;\n}\n\nfunction selectProject(\n projects: readonly TsConfigProject[],\n importer: string,\n): TsConfigProject | undefined {\n const absoluteImporter = path.resolve(importer);\n return projects.find((project) =>\n projectMatchesImporter(project, absoluteImporter),\n );\n}\n\nfunction projectMatchesImporter(\n project: TsConfigProject,\n importer: string,\n): boolean {\n if (path.extname(importer) !== '.vue') {\n return Boolean(project.filesMatcher(importer));\n }\n return matchesExplicitVueInput(project.config, importer);\n}\n\nfunction matchesExplicitVueInput(\n { config, path: configPath }: TsConfigResult,\n importer: string,\n): boolean {\n const directory = path.dirname(configPath);\n const relative = normalizePath(path.relative(directory, importer));\n if (relative.startsWith('../')) return false;\n\n if (\n config.files?.some(\n (file) => path.resolve(directory, file) === path.resolve(importer),\n )\n ) {\n return true;\n }\n if (config.exclude?.some((pattern) => matchesExclude(relative, pattern))) {\n return false;\n }\n return Boolean(\n config.include?.some(\n (pattern) =>\n pattern.includes('.vue') &&\n picomatch.isMatch(relative, normalizePath(pattern), { dot: true }),\n ),\n );\n}\n\nfunction matchesExclude(relative: string, pattern: string): boolean {\n pattern = normalizePath(pattern).replace(/\\/+$/, '');\n return (\n picomatch.isMatch(relative, pattern, { dot: true }) ||\n picomatch.isMatch(relative, `${pattern}/**`, { dot: true })\n );\n}\n\nfunction projectGraphIsFresh(cached: CachedProjectGraph): boolean {\n if (cached.expiresAt <= Date.now()) return false;\n for (const [filename, stamp] of cached.stamps) {\n if (readFileStamp(filename) !== stamp) return false;\n }\n return true;\n}\n\nfunction readFileStamp(filename: string): string | null {\n try {\n const stat = fs.statSync(filename);\n return stat.isFile()\n ? `${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`\n : null;\n } catch {\n return null;\n }\n}\n\nfunction isFile(filename: string): boolean {\n try {\n return fs.statSync(filename).isFile();\n } catch {\n return false;\n }\n}\n\nfunction normalizePath(filename: string): string {\n return filename.replaceAll('\\\\', '/');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { createTsconfigResolver } from './tsconfig-projects.js';\n\nconst SOURCE_EXTENSIONS = [\n '',\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mts',\n '.cts',\n '.mjs',\n '.cjs',\n] as const;\nconst DIRECTORY_STAMP_TTL_MS = 250;\nconst MAX_PROBE_CACHE_ENTRIES = 10_000;\n\ninterface CachedDirectoryStamp {\n expiresAt: number;\n value: string | null;\n}\n\ninterface CachedProbe {\n candidateDirectory: string | null;\n parentDirectory: string | null;\n resolved: string | null;\n}\n\nconst directoryStampCache = new Map<string, CachedDirectoryStamp>();\nconst probeCache = new Map<string, CachedProbe>();\n\nexport function createImportResolver(tsconfigPath?: string) {\n const resolveTsconfig = createTsconfigResolver(tsconfigPath);\n\n return (specifier: string, importer: string): string | null => {\n if (specifier === 'virtual:ai-i18n') return specifier;\n if (specifier.startsWith('.')) {\n return probeSource(path.resolve(path.dirname(importer), specifier));\n }\n for (const candidate of resolveTsconfig(specifier, importer)) {\n const resolved = probeSource(candidate);\n if (resolved) return resolved;\n }\n return null;\n };\n}\n\nfunction probeSource(candidate: string): string | null {\n candidate = path.normalize(candidate);\n const cached = probeCache.get(candidate);\n if (\n cached &&\n cached.parentDirectory === readDirectoryStamp(path.dirname(candidate)) &&\n cached.candidateDirectory === readDirectoryStamp(candidate)\n ) {\n return cached.resolved;\n }\n\n let resolved: string | null = null;\n for (const extension of SOURCE_EXTENSIONS) {\n const file = `${candidate}${extension}`;\n if (isFile(file)) {\n resolved = path.normalize(file);\n break;\n }\n }\n if (!resolved) {\n for (const extension of SOURCE_EXTENSIONS.slice(1)) {\n const file = path.join(candidate, `index${extension}`);\n if (isFile(file)) {\n resolved = path.normalize(file);\n break;\n }\n }\n }\n if (probeCache.size >= MAX_PROBE_CACHE_ENTRIES) probeCache.clear();\n probeCache.set(candidate, {\n parentDirectory: readDirectoryStamp(path.dirname(candidate)),\n candidateDirectory: readDirectoryStamp(candidate),\n resolved,\n });\n return resolved;\n}\n\nfunction isFile(file: string) {\n try {\n return fs.statSync(file).isFile();\n } catch {\n return false;\n }\n}\n\nfunction readDirectoryStamp(directory: string): string | null {\n const now = Date.now();\n const cached = directoryStampCache.get(directory);\n if (cached && cached.expiresAt > now) return cached.value;\n let value: string | null = null;\n try {\n const stat = fs.statSync(directory);\n if (stat.isDirectory()) {\n value = `${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`;\n }\n } catch {\n // 不存在的目录也要缓存,避免重复的失败探测。\n }\n if (directoryStampCache.size >= MAX_PROBE_CACHE_ENTRIES) {\n directoryStampCache.clear();\n }\n directoryStampCache.set(directory, {\n expiresAt: now + DIRECTORY_STAMP_TTL_MS,\n value,\n });\n return value;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n AI_I18N_VIRTUAL_MODULE_ID,\n Analyzer,\n extractMessages,\n findInvalidDefineI18nMessagesReferences,\n findTranslationCalls,\n findUnboundCalls,\n validateRecommendedUsage,\n type Module,\n type ExtractWarningCode,\n type RecommendedUsageCode,\n type AnalysisLanguage,\n type TranslationCall,\n type TranslationHookBinding,\n} from '@ai-i18n/analyzer';\nimport { createImportResolver } from './resolve-import.js';\n\nexport interface StaticArgsWarning {\n code: ExtractWarningCode | RecommendedUsageCode;\n line: number;\n column: number;\n message: string;\n}\n\nexport interface StaticAnalysisResult {\n warnings: StaticArgsWarning[];\n translationCalls: TranslationCall[];\n}\n\nexport type AutoImportApi = 't' | 'tRef' | 'useI18n';\nexport type AutoImportOption = boolean | readonly AutoImportApi[];\n\ninterface AutoImportBindings {\n t: boolean;\n tRef: boolean;\n useI18n: boolean;\n}\n\nconst POTENTIAL_TRANSLATION_RE =\n /virtual:ai-i18n|\\b(?:t|tRef|useI18n|defineI18nMessages)\\b/;\n\nexport function analyzeStaticArgs(\n code: string,\n filename: string,\n tsconfigPath?: string,\n lang?: AnalysisLanguage,\n autoImport: AutoImportOption = false,\n maxStaticCandidates = Number.POSITIVE_INFINITY,\n): StaticArgsWarning[] {\n return analyzeStaticSource(\n code,\n filename,\n tsconfigPath,\n lang,\n autoImport,\n maxStaticCandidates,\n ).warnings;\n}\n\nexport function analyzeStaticSource(\n code: string,\n filename: string,\n tsconfigPath?: string,\n lang?: AnalysisLanguage,\n autoImport: AutoImportOption = false,\n maxStaticCandidates = Number.POSITIVE_INFINITY,\n): StaticAnalysisResult {\n if (!hasPotentialTranslationCandidate(code)) {\n return { warnings: [], translationCalls: [] };\n }\n const autoImports = normalizeAutoImports(autoImport);\n const resolve = createImportResolver(tsconfigPath);\n const analyzer = new Analyzer({ resolve });\n analyzer.addFile(\n AI_I18N_VIRTUAL_MODULE_ID,\n 'export function t(source) { return source } export function tRef(source) { return source }',\n );\n const entryPath = normalizeFilename(filename);\n const entry = analyzer.addFile(entryPath, code, lang ? { lang } : undefined);\n if (!hasTranslationCandidate(entry, autoImports)) {\n return { warnings: [], translationCalls: [] };\n }\n loadDependencies(analyzer, entry, resolve);\n analyzer.link();\n const hooks = translationHooks(autoImports);\n const extraction = extractMessages(\n entry,\n AI_I18N_VIRTUAL_MODULE_ID,\n hooks,\n autoImports.t || autoImports.tRef,\n maxStaticCandidates,\n ).warnings;\n const recommended = validateRecommendedUsage(\n entry,\n AI_I18N_VIRTUAL_MODULE_ID,\n hooks,\n autoImports.t || autoImports.tRef,\n );\n const warnings = recommended.length\n ? [\n ...extraction.filter((warning) => warning.code === 'parse-error'),\n ...extraction.filter(\n (warning) => warning.code === 'static-candidate-limit',\n ),\n ...recommended,\n ]\n : extraction;\n return {\n warnings: warnings.map(({ code: warningCode, line, column, message }) => ({\n code: warningCode,\n line,\n column,\n message,\n })),\n translationCalls: findTranslationCalls(\n entry,\n AI_I18N_VIRTUAL_MODULE_ID,\n hooks,\n autoImports.t || autoImports.tRef,\n ),\n };\n}\n\nexport function hasPotentialTranslationCandidate(code: string): boolean {\n return POTENTIAL_TRANSLATION_RE.test(code);\n}\n\nexport function normalizeAutoImports(\n autoImport: AutoImportOption | undefined,\n): AutoImportBindings {\n if (typeof autoImport === 'boolean') {\n // boolean 保留跨框架的历史语义;Vue-only tRef 由 Vue preset 显式声明。\n return { t: autoImport, tRef: false, useI18n: autoImport };\n }\n return {\n t: autoImport?.includes('t') ?? false,\n tRef: autoImport?.includes('tRef') ?? false,\n useI18n: autoImport?.includes('useI18n') ?? false,\n };\n}\n\nfunction hasTranslationCandidate(\n module: Module,\n autoImports: AutoImportBindings,\n): boolean {\n const unbound = ['defineI18nMessages'];\n if (autoImports.t) unbound.push('t');\n if (autoImports.tRef) unbound.push('tRef');\n if (autoImports.useI18n) unbound.push('useI18n');\n return (\n module.imports.some(\n (item) =>\n !item.typeOnly &&\n (item.specifier === AI_I18N_VIRTUAL_MODULE_ID ||\n item.name === 't' ||\n item.name === 'tRef' ||\n item.name === 'useI18n'),\n ) ||\n findInvalidDefineI18nMessagesReferences(module).length > 0 ||\n findUnboundCalls(module, new Set(unbound)).length > 0\n );\n}\n\nfunction translationHooks(\n autoImports: AutoImportBindings,\n): readonly TranslationHookBinding[] {\n return [\n {\n module: AI_I18N_VIRTUAL_MODULE_ID,\n hook: 'useI18n',\n property: 't',\n autoImport: autoImports.useI18n,\n },\n ];\n}\n\nfunction loadDependencies(\n analyzer: Analyzer,\n entry: Module,\n resolve: (specifier: string, importer: string) => string | null,\n) {\n const queue = [entry];\n const visited = new Set<string>();\n while (queue.length) {\n const module = queue.shift()!;\n if (visited.has(module.path)) continue;\n visited.add(module.path);\n for (const item of module.imports) {\n const resolved = resolve(item.specifier, module.path);\n if (!resolved || resolved === AI_I18N_VIRTUAL_MODULE_ID) continue;\n let dependency = analyzer.module(resolved);\n if (!dependency) {\n try {\n dependency = analyzer.addFile(\n resolved,\n fs.readFileSync(resolved, 'utf8'),\n );\n } catch {\n continue;\n }\n }\n queue.push(dependency);\n }\n }\n}\n\nfunction normalizeFilename(filename: string) {\n return filename.startsWith('<')\n ? path.resolve('eslint-input.ts')\n : path.resolve(filename);\n}\n","import { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { diagnosticMessage } from '@ai-i18n/analyzer';\nimport {\n analyzeVueSource,\n type VueAnalysisSource,\n type VueCompiler,\n} from '@ai-i18n/analyzer/vue';\n\ninterface VueParserServices {\n getDocumentFragment?: () => unknown;\n}\n\ntype Require = ReturnType<typeof createRequire>;\ntype NodeVueCompiler = VueCompiler & {\n registerTS?: (loadTypeScript: () => unknown) => void;\n};\n\nconst packageRequire = createRequire(import.meta.url);\n\nexport function createVueAnalysisSource(\n source: string,\n filename: string,\n parserServices: VueParserServices,\n): VueAnalysisSource {\n if (!parserServices.getDocumentFragment?.()) {\n throw new Error(\n diagnosticMessage(\n '检查 .vue 文件需要配置 vue-eslint-parser。',\n 'Configure vue-eslint-parser to lint .vue files.',\n ),\n );\n }\n\n try {\n // Vue preset 真正处理 SFC 时才加载编译器,不影响 React/Vanilla 项目。\n const compiler = loadVueCompiler(filename);\n return analyzeVueSource(source, filename, compiler);\n } catch (error) {\n if (isMissingVueCompiler(error)) {\n throw new Error(\n diagnosticMessage(\n '检查 .vue 文件需要安装 @vue/compiler-sfc。',\n 'Install @vue/compiler-sfc to lint .vue files.',\n ),\n );\n }\n throw error;\n }\n}\n\nfunction loadVueCompiler(filename: string): VueCompiler {\n const hostRequire = createRequire(path.resolve(filename));\n // 优先复用宿主 Vue 的 Node wrapper,它会为 imported types 注册 TypeScript。\n const compiler = (tryRequire(hostRequire, 'vue/compiler-sfc') ??\n tryRequire(packageRequire, 'vue/compiler-sfc') ??\n tryRequire(hostRequire, '@vue/compiler-sfc') ??\n packageRequire('@vue/compiler-sfc')) as NodeVueCompiler;\n compiler.registerTS?.(() => {\n const typescript =\n tryResolve(hostRequire, 'typescript') ??\n tryResolve(packageRequire, 'typescript');\n return packageRequire(typescript ?? 'typescript');\n });\n return compiler;\n}\n\nfunction tryRequire(require: Require, id: string): unknown {\n const resolved = tryResolve(require, id);\n return resolved ? require(resolved) : null;\n}\n\nfunction tryResolve(require: Require, id: string): string | null {\n try {\n return require.resolve(id);\n } catch (error) {\n if (isMissingVueCompiler(error)) return null;\n throw error;\n }\n}\n\nfunction isMissingVueCompiler(error: unknown): boolean {\n return (\n error instanceof Error &&\n 'code' in error &&\n (error.code === 'MODULE_NOT_FOUND' ||\n error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')\n );\n}\n","import type { Rule } from 'eslint';\nimport {\n analyzeStaticSource,\n hasPotentialTranslationCandidate,\n normalizeAutoImports,\n type AutoImportOption,\n type StaticAnalysisResult,\n type StaticArgsWarning,\n} from './analyze.js';\nimport { diagnosticMessage, type TranslationCall } from '@ai-i18n/analyzer';\nimport { createVueAnalysisSource } from './vue-sfc.js';\n\ninterface RuleAnalysisOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n}\n\ninterface CachedAnalysis {\n source: ReturnType<typeof createAnalysisSource>;\n analyses: Map<string, StaticAnalysisResult>;\n}\n\nconst DEFAULT_CANDIDATE_LIMIT = 1_000;\nconst cache = new WeakMap<object, CachedAnalysis>();\nconst reportedAnalysisFailures = new WeakSet<object>();\nconst EMPTY_ANALYSIS: StaticAnalysisResult = {\n warnings: [],\n translationCalls: [],\n};\n\nexport function analyzeRuleContext(\n context: Rule.RuleContext,\n options: RuleAnalysisOptions,\n maxStaticCandidates = Number.POSITIVE_INFINITY,\n): StaticArgsWarning[] {\n return analyzeRuleContextResult(context, options, maxStaticCandidates)\n .warnings;\n}\n\nexport function analyzeTranslationCalls(\n context: Rule.RuleContext,\n options: RuleAnalysisOptions,\n): TranslationCall[] {\n return analyzeRuleContextResult(context, options, DEFAULT_CANDIDATE_LIMIT)\n .translationCalls;\n}\n\nexport function reportAnalysisFailureOnce(\n context: Rule.RuleContext,\n node: Rule.Node,\n error: unknown,\n): void {\n if (reportedAnalysisFailures.has(context.sourceCode)) return;\n reportedAnalysisFailures.add(context.sourceCode);\n const detail = error instanceof Error ? error.message : String(error);\n context.report({\n node,\n message: diagnosticMessage(\n `静态分析失败:${detail}`,\n `Static analysis failed: ${detail}`,\n ),\n });\n}\n\nfunction analyzeRuleContextResult(\n context: Rule.RuleContext,\n options: RuleAnalysisOptions,\n maxStaticCandidates: number,\n): StaticAnalysisResult {\n if (!hasPotentialTranslationCandidate(context.sourceCode.text)) {\n return EMPTY_ANALYSIS;\n }\n const cached: CachedAnalysis = cache.get(context.sourceCode) ?? {\n source: createAnalysisSource(context),\n analyses: new Map<string, StaticAnalysisResult>(),\n };\n cache.set(context.sourceCode, cached);\n\n const analyze = (limit: number) => {\n const autoImports = normalizeAutoImports(options.autoImport);\n const key = JSON.stringify([\n options.tsconfigPath ?? null,\n autoImports.t,\n autoImports.tRef,\n autoImports.useI18n,\n limit,\n ]);\n const existing = cached.analyses.get(key);\n if (existing) return existing;\n const result = analyzeStaticSource(\n cached.source.code,\n context.filename,\n options.tsconfigPath,\n cached.source.lang,\n options.autoImport,\n limit,\n );\n const analysis = {\n warnings: result.warnings.map((warning) => ({\n ...warning,\n ...cached.source.mapLocation(warning),\n })),\n translationCalls: result.translationCalls.map((call) => ({\n ...call,\n ...cached.source.mapLocation(call),\n })),\n };\n cached.analyses.set(key, analysis);\n return analysis;\n };\n\n if (Number.isFinite(maxStaticCandidates)) {\n return analyze(maxStaticCandidates);\n }\n const preflight = analyze(DEFAULT_CANDIDATE_LIMIT);\n return preflight.warnings.some(\n (warning) => warning.code === 'static-candidate-limit',\n )\n ? analyze(Number.POSITIVE_INFINITY)\n : preflight;\n}\n\nfunction createAnalysisSource(context: Rule.RuleContext) {\n return context.filename.endsWith('.vue')\n ? createVueAnalysisSource(\n context.sourceCode.text,\n context.filename,\n context.sourceCode.parserServices,\n )\n : {\n code: context.sourceCode.text,\n lang: undefined,\n mapLocation: (location: { line: number; column: number }) => location,\n };\n}\n","import type { TranslationCall } from '@ai-i18n/analyzer';\nimport type { Rule } from 'eslint';\nimport type { AutoImportOption } from '../analyze.js';\nimport { analyzeTranslationCalls } from '../rule-analysis.js';\n\nexport interface TranslationRuleOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n}\n\nexport interface TranslationCandidate {\n kind: TranslationCall['kind'];\n node: Rule.Node;\n}\n\nexport interface MatchedTranslationCall extends TranslationCandidate {\n call: TranslationCall;\n}\n\nexport function matchTranslationCalls(\n context: Rule.RuleContext,\n options: TranslationRuleOptions,\n candidates: readonly TranslationCandidate[],\n): MatchedTranslationCall[] {\n const calls = new Map(\n analyzeTranslationCalls(context, options).map((call) => [\n locationKey(call.line, call.column, call.kind),\n call,\n ]),\n );\n return candidates.flatMap((candidate) => {\n const location = candidate.node.loc?.start;\n if (!location) return [];\n const call = calls.get(\n locationKey(location.line, location.column, candidate.kind),\n );\n return call ? [{ ...candidate, call }] : [];\n });\n}\n\nfunction locationKey(\n line: number,\n column: number,\n kind: TranslationCall['kind'],\n): string {\n return `${kind}:${line}:${column}`;\n}\n","import { diagnosticMessage } from '@ai-i18n/analyzer';\nimport type { Rule } from 'eslint';\nimport { reportAnalysisFailureOnce } from '../rule-analysis.js';\nimport {\n matchTranslationCalls,\n type TranslationCandidate,\n type TranslationRuleOptions,\n} from './translation-call.js';\n\nconst FUNCTION_TYPES = new Set([\n 'ArrowFunctionExpression',\n 'FunctionDeclaration',\n 'FunctionExpression',\n]);\n\nexport const noEagerTranslation: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: '警告不会随语言切换更新的提前求值翻译结果',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n eagerTranslation: diagnosticMessage(\n '初始化期间保存的 t() 结果不会随语言切换更新。请改为函数或 Getter,在使用时调用 t();Vue setup 中的响应式展示值可使用 tRef()。',\n 'A t() result stored during initialization will not update when the language changes. Evaluate it lazily in a function or getter; reactive display values in Vue setup can use tRef().',\n ),\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as TranslationRuleOptions;\n const isVueSfc = context.filename.toLowerCase().endsWith('.vue');\n const candidates: TranslationCandidate[] = [];\n return {\n CallExpression(node) {\n candidates.push({ kind: 'call', node });\n },\n TaggedTemplateExpression(node) {\n candidates.push({ kind: 'tagged-template', node });\n },\n 'Program:exit'(program) {\n let matches;\n try {\n matches = matchTranslationCalls(context, options, candidates);\n } catch (error) {\n reportAnalysisFailureOnce(context, program as Rule.Node, error);\n return;\n }\n for (const { call, node } of matches) {\n if (call.origin === 'vue-ref') continue;\n if (!storesOutsideFunction(node, context, isVueSfc)) continue;\n context.report({ node, messageId: 'eagerTranslation' });\n }\n },\n };\n },\n};\n\nfunction storesOutsideFunction(\n node: Rule.Node,\n context: Rule.RuleContext,\n isVueSfc: boolean,\n): boolean {\n let current = node as ParentNode;\n let storesResult = false;\n while (current.parent) {\n const parent = current.parent;\n if (FUNCTION_TYPES.has(parent.type)) {\n // 普通 setup() 与 <script setup> 一样只执行一次;其他函数仍视为延迟求值。\n const returnsExpression =\n parent.type === 'ArrowFunctionExpression' && parent.body === current;\n return (\n isVueComponentSetup(parent, context, isVueSfc) &&\n (storesResult || returnsExpression)\n );\n }\n if (storesTranslationResult(parent, current)) storesResult = true;\n if (parent.type === 'Program') return storesResult;\n current = parent;\n }\n return storesResult;\n}\n\nfunction isVueComponentSetup(\n node: ParentNode,\n context: Rule.RuleContext,\n isVueSfc: boolean,\n): boolean {\n const directOwner = node.parent;\n if (\n directOwner?.type === 'CallExpression' &&\n directOwner.arguments?.[0] === node\n ) {\n return isImportedDefineComponent(directOwner.callee, context);\n }\n\n const property = node.parent;\n if (\n property?.type !== 'Property' ||\n property.value !== node ||\n property.computed ||\n propertyName(property.key) !== 'setup'\n ) {\n return false;\n }\n\n const options = property.parent;\n if (options?.type !== 'ObjectExpression') return false;\n const owner = options.parent;\n if (\n isVueSfc &&\n owner?.type === 'ExportDefaultDeclaration' &&\n owner.declaration === options\n ) {\n return true;\n }\n return (\n owner?.type === 'CallExpression' &&\n owner.arguments?.[0] === options &&\n isImportedDefineComponent(owner.callee, context)\n );\n}\n\nfunction propertyName(node: unknown): string | undefined {\n const key = node as { name?: string; type?: string; value?: unknown };\n if (key?.type === 'Identifier') return key.name;\n return key?.type === 'Literal' && typeof key.value === 'string'\n ? key.value\n : undefined;\n}\n\nfunction isImportedDefineComponent(\n node: unknown,\n context: Rule.RuleContext,\n): boolean {\n const callee = node as {\n computed?: boolean;\n name?: string;\n object?: unknown;\n property?: unknown;\n type?: string;\n };\n if (callee?.type === 'Identifier') {\n return isVueImport(callee, context, 'ImportSpecifier');\n }\n return (\n callee?.type === 'MemberExpression' &&\n !callee.computed &&\n propertyName(callee.property) === 'defineComponent' &&\n isVueImport(callee.object, context, 'ImportNamespaceSpecifier')\n );\n}\n\nfunction isVueImport(\n node: unknown,\n context: Rule.RuleContext,\n specifierType: 'ImportNamespaceSpecifier' | 'ImportSpecifier',\n): boolean {\n const identifier = node as Rule.Node & { name?: string };\n if (identifier?.type !== 'Identifier' || !identifier.name) return false;\n\n let scope = context.sourceCode.getScope(identifier);\n while (true) {\n const variable = scope.set.get(identifier.name);\n if (!variable) {\n if (!scope.upper) return false;\n scope = scope.upper;\n continue;\n }\n return variable.defs.some((definition) => {\n if (\n definition.type !== 'ImportBinding' ||\n definition.node.type !== specifierType ||\n definition.parent.source.value !== 'vue'\n ) {\n return false;\n }\n return (\n specifierType === 'ImportNamespaceSpecifier' ||\n propertyName((definition.node as { imported?: unknown }).imported) ===\n 'defineComponent'\n );\n });\n }\n}\n\nfunction storesTranslationResult(\n parent: ParentNode,\n child: ParentNode,\n): boolean {\n if (\n (parent as { type: string }).type === 'AccessorProperty' &&\n parent.value === child\n ) {\n return true;\n }\n switch (parent.type) {\n case 'VariableDeclarator':\n return parent.init === child;\n case 'AssignmentExpression':\n case 'AssignmentPattern':\n return parent.right === child;\n case 'PropertyDefinition':\n return parent.value === child;\n case 'ReturnStatement':\n return parent.argument === child;\n case 'ExportDefaultDeclaration':\n return parent.declaration === child;\n default:\n return false;\n }\n}\n\ntype ParentNode = Rule.Node & {\n arguments?: unknown[];\n argument?: unknown;\n body?: unknown;\n callee?: unknown;\n computed?: boolean;\n parent: ParentNode | null;\n declaration?: unknown;\n init?: unknown;\n key?: unknown;\n right?: unknown;\n value?: unknown;\n};\n","import type { Rule } from 'eslint';\nimport { diagnosticMessage } from '@ai-i18n/analyzer';\n\nconst RUNTIME_MODULE = 'virtual:ai-i18n';\nconst AUTO_IMPORT_APIS = [\n 't',\n 'tRef',\n 'useI18n',\n 'setLang',\n 'getLang',\n 'getLangs',\n 'getLangLoadState',\n 'subscribe',\n] as const;\n\ntype AutoImportApi = (typeof AUTO_IMPORT_APIS)[number];\n\ninterface RuleOptions {\n autoImport: readonly AutoImportApi[];\n}\n\ntype ImportSpecifierNode = Extract<Rule.Node, { type: 'ImportSpecifier' }> & {\n importKind?: 'type' | 'value';\n};\n\ntype ImportDeclarationNode = Extract<\n Rule.Node,\n { type: 'ImportDeclaration' }\n> & {\n importKind?: 'type' | 'value';\n};\n\nexport const noRedundantAutoImport: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: '禁止显式导入已由 ai-i18n autoImport 注入的 Runtime API',\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n autoImport: {\n type: 'array',\n items: { enum: AUTO_IMPORT_APIS },\n minItems: 1,\n uniqueItems: true,\n },\n },\n required: ['autoImport'],\n additionalProperties: false,\n },\n ],\n messages: {\n redundantImport: diagnosticMessage(\n 'ai-i18n autoImport 已注入以下 API:{{names}}。请删除来自 virtual:ai-i18n 的冗余导入。',\n 'ai-i18n autoImport injects these APIs: {{names}}. Remove the redundant imports from virtual:ai-i18n.',\n ),\n },\n },\n create(context) {\n const enabled = new Set(\n ((context.options[0] as RuleOptions | undefined)?.autoImport ??\n []) as readonly string[],\n );\n return {\n ImportDeclaration(rawNode) {\n const node = rawNode as ImportDeclarationNode;\n if (\n node.importKind === 'type' ||\n node.source.value !== RUNTIME_MODULE\n ) {\n return;\n }\n const named = node.specifiers.filter(\n (specifier): specifier is ImportSpecifierNode =>\n specifier.type === 'ImportSpecifier',\n );\n const redundant = named.filter((specifier) => {\n const imported = importedName(specifier);\n return (\n specifier.importKind !== 'type' &&\n imported !== null &&\n imported === specifier.local.name &&\n enabled.has(imported)\n );\n });\n if (!redundant.length) return;\n\n const names = redundant\n .map((specifier) => importedName(specifier))\n .filter((name): name is string => name !== null);\n const fix = createFix(context, node, named, redundant);\n context.report({\n node,\n messageId: 'redundantImport',\n data: { names: names.join(', ') },\n fix,\n });\n },\n };\n },\n};\n\nfunction importedName(specifier: ImportSpecifierNode): string | null {\n if (\n specifier.imported.type === 'Identifier' &&\n 'name' in specifier.imported\n ) {\n return specifier.imported.name;\n }\n return typeof specifier.imported.value === 'string'\n ? specifier.imported.value\n : null;\n}\n\nfunction createFix(\n context: Rule.RuleContext,\n node: ImportDeclarationNode,\n named: ImportSpecifierNode[],\n redundant: ImportSpecifierNode[],\n): Rule.ReportFixer | undefined {\n // import 内部注释可能携带迁移说明,自动修复时不能静默丢弃。\n if (context.sourceCode.getCommentsInside(node).length) return undefined;\n const redundantSet = new Set<unknown>(redundant);\n const remaining = node.specifiers.filter(\n (specifier) => !redundantSet.has(specifier),\n );\n if (!remaining.length) {\n return (fixer) => fixer.remove(node);\n }\n\n const remainingNamed = named.filter(\n (specifier) => !redundantSet.has(specifier),\n );\n const firstNamed = named[0];\n const lastNamed = named.at(-1);\n if (!firstNamed || !lastNamed) return undefined;\n const openBrace = context.sourceCode.getTokenBefore(firstNamed);\n const closeBrace = context.sourceCode.getTokenAfter(lastNamed);\n if (openBrace?.value !== '{' || closeBrace?.value !== '}') return undefined;\n\n if (remainingNamed.length) {\n const [, openEnd] = context.sourceCode.getRange(openBrace);\n const [closeStart] = context.sourceCode.getRange(closeBrace);\n const replacement = remainingNamed\n .map((specifier) => context.sourceCode.getText(specifier))\n .join(', ');\n return (fixer) =>\n fixer.replaceTextRange([openEnd, closeStart], ` ${replacement} `);\n }\n\n const beforeOpen = context.sourceCode.getTokenBefore(openBrace);\n if (beforeOpen?.value !== ',') return undefined;\n const [commaStart] = context.sourceCode.getRange(beforeOpen);\n const [, closeEnd] = context.sourceCode.getRange(closeBrace);\n return (fixer) => fixer.removeRange([commaStart, closeEnd]);\n}\n","import { diagnosticMessage } from '@ai-i18n/analyzer';\nimport type { Rule } from 'eslint';\nimport { reportAnalysisFailureOnce } from '../rule-analysis.js';\nimport {\n matchTranslationCalls,\n type TranslationCandidate,\n type TranslationRuleOptions,\n} from './translation-call.js';\n\nconst FUNCTION_TYPES = new Set([\n 'ArrowFunctionExpression',\n 'FunctionDeclaration',\n 'FunctionExpression',\n]);\nconst IMMEDIATE_CONSOLE_METHODS = new Set([\n 'debug',\n 'error',\n 'info',\n 'log',\n 'warn',\n]);\n\nexport const noUnsubscribedT: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: '检查组件渲染路径中的翻译 API 生命周期',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n unsubscribedT: diagnosticMessage(\n '组件渲染期间使用顶层 t 不会订阅语言状态,语言切换不会主动刷新,缓存的渲染结果也可能继续使用旧译文。请使用 useI18n() 返回的 t。',\n 'Top-level t does not subscribe the component to language updates, so language changes do not trigger a render and cached render results may remain stale. Use the t returned by useI18n().',\n ),\n renderTRef: diagnosticMessage(\n '不要在组件渲染或 template 中调用 tRef(),否则每次渲染都会创建新的 computed。请在 setup 中创建一次并使用返回的 Ref;渲染函数中请直接使用 useI18n() 返回的 t。',\n 'Do not call tRef() during component rendering or in a template because each render creates a new computed. Create it once in setup and use the returned Ref; in render functions, call the t returned by useI18n() directly.',\n ),\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as TranslationRuleOptions;\n const candidates: TranslationCandidate[] = [];\n const jsxOwners = new Set<ParentNode>();\n const templateNodes = new Set<Rule.Node>();\n const collectJsxOwner = (node: Rule.Node) => {\n const owner = nearestFunction(node);\n if (owner) jsxOwners.add(owner);\n };\n const scriptVisitor: Rule.RuleListener = {\n CallExpression(node) {\n candidates.push({ kind: 'call', node });\n },\n TaggedTemplateExpression(node) {\n candidates.push({ kind: 'tagged-template', node });\n },\n JSXElement: collectJsxOwner,\n JSXFragment: collectJsxOwner,\n 'Program:exit'(program) {\n let matches;\n try {\n matches = matchTranslationCalls(context, options, candidates);\n } catch (error) {\n reportAnalysisFailureOnce(context, program as Rule.Node, error);\n return;\n }\n for (const { call, node } of matches) {\n if (call.origin === 'vue-ref') {\n if (templateNodes.has(node)) {\n if (!isVueTemplateEventHandler(node)) {\n context.report({ node, messageId: 'renderTRef' });\n }\n continue;\n }\n const owner = nearestFunction(node);\n if (owner && jsxOwners.has(owner)) {\n context.report({ node, messageId: 'renderTRef' });\n }\n continue;\n }\n if (call.origin !== 'runtime') continue;\n if (templateNodes.has(node)) {\n if (!isVueTemplateEventHandler(node)) {\n context.report({ node, messageId: 'unsubscribedT' });\n }\n continue;\n }\n // ponytail: 先检查直接拥有 JSX 的最近函数;出现真实漏报后再引入调用图与框架数据流。\n const owner = nearestFunction(node);\n if (\n !owner ||\n !jsxOwners.has(owner) ||\n isImmediateConsoleEffect(node)\n ) {\n continue;\n }\n context.report({ node, messageId: 'unsubscribedT' });\n }\n },\n };\n const parserServices = context.sourceCode\n .parserServices as VueParserServices;\n if (!parserServices.defineTemplateBodyVisitor) return scriptVisitor;\n return parserServices.defineTemplateBodyVisitor(\n {\n CallExpression(node) {\n candidates.push({ kind: 'call', node });\n templateNodes.add(node);\n },\n TaggedTemplateExpression(node) {\n candidates.push({ kind: 'tagged-template', node });\n templateNodes.add(node);\n },\n },\n scriptVisitor,\n // 模板默认在 Program:exit 才遍历;提前到 Program,确保汇总前已收集候选。\n { templateBodyTriggerSelector: 'Program' },\n );\n },\n};\n\nfunction nearestFunction(node: Rule.Node): ParentNode | null {\n let current = node as ParentNode;\n while (current.parent) {\n current = current.parent;\n if (FUNCTION_TYPES.has(current.type)) return current;\n }\n return null;\n}\n\nfunction isImmediateConsoleEffect(node: Rule.Node): boolean {\n const call = (node as ParentNode).parent;\n if (\n call?.type !== 'CallExpression' ||\n call.parent?.type !== 'ExpressionStatement' ||\n !call.arguments.some((argument) => argument === node) ||\n call.callee.type !== 'MemberExpression' ||\n call.callee.object.type !== 'Identifier' ||\n call.callee.object.name !== 'console'\n ) {\n return false;\n }\n const property = call.callee.property;\n const method =\n !call.callee.computed && property.type === 'Identifier'\n ? property.name\n : call.callee.computed &&\n property.type === 'Literal' &&\n typeof property.value === 'string'\n ? property.value\n : null;\n return method !== null && IMMEDIATE_CONSOLE_METHODS.has(method);\n}\n\nfunction isVueTemplateEventHandler(node: Rule.Node): boolean {\n let current = node as unknown as TemplateParentNode;\n while (current.parent) {\n current = current.parent;\n if (current.type === 'VOnExpression') return true;\n }\n return false;\n}\n\ntype ParentNode = Rule.Node & {\n parent: ParentNode | null;\n};\n\ninterface TemplateParentNode {\n type: string;\n parent: TemplateParentNode | null;\n}\n\ninterface VueParserServices {\n defineTemplateBodyVisitor?(\n templateBodyVisitor: Record<string, (node: Rule.Node) => void>,\n scriptVisitor: Rule.RuleListener,\n options: { templateBodyTriggerSelector: 'Program' | 'Program:exit' },\n ): Rule.RuleListener;\n}\n","import type { Rule } from 'eslint';\nimport type { AutoImportOption } from '../analyze.js';\nimport {\n analyzeRuleContext,\n reportAnalysisFailureOnce,\n} from '../rule-analysis.js';\n\ninterface RuleOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n maxStaticCandidates?: number;\n}\n\nexport const staticCandidateLimit: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: '警告单个 t() 展开的静态候选数量过多',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n maxStaticCandidates: {\n type: 'integer',\n minimum: 1,\n maximum: Number.MAX_SAFE_INTEGER,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n candidateLimit: '{{reason}}',\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as RuleOptions;\n return {\n 'Program:exit'(node) {\n try {\n const warnings = analyzeRuleContext(\n context,\n options,\n options.maxStaticCandidates ?? 1_000,\n );\n for (const warning of warnings) {\n if (warning.code !== 'static-candidate-limit') continue;\n context.report({\n node,\n loc: {\n start: warning,\n end: { line: warning.line, column: warning.column + 1 },\n },\n messageId: 'candidateLimit',\n data: { reason: warning.message },\n });\n }\n } catch (error) {\n reportAnalysisFailureOnce(context, node as Rule.Node, error);\n }\n },\n };\n },\n};\n","import type { Rule } from 'eslint';\nimport { diagnosticMessage } from '@ai-i18n/analyzer';\nimport { normalizeAutoImports, type AutoImportOption } from '../analyze.js';\nimport {\n analyzeRuleContext,\n reportAnalysisFailureOnce,\n} from '../rule-analysis.js';\n\ninterface RuleOptions {\n tsconfigPath?: string;\n autoImport?: AutoImportOption;\n}\n\ninterface AstNode {\n type: string;\n name?: string;\n importKind?: string;\n range?: [number, number];\n argument?: AstNode;\n left?: AstNode;\n elements?: Array<AstNode | null>;\n properties?: AstNode[];\n value?: AstNode;\n id?: AstNode;\n declarations?: AstNode[];\n specifiers?: AstNode[];\n local?: AstNode;\n declaration?: AstNode | null;\n body?: AstNode[];\n parent?: AstNode | null;\n}\n\ninterface VueElement extends AstNode {\n startTag?: {\n attributes?: Array<{\n directive?: boolean;\n key?: { name?: string };\n }>;\n };\n}\n\ninterface VueDocument {\n children?: AstNode[];\n}\n\ninterface VueParserServices {\n defineTemplateBodyVisitor?: (\n templateVisitor: Record<string, (node: AstNode) => void>,\n scriptVisitor: Rule.RuleListener,\n ) => Rule.RuleListener;\n getDocumentFragment?: () => VueDocument | null;\n}\n\ninterface VueReference {\n id?: AstNode;\n variable?: unknown;\n}\n\nexport const tStaticArgs: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description: '要求 virtual:ai-i18n 的 t() 参数可被静态提取',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tsconfigPath: { type: 'string' },\n autoImport: {\n anyOf: [\n { type: 'boolean' },\n {\n type: 'array',\n items: { enum: ['t', 'tRef', 'useI18n'] },\n uniqueItems: true,\n },\n ],\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n analysisFailed: '{{reason}}',\n dynamicArg: '{{reason}}',\n invalidUsage: '{{reason}}',\n templateNeedsI18n: '{{reason}}',\n },\n },\n create(context) {\n const options = (context.options[0] ?? {}) as RuleOptions;\n const scriptVisitor: Rule.RuleListener = {\n 'Program:exit'(node) {\n let warnings;\n try {\n warnings = analyzeRuleContext(context, options);\n } catch (error) {\n reportAnalysisFailureOnce(context, node as Rule.Node, error);\n return;\n }\n for (const warning of warnings) {\n const analysisFailed = warning.code === 'parse-error';\n const invalidUsage =\n warning.code !== 'dynamic-argument' &&\n warning.code !== 'unresolved-argument' &&\n !analysisFailed;\n context.report({\n node,\n loc: {\n start: warning,\n end: { line: warning.line, column: warning.column + 1 },\n },\n messageId: analysisFailed\n ? 'analysisFailed'\n : invalidUsage\n ? 'invalidUsage'\n : 'dynamicArg',\n data: {\n reason: analysisFailed\n ? diagnosticMessage(\n `静态分析失败:${warning.message}`,\n `Static analysis failed: ${warning.message}`,\n )\n : invalidUsage\n ? warning.message\n : diagnosticMessage(\n 't() 的参数无法静态提取。source 请使用静态字符串,options 请使用只包含 comment 的静态对象。',\n 'The t() arguments cannot be statically extracted. Use a static string for source and a static object containing only comment for options.',\n ),\n },\n });\n }\n },\n };\n\n if (\n !context.filename.toLowerCase().endsWith('.vue') ||\n !normalizeAutoImports(options.autoImport).t\n ) {\n return scriptVisitor;\n }\n const services = context.sourceCode\n .parserServices as unknown as VueParserServices;\n const document = services.getDocumentFragment?.();\n const scripts = document?.children?.filter(isScriptElement) ?? [];\n const setupScript = scripts.find(isScriptSetup);\n // 普通 Options API 的模板可从组件实例获得 t,无法仅凭 SFC 静态确认来源。\n if ((!setupScript && scripts.length > 0) || !document) {\n return scriptVisitor;\n }\n if (\n setupScript &&\n hasTopLevelBinding(\n context.sourceCode.ast as unknown as AstNode,\n setupScript,\n 't',\n )\n ) {\n return scriptVisitor;\n }\n if (!services.defineTemplateBodyVisitor) return scriptVisitor;\n\n const reportTemplateT = (node: AstNode) => {\n const target =\n node.type === 'CallExpression'\n ? (node as AstNode & { callee?: AstNode }).callee\n : (node as AstNode & { tag?: AstNode }).tag;\n if (\n target?.type !== 'Identifier' ||\n target.name !== 't' ||\n isTemplateLocal(target)\n ) {\n return;\n }\n context.report({\n node: target as Rule.Node,\n messageId: 'templateNeedsI18n',\n data: {\n reason: diagnosticMessage(\n '模板中的裸 t 无法绑定到 ai-i18n,也不会订阅语言变化。请在 <script setup> 中声明 const { t } = useI18n()。',\n 'A bare t in the template is not bound to ai-i18n and does not subscribe to language changes. Declare const { t } = useI18n() in <script setup>.',\n ),\n },\n });\n };\n return services.defineTemplateBodyVisitor(\n {\n CallExpression: reportTemplateT,\n TaggedTemplateExpression: reportTemplateT,\n },\n scriptVisitor,\n );\n },\n};\n\nfunction isScriptElement(node: AstNode): node is VueElement {\n return node.type === 'VElement' && node.name === 'script';\n}\n\nfunction isScriptSetup(node: VueElement): boolean {\n return (\n node.startTag?.attributes?.some(\n (attribute) => !attribute.directive && attribute.key?.name === 'setup',\n ) ?? false\n );\n}\n\nfunction hasTopLevelBinding(\n program: AstNode,\n script: VueElement,\n name: string,\n): boolean {\n return (\n program.body?.some(\n (statement) =>\n isInside(statement, script) && statementDeclares(statement, name),\n ) ?? false\n );\n}\n\nfunction isInside(node: AstNode, container: AstNode): boolean {\n return Boolean(\n node.range &&\n container.range &&\n node.range[0] >= container.range[0] &&\n node.range[1] <= container.range[1],\n );\n}\n\nfunction statementDeclares(node: AstNode, name: string): boolean {\n switch (node.type) {\n case 'ImportDeclaration':\n return (\n node.importKind !== 'type' &&\n (node.specifiers?.some(\n (item) => item.importKind !== 'type' && item.local?.name === name,\n ) ??\n false)\n );\n case 'VariableDeclaration':\n return (\n node.declarations?.some((item) => patternDeclares(item.id, name)) ??\n false\n );\n case 'FunctionDeclaration':\n case 'ClassDeclaration':\n case 'TSEnumDeclaration':\n case 'TSImportEqualsDeclaration':\n return node.id?.name === name;\n case 'ExportNamedDeclaration':\n case 'ExportDefaultDeclaration':\n return node.declaration\n ? statementDeclares(node.declaration, name)\n : false;\n default:\n return false;\n }\n}\n\nfunction patternDeclares(node: AstNode | undefined, name: string): boolean {\n if (!node) return false;\n switch (node.type) {\n case 'Identifier':\n return node.name === name;\n case 'RestElement':\n return patternDeclares(node.argument, name);\n case 'AssignmentPattern':\n return patternDeclares(node.left, name);\n case 'ArrayPattern':\n return (\n node.elements?.some((item) =>\n patternDeclares(item ?? undefined, name),\n ) ?? false\n );\n case 'ObjectPattern':\n return (\n node.properties?.some((item) =>\n patternDeclares(\n item.type === 'RestElement' ? item.argument : item.value,\n name,\n ),\n ) ?? false\n );\n default:\n return false;\n }\n}\n\nfunction isTemplateLocal(node: AstNode): boolean {\n let parent = node.parent;\n while (parent && parent.type !== 'VExpressionContainer') {\n parent = parent.parent;\n }\n const references = (\n parent as (AstNode & { references?: VueReference[] }) | null | undefined\n )?.references;\n return (\n references?.some(\n (reference) =>\n reference.variable &&\n (reference.id === node ||\n (reference.id?.range?.[0] === node.range?.[0] &&\n reference.id?.range?.[1] === node.range?.[1])),\n ) ?? false\n );\n}\n","import { createRequire } from 'node:module';\nimport type { ESLint } from 'eslint';\nimport { noEagerTranslation } from './rules/no-eager-translation.js';\nimport { noRedundantAutoImport } from './rules/no-redundant-auto-import.js';\nimport { noUnsubscribedT } from './rules/no-unsubscribed-t.js';\nimport { staticCandidateLimit } from './rules/static-candidate-limit.js';\nimport { tStaticArgs } from './rules/t-static-args.js';\n\nconst { version } = createRequire(import.meta.url)('../package.json') as {\n version: string;\n};\n\nconst plugin: ESLint.Plugin = {\n meta: {\n name: '@ai-i18n/eslint-plugin',\n version,\n namespace: 'ai-i18n',\n },\n rules: {\n 'no-eager-translation': noEagerTranslation,\n 'no-redundant-auto-import': noRedundantAutoImport,\n 'no-unsubscribed-t': noUnsubscribedT,\n 'static-candidate-limit': staticCandidateLimit,\n 't-static-args': tStaticArgs,\n },\n configs: {},\n};\n\n// 规则集必须由使用者显式引入,不会修改宿主项目的 ESLint 配置。\nplugin.configs!.recommended = [\n {\n ignores: ['**/*.vue'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: { defineI18nMessages: 'readonly' },\n },\n rules: {\n 'ai-i18n/t-static-args': 'error',\n 'ai-i18n/no-eager-translation': 'warn',\n 'ai-i18n/no-unsubscribed-t': 'warn',\n 'ai-i18n/static-candidate-limit': 'warn',\n },\n },\n];\n\nplugin.configs!.vue = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,vue}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: { defineI18nMessages: 'readonly' },\n },\n rules: {\n 'ai-i18n/t-static-args': 'error',\n 'ai-i18n/no-eager-translation': 'warn',\n 'ai-i18n/no-unsubscribed-t': 'warn',\n 'ai-i18n/static-candidate-limit': 'warn',\n },\n },\n];\n\n// 自动导入 preset 必须与 Vite 各模式实际注入的 API 一一对应。\nplugin.configs!['vanilla-auto-import'] = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: {\n t: 'readonly',\n setLang: 'readonly',\n getLang: 'readonly',\n getLangs: 'readonly',\n getLangLoadState: 'readonly',\n subscribe: 'readonly',\n defineI18nMessages: 'readonly',\n },\n },\n rules: {\n 'ai-i18n/t-static-args': ['error', { autoImport: ['t'] }],\n 'ai-i18n/no-eager-translation': ['warn', { autoImport: ['t'] }],\n 'ai-i18n/static-candidate-limit': ['warn', { autoImport: ['t'] }],\n },\n },\n];\n\nplugin.configs!['vue-auto-import'] = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx,vue}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: {\n t: 'readonly',\n tRef: 'readonly',\n useI18n: 'readonly',\n defineI18nMessages: 'readonly',\n },\n },\n rules: {\n 'ai-i18n/t-static-args': [\n 'error',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n 'ai-i18n/no-eager-translation': [\n 'warn',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n 'ai-i18n/no-unsubscribed-t': [\n 'warn',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n 'ai-i18n/static-candidate-limit': [\n 'warn',\n { autoImport: ['t', 'tRef', 'useI18n'] },\n ],\n },\n },\n];\n\nplugin.configs!['react-auto-import'] = [\n {\n files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],\n plugins: { 'ai-i18n': plugin },\n languageOptions: {\n globals: {\n t: 'readonly',\n useI18n: 'readonly',\n defineI18nMessages: 'readonly',\n },\n },\n rules: {\n 'ai-i18n/t-static-args': ['error', { autoImport: ['t', 'useI18n'] }],\n 'ai-i18n/no-eager-translation': [\n 'warn',\n { autoImport: ['t', 'useI18n'] },\n ],\n 'ai-i18n/no-unsubscribed-t': ['warn', { autoImport: ['t', 'useI18n'] }],\n 'ai-i18n/static-candidate-limit': [\n 'warn',\n { autoImport: ['t', 'useI18n'] },\n ],\n },\n },\n];\n\nexport {\n noEagerTranslation,\n noRedundantAutoImport,\n noUnsubscribedT,\n staticCandidateLimit,\n tStaticArgs,\n};\nexport default plugin;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-i18n/eslint-plugin",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.9",
|
|
4
4
|
"description": "ESLint static-argument checks aligned with ai-i18n extraction semantics.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"
|
|
36
|
+
"get-tsconfig": "^4.14.0",
|
|
37
|
+
"picomatch": "^4.0.5",
|
|
38
|
+
"@ai-i18n/analyzer": "^1.0.0-alpha.8"
|
|
37
39
|
},
|
|
38
40
|
"peerDependencies": {
|
|
39
41
|
"@vue/compiler-sfc": ">=3.2.0",
|
|
@@ -60,6 +62,7 @@
|
|
|
60
62
|
"access": "public"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
65
|
+
"@types/picomatch": "^4.0.3",
|
|
63
66
|
"@vue/compiler-sfc": "^3.5.34",
|
|
64
67
|
"vue-eslint-parser": "^10.4.1"
|
|
65
68
|
},
|