@ai-i18n/eslint-plugin 1.0.0-alpha.0 → 1.0.0-alpha.10
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 +202 -19
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1117 -126
- package/dist/index.js.map +1 -1
- package/package.json +17 -6
package/README.md
CHANGED
|
@@ -1,29 +1,72 @@
|
|
|
1
1
|
# @ai-i18n/eslint-plugin
|
|
2
2
|
|
|
3
|
-
用于提前报告无法被 Vite/Yuku
|
|
4
|
-
`virtual:ai-i18n`
|
|
5
|
-
`
|
|
3
|
+
用于提前报告无法被 Vite/Yuku 静态提取、不符合推荐语法,或不会随语言切换刷新的 Runtime
|
|
4
|
+
用法。规则检查解析到 `virtual:ai-i18n` 的翻译与状态 API,以及 Vue/React 模式下
|
|
5
|
+
`useI18n()` 返回的订阅状态。其他库或局部同名函数不受影响。
|
|
6
|
+
|
|
7
|
+
alpha 阶段请安装 `@ai-i18n/eslint-plugin@alpha`;peer 支持 ESLint 9 和 10。
|
|
6
8
|
|
|
7
9
|
## 按模式配置
|
|
8
10
|
|
|
9
|
-
显式 import
|
|
10
|
-
Vite `framework` 一致的 preset;它会声明对应只读全局并启用同一条静态参数规则:
|
|
11
|
+
显式 import 时,Vanilla / React 使用 `recommended`,Vue 使用覆盖 SFC 的 `vue`:
|
|
11
12
|
|
|
12
13
|
```js
|
|
13
14
|
import aiI18n from '@ai-i18n/eslint-plugin';
|
|
14
15
|
|
|
15
16
|
export default [
|
|
16
|
-
...aiI18n.configs.
|
|
17
|
+
...aiI18n.configs.recommended, // Vue 改用 .vue
|
|
18
|
+
];
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
启用 `aiI18n({ autoImport: true })` 时,改用与 Vite 框架模式一致的自动导入 preset:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
export default [
|
|
25
|
+
...aiI18n.configs['vanilla-auto-import'],
|
|
26
|
+
// Vue:...aiI18n.configs['vue-auto-import']
|
|
27
|
+
// React:...aiI18n.configs['react-auto-import']
|
|
28
|
+
];
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
只有 `*-auto-import` preset 会声明 Runtime 全局。所有 preset 都声明只读的
|
|
32
|
+
`defineI18nMessages` 编译宏。
|
|
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
|
+
{
|
|
44
|
+
autoImport: [
|
|
45
|
+
'useI18n',
|
|
46
|
+
't',
|
|
47
|
+
'setLang',
|
|
48
|
+
'getLang',
|
|
49
|
+
'getLangs',
|
|
50
|
+
'getLangLoadState',
|
|
51
|
+
'subscribe',
|
|
52
|
+
'tRef',
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
17
58
|
];
|
|
18
59
|
```
|
|
19
60
|
|
|
20
|
-
|
|
21
|
-
|
|
61
|
+
React 使用上面的完整列表并移除 `tRef`;Vanilla 再移除 `useI18n`。该规则不在 preset 中
|
|
62
|
+
默认启用。它只检查来自 `virtual:ai-i18n` 的未改名值导入,保留改名导入、namespace import、
|
|
63
|
+
type import 和当前模式不会自动注入的 API,并支持 `eslint --fix`。import 内部有注释时只
|
|
64
|
+
报告,不自动修改。
|
|
22
65
|
|
|
23
66
|
## Vue SFC
|
|
24
67
|
|
|
25
68
|
`.vue` 是可选文件格式。Vue 项目应先通过 `eslint-plugin-vue` 配置
|
|
26
|
-
`vue-eslint-parser
|
|
69
|
+
`vue-eslint-parser`,再按 Vite 的 `autoImport` 值选择 preset:
|
|
27
70
|
|
|
28
71
|
```js
|
|
29
72
|
import aiI18n from '@ai-i18n/eslint-plugin';
|
|
@@ -38,34 +81,162 @@ export default [
|
|
|
38
81
|
parserOptions: { parser: tseslint.parser },
|
|
39
82
|
},
|
|
40
83
|
},
|
|
41
|
-
...aiI18n.configs.vue,
|
|
84
|
+
...aiI18n.configs.vue, // autoImport: false
|
|
85
|
+
// ...aiI18n.configs['vue-auto-import'], // autoImport: true
|
|
42
86
|
];
|
|
43
87
|
```
|
|
44
88
|
|
|
45
|
-
|
|
46
|
-
Vue 项目已有的
|
|
47
|
-
`<script>`、`<script setup
|
|
48
|
-
|
|
89
|
+
两个 Vue preset 都复用宿主的 Vue parser,并启用五条适用规则。语义分析使用
|
|
90
|
+
Vue 项目已有的 `vue/compiler-sfc` Node 入口,与 Vite 提取器复用相同分析语义和
|
|
91
|
+
source-map 映射,覆盖 `<script>`、`<script setup>`、模板插值和指令表达式。Vue、
|
|
92
|
+
TypeScript 与 SFC 编译相关依赖均为可选 peer,不会安装到 React/Vanilla 项目。
|
|
49
93
|
|
|
50
94
|
Vue preset 同时覆盖 Vue JSX/TSX,但宿主仍需用 `@vitejs/plugin-vue-jsx` 编译这些文件。
|
|
51
|
-
|
|
95
|
+
同一个 Vite build 不支持两种框架模式混用。
|
|
52
96
|
|
|
53
|
-
|
|
54
|
-
|
|
97
|
+
## 生命周期检查
|
|
98
|
+
|
|
99
|
+
静态提取成功不代表字符串会自动刷新。所有 preset 都启用
|
|
100
|
+
`ai-i18n/no-eager-translation`:在模块或 `<script setup>` 初始化期间保存 `t()` 结果会
|
|
101
|
+
收到 warning。Vue SFC 的 `export default { setup() {} }`,以及 `.vue` / `.ts` / `.tsx`
|
|
102
|
+
中从 `vue` 导入的 `defineComponent({ setup() {} })` 和函数签名也按一次性 setup
|
|
103
|
+
初始化检查。
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
export const label = t('保存'); // warning:只保存初始化时的译文
|
|
107
|
+
export const getLabel = () => t('保存'); // 允许:每次调用重新读取当前语言
|
|
108
|
+
export const label = tRef('保存'); // Vue:允许,返回响应式 ComputedRef
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`recommended`、`vue`、`vue-auto-import` 与 `react-auto-import` 还启用
|
|
112
|
+
`ai-i18n/no-unsubscribed-t`。Vue / React JSX 或 TSX 的组件渲染函数不能只调用 Runtime
|
|
113
|
+
顶层 `t`,应从 `useI18n()` 获取 `t` 来建立框架订阅。React Compiler 的 `"use memo"`
|
|
114
|
+
与 `"use no memo"` 都不会替代订阅。
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
function SaveButton() {
|
|
118
|
+
return <button>{t('保存')}</button>; // warning
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function SaveButton() {
|
|
122
|
+
const { t } = useI18n();
|
|
123
|
+
return <button>{t('保存')}</button>; // 允许
|
|
124
|
+
}
|
|
125
|
+
```
|
|
55
126
|
|
|
56
|
-
|
|
127
|
+
事件回调和普通延迟函数中的 Runtime `t` 允许,独立的 `console.log` / `warn` / `error` /
|
|
128
|
+
`info` / `debug` 调用也视为即时消费。其他未知调用仍会 warning。第一版不追踪跨函数、
|
|
129
|
+
跨文件或 `useMemo` / `useState` 等任意数据流,因此规则提示的是可以确定的常见问题,
|
|
130
|
+
不代表覆盖所有译文生命周期错误。
|
|
131
|
+
|
|
132
|
+
所有 preset 还启用 `ai-i18n/no-unsubscribed-runtime-state`。模块顶层不能缓存
|
|
133
|
+
`getLang()` 或 `getLangLoadState()` 的初始化快照;Vue template 与 JSX/TSX 渲染路径应使用
|
|
134
|
+
`useI18n()` 返回的 `currentLang` 或 `langLoadState`。事件处理器、action、普通工具函数和
|
|
135
|
+
即时 console 调用允许按需读取。规则只分析当前文件,不追踪跨文件 store 数据流。
|
|
136
|
+
|
|
137
|
+
六条规则可独立启用。四条静态分析规则无法启动时,同一文件只报告一次双语错误;官方 preset 由
|
|
138
|
+
`t-static-args` 优先报告,避免次级规则重复提示。
|
|
139
|
+
|
|
140
|
+
规则与 Vite 共用静态参数语义,包括从 `useI18n()` 获得的对象成员调用
|
|
141
|
+
`i18n.t()`、`i18n['t']()`、省略式 `t('source', undefined)` 和 tagged template。
|
|
142
|
+
整棵可静态求值的纯文案对象或数组可以直接传给 `t()` 或 Vue `tRef()`,不要求
|
|
143
|
+
`defineI18nMessages()` 或 `as const`;规则会按去重后的字符串叶子计算静态候选数。
|
|
144
|
+
Vue 模板必须在 `<script setup>` 中绑定 `useI18n()` 返回的 `t`;自动导入只省略 import,
|
|
145
|
+
不会自动合成 Hook。`vue-auto-import` 会把裸 template-only `t()` 作为 error;模板中已
|
|
146
|
+
绑定到 Runtime 顶层 `t` 的调用则由 `no-unsubscribed-t` warning。
|
|
147
|
+
在 template 或 JSX/TSX 渲染期间调用 `tRef()` 会重复创建 `computed`,同一规则会提示在
|
|
148
|
+
Vue setup 中只创建一次并使用返回的 Ref。
|
|
149
|
+
|
|
150
|
+
对象或数组的成员级引用只有在根集合由 `defineI18nMessages()` 标记后才属于推荐写法。
|
|
151
|
+
动态生成的树、非普通对象以及带第二参数的整树调用会报错。字符串拼接、
|
|
152
|
+
逻辑表达式、`let` 文案、普通集合成员、`const tr = t`、命名空间调用、二次 Hook 解构、
|
|
153
|
+
`useI18n().t()` 与 `require()` 都会报错。
|
|
154
|
+
|
|
155
|
+
## tsconfig 自动发现与路径别名
|
|
156
|
+
|
|
157
|
+
静态分析规则默认从当前文件向上寻找最近的 `tsconfig.json`,解析 `extends`,递归读取
|
|
158
|
+
`references`,再按 importer 是否满足各项目的 `files`、`include`、`exclude` 选择实际
|
|
159
|
+
配置。因此常见的 `@/*` TypeScript path alias 不需要额外选项。Vue 文件必须由项目显式
|
|
160
|
+
包含,例如 `include: ['src/**/*.ts', 'src/**/*.vue']`。
|
|
161
|
+
|
|
162
|
+
`tsconfigPath` 是自动发现入口的可选覆盖项,适用于非标准配置名或希望固定从某个 solution
|
|
163
|
+
config 开始解析的项目。相对路径按 ESLint 进程的工作目录解析;指向带 `references` 的根
|
|
164
|
+
配置后,仍会执行相同的递归与项目选择逻辑。
|
|
165
|
+
|
|
166
|
+
TypeScript 6 的编译器仍执行已有的 `baseUrl` 解析,但会报告弃用诊断;TypeScript 7 将
|
|
167
|
+
不再支持该选项。插件兼容 TypeScript 5/6 的 `baseUrl + paths`;新项目推荐省略
|
|
168
|
+
`baseUrl`,写成 `"paths": { "@/*": ["./src/*"] }`。若旧项目还依赖 `baseUrl` 的未匹配
|
|
169
|
+
bare import 查找,则用 `"*": ["./src/*"]` 显式保留。只存在于 Vite `resolve.alias`、
|
|
170
|
+
未同步到 tsconfig `paths` 的 alias 不属于 ESLint 的 TypeScript 配置。
|
|
171
|
+
|
|
172
|
+
需要覆盖自动发现入口时,可以显式配置规则:
|
|
57
173
|
|
|
58
174
|
```js
|
|
59
175
|
import aiI18n from '@ai-i18n/eslint-plugin';
|
|
60
176
|
|
|
61
177
|
export default [
|
|
62
178
|
{
|
|
179
|
+
languageOptions: {
|
|
180
|
+
globals: {
|
|
181
|
+
t: 'readonly',
|
|
182
|
+
setLang: 'readonly',
|
|
183
|
+
getLang: 'readonly',
|
|
184
|
+
getLangs: 'readonly',
|
|
185
|
+
getLangLoadState: 'readonly',
|
|
186
|
+
subscribe: 'readonly',
|
|
187
|
+
useI18n: 'readonly',
|
|
188
|
+
tRef: 'readonly',
|
|
189
|
+
defineI18nMessages: 'readonly',
|
|
190
|
+
},
|
|
191
|
+
},
|
|
63
192
|
plugins: { 'ai-i18n': aiI18n },
|
|
64
193
|
rules: {
|
|
194
|
+
'ai-i18n/no-eager-translation': [
|
|
195
|
+
'warn',
|
|
196
|
+
{
|
|
197
|
+
autoImport: ['t', 'tRef', 'useI18n'],
|
|
198
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
'ai-i18n/no-unsubscribed-t': [
|
|
202
|
+
'warn',
|
|
203
|
+
{
|
|
204
|
+
autoImport: ['t', 'tRef', 'useI18n'],
|
|
205
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
'ai-i18n/no-unsubscribed-runtime-state': [
|
|
209
|
+
'warn',
|
|
210
|
+
{ autoImport: ['getLang', 'getLangLoadState'] },
|
|
211
|
+
],
|
|
212
|
+
'ai-i18n/static-candidate-limit': [
|
|
213
|
+
'warn',
|
|
214
|
+
{
|
|
215
|
+
autoImport: ['t', 'tRef', 'useI18n'],
|
|
216
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
217
|
+
maxStaticCandidates: 2_000,
|
|
218
|
+
},
|
|
219
|
+
],
|
|
65
220
|
'ai-i18n/t-static-args': [
|
|
66
221
|
'error',
|
|
67
222
|
{
|
|
68
|
-
|
|
223
|
+
autoImport: ['t', 'tRef', 'useI18n'],
|
|
224
|
+
tsconfigPath: './tsconfig.json', // 可选:覆盖自动发现入口
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
'ai-i18n/no-redundant-auto-import': [
|
|
228
|
+
'warn',
|
|
229
|
+
{
|
|
230
|
+
autoImport: [
|
|
231
|
+
'useI18n',
|
|
232
|
+
't',
|
|
233
|
+
'setLang',
|
|
234
|
+
'getLang',
|
|
235
|
+
'getLangs',
|
|
236
|
+
'getLangLoadState',
|
|
237
|
+
'subscribe',
|
|
238
|
+
'tRef',
|
|
239
|
+
],
|
|
69
240
|
},
|
|
70
241
|
],
|
|
71
242
|
},
|
|
@@ -73,4 +244,16 @@ export default [
|
|
|
73
244
|
];
|
|
74
245
|
```
|
|
75
246
|
|
|
247
|
+
上例匹配 Vue 模式;React 应移除 `tRef`,Vanilla 再移除 `useI18n`。翻译静态分析规则的
|
|
248
|
+
`autoImport` 仍只列 `t`、`tRef` 与 `useI18n`,状态快照规则只列 `getLang` 与
|
|
249
|
+
`getLangLoadState`。日常接入优先使用预设,避免 Vite 与 ESLint 的 API 集合不一致。
|
|
250
|
+
|
|
251
|
+
`ai-i18n/static-candidate-limit` 默认在单个 `t()` 的 source 与 options 组合超过 1000 个
|
|
252
|
+
时警告。`maxStaticCandidates` 必须是正整数,只改变 ESLint 的提示阈值;Vite 提取不设
|
|
253
|
+
上限,也没有对应插件选项。
|
|
254
|
+
|
|
76
255
|
插件不会自动修改宿主 ESLint 配置。
|
|
256
|
+
|
|
257
|
+
诊断默认按 Node 时区选择语言:`Asia/Shanghai` 与 `Asia/Urumqi` 使用中文,其他时区使用
|
|
258
|
+
英文。设置 `AI_I18N_DIAGNOSTIC_LOCALE=zh-CN` 或 `en-US` 可以固定语言,`auto` 恢复自动
|
|
259
|
+
检测。
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { ESLint, Rule } from "eslint";
|
|
2
|
+
//#region src/rules/no-eager-translation.d.ts
|
|
3
|
+
declare const noEagerTranslation: Rule.RuleModule;
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/rules/no-redundant-auto-import.d.ts
|
|
6
|
+
declare const noRedundantAutoImport: Rule.RuleModule;
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/rules/no-unsubscribed-runtime-state.d.ts
|
|
9
|
+
declare const noUnsubscribedRuntimeState: Rule.RuleModule;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/rules/no-unsubscribed-t.d.ts
|
|
12
|
+
declare const noUnsubscribedT: Rule.RuleModule;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/rules/static-candidate-limit.d.ts
|
|
15
|
+
declare const staticCandidateLimit: Rule.RuleModule;
|
|
16
|
+
//#endregion
|
|
2
17
|
//#region src/rules/t-static-args.d.ts
|
|
3
18
|
declare const tStaticArgs: Rule.RuleModule;
|
|
4
19
|
//#endregion
|
|
5
20
|
//#region src/index.d.ts
|
|
6
21
|
declare const plugin: ESLint.Plugin;
|
|
7
22
|
//#endregion
|
|
8
|
-
export { plugin as default, tStaticArgs };
|
|
23
|
+
export { plugin as default, noEagerTranslation, noRedundantAutoImport, noUnsubscribedRuntimeState, noUnsubscribedT, staticCandidateLimit, tStaticArgs };
|
|
9
24
|
//# 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/t-static-args.ts","../src/index.ts"],"mappings":";;
|
|
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-runtime-state.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;;;cCMzB,uBAAuB,KAAK;;;cCG5B,4BAA4B,KAAK;;;cCFjC,iBAAiB,KAAK;;;cCTtB,sBAAsB,KAAK;;;cC6C3B,aAAa,KAAK;;;cCxCzB,QAAQ,OAAO"}
|