@longzai-intelligence-oxlint/code-style-plugin 0.1.18 → 0.1.19

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.
Files changed (3) hide show
  1. package/README.md +12 -11
  2. package/dist/index.js +1 -1
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # @longzai-intelligence-oxlint/code-style-plugin
2
2
 
3
- ## 意图
3
+ Longzai Intelligence Oxlint 的代码风格插件,统一命名约定、导出形态与控制台使用等风格基线。
4
4
 
5
- 在静态检查阶段统一组织内的代码风格基线,覆盖命名约定、导出形态与控制台使用等高频风格问题。
5
+ ## 简介
6
6
 
7
- ## 定位
7
+ 治理代码风格基线漂移的问题:命名约定、导出形态与控制台使用等高频风格问题在静态检查阶段统一拦截,防止风格问题流入代码评审。
8
8
 
9
- Oxlint 生态的通用代码风格插件,通过 `eslintCompatPlugin` 包装以同时兼容 Oxlint 与 ESLint 运行时。
9
+ ## 能力
10
10
 
11
- ## 职能
12
-
13
- 提供禁用 console、禁用内联类型定义、推荐拆分 export const、要求接口 I 前缀命名、要求 DTO 命名、要求类后缀、禁止类后缀别名等共七条规则;同时导出内联类型检测、接口前缀判断、DTO 文件判断、类后缀映射等工具函数与类型守卫供外部复用。
14
-
15
- ## 实现情况
16
-
17
- 七条规则均已实现并按规则文件组织,工具函数与守卫随插件一并导出;当前规则集合覆盖组织内最常用的风格约束,未见空占位。
11
+ - 禁用 console
12
+ - 禁用内联类型定义
13
+ - 推荐拆分 export const
14
+ - 接口 I 前缀命名要求
15
+ - DTO 命名要求
16
+ - 类后缀要求与类后缀别名禁止
17
+ - 内联类型检测、接口前缀判断、DTO 文件判断、类后缀映射等工具函数与类型守卫导出
18
+ - 同时兼容 Oxlint 与 ESLint 运行时
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import*as n from"zod/mini";import{hasIPrefix as r,isValidDtoNameCamelCase as i,isValidDtoNameUpperCase as a}from"@longzai-intelligence-oxlint/utils";function o(e){return e.endsWith(`Class`)}const s=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&&o(n)&&e.report({node:t,message:`导入别名 "${n}" 使用了 "Class" 后缀,这是不允许的命名方式。请使用更具描述性的名称,或直接使用原始名称 "${r}"`})}}}}),c=new Set([`console.log`,`console.warn`,`console.error`,`console.info`,`console.debug`,`console.trace`,`console.dir`,`console.table`]);function l(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 u=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=l(t);n&&c.has(n)&&e.report({node:t,message:`检测到 '${n}' — 必须使用 @longzai-intelligence/logger-preset-* 代替(ADR 0038)`})}}}}),ee=n.object({type:n.literal(`TSTypeLiteral`)}),d=n.object({type:n.literal(`TSFunctionType`)}),te=n.object({type:n.literal(`TSUnionType`),types:n.array(n.unknown())}),f=n.object({type:n.literal(`TSIntersectionType`),types:n.array(n.unknown())}),p=n.object({type:n.literal(`Identifier`),name:n.string()}),m=n.object({type:n.literal(`RestElement`),argument:n.object({name:n.string()})}),h=n.union([p,m]),g=n.object({typeAnnotation:n.unknown()}),_=n.object({type:n.literal(`MethodDefinition`),key:n.object({type:n.string(),name:n.optional(n.string())}),value:n.object({params:n.optional(n.array(n.unknown())),returnType:n.optional(n.union([g,n.null()]))}),kind:n.enum([`method`,`constructor`,`get`,`set`])}),ne=n.object({returnType:g}),v=n.object({id:n.optional(n.object({name:n.optional(n.string())})),params:n.optional(n.array(n.unknown())),returnType:n.optional(n.unknown())}),y=n.object({params:n.optional(n.array(n.unknown()))});function b(e){return ee.safeParse(e).success}function x(e){return d.safeParse(e).success}function S(e){return te.safeParse(e).success}function C(e){return f.safeParse(e).success}function w(e){return h.safeParse(e).success}function T(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 E(e){return _.safeParse(e).success}function D(e){return ne.safeParse(e).success}function O(e){return v.safeParse(e).success}function k(e){return y.safeParse(e).success}function A(e){return b(e)||x(e)?!0:S(e)||C(e)?e.types.some(A):!1}function j(e){return b(e)?`对象类型`:x(e)?`函数类型`:`内联类型`}function M(e){return w(e)?e.type===`Identifier`?e.name:e.type===`RestElement`?e.argument.name:null:null}function N(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 P(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 F(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;A(a)&&N(t,n,M(e)??`未知参数`,j(a),r,i)}function I(e,t,n,r,i){for(let a of e)F(a,t,n,i??!1,r)}function L(e,t,n,r,i){if(typeof e!=`object`||!e||!(`typeAnnotation`in e))return;let a=e.typeAnnotation;A(a)&&P(t,n,j(a),r,i)}function R(e,t){if(!T(e)||!O(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),k(e)&&I(e.params??[],t,e),D(e)&&L(e.returnType,t,e,n??void 0)}function z(e,t){T(e)&&k(e)&&(k(e)&&I(e.params??[],t,e),D(e)&&L(e.returnType,t,e))}function B(e,t){if(!E(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)&&I(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;L(n,t,e,void 0,r)}}const V=e({meta:{type:`suggestion`,docs:{description:`禁止在函数参数和返回值中使用内联类型定义,强制使用具名类型`}},createOnce(e){return{FunctionDeclaration(t){R(t,e)},FunctionExpression(t){R(t,e)},ArrowFunctionExpression(t){z(t,e)},MethodDefinition(t){B(t,e)}}}});function H(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 U=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(...H(e.id));r.length!==0&&e.report({node:t,message:`应将 \`export ${n.kind} ${r.join(`, `)}\` 拆分为独立的声明和导出语句`})}}}}),W=[{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 G(e){for(let t of W)if(e.endsWith(t.suffix))return t;return null}function K(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 q(e){return e.isPrefix&&e.isInterface?e.classSuffix?`I${e.classSuffix}(如 IUser${e.classSuffix})`:`I 前缀(如 IUser)`:e.isPrefix?`I 前缀(如 IUser)`:`${e.classSuffix} 后缀`}const J=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`)||!G(t))return!1},ClassDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=G(n);if(!r||r.isInterface||!t.id)return;let i=t.id.name;K(i,r)||e.report({node:t,message:`类名 "${i}" 不符合命名规范。文件 "${n}" 中的类应使用 ${q(r)}`})},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=G(n);if(!r||!r.isInterface)return;let i=t.id.name;K(i,r)||e.report({node:t,message:`接口名 "${i}" 不符合命名规范。文件 "${n}" 中的接口应使用 ${q(r)}`})}}}});function Y(e){return e.endsWith(`.dto.ts`)}function X(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`)||!Y(t))return!1},TSInterfaceDeclaration(t){let n=t.id.name;X(n)?e.report({node:t,message:`接口名 "${n}" 后缀应使用 Dto(驼峰式)而非 DTO(全大写)`}):i(n)||e.report({node:t,message:`DTO 文件中的接口名 "${n}" 应以 Dto 结尾(如 UserResponseDto)`})}}}}),re=[`types.ts`,`types.d.ts`],ie=[`.port.interface.ts`,`.repository.interface.ts`,`.types.ts`,`.types.d.ts`,`.events.ts`,`.d.ts`];function ae(e,t){return r(e)?!1:!t.endsWith(`.dto.ts`)||a(e)}function oe(e){return e.endsWith(`.dto.ts`)?`DTO 后缀(如 UserDTO),禁止 I 前缀`:`PascalCase(如 User、UserRepository),禁止 I 前缀`}function se(e){if(re.includes(e))return!0;for(let t of ie)if(e.endsWith(t))return!0;return!1}function ce(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`)||se(t))return!1},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,i=n.endsWith(`.interface.ts`)||n.endsWith(`.interface.tsx`),a=t.id.name;ae(a,n)||(r(a)?e.report({node:t,message:`接口名 "${a}" 使用了 I 前缀。现代 TypeScript 不推荐使用匈牙利命名法,建议使用 "${a.substring(1)}"`}):n.endsWith(`.dto.ts`)&&a.endsWith(`Dto`)?e.report({node:t,message:`接口名 "${a}" 后缀应使用 DTO(全大写)而非 Dto(驼峰式)`}):e.report({node:t,message:`接口名 "${a}" 不符合命名规范。接口名应使用 ${oe(n)}`})),!i&&!n.endsWith(`.dto.ts`)&&!n.endsWith(`.tsx`)&&e.report({node:t,message:`包含接口 "${a}" 的文件 "${n}" 应命名为 "${ce(n)}"`})}}}}),$=t({meta:{name:`@longzai-intelligence-oxlint/code-style-plugin`},rules:{"no-console":u,"no-inline-type-definition":V,"prefer-split-export-const":U,"require-interface-naming":Q,"require-dto-naming":Z,"require-class-suffix":J,"no-class-suffix-alias":s}});export{W as SUFFIX_MAPPINGS,$ as codeStylePlugin,$ as default,j as getInlineTypeDescription,M as getParamName,r as hasIPrefix,A as hasInlineType,Y as isDtoFile,T as isFunctionNode,E as isMethodDefinition,x as isTSFunctionType,C as isTSIntersectionType,b as isTSTypeLiteral,S as isTSUnionType,s as noClassSuffixAliasRule,u as noConsoleRule,V as noInlineTypeDefinitionRule,U as preferSplitExportConstRule,J as requireClassSuffixRule,Z as requireDtoNamingRule,Q as requireInterfaceNamingRule};
1
+ import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import*as n from"zod/mini";import{hasIPrefix as r,isValidDtoNameCamelCase as i,isValidDtoNameUpperCase as a}from"@longzai-intelligence-oxlint/utils";function o(e){return e.endsWith(`Class`)}const s=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&&o(n)&&e.report({node:t,message:`导入别名 "${n}" 使用了 "Class" 后缀,这是不允许的命名方式。请使用更具描述性的名称,或直接使用原始名称 "${r}"`})}}}}),c=/* @__PURE__ */ new Set([`console.log`,`console.warn`,`console.error`,`console.info`,`console.debug`,`console.trace`,`console.dir`,`console.table`]);function l(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 u=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=l(t);n&&c.has(n)&&e.report({node:t,message:`检测到 '${n}' — 必须使用 @longzai-intelligence/logger-preset-* 代替(ADR 0038)`})}}}}),ee=n.object({type:n.literal(`TSTypeLiteral`)}),d=n.object({type:n.literal(`TSFunctionType`)}),te=n.object({type:n.literal(`TSUnionType`),types:n.array(n.unknown())}),f=n.object({type:n.literal(`TSIntersectionType`),types:n.array(n.unknown())}),p=n.object({type:n.literal(`Identifier`),name:n.string()}),m=n.object({type:n.literal(`RestElement`),argument:n.object({name:n.string()})}),h=n.union([p,m]),g=n.object({typeAnnotation:n.unknown()}),_=n.object({type:n.literal(`MethodDefinition`),key:n.object({type:n.string(),name:n.optional(n.string())}),value:n.object({params:n.optional(n.array(n.unknown())),returnType:n.optional(n.union([g,n.null()]))}),kind:n.enum([`method`,`constructor`,`get`,`set`])}),ne=n.object({returnType:g}),v=n.object({id:n.optional(n.object({name:n.optional(n.string())})),params:n.optional(n.array(n.unknown())),returnType:n.optional(n.unknown())}),y=n.object({params:n.optional(n.array(n.unknown()))});function b(e){return ee.safeParse(e).success}function x(e){return d.safeParse(e).success}function S(e){return te.safeParse(e).success}function C(e){return f.safeParse(e).success}function w(e){return h.safeParse(e).success}function T(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 E(e){return _.safeParse(e).success}function D(e){return ne.safeParse(e).success}function O(e){return v.safeParse(e).success}function k(e){return y.safeParse(e).success}function A(e){return b(e)||x(e)?!0:S(e)||C(e)?e.types.some(A):!1}function j(e){return b(e)?`对象类型`:x(e)?`函数类型`:`内联类型`}function M(e){return w(e)?e.type===`Identifier`?e.name:e.type===`RestElement`?e.argument.name:null:null}function N(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 P(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 F(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;A(a)&&N(t,n,M(e)??`未知参数`,j(a),r,i)}function I(e,t,n,r,i){for(let a of e)F(a,t,n,i??!1,r)}function L(e,t,n,r,i){if(typeof e!=`object`||!e||!(`typeAnnotation`in e))return;let a=e.typeAnnotation;A(a)&&P(t,n,j(a),r,i)}function R(e,t){if(!T(e)||!O(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),k(e)&&I(e.params??[],t,e),D(e)&&L(e.returnType,t,e,n??void 0)}function z(e,t){T(e)&&k(e)&&(k(e)&&I(e.params??[],t,e),D(e)&&L(e.returnType,t,e))}function B(e,t){if(!E(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)&&I(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;L(n,t,e,void 0,r)}}const V=e({meta:{type:`suggestion`,docs:{description:`禁止在函数参数和返回值中使用内联类型定义,强制使用具名类型`}},createOnce(e){return{FunctionDeclaration(t){R(t,e)},FunctionExpression(t){R(t,e)},ArrowFunctionExpression(t){z(t,e)},MethodDefinition(t){B(t,e)}}}});function H(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 U=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(...H(e.id));r.length!==0&&e.report({node:t,message:`应将 \`export ${n.kind} ${r.join(`, `)}\` 拆分为独立的声明和导出语句`})}}}}),W=[{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 G(e){for(let t of W)if(e.endsWith(t.suffix))return t;return null}function K(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 q(e){return e.isPrefix&&e.isInterface?e.classSuffix?`I${e.classSuffix}(如 IUser${e.classSuffix})`:`I 前缀(如 IUser)`:e.isPrefix?`I 前缀(如 IUser)`:`${e.classSuffix} 后缀`}const J=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`)||!G(t))return!1},ClassDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=G(n);if(!r||r.isInterface||!t.id)return;let i=t.id.name;K(i,r)||e.report({node:t,message:`类名 "${i}" 不符合命名规范。文件 "${n}" 中的类应使用 ${q(r)}`})},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,r=G(n);if(!r||!r.isInterface)return;let i=t.id.name;K(i,r)||e.report({node:t,message:`接口名 "${i}" 不符合命名规范。文件 "${n}" 中的接口应使用 ${q(r)}`})}}}});function Y(e){return e.endsWith(`.dto.ts`)}function X(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`)||!Y(t))return!1},TSInterfaceDeclaration(t){let n=t.id.name;X(n)?e.report({node:t,message:`接口名 "${n}" 后缀应使用 Dto(驼峰式)而非 DTO(全大写)`}):i(n)||e.report({node:t,message:`DTO 文件中的接口名 "${n}" 应以 Dto 结尾(如 UserResponseDto)`})}}}}),re=[`types.ts`,`types.d.ts`],ie=[`.port.interface.ts`,`.repository.interface.ts`,`.types.ts`,`.types.d.ts`,`.events.ts`,`.d.ts`];function ae(e,t){return r(e)?!1:!t.endsWith(`.dto.ts`)||a(e)}function oe(e){return e.endsWith(`.dto.ts`)?`DTO 后缀(如 UserDTO),禁止 I 前缀`:`PascalCase(如 User、UserRepository),禁止 I 前缀`}function se(e){if(re.includes(e))return!0;for(let t of ie)if(e.endsWith(t))return!0;return!1}function ce(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`)||se(t))return!1},TSInterfaceDeclaration(t){let n=e.filename.replace(/\\/g,`/`).split(`/`).pop()??``,i=n.endsWith(`.interface.ts`)||n.endsWith(`.interface.tsx`),a=t.id.name;ae(a,n)||(r(a)?e.report({node:t,message:`接口名 "${a}" 使用了 I 前缀。现代 TypeScript 不推荐使用匈牙利命名法,建议使用 "${a.substring(1)}"`}):n.endsWith(`.dto.ts`)&&a.endsWith(`Dto`)?e.report({node:t,message:`接口名 "${a}" 后缀应使用 DTO(全大写)而非 Dto(驼峰式)`}):e.report({node:t,message:`接口名 "${a}" 不符合命名规范。接口名应使用 ${oe(n)}`})),!i&&!n.endsWith(`.dto.ts`)&&!n.endsWith(`.tsx`)&&e.report({node:t,message:`包含接口 "${a}" 的文件 "${n}" 应命名为 "${ce(n)}"`})}}}}),$=t({meta:{name:`@longzai-intelligence-oxlint/code-style-plugin`},rules:{"no-console":u,"no-inline-type-definition":V,"prefer-split-export-const":U,"require-interface-naming":Q,"require-dto-naming":Z,"require-class-suffix":J,"no-class-suffix-alias":s}});export{W as SUFFIX_MAPPINGS,$ as codeStylePlugin,$ as default,j as getInlineTypeDescription,M as getParamName,r as hasIPrefix,A as hasInlineType,Y as isDtoFile,T as isFunctionNode,E as isMethodDefinition,x as isTSFunctionType,C as isTSIntersectionType,b as isTSTypeLiteral,S as isTSUnionType,s as noClassSuffixAliasRule,u as noConsoleRule,V as noInlineTypeDefinitionRule,U as preferSplitExportConstRule,J 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.1.18",
3
+ "version": "0.1.19",
4
4
  "license": "UNLICENSED",
5
5
  "files": [
6
6
  "dist"
@@ -35,8 +35,8 @@
35
35
  "typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
36
36
  },
37
37
  "dependencies": {
38
- "@longzai-intelligence-oxlint/core": "0.1.18",
39
- "@longzai-intelligence-oxlint/utils": "0.1.18",
38
+ "@longzai-intelligence-oxlint/core": "0.1.19",
39
+ "@longzai-intelligence-oxlint/utils": "0.1.19",
40
40
  "@oxlint/plugins": "^1.74.0",
41
41
  "zod": "^4.4.3"
42
42
  },