@longzai-intelligence-oxlint/code-style-plugin 0.2.10 → 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.
@@ -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.10",
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.10",
47
- "@longzai-intelligence-oxlint/utils": "0.2.10",
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
  },