@longzai-intelligence-oxlint/code-style-plugin 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configs/recommended.d.ts +0 -2
- package/dist/index.d.ts +0 -26
- package/dist/index.js +1 -1
- package/package.json +3 -3
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import "../rolldown-runtime-BqT_7tdF.js";
|
|
2
2
|
import { OxlintRules } from "@longzai-intelligence-oxlint/core";
|
|
3
|
-
//#region src/configs/recommended.config.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Code-style 插件推荐规则配置
|
|
6
5
|
*/
|
|
7
6
|
declare const recommended: OxlintRules;
|
|
8
|
-
//#endregion
|
|
9
7
|
export { recommended };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import "./rolldown-runtime-BqT_7tdF.js";
|
|
2
2
|
import * as z from "zod/mini";
|
|
3
3
|
import { hasIPrefix } from "@longzai-intelligence-oxlint/utils";
|
|
4
|
-
//#region src/rules/no-class-suffix-alias.rule.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* no-class-suffix-alias 规则定义
|
|
7
6
|
*
|
|
8
7
|
* 检测 import 语句中别名以 "Class" 结尾的情况
|
|
9
8
|
*/
|
|
10
9
|
declare const noClassSuffixAliasRule: import("@oxlint/plugins").Rule;
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region src/rules/no-console.rule.d.ts
|
|
13
10
|
/**
|
|
14
11
|
* no-console 规则定义
|
|
15
12
|
*
|
|
@@ -17,8 +14,6 @@ declare const noClassSuffixAliasRule: import("@oxlint/plugins").Rule;
|
|
|
17
14
|
* 无任何路径或注释豁免。
|
|
18
15
|
*/
|
|
19
16
|
declare const noConsoleRule: import("@oxlint/plugins").Rule;
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/rules/no-handwritten-shebang.rule.d.ts
|
|
22
17
|
/**
|
|
23
18
|
* 禁止 bin 产物包入口源码手写 shebang 的门禁规则
|
|
24
19
|
*
|
|
@@ -26,8 +21,6 @@ declare const noConsoleRule: import("@oxlint/plugins").Rule;
|
|
|
26
21
|
* 仅当所属包 bin 指向 dist 产物时报错,此时 banner 是 shebang 的唯一合法来源
|
|
27
22
|
*/
|
|
28
23
|
declare const noHandwrittenShebangRule: import("@oxlint/plugins").Rule;
|
|
29
|
-
//#endregion
|
|
30
|
-
//#region src/rules/no-inline-type-definition/utils.d.ts
|
|
31
24
|
/**
|
|
32
25
|
* 递归检查类型节点是否包含内联类型定义
|
|
33
26
|
*
|
|
@@ -54,8 +47,6 @@ declare function getInlineTypeDescription(typeNode: unknown): string;
|
|
|
54
47
|
* @returns 参数名称字符串,无法提取时返回 null
|
|
55
48
|
*/
|
|
56
49
|
declare function getParamName(node: unknown): string | null;
|
|
57
|
-
//#endregion
|
|
58
|
-
//#region src/rules/no-inline-type-definition/schemas.d.ts
|
|
59
50
|
/**
|
|
60
51
|
* TSTypeLiteral 节点 schema(对象字面量类型)
|
|
61
52
|
*/
|
|
@@ -124,8 +115,6 @@ type TSIntersectionTypeNode = z.infer<typeof TSIntersectionTypeSchema>;
|
|
|
124
115
|
* 方法定义节点类型
|
|
125
116
|
*/
|
|
126
117
|
type MethodDefinitionNode = z.infer<typeof MethodDefinitionNodeSchema>;
|
|
127
|
-
//#endregion
|
|
128
|
-
//#region src/rules/no-inline-type-definition/guards.d.ts
|
|
129
118
|
/**
|
|
130
119
|
* 检查节点是否为 TSTypeLiteral(对象字面量类型)
|
|
131
120
|
*
|
|
@@ -168,8 +157,6 @@ declare function isFunctionNode(node: unknown): boolean;
|
|
|
168
157
|
* @returns 当节点类型为 MethodDefinition 时返回 true
|
|
169
158
|
*/
|
|
170
159
|
declare function isMethodDefinition(node: unknown): node is MethodDefinitionNode;
|
|
171
|
-
//#endregion
|
|
172
|
-
//#region src/rules/no-inline-type-definition/index.d.ts
|
|
173
160
|
/**
|
|
174
161
|
* 禁止内联类型定义的 Oxlint 规则
|
|
175
162
|
*
|
|
@@ -177,8 +164,6 @@ declare function isMethodDefinition(node: unknown): node is MethodDefinitionNode
|
|
|
177
164
|
* 确保在 Oxlint 中获得最佳性能,同时保持 ESLint 兼容性
|
|
178
165
|
*/
|
|
179
166
|
declare const noInlineTypeDefinitionRule: import("@oxlint/plugins").Rule;
|
|
180
|
-
//#endregion
|
|
181
|
-
//#region src/rules/no-lint-suppression.rule.d.ts
|
|
182
167
|
/**
|
|
183
168
|
* 禁止 lint 抑制注释的门禁规则
|
|
184
169
|
*
|
|
@@ -187,16 +172,12 @@ declare const noInlineTypeDefinitionRule: import("@oxlint/plugins").Rule;
|
|
|
187
172
|
* 报告落在指令注释自身,指令压制的是后续行的报告,压制不了对指令本身的报告
|
|
188
173
|
*/
|
|
189
174
|
declare const noLintSuppressionRule: import("@oxlint/plugins").Rule;
|
|
190
|
-
//#endregion
|
|
191
|
-
//#region src/rules/prefer-split-export-const.rule.d.ts
|
|
192
175
|
/**
|
|
193
176
|
* prefer-split-export-const 规则定义
|
|
194
177
|
*
|
|
195
178
|
* 检测 export const/let/var 形式的合并导出声明,建议拆分为独立声明和导出语句
|
|
196
179
|
*/
|
|
197
180
|
declare const preferSplitExportConstRule: import("@oxlint/plugins").Rule;
|
|
198
|
-
//#endregion
|
|
199
|
-
//#region src/rules/require-class-suffix.rule.d.ts
|
|
200
181
|
/**
|
|
201
182
|
* 文件后缀与类名后缀映射配置
|
|
202
183
|
*/
|
|
@@ -230,8 +211,6 @@ declare const SUFFIX_MAPPINGS: SuffixMapping[];
|
|
|
230
211
|
* 检测类名和接口名与文件类型后缀的一致性
|
|
231
212
|
*/
|
|
232
213
|
declare const requireClassSuffixRule: import("@oxlint/plugins").Rule;
|
|
233
|
-
//#endregion
|
|
234
|
-
//#region src/rules/require-dto-naming.rule.d.ts
|
|
235
214
|
/**
|
|
236
215
|
* 检查是否为 DTO 文件
|
|
237
216
|
*
|
|
@@ -245,16 +224,12 @@ declare function isDtoFile(fileName: string): boolean;
|
|
|
245
224
|
* 在 *.dto.ts 文件中检测接口命名规范违规
|
|
246
225
|
*/
|
|
247
226
|
declare const requireDtoNamingRule: import("@oxlint/plugins").Rule;
|
|
248
|
-
//#endregion
|
|
249
|
-
//#region src/rules/require-interface-naming.rule.d.ts
|
|
250
227
|
/**
|
|
251
228
|
* require-interface-naming 规则定义
|
|
252
229
|
*
|
|
253
230
|
* 检测接口命名规范和文件命名规范违规
|
|
254
231
|
*/
|
|
255
232
|
declare const requireInterfaceNamingRule: import("@oxlint/plugins").Rule;
|
|
256
|
-
//#endregion
|
|
257
|
-
//#region src/index.d.ts
|
|
258
233
|
/**
|
|
259
234
|
* code-style 插件
|
|
260
235
|
*
|
|
@@ -262,5 +237,4 @@ declare const requireInterfaceNamingRule: import("@oxlint/plugins").Rule;
|
|
|
262
237
|
* 通过 eslintCompatPlugin 包装以同时支持 Oxlint 和 ESLint 运行时
|
|
263
238
|
*/
|
|
264
239
|
declare const codeStylePlugin: import("@oxlint/plugins").Plugin;
|
|
265
|
-
//#endregion
|
|
266
240
|
export { SUFFIX_MAPPINGS, type SuffixMapping, codeStylePlugin, codeStylePlugin as default, getInlineTypeDescription, getParamName, hasIPrefix, hasInlineType, isDtoFile, isFunctionNode, isMethodDefinition, isTSFunctionType, isTSIntersectionType, isTSTypeLiteral, isTSUnionType, noClassSuffixAliasRule, noConsoleRule, noHandwrittenShebangRule, noInlineTypeDefinitionRule, noLintSuppressionRule, preferSplitExportConstRule, requireClassSuffixRule, requireDtoNamingRule, requireInterfaceNamingRule };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./rolldown-runtime-BqT_7tdF.js";import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import{existsSync as n,readFileSync as r}from"node:fs";import{dirname as i,join as a}from"node:path";import*as o from"zod/mini";import{hasIPrefix as s,isValidDtoNameCamelCase as ee,isValidDtoNameUpperCase as te}from"@longzai-intelligence-oxlint/utils";function c(e){return e.endsWith(`Class`)}const l=e({meta:{type:`problem`,docs:{description:`禁止在 import 别名中使用 "Class" 后缀`}},createOnce(e){return{ImportSpecifier(t){let n=t.local.name,r=t.imported.type===`Identifier`?t.imported.name:n;n!==r&&c(n)&&e.report({node:t,message:`导入别名 "${n}" 使用了 "Class" 后缀,这是不允许的命名方式。请使用更具描述性的名称,或直接使用原始名称 "${r}"`})}}}}),u=/* @__PURE__ */ new Set([`console.log`,`console.warn`,`console.error`,`console.info`,`console.debug`,`console.trace`,`console.dir`,`console.table`]);function d(e){let t=e.callee;return t.type===`Identifier`?t.name:t.type===`MemberExpression`&&t.property.type===`Identifier`&&t.object.type===`Identifier`?`${t.object.name}.${t.property.name}`:null}const f=e({meta:{type:`problem`,docs:{description:`禁止使用 console 方法,必须使用 @longzai-intelligence/logger-preset-* 或 react-devtools(ADR 0038)`}},createOnce(e){return{CallExpression(t){if(t.type!==`CallExpression`)return;let n=d(t);n&&u.has(n)&&e.report({node:t,message:`检测到 '${n}' — 必须使用 @longzai-intelligence/logger-preset-* 代替(ADR 0038)`})}}}}),p=/\/src\/(index|main|cli)\.(ts|mts|cts)$/,m=/(^|\/)dist\//;function ne(e){return e.replace(/\\/g,`/`)}function h(e){return typeof e==`object`&&!!e}function re(e){return e.startsWith(`#!`)}function g(e){return p.test(ne(e))}function ie(e){let t=e;for(let e=0;e<4;e+=1){let e=a(t,`package.json`);if(n(e))try{let t=JSON.parse(r(e,`utf8`));return h(t)?t:null}catch{return null}let o=i(t);if(o===t)break;t=o}return null}function ae(e){let t=e.bin;return(typeof t==`string`?[t]:h(t)?Object.values(t).filter(e=>typeof e==`string`):[]).some(e=>m.test(e.replace(/^\.\//,``)))}const oe={noHandwrittenShebang:`入口源码手写 shebang 属被裁定的错误方式,bin 产物首行 shebang 应由 "lzi-builder.config.ts" 的 banner 在构建期注入。请删除源码首行的 "#!" 行并补 banner 配置。`},_=e({meta:{type:`problem`,docs:{description:`禁止所属包 bin 指向 dist 产物的入口源码手写 shebang`}},createOnce(e){return{before(){let t=e.filename;if(!g(t))return!1},Program(t){let n=e.sourceCode.text;if(!re(n))return;let r=ie(i(i(e.filename)));r!==null&&ae(r)&&e.report({node:t,message:oe.noHandwrittenShebang})}}}}),v=o.object({type:o.literal(`TSTypeLiteral`)}),y=o.object({type:o.literal(`TSFunctionType`)}),b=o.object({type:o.literal(`TSUnionType`),types:o.array(o.unknown())}),se=o.object({type:o.literal(`TSIntersectionType`),types:o.array(o.unknown())}),ce=o.object({type:o.literal(`Identifier`),name:o.string()}),le=o.object({type:o.literal(`RestElement`),argument:o.object({name:o.string()})}),ue=o.union([ce,le]),x=o.object({typeAnnotation:o.unknown()}),de=o.object({type:o.literal(`MethodDefinition`),key:o.object({type:o.string(),name:o.optional(o.string())}),value:o.object({params:o.optional(o.array(o.unknown())),returnType:o.optional(o.union([x,o.null()]))}),kind:o.enum([`method`,`constructor`,`get`,`set`])}),fe=o.object({returnType:x}),pe=o.object({id:o.optional(o.object({name:o.optional(o.string())})),params:o.optional(o.array(o.unknown())),returnType:o.optional(o.unknown())}),me=o.object({params:o.optional(o.array(o.unknown()))});function S(e){return v.safeParse(e).success}function C(e){return y.safeParse(e).success}function w(e){return b.safeParse(e).success}function T(e){return se.safeParse(e).success}function E(e){return ue.safeParse(e).success}function D(e){if(typeof e!=`object`||!e||!(`type`in e))return!1;let t=[`FunctionDeclaration`,`FunctionExpression`,`ArrowFunctionExpression`,`MethodDefinition`],n=e.type;return typeof n==`string`&&t.includes(n)}function O(e){return de.safeParse(e).success}function k(e){return fe.safeParse(e).success}function A(e){return pe.safeParse(e).success}function j(e){return me.safeParse(e).success}function M(e){return S(e)||C(e)?!0:w(e)||T(e)?e.types.some(M):!1}function N(e){return S(e)?`对象类型`:C(e)?`函数类型`:`内联类型`}function P(e){return E(e)?e.type===`Identifier`?e.name:e.type===`RestElement`?e.argument.name:null:null}function F(e,t,n,r,i,a){i?e.report({node:t,message:`参数 "${n}" 使用了内联${r}定义。请将其提取为具名的 type。`}):a?e.report({node:t,message:`方法 "${a}" 的参数 "${n}" 使用了内联${r}定义。请将其提取为具名的 type。`}):e.report({node:t,message:`参数 "${n}" 使用了内联${r}定义。请将其提取为具名的 type。`})}function I(e,t,n,r,i){i?e.report({node:t,message:`方法 "${i}" 的返回值使用了内联${n}定义。请将其提取为具名的 type。`}):r?e.report({node:t,message:`函数 "${r}" 的返回值使用了内联${n}定义。请将其提取为具名的 type。`}):e.report({node:t,message:`匿名函数的返回值使用了内联${n}定义。请将其提取为具名的 type。`})}function L(e,t,n,r,i){if(typeof e!=`object`||!e||!(`typeAnnotation`in e)||e.typeAnnotation===null||typeof e.typeAnnotation!=`object`||!(`typeAnnotation`in e.typeAnnotation))return;let a=e.typeAnnotation.typeAnnotation;M(a)&&F(t,n,P(e)??`未知参数`,N(a),r,i)}function R(e,t,n,r,i){for(let a of e)L(a,t,n,i??!1,r)}function z(e,t,n,r,i){if(typeof e!=`object`||!e||!(`typeAnnotation`in e))return;let a=e.typeAnnotation;M(a)&&I(t,n,N(a),r,i)}function B(e,t){if(!D(e)||!A(e))return;let n=null;`id`in e&&e.id!==null&&typeof e.id==`object`&&`name`in e.id&&typeof e.id.name==`string`&&(n=e.id.name),j(e)&&R(e.params??[],t,e),k(e)&&z(e.returnType,t,e,n??void 0)}function he(e,t){D(e)&&j(e)&&(j(e)&&R(e.params??[],t,e),k(e)&&z(e.returnType,t,e))}function ge(e,t){if(!O(e))return;let n=e.key,r=n&&`name`in n&&typeof n.name==`string`?n.name:`未知方法`,i=e.value;if(i&&`params`in i&&Array.isArray(i.params)&&R(i.params,t,e,r,e.kind===`constructor`),i&&`returnType`in i&&i.returnType&&typeof i.returnType==`object`&&`typeAnnotation`in i.returnType){let n=i.returnType;z(n,t,e,void 0,r)}}const V=e({meta:{type:`suggestion`,docs:{description:`禁止在函数参数和返回值中使用内联类型定义,强制使用具名类型`}},createOnce(e){return{FunctionDeclaration(t){B(t,e)},FunctionExpression(t){B(t,e)},ArrowFunctionExpression(t){he(t,e)},MethodDefinition(t){ge(t,e)}}}}),H=/^(?:eslint-disable(?:-next-line|-line)?|eslint-enable|oxlint-disable(?:-next-line|-line)?|oxlint-enable|biome-ignore|stylelint-disable(?:-next-line|-line)?|stylelint-enable|@ts-ignore|@ts-nocheck)(?=[\s,]|$)/;function _e(e){let t=e.trimStart();return H.exec(t)?.[0]??null}const U=e({meta:{type:`problem`,docs:{description:`禁止 eslint-disable、oxlint-disable、biome-ignore、stylelint-disable、@ts-ignore、@ts-nocheck 等抑制注释`}},createOnce(e){return{Program(){let t=e.sourceCode.getAllComments();for(let n of t){if(n.type===`Shebang`)continue;let t=_e(n.value);t!==null&&e.report({node:n,message:`检测到抑制指令 ${t},抑制注释不是合法豁免手段,请从源头消除违例或在包级 oxlint.config.ts 的 ignorePatterns 登记路径豁免`})}}}}});function ve(e){let t=[];if(e.type===`Identifier`&&t.push(e.name),e.type===`ObjectPattern`)for(let n of e.properties)n.type===`Property`&&`value`in n?n.value.type===`Identifier`&&t.push(n.value.name):n.type===`RestElement`&&n.argument.type===`Identifier`&&t.push(n.argument.name);if(e.type===`ArrayPattern`)for(let n of e.elements)n&&n.type===`Identifier`&&t.push(n.name);return t}const W=e({meta:{type:`suggestion`,docs:{description:`将 export const/let/var 拆分为独立的声明和导出语句`}},createOnce(e){return{ExportNamedDeclaration(t){if(!t.declaration||t.declaration.type!==`VariableDeclaration`)return;let n=t.declaration;if(n.kind!==`const`&&n.kind!==`let`&&n.kind!==`var`||n.declarations.length===0)return;let r=[];for(let e of n.declarations)r.push(...ve(e.id));r.length!==0&&e.report({node:t,message:`应将 \`export ${n.kind} ${r.join(`, `)}\` 拆分为独立的声明和导出语句`})}}}}),G=[{suffix:`.service.drizzle.ts`,classSuffix:`Service`,isInterface:!1,isPrefix:!1},{suffix:`.service.memory.ts`,classSuffix:`Service`,isInterface:!1,isPrefix:!1},{suffix:`.service.ts`,classSuffix:`Service`,isInterface:!1,isPrefix:!1},{suffix:`.controller.ts`,classSuffix:`Controller`,isInterface:!1,isPrefix:!1},{suffix:`.module.ts`,classSuffix:`Module`,isInterface:!1,isPrefix:!1},{suffix:`.repository.drizzle.ts`,classSuffix:`Repository`,isInterface:!1,isPrefix:!1},{suffix:`.repository.memory.ts`,classSuffix:`Repository`,isInterface:!1,isPrefix:!1},{suffix:`.repository.interface.ts`,classSuffix:`Repository`,isInterface:!0,isPrefix:!0},{suffix:`.repository.ts`,classSuffix:`Repository`,isInterface:!1,isPrefix:!1},{suffix:`.entity.ts`,classSuffix:`Entity`,isInterface:!1,isPrefix:!1},{suffix:`.orm-entity.ts`,classSuffix:`OrmEntity`,isInterface:!1,isPrefix:!1},{suffix:`.dto.ts`,classSuffix:`DTO`,isInterface:!1,isPrefix:!1},{suffix:`.port.interface.ts`,classSuffix:`Port`,isInterface:!0,isPrefix:!0},{suffix:`.interface.ts`,classSuffix:``,isInterface:!0,isPrefix:!0},{suffix:`.port.ts`,classSuffix:`Port`,isInterface:!0,isPrefix:!1},{suffix:`.port.adapter.ts`,classSuffix:`PortAdapter`,isInterface:!1,isPrefix:!1},{suffix:`.adapter.ts`,classSuffix:`Adapter`,isInterface:!1,isPrefix:!1},{suffix:`.events.ts`,classSuffix:`Event`,isInterface:!1,isPrefix:!1},{suffix:`.mapper.ts`,classSuffix:`Mapper`,isInterface:!1,isPrefix:!1},{suffix:`.vo.ts`,classSuffix:`VO`,isInterface:!1,isPrefix:!1},{suffix:`.usecase.ts`,classSuffix:`UseCase`,isInterface:!1,isPrefix:!1},{suffix:`.errors.ts`,classSuffix:`Error`,isInterface:!1,isPrefix:!1}];function K(e){for(let t of G)if(e.endsWith(t.suffix))return t;return null}function q(e,t){if(t.isPrefix&&t.isInterface){if(t.classSuffix){let n=t.classSuffix;return e.startsWith(`I`)&&e.endsWith(n)&&e.length>1+n.length}return e.startsWith(`I`)&&e.length>1}return t.isPrefix?e.startsWith(`I`)&&e.length>1:e.endsWith(t.classSuffix)&&e.length>t.classSuffix.length}function J(e){return e.isPrefix&&e.isInterface?e.classSuffix?`I${e.classSuffix}(如 IUser${e.classSuffix})`:`I 前缀(如 IUser)`:e.isPrefix?`I 前缀(如 IUser)`:`${e.classSuffix} 后缀`}const Y=e({meta:{type:`problem`,docs:{description:`强制执行类名与文件类型后缀的一致性`}},createOnce(e){return{before(){let t=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``;if(t.endsWith(`.test.ts`)||t.endsWith(`.spec.ts`)||!K(t))return!1},ClassDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=K(n);if(!r||r.isInterface||!t.id)return;let i=t.id.name;q(i,r)||e.report({node:t,message:`类名 "${i}" 不符合命名规范。文件 "${n}" 中的类应使用 ${J(r)}`})},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=K(n);if(!r||!r.isInterface)return;let i=t.id.name;q(i,r)||e.report({node:t,message:`接口名 "${i}" 不符合命名规范。文件 "${n}" 中的接口应使用 ${J(r)}`})}}}});function X(e){return e.endsWith(`.dto.ts`)}function ye(e){return e.endsWith(`DTO`)}const Z=e({meta:{type:`problem`,docs:{description:`强制执行 DTO 文件中的接口命名规范(Dto 后缀,驼峰式)`}},createOnce(e){return{before(){let t=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``;if(t.endsWith(`.test.ts`)||t.endsWith(`.spec.ts`)||!X(t))return!1},TSInterfaceDeclaration(t){let n=t.id.name;ye(n)?e.report({node:t,message:`接口名 "${n}" 后缀应使用 Dto(驼峰式)而非 DTO(全大写)`}):ee(n)||e.report({node:t,message:`DTO 文件中的接口名 "${n}" 应以 Dto 结尾(如 UserResponseDto)`})}}}}),be=[`types.ts`,`types.d.ts`],xe=[`.port.interface.ts`,`.repository.interface.ts`,`.types.ts`,`.types.d.ts`,`.events.ts`,`.d.ts`];function Se(e,t){return s(e)?!1:!t.endsWith(`.dto.ts`)||te(e)}function Ce(e){return e.endsWith(`.dto.ts`)?`DTO 后缀(如 UserDTO),禁止 I 前缀`:`PascalCase(如 User、UserRepository),禁止 I 前缀`}function we(e){if(be.includes(e))return!0;for(let t of xe)if(e.endsWith(t))return!0;return!1}function Te(e){return e.endsWith(`.port.ts`)?e.replace(`.port.ts`,`.port.interface.ts`):e.endsWith(`.repository.ts`)?e.replace(`.repository.ts`,`.repository.interface.ts`):e.replace(`.ts`,`.interface.ts`)}const Q=e({meta:{type:`problem`,docs:{description:`强制执行接口命名规范(禁止 I 前缀,使用 PascalCase)和文件命名规范`}},createOnce(e){return{before(){let t=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``;if(t.endsWith(`.test.ts`)||t.endsWith(`.spec.ts`)||we(t))return!1},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=n.endsWith(`.interface.ts`)||n.endsWith(`.interface.tsx`),i=t.id.name;Se(i,n)||(s(i)?e.report({node:t,message:`接口名 "${i}" 使用了 I 前缀。现代 TypeScript 不推荐使用匈牙利命名法,建议使用 "${i.substring(1)}"`}):n.endsWith(`.dto.ts`)&&i.endsWith(`Dto`)?e.report({node:t,message:`接口名 "${i}" 后缀应使用 DTO(全大写)而非 Dto(驼峰式)`}):e.report({node:t,message:`接口名 "${i}" 不符合命名规范。接口名应使用 ${Ce(n)}`})),!r&&!n.endsWith(`.dto.ts`)&&!n.endsWith(`.tsx`)&&e.report({node:t,message:`包含接口 "${i}" 的文件 "${n}" 应命名为 "${Te(n)}"`})}}}}),$=t({meta:{name:`@longzai-intelligence-oxlint/code-style-plugin`},rules:{"no-console":f,"no-inline-type-definition":V,"prefer-split-export-const":W,"require-interface-naming":Q,"require-dto-naming":Z,"require-class-suffix":Y,"no-class-suffix-alias":l,"no-handwritten-shebang":_,"no-lint-suppression":U}});export{G as SUFFIX_MAPPINGS,$ as codeStylePlugin,$ as default,N as getInlineTypeDescription,P as getParamName,s as hasIPrefix,M as hasInlineType,X as isDtoFile,D as isFunctionNode,O as isMethodDefinition,C as isTSFunctionType,T as isTSIntersectionType,S as isTSTypeLiteral,w as isTSUnionType,l as noClassSuffixAliasRule,f as noConsoleRule,_ as noHandwrittenShebangRule,V as noInlineTypeDefinitionRule,U as noLintSuppressionRule,W as preferSplitExportConstRule,Y as requireClassSuffixRule,Z as requireDtoNamingRule,Q as requireInterfaceNamingRule};
|
|
1
|
+
import"./rolldown-runtime-BqT_7tdF.js";import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import{existsSync as n,readFileSync as r}from"node:fs";import{dirname as i,join as a}from"node:path";import*as o from"zod/mini";import{hasIPrefix as s,isValidDtoNameCamelCase as ee,isValidDtoNameUpperCase as te}from"@longzai-intelligence-oxlint/utils";function c(e){return e.endsWith(`Class`)}const l=e({meta:{type:`problem`,docs:{description:`禁止在 import 别名中使用 "Class" 后缀`}},createOnce(e){return{ImportSpecifier(t){let n=t.local.name,r=t.imported.type===`Identifier`?t.imported.name:n;n!==r&&c(n)&&e.report({node:t,message:`导入别名 "${n}" 使用了 "Class" 后缀,这是不允许的命名方式。请使用更具描述性的名称,或直接使用原始名称 "${r}"`})}}}}),u=/* @__PURE__ */ new Set([`console.log`,`console.warn`,`console.error`,`console.info`,`console.debug`,`console.trace`,`console.dir`,`console.table`]);function d(e){let t=e.callee;return t.type===`Identifier`?t.name:t.type===`MemberExpression`&&t.property.type===`Identifier`&&t.object.type===`Identifier`?`${t.object.name}.${t.property.name}`:null}const f=e({meta:{type:`problem`,docs:{description:`禁止使用 console 方法,必须使用 @longzai-intelligence/logger-preset-* 或 react-devtools(ADR 0038)`}},createOnce(e){return{CallExpression(t){if(t.type!==`CallExpression`)return;let n=d(t);n&&u.has(n)&&e.report({node:t,message:`检测到 '${n}' — 必须使用 @longzai-intelligence/logger-preset-* 代替(ADR 0038)`})}}}}),p=/\/src\/(index|main|cli)\.(ts|mts|cts)$/,m=/(^|\/)dist\//;function ne(e){return e.replace(/\\/g,`/`)}function h(e){return typeof e==`object`&&!!e}function re(e){return e.startsWith(`#!`)}function g(e){return p.test(ne(e))}function ie(e){let t=e;for(let e=0;e<4;e+=1){let e=a(t,`package.json`);if(n(e))try{let t=JSON.parse(r(e,`utf8`));return h(t)?t:null}catch{return null}let o=i(t);if(o===t)break;t=o}return null}function ae(e){let t=e.bin;return(typeof t==`string`?[t]:h(t)?Object.values(t).filter(e=>typeof e==`string`):[]).some(e=>m.test(e.replace(/^\.\//,``)))}const oe={noHandwrittenShebang:`入口源码手写 shebang 属被裁定的错误方式,bin 产物首行 shebang 应由 "lzi-builder.config.ts" 的 banner 在构建期注入。请删除源码首行的 "#!" 行并补 banner 配置。`},_=e({meta:{type:`problem`,docs:{description:`禁止所属包 bin 指向 dist 产物的入口源码手写 shebang`}},createOnce(e){return{before(){let t=e.filename;if(!g(t))return!1},Program(t){let n=e.sourceCode.text;if(!re(n))return;let r=ie(i(i(e.filename)));r!==null&&ae(r)&&e.report({node:t,message:oe.noHandwrittenShebang})}}}}),v=o.object({type:o.literal(`TSTypeLiteral`)}),y=o.object({type:o.literal(`TSFunctionType`)}),b=o.object({type:o.literal(`TSUnionType`),types:o.array(o.unknown())}),se=o.object({type:o.literal(`TSIntersectionType`),types:o.array(o.unknown())}),ce=o.object({type:o.literal(`Identifier`),name:o.string()}),le=o.object({type:o.literal(`RestElement`),argument:o.object({name:o.string()})}),ue=o.union([ce,le]),x=o.object({typeAnnotation:o.unknown()}),de=o.object({type:o.literal(`MethodDefinition`),key:o.object({type:o.string(),name:o.optional(o.string())}),value:o.object({params:o.optional(o.array(o.unknown())),returnType:o.optional(o.union([x,o.null()]))}),kind:o.enum([`method`,`constructor`,`get`,`set`])}),fe=o.object({returnType:x}),pe=o.object({id:o.optional(o.object({name:o.optional(o.string())})),params:o.optional(o.array(o.unknown())),returnType:o.optional(o.unknown())}),me=o.object({params:o.optional(o.array(o.unknown()))});function S(e){return v.safeParse(e).success}function C(e){return y.safeParse(e).success}function w(e){return b.safeParse(e).success}function T(e){return se.safeParse(e).success}function E(e){return ue.safeParse(e).success}function D(e){if(typeof e!=`object`||!e||!(`type`in e))return!1;let t=[`FunctionDeclaration`,`FunctionExpression`,`ArrowFunctionExpression`,`MethodDefinition`],n=e.type;return typeof n==`string`&&t.includes(n)}function O(e){return de.safeParse(e).success}function k(e){return fe.safeParse(e).success}function A(e){return pe.safeParse(e).success}function j(e){return me.safeParse(e).success}function M(e){return S(e)||C(e)?!0:w(e)||T(e)?e.types.some(M):!1}function N(e){return S(e)?`对象类型`:C(e)?`函数类型`:`内联类型`}function P(e){return E(e)?e.type===`Identifier`?e.name:e.type===`RestElement`?e.argument.name:null:null}function F(e,t,n,r,i,a){i?e.report({node:t,message:`参数 "${n}" 使用了内联${r}定义。请将其提取为具名的 type。`}):a?e.report({node:t,message:`方法 "${a}" 的参数 "${n}" 使用了内联${r}定义。请将其提取为具名的 type。`}):e.report({node:t,message:`参数 "${n}" 使用了内联${r}定义。请将其提取为具名的 type。`})}function I(e,t,n,r,i){i?e.report({node:t,message:`方法 "${i}" 的返回值使用了内联${n}定义。请将其提取为具名的 type。`}):r?e.report({node:t,message:`函数 "${r}" 的返回值使用了内联${n}定义。请将其提取为具名的 type。`}):e.report({node:t,message:`匿名函数的返回值使用了内联${n}定义。请将其提取为具名的 type。`})}function L(e,t,n,r,i){if(typeof e!=`object`||!e||!(`typeAnnotation`in e)||e.typeAnnotation===null||typeof e.typeAnnotation!=`object`||!(`typeAnnotation`in e.typeAnnotation))return;let a=e.typeAnnotation.typeAnnotation;M(a)&&F(t,n,P(e)??`未知参数`,N(a),r,i)}function R(e,t,n,r,i){for(let a of e)L(a,t,n,i??!1,r)}function z(e,t,n,r,i){if(typeof e!=`object`||!e||!(`typeAnnotation`in e))return;let a=e.typeAnnotation;M(a)&&I(t,n,N(a),r,i)}function B(e,t){if(!D(e)||!A(e))return;let n=null;`id`in e&&e.id!==null&&typeof e.id==`object`&&`name`in e.id&&typeof e.id.name==`string`&&(n=e.id.name),j(e)&&R(e.params??[],t,e),k(e)&&z(e.returnType,t,e,n??void 0)}function he(e,t){D(e)&&j(e)&&(j(e)&&R(e.params??[],t,e),k(e)&&z(e.returnType,t,e))}function ge(e,t){if(!O(e))return;let n=e.key,r=n&&`name`in n&&typeof n.name==`string`?n.name:`未知方法`,i=e.value;if(i&&`params`in i&&Array.isArray(i.params)&&R(i.params,t,e,r,e.kind===`constructor`),i&&`returnType`in i&&i.returnType&&typeof i.returnType==`object`&&`typeAnnotation`in i.returnType){let n=i.returnType;z(n,t,e,void 0,r)}}const V=e({meta:{type:`suggestion`,docs:{description:`禁止在函数参数和返回值中使用内联类型定义,强制使用具名类型`}},createOnce(e){return{FunctionDeclaration(t){B(t,e)},FunctionExpression(t){B(t,e)},ArrowFunctionExpression(t){he(t,e)},MethodDefinition(t){ge(t,e)}}}}),H=/^(?:eslint-disable(?:-next-line|-line)?|eslint-enable|oxlint-disable(?:-next-line|-line)?|oxlint-enable|biome-ignore|stylelint-disable(?:-next-line|-line)?|stylelint-enable|@ts-ignore|@ts-nocheck)(?=[\s,]|$)/;function _e(e){let t=e.trimStart();return H.exec(t)?.[0]??null}const U=e({meta:{type:`problem`,docs:{description:`禁止 eslint-disable、oxlint-disable、biome-ignore、stylelint-disable、@ts-ignore、@ts-nocheck 等抑制注释`}},createOnce(e){return{Program(){let t=e.sourceCode.getAllComments();for(let n of t){if(n.type===`Shebang`)continue;let t=_e(n.value);t!==null&&e.report({node:n,message:`检测到抑制指令 ${t},抑制注释不是合法豁免手段,请从源头消除违例或在包级 oxlint.config.ts 的 ignorePatterns 登记路径豁免`})}}}}});function ve(e){let t=[];if(e.type===`Identifier`&&t.push(e.name),e.type===`ObjectPattern`)for(let n of e.properties)n.type===`Property`&&`value`in n?n.value.type===`Identifier`&&t.push(n.value.name):n.type===`RestElement`&&n.argument.type===`Identifier`&&t.push(n.argument.name);if(e.type===`ArrayPattern`)for(let n of e.elements)n&&n.type===`Identifier`&&t.push(n.name);return t}const W=e({meta:{type:`suggestion`,docs:{description:`将 export const/let/var 拆分为独立的声明和导出语句`}},createOnce(e){return{ExportNamedDeclaration(t){if(!t.declaration||t.declaration.type!==`VariableDeclaration`)return;let n=t.declaration;if(n.kind!==`const`&&n.kind!==`let`&&n.kind!==`var`||n.declarations.length===0)return;let r=[];for(let e of n.declarations)r.push(...ve(e.id));r.length!==0&&e.report({node:t,message:`应将 \`export ${n.kind} ${r.join(`, `)}\` 拆分为独立的声明和导出语句`})}}}}),G=[{suffix:`.service.drizzle.ts`,classSuffix:`Service`,isInterface:!1,isPrefix:!1},{suffix:`.service.memory.ts`,classSuffix:`Service`,isInterface:!1,isPrefix:!1},{suffix:`.service.ts`,classSuffix:`Service`,isInterface:!1,isPrefix:!1},{suffix:`.controller.ts`,classSuffix:`Controller`,isInterface:!1,isPrefix:!1},{suffix:`.module.ts`,classSuffix:`Module`,isInterface:!1,isPrefix:!1},{suffix:`.repository.drizzle.ts`,classSuffix:`Repository`,isInterface:!1,isPrefix:!1},{suffix:`.repository.memory.ts`,classSuffix:`Repository`,isInterface:!1,isPrefix:!1},{suffix:`.repository.interface.ts`,classSuffix:`Repository`,isInterface:!0,isPrefix:!0},{suffix:`.repository.ts`,classSuffix:`Repository`,isInterface:!1,isPrefix:!1},{suffix:`.entity.ts`,classSuffix:`Entity`,isInterface:!1,isPrefix:!1},{suffix:`.orm-entity.ts`,classSuffix:`OrmEntity`,isInterface:!1,isPrefix:!1},{suffix:`.dto.ts`,classSuffix:`DTO`,isInterface:!1,isPrefix:!1},{suffix:`.port.interface.ts`,classSuffix:`Port`,isInterface:!0,isPrefix:!0},{suffix:`.interface.ts`,classSuffix:``,isInterface:!0,isPrefix:!0},{suffix:`.port.ts`,classSuffix:`Port`,isInterface:!0,isPrefix:!1},{suffix:`.port.adapter.ts`,classSuffix:`PortAdapter`,isInterface:!1,isPrefix:!1},{suffix:`.adapter.ts`,classSuffix:`Adapter`,isInterface:!1,isPrefix:!1},{suffix:`.events.ts`,classSuffix:`Event`,isInterface:!1,isPrefix:!1},{suffix:`.mapper.ts`,classSuffix:`Mapper`,isInterface:!1,isPrefix:!1},{suffix:`.vo.ts`,classSuffix:`VO`,isInterface:!1,isPrefix:!1},{suffix:`.usecase.ts`,classSuffix:`UseCase`,isInterface:!1,isPrefix:!1},{suffix:`.errors.ts`,classSuffix:`Error`,isInterface:!1,isPrefix:!1}];function K(e){for(let t of G)if(e.endsWith(t.suffix))return t;return null}function q(e,t){if(t.isPrefix&&t.isInterface){if(t.classSuffix){let n=t.classSuffix;return e.startsWith(`I`)&&e.endsWith(n)&&e.length>1+n.length}return e.startsWith(`I`)&&e.length>1}return t.isPrefix?e.startsWith(`I`)&&e.length>1:e.endsWith(t.classSuffix)&&e.length>t.classSuffix.length}function J(e){return e.isPrefix&&e.isInterface?e.classSuffix?`I${e.classSuffix}(如 IUser${e.classSuffix})`:`I 前缀(如 IUser)`:e.isPrefix?`I 前缀(如 IUser)`:`${e.classSuffix} 后缀`}const Y=e({meta:{type:`problem`,docs:{description:`强制执行类名与文件类型后缀的一致性`}},createOnce(e){return{before(){let t=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``;if(t.endsWith(`.test.ts`)||t.endsWith(`.spec.ts`)||!K(t))return!1},ClassDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=K(n);if(!r||r.isInterface||!t.id)return;let i=t.id.name;q(i,r)||e.report({node:t,message:`类名 "${i}" 不符合命名规范。文件 "${n}" 中的类应使用 ${J(r)}`})},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=K(n);if(!r||!r.isInterface)return;let i=t.id.name;q(i,r)||e.report({node:t,message:`接口名 "${i}" 不符合命名规范。文件 "${n}" 中的接口应使用 ${J(r)}`})}}}});function X(e){return e.endsWith(`.dto.ts`)}function ye(e){return e.endsWith(`DTO`)}const Z=e({meta:{type:`problem`,docs:{description:`强制执行 DTO 文件中的接口命名规范(Dto 后缀,驼峰式)`}},createOnce(e){return{before(){let t=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``;if(t.endsWith(`.test.ts`)||t.endsWith(`.spec.ts`)||!X(t))return!1},TSInterfaceDeclaration(t){let n=t.id.name;ye(n)?e.report({node:t,message:`接口名 "${n}" 后缀应使用 Dto(驼峰式)而非 DTO(全大写)`}):ee(n)||e.report({node:t,message:`DTO 文件中的接口名 "${n}" 应以 Dto 结尾(如 UserResponseDto)`})}}}}),be=[`types.ts`,`types.d.ts`],xe=[`.port.interface.ts`,`.repository.interface.ts`,`.types.ts`,`.types.d.ts`,`.events.ts`,`.d.ts`];function Se(e,t){return s(e)?!1:!t.endsWith(`.dto.ts`)||te(e)}function Ce(e){return e.endsWith(`.dto.ts`)?`DTO 后缀(如 UserDTO),禁止 I 前缀`:`PascalCase(如 User、UserRepository),禁止 I 前缀`}function we(e){if(be.includes(e))return!0;for(let t of xe)if(e.endsWith(t))return!0;return!1}function Te(e){return e.endsWith(`.port.ts`)?e.replace(`.port.ts`,`.port.interface.ts`):e.endsWith(`.repository.ts`)?e.replace(`.repository.ts`,`.repository.interface.ts`):e.replace(`.ts`,`.interface.ts`)}const Q=e({meta:{type:`problem`,docs:{description:`强制执行接口命名规范(禁止 I 前缀,使用 PascalCase)和文件命名规范`}},createOnce(e){return{before(){let t=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``;if(t.endsWith(`.test.ts`)||t.endsWith(`.spec.ts`)||we(t))return!1},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=n.endsWith(`.interface.ts`)||n.endsWith(`.interface.tsx`),i=t.id.name;Se(i,n)||(s(i)?e.report({node:t,message:`接口名 "${i}" 使用了 I 前缀。现代 TypeScript 不推荐使用匈牙利命名法,建议使用 "${i.substring(1)}"`}):n.endsWith(`.dto.ts`)&&i.endsWith(`Dto`)?e.report({node:t,message:`接口名 "${i}" 后缀应使用 DTO(全大写)而非 Dto(驼峰式)`}):e.report({node:t,message:`接口名 "${i}" 不符合命名规范。接口名应使用 ${Ce(n)}`})),r||n.endsWith(`.dto.ts`)||n.endsWith(`.tsx`)||e.report({node:t,message:`包含接口 "${i}" 的文件 "${n}" 应命名为 "${Te(n)}"`})}}}}),$=t({meta:{name:`@longzai-intelligence-oxlint/code-style-plugin`},rules:{"no-console":f,"no-inline-type-definition":V,"prefer-split-export-const":W,"require-interface-naming":Q,"require-dto-naming":Z,"require-class-suffix":Y,"no-class-suffix-alias":l,"no-handwritten-shebang":_,"no-lint-suppression":U}});export{G as SUFFIX_MAPPINGS,$ as codeStylePlugin,$ as default,N as getInlineTypeDescription,P as getParamName,s as hasIPrefix,M as hasInlineType,X as isDtoFile,D as isFunctionNode,O as isMethodDefinition,C as isTSFunctionType,T as isTSIntersectionType,S as isTSTypeLiteral,w as isTSUnionType,l as noClassSuffixAliasRule,f as noConsoleRule,_ as noHandwrittenShebangRule,V as noInlineTypeDefinitionRule,U as noLintSuppressionRule,W as preferSplitExportConstRule,Y as requireClassSuffixRule,Z as requireDtoNamingRule,Q as requireInterfaceNamingRule};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longzai-intelligence-oxlint/code-style-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Longzai Intelligence oxlint 代码风格规则插件",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"code-style",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@longzai-intelligence-oxlint/core": "0.2.
|
|
47
|
-
"@longzai-intelligence-oxlint/utils": "0.2.
|
|
46
|
+
"@longzai-intelligence-oxlint/core": "0.2.11",
|
|
47
|
+
"@longzai-intelligence-oxlint/utils": "0.2.11",
|
|
48
48
|
"@oxlint/plugins": "^1.74.0",
|
|
49
49
|
"zod": "^4.4.3"
|
|
50
50
|
},
|