@longzai-intelligence-oxlint/code-style-plugin 0.1.17 → 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.
- package/README.md +12 -11
- package/dist/index.js +1 -1
- 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
|
-
|
|
9
|
+
## 能力
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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.
|
|
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.
|
|
39
|
-
"@longzai-intelligence-oxlint/utils": "0.1.
|
|
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
|
},
|