@longzai-intelligence-oxlint/architecture-core-plugin 0.1.47 → 0.2.1
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 +1 -0
- package/dist/configs/recommended-core.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e={"@longzai-intelligence-oxlint/architecture-core-plugin/business-leak":`off`,"@longzai-intelligence-oxlint/architecture-core-plugin/no-cross-package-relative-import":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/implementation-export-control":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/no-implementation-import-in-apps":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/domain-export-compliance":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/dependency-direction":`error`};export{e as recommended};
|
|
1
|
+
const e={"@longzai-intelligence-oxlint/architecture-core-plugin/business-leak":`off`,"@longzai-intelligence-oxlint/architecture-core-plugin/no-cross-package-relative-import":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/implementation-export-control":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/no-implementation-import-in-apps":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/domain-export-compliance":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/dependency-direction":`error`,"@longzai-intelligence-oxlint/architecture-core-plugin/no-raw-network-call-in-ui":`error`};export{e as recommended};
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ declare const coreRules: {
|
|
|
11
11
|
'no-implementation-import-in-apps': import("@oxlint/plugins").Rule;
|
|
12
12
|
'no-restricted-call-in-paths': import("@oxlint/plugins").Rule;
|
|
13
13
|
'no-restricted-import-in-paths': import("@oxlint/plugins").Rule;
|
|
14
|
+
'no-raw-network-call-in-ui': import("@oxlint/plugins").Rule;
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
17
|
* architecture-core 子插件
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import*as n from"zod/mini";const r=[`/packages/platform/`,`/packages/ui/`,`/packages/ui-kit/`,`/packages/engine/`,`/packages/utils/`,`/packages/types/`],i=[],a=[`^validate`,`^calculate`,`^compute`,`^determine`,`^check`,`^can`,`^should`,`^isAllowed`,`^isPermitted`,`^hasPermission`,`^applyRule`,`^enforce`,`^execute`];function o(e){return typeof e==`object`&&!!e}function s(e){let t=Array.isArray(e)?e[0]:e;return o(t)?{techLayerPatterns:t.techLayerPatterns??r,businessKeywords:t.businessKeywords??i,businessRuleMethods:t.businessRuleMethods??a}:{techLayerPatterns:r,businessKeywords:i,businessRuleMethods:a}}function c(e){return e.map(e=>new RegExp(e))}function ee(e,t){let n=e.replace(/\\/g,`/`);return t.some(e=>e.test(n))}function l(e,t){let n=e.charAt(0).toUpperCase()+e.slice(1);return t.some(e=>n.includes(e))}function u(e,t){return t.some(t=>t.test(e))}const te=e({meta:{type:`suggestion`,docs:{description:`检测技术层中可能存在的业务逻辑`}},createOnce(e){let t=[],n=[],r=[];return{before(){let i=s(e.options);return t=c(i.techLayerPatterns),n=i.businessKeywords,r=c(i.businessRuleMethods),ee(e.filename,t)?void 0:!1},ClassDeclaration(t){t.id&&l(t.id.name,n)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务相关类 '${t.id.name}'。业务逻辑应该放在 domains/ 层。`})},MethodDefinition(t){t.key.type===`Identifier`&&u(t.key.name,r)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务规则方法 '${t.key.name}'。业务规则应该放在 domains/ 层。`})},TSInterfaceDeclaration(t){l(t.id.name,n)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务相关接口 '${t.id.name}'。业务接口应该放在 domains/ 层。`})},TSTypeAliasDeclaration(t){l(t.id.name,n)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务相关类型 '${t.id.name}'。业务类型应该放在 domains/ 层。`})}}}}),d={types:0,utils:1,"shared-kernel":2,contract:3,engine:4,platform:5,infrastructure:6,implementation:6,domain:6,capability:7,ui:7,"electron-shared":7,"electron-main":8,"electron-preload":8,"electron-renderer":9},ne={types:`类型层 (Types)`,utils:`工具层 (Utils)`,"shared-kernel":`共享内核层 (Shared Kernel)`,contract:`契约层 (Contract)`,engine:`引擎层 (Engine)`,platform:`平台层 (Platform)`,infrastructure:`基础设施层 (Infrastructure)`,implementation:`实现层 (Implementation)`,domain:`门面层 (Domain Facade)`,capability:`能力装配层 (Capability)`,ui:`UI 表现层`,"electron-shared":`Electron 共享层 (Shared)`,"electron-main":`Electron 主进程 (Main)`,"electron-preload":`Electron 预加载层 (Preload)`,"electron-renderer":`Electron 渲染进程 (Renderer)`},f=new Set(Object.keys(d)),p={pathPatterns:[{pattern:/\/apps\/capabilities\//,layer:`capability`},{pattern:/\/domains\/[^/]+\/implementation\//,layer:`implementation`},{pattern:/\/domains\/[^/]+\/contract\//,layer:`contract`},{pattern:/\/domains\/[^/]+\/ui\//,layer:`ui`},{pattern:/\/domains\/shared-kernel\//,layer:`shared-kernel`},{pattern:/\/infrastructure\//,layer:`infrastructure`}],packagePrefix:``,specialPackageMappings:{},packageSuffixMappings:[{suffix:`-contract`,layer:`contract`},{suffix:`-implementation`,layer:`implementation`},{suffix:`-infrastructure`,layer:`infrastructure`},{suffix:`-domain`,layer:`domain`},{suffix:`-ui`,layer:`ui`},{suffix:`-engine`,layer:`engine`},{suffix:`-runtime`,layer:`engine`}]},m=[{from:`capability`,to:`implementation`},{from:`capability`,to:`infrastructure`},{from:`ui`,to:`domain`,hint:`UI 面禁止经门面包取件,域外依赖唯一指向生成式 sdk 客户端,ADR 0127。`},{from:`ui`,to:`contract`,hint:`contract 为 DDD 主体对 UI 全封闭,类型正源为 sdk 生成契约推断类型,ADR 0127。`},{from:`ui`,to:`implementation`,hint:`UI 面禁止直连实现层,域外依赖唯一指向生成式 sdk 客户端,ADR 0127。`},{from:`ui`,to:`infrastructure`,hint:`UI 面禁止直连基础设施层,域外依赖唯一指向生成式 sdk 客户端,ADR 0127。`}],re=[`-main`],h=[{pattern:/\/src\/domain\//,layer:`contract`},{pattern:/\/src\/engine\//,layer:`engine`},{pattern:/\/src\/platform\//,layer:`platform`},{pattern:/\/src\/infrastructure\//,layer:`infrastructure`},{pattern:/\/src\/shared\//,layer:`shared-kernel`}];function g(e){return f.has(e)}function ie(e,t){return d[e]>=d[t]}function _(e){return ne[e]}function ae(e,t){let n=e.replace(/\\/g,`/`),r=t??p.pathPatterns;if(r){for(let{pattern:e,layer:t}of r)if(e.test(n))return{layer:t,priority:d[t]}}if(n.includes(`/apps/`)){for(let{pattern:e,layer:t}of h)if(e.test(n))return{layer:t,priority:d[t]}}if(n.includes(`/packages/`)){for(let{pattern:e,layer:t}of h)if(e.test(n))return{layer:t,priority:d[t]}}return null}function v(e,t){if(!e.startsWith(t))return null;let n=e.split(`/`),r=n[0]===``?n.slice(1):n;return(r[0]?.startsWith(`@`)?r[1]:r[0])??null}function y(e,t){for(let n of t)if(e.endsWith(n))return n;return null}function b(e,t){let n=t.packagePrefix??``,r=v(e,n);if(!r)return null;let i=(t.specialPackageMappings??{})[`${n}${r}`];if(i)return{layer:i,priority:d[i]};let a=t.packageSuffixMappings??[];for(let{suffix:e,layer:t}of a)if(r.endsWith(e))return{layer:t,priority:d[t]};return null}function x(e,t,n){return e.find(e=>e.from===t&&e.to===n)??null}function S(e){let t=e.match(/^\/(.+)\/([gimsuvy]*)$/);return t?.[1]?new RegExp(t[1],t[2]??``):new RegExp(e)}function C(e){let t={};if(e.pathPatterns&&(t.pathPatterns=e.pathPatterns.reduce((e,{pattern:t,layer:n})=>(g(n)&&e.push({pattern:S(t),layer:n}),e),[])),e.packagePrefix&&(t.packagePrefix=e.packagePrefix),e.specialPackageMappings){let n={};for(let[t,r]of Object.entries(e.specialPackageMappings))g(r)&&(n[t]=r);t.specialPackageMappings=Object.keys(n).length>0?n:void 0}if(e.packageSuffixMappings){let n=[];for(let{suffix:t,layer:r}of e.packageSuffixMappings)g(r)&&n.push({suffix:t,layer:r});t.packageSuffixMappings=n.length>0?n:void 0}if(e.forbiddenLayerPairs){let n=[];for(let{from:t,to:r}of e.forbiddenLayerPairs)g(t)&&g(r)&&n.push({from:t,to:r});t.forbiddenLayerPairs=n.length>0?n:void 0}if(e.forbiddenPackageSuffixes){let n=e.forbiddenPackageSuffixes.filter(e=>e.length>0);t.forbiddenPackageSuffixes=n.length>0?n:void 0}return t}function w(e){return typeof e==`object`&&!!e}function T(e){let t=C(w(e)?e:{});return{pathPatterns:t.pathPatterns??p.pathPatterns,packagePrefix:t.packagePrefix??p.packagePrefix,specialPackageMappings:t.specialPackageMappings??p.specialPackageMappings,packageSuffixMappings:t.packageSuffixMappings??p.packageSuffixMappings,forbiddenLayerPairs:[...m,...t.forbiddenLayerPairs??[]],forbiddenPackageSuffixes:[...re,...t.forbiddenPackageSuffixes??[]]}}const E=e({meta:{type:`problem`,docs:{description:`强制执行 Clean Architecture 的依赖方向规则`},schema:[{type:`object`,properties:{pathPatterns:{type:`array`,items:{type:`object`,properties:{pattern:{type:`string`},layer:{type:`string`}},required:[`pattern`,`layer`],additionalProperties:!1}},packagePrefix:{type:`string`},specialPackageMappings:{type:`object`,additionalProperties:{type:`string`}},packageSuffixMappings:{type:`array`,items:{type:`object`,properties:{suffix:{type:`string`},layer:{type:`string`}},required:[`suffix`,`layer`],additionalProperties:!1}},forbiddenLayerPairs:{type:`array`,items:{type:`object`,properties:{from:{type:`string`},to:{type:`string`}},required:[`from`,`to`],additionalProperties:!1}},forbiddenPackageSuffixes:{type:`array`,items:{type:`string`}}},additionalProperties:!1}]},createOnce(e){let t=null,n=null;return{before(){return t===null&&(t=T(e.options?.[0])),n=ae(e.filename,t.pathPatterns),n!==null},ImportDeclaration(r){if(!n||!t)return;let i=r.source.type===`Literal`&&typeof r.source.value==`string`?r.source.value:null;if(!i)return;let a=v(i,t.packagePrefix??``);if(a){let n=y(a,t.forbiddenPackageSuffixes??[]);if(n){e.report({node:r,message:`禁止导入已禁用的 ${n} 门面包,门面包统一使用 -domain 后缀。`});return}}let o=b(i,t);if(!o)return;let s=x(t.forbiddenLayerPairs??[],n.layer,o.layer);s?e.report({node:r,message:`依赖方向违规: ${n.layer} (${_(n.layer)}) 不能导入 ${o.layer} (${_(o.layer)})。${s.hint??`域内取件必须经 -domain 门面包。`}`}):ie(n.layer,o.layer)||e.report({node:r,message:`依赖方向违规: ${n.layer} (${_(n.layer)}) 不能导入 ${o.layer} (${_(o.layer)})。依赖应该从外层指向内层。`})}}}});function D(e){let t=e.replace(/\\/g,`/`);return t.includes(`/domains/`)&&t.includes(`/implementation/src/index.ts`)}function O(e){return e.endsWith(`Entity`)}function k(e){return e.endsWith(`Repository`)&&!e.startsWith(`I`)}function A(e){return e.endsWith(`OrmEntity`)}function j(e){return e.endsWith(`_ORM_ENTITIES`)}function M(e){return e.endsWith(`OrmMapper`)}function N(e){return e.endsWith(`Schema`)}function P(e){return`exportKind`in e&&typeof e.exportKind==`string`?e.exportKind===`value`:!0}function F(e){return e.exported.type===`Identifier`?e.exported.name:e.exported.type===`Literal`&&typeof e.exported.value==`string`?e.exported.value:null}function I(e,t,n){O(e)&&P(t)?n.report({node:t,message:`禁止从主入口值导出 Entity 类 "${e}"。请改为类型导出或移除导出。`}):k(e)?n.report({node:t,message:`禁止导出 Repository 类型 "${e}"。Repository 是内部实现,不应暴露给外部。`}):A(e)?n.report({node:t,message:`禁止从主入口导出 ORM 实体 "${e}"。请创建 src/typeorm.ts 入口文件,从该入口导出。`}):j(e)?n.report({node:t,message:`禁止从主入口导出 ORM 实体集合 "${e}"。请创建 src/typeorm.ts 入口文件,从该入口导出。`}):M(e)?n.report({node:t,message:`禁止导出 ORM Mapper "${e}"。ORM Mapper 是基础设施实现细节,不应暴露给外部。`}):N(e)&&n.report({node:t,message:`禁止导出 Schema 定义 "${e}"。Schema 是实现细节,应仅导出推断类型。`})}const L=e({meta:{type:`problem`,docs:{description:`检查 Domain Implementation 包的导出是否符合规范`}},createOnce(e){return{before(){let t=e.filename;if(!D(t))return!1},ExportNamedDeclaration(t){if(t.source&&t.source.type===`Literal`&&typeof t.source.value==`string`)for(let n of t.specifiers){if(n.type!==`ExportSpecifier`)continue;let t=F(n);t&&I(t,n,e)}}}}});function R(e){let t=e.replace(/\\/g,`/`);return t.includes(`/domains/`)&&t.includes(`/implementation/src/index.ts`)}const z=[/\.\/typeorm/,/\.\/typeorm'/,/\.\/typeorm"/],B=[{pattern:/OrmEntity$/,message:`禁止从主入口导出 ORM 实体。应使用 typeorm 入口导出。`},{pattern:/Entity$/,message:`禁止从主入口导出 Entity 类。Entity 是内部实现,应使用 contract 包的 DTO。`},{pattern:/UseCase$/,message:`禁止导出 UseCase 类。UseCase 是内部实现,应通过 Port 接口访问。`},{pattern:/^I[A-Z].*Repository$/,message:`禁止导出 Repository 接口。Repository 是内部接口,外部应通过 Port 访问。`},{pattern:/^create[A-Za-z]+Repository$/,message:`禁止导出 Repository 工厂函数。外部应使用 PortAdapter 工厂函数。`},{pattern:/Repository/,message:`禁止导出 Repository 实现类。应使用工厂函数或 Port 接口。`},{pattern:/Schema$/,message:`禁止导出 Schema 定义。Schema 是实现细节,应导出推断类型。`},{pattern:/_ORM_ENTITIES$/,message:`禁止从主入口导出实体集合。应使用 typeorm 入口导出。`},{pattern:/OrmMapper$/,message:`禁止导出 ORM Mapper。这是基础设施实现细节。`},{pattern:/Table$/,message:`禁止导出数据库表定义。这是数据存储细节。`}];function V(e){return z.some(t=>t.test(e))}function H(e){for(let{pattern:t,message:n}of B)if(t.test(e))return n;return null}function U(e,t){if(e.source){let n=e.source.type===`Literal`&&typeof e.source.value==`string`?e.source.value:null;if(n&&V(n)){t.report({node:e,message:`禁止从主入口重新导出 typeorm.ts 的内容。typeorm.ts 是专门的持久化入口。`});return}}if(e.specifiers){for(let n of e.specifiers)if(n.type===`ExportSpecifier`){let e=n.exported.type===`Identifier`?n.exported.name:n.exported.type===`Literal`&&typeof n.exported.value==`string`?n.exported.value:null;if(e){let r=H(e);r&&t.report({node:n,message:`${e}: ${r}`})}}}e.declaration&&K(e.declaration,t.report)}function W(e,t){if(e.source){let n=e.source.type===`Literal`&&typeof e.source.value==`string`?e.source.value:null;if(n&&V(n)){t.report({node:e,message:`禁止从主入口重新导出 typeorm.ts 的内容。typeorm.ts 是专门的持久化入口。`});return}}t.report({node:e,message:`禁止使用 export *。请使用显式导出,明确导出内容。`})}function G(e,t){q(e.declaration)&&K(e.declaration,t.report)}function K(e,t){if(e.type===`ClassDeclaration`&&e.id){let n=e.id.name,r=H(n);r&&t({node:e,message:`${n}: ${r}`})}else if(e.type===`VariableDeclaration`){for(let n of e.declarations)if(n.id.type===`Identifier`){let e=n.id.name,r=H(e);r&&t({node:n,message:`${e}: ${r}`})}}else if(e.type===`FunctionDeclaration`&&e.id){let n=e.id.name,r=H(n);r&&t({node:e,message:`${n}: ${r}`})}}function q(e){return e.type===`ClassDeclaration`||e.type===`FunctionDeclaration`||e.type===`VariableDeclaration`}const J=e({meta:{type:`problem`,docs:{description:`Implementation 包导出控制:禁止从主入口导出内部实现`}},createOnce(e){return{before(){let t=e.filename;if(!R(t))return!1},ExportNamedDeclaration(t){U(t,e)},ExportAllDeclaration(t){W(t,e)},ExportDefaultDeclaration(t){G(t,e)}}}}),oe=[{alias:`@main`,pathPattern:`src/main`},{alias:`@preload`,pathPattern:`src/preload`},{alias:`@renderer`,pathPattern:`src/renderer/src`},{alias:`@shared`,pathPattern:`src/shared`}];function se(e){return typeof e==`object`&&!!e&&`packageBoundaries`in e}function Y(e,t){let n=e.replace(/\\/g,`/`);for(let e of t){let t=e.pathPattern.replace(/\\/g,`/`);if(n.includes(`/${t}/`)||n.endsWith(`/${t}`)||n.includes(`/${t}\\`))return e.alias}return null}function ce(e,t,n){if(!t.startsWith(`.`))return null;let r=e.replace(/\\/g,`/`),i=r.lastIndexOf(`/`);return Y(le(i>0?r.substring(0,i):``,t),n)}function le(e,t){let n=(e+`/`+t).split(`/`),r=[];for(let e of n)e===`..`?r.pop():e!==`.`&&e!==``&&r.push(e);return`/`+r.join(`/`)}function ue(e){return e.startsWith(`./`)||e.startsWith(`../`)}const de=e({meta:{type:`problem`,docs:{description:`禁止使用相对路径穿越不同包范围,强制使用别名导入`}},createOnce(e){let t=null;function n(){if(t===null){let n=e.options?.[0],r=n&&typeof n==`object`?n:{};t=se(r)&&Array.isArray(r.packageBoundaries)?r.packageBoundaries:oe}return t}return{before(){n()},ImportDeclaration(t){let r=e.filename,i=t.source.type===`Literal`&&typeof t.source.value==`string`?t.source.value:null;if(!i||!ue(i))return;let a=Y(r,n());if(!a)return;let o=ce(r,i,n());o&&a!==o&&e.report({node:t,message:`禁止使用相对路径跨包导入。当前文件位于 "${a}" 包,目标位于 "${o}" 包。请使用别名导入:${o}/...`})}}}});function fe(e){return e.replace(/\\/g,`/`).includes(`/apps/`)}function pe(e){return/-implementation(\/|$)/.test(e)}function me(e){return e.endsWith(`Entity`)}function he(e){return e.endsWith(`UseCase`)}function ge(e){return[/PortAdapter$/,/_TOKEN$/,/^create\w+Repository$/,/^create\w+Adapter$/].some(t=>t.test(e))}const _e=e({meta:{type:`problem`,docs:{description:`禁止在 apps 层导入 implementation 包的 Entity 和 UseCase`}},createOnce(e){return{before(){let t=e.filename;if(!fe(t))return!1},ImportDeclaration(t){let n=t.source.type===`Literal`&&typeof t.source.value==`string`?t.source.value:null;if(n&&pe(n))for(let n of t.specifiers){if(n.type!==`ImportSpecifier`)continue;let t=n.imported.type===`Identifier`?n.imported.name:null;t&&(me(t)?e.report({node:n,message:`禁止在 apps 层导入 Entity 类 "${t}"。请使用 contract 包的 Port 接口进行操作。`}):he(t)?e.report({node:n,message:`禁止在 apps 层导入 UseCase 类 "${t}"。请使用 contract 包的 Port 接口进行操作。`}):ge(t)||e.report({node:n,message:`禁止在 apps 层从 implementation 包导入 "${t}"。请使用 contract 包的 Port 接口。`}))}}}}}),ve=n.object({functionName:n.string(),forbiddenPaths:n.array(n.string()),message:n.optional(n.string())}),ye=n.object({restrictions:n.array(ve)});function X(e,t){let n=e.replace(/\\/g,`/`),r=t.replace(/\\/g,`/`);if(!r.includes(`*`))return n.includes(r);let i=r.split(`*`).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)).join(`[^/]+`);return new RegExp(i).test(n)}function be(e,t){return t.some(t=>X(e,t))}function xe(e){let t=ye.safeParse(e);return t.success?t.data.restrictions:[]}function Se(e){return e.type===`Identifier`?e.name:e.type===`MemberExpression`&&e.property&&!e.computed&&e.property.type===`Identifier`?e.property.name:null}const Ce=e({meta:{type:`problem`,docs:{description:`禁止在指定路径中调用特定函数,强制架构约束`}},createOnce(e){let t=[],n=/* @__PURE__ */ new Set;return{before(){let r=e.options?.[0];if(t=xe(r),t.length===0)return!1;n=/* @__PURE__ */ new Set;for(let r=0;r<t.length;r++){let i=t[r];i&&be(e.filename,i.forbiddenPaths)&&n.add(r)}if(n.size===0)return!1},CallExpression(r){let i=Se(r.callee);if(i)for(let a of n){let n=t[a];if(!n||i!==n.functionName)continue;let o=n.message??`禁止在当前路径中调用 "${n.functionName}"`;e.report({node:r,message:o})}}}}}),we=n.object({importSource:n.string(),importNames:n.optional(n.array(n.string())),forbiddenPaths:n.array(n.string()),message:n.optional(n.string())}),Te=n.object({restrictions:n.array(we)});function Z(e,t,n){return e===n.importSource?!n.importNames||n.importNames.length===0?t.length>0:t.some(e=>n.importNames?.includes(e)??!1):!1}function Ee(e){let t=[];for(let n of e)n.type===`ImportSpecifier`&&n.imported.type===`Identifier`?t.push(n.imported.name):n.type===`ImportDefaultSpecifier`&&t.push(n.local.name);return t}function De(e){let t=Te.safeParse(e);return t.success?t.data.restrictions:[]}function Oe(e,t){return t.some(t=>X(e,t))}const Q={"dependency-direction":E,"business-leak":te,"no-cross-package-relative-import":de,"domain-export-compliance":L,"implementation-export-control":J,"no-implementation-import-in-apps":_e,"no-restricted-call-in-paths":Ce,"no-restricted-import-in-paths":e({meta:{type:`problem`,docs:{description:`禁止在指定路径中导入特定模块,强制架构约束`},schema:[{type:`object`,properties:{restrictions:{type:`array`,items:{type:`object`,properties:{importSource:{type:`string`},importNames:{type:`array`,items:{type:`string`}},forbiddenPaths:{type:`array`,items:{type:`string`}},message:{type:`string`}},required:[`importSource`,`forbiddenPaths`],additionalProperties:!1}}},additionalProperties:!1}]},createOnce(e){let t=[],n=/* @__PURE__ */ new Set;return{before(){let r=e.options?.[0];if(t=De(r),t.length===0)return!1;n=/* @__PURE__ */ new Set;for(let r=0;r<t.length;r++){let i=t[r];i&&Oe(e.filename,i.forbiddenPaths)&&n.add(r)}if(n.size===0)return!1},ImportDeclaration(r){let i=r.source.value;if(typeof i!=`string`)return;let a=Ee(r.specifiers??[]);for(let o of n){let n=t[o];if(!n||!Z(i,a,n))continue;let s=n.importNames?a.filter(e=>n.importNames?.includes(e)??!1):a;for(let t of s){let i=n.message??`禁止在当前路径中导入 "${t}"(来源:${n.importSource})`;e.report({node:r,message:i})}}}}}})},$=t({meta:{name:`@longzai-intelligence-oxlint/architecture-core-plugin`},rules:Q});export{$ as architectureCorePlugin,$ as default,Q as coreRules};
|
|
1
|
+
import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import*as n from"zod/mini";const r=[`/packages/platform/`,`/packages/ui/`,`/packages/ui-kit/`,`/packages/engine/`,`/packages/utils/`,`/packages/types/`],i=[],a=[`^validate`,`^calculate`,`^compute`,`^determine`,`^check`,`^can`,`^should`,`^isAllowed`,`^isPermitted`,`^hasPermission`,`^applyRule`,`^enforce`,`^execute`];function o(e){return typeof e==`object`&&!!e}function s(e){let t=Array.isArray(e)?e[0]:e;return o(t)?{techLayerPatterns:t.techLayerPatterns??r,businessKeywords:t.businessKeywords??i,businessRuleMethods:t.businessRuleMethods??a}:{techLayerPatterns:r,businessKeywords:i,businessRuleMethods:a}}function c(e){return e.map(e=>new RegExp(e))}function l(e,t){let n=e.replace(/\\/g,`/`);return t.some(e=>e.test(n))}function u(e,t){let n=e.charAt(0).toUpperCase()+e.slice(1);return t.some(e=>n.includes(e))}function d(e,t){return t.some(t=>t.test(e))}const ee=e({meta:{type:`suggestion`,docs:{description:`检测技术层中可能存在的业务逻辑`}},createOnce(e){let t=[],n=[],r=[];return{before(){let i=s(e.options);return t=c(i.techLayerPatterns),n=i.businessKeywords,r=c(i.businessRuleMethods),l(e.filename,t)?void 0:!1},ClassDeclaration(t){t.id&&u(t.id.name,n)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务相关类 '${t.id.name}'。业务逻辑应该放在 domains/ 层。`})},MethodDefinition(t){t.key.type===`Identifier`&&d(t.key.name,r)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务规则方法 '${t.key.name}'。业务规则应该放在 domains/ 层。`})},TSInterfaceDeclaration(t){u(t.id.name,n)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务相关接口 '${t.id.name}'。业务接口应该放在 domains/ 层。`})},TSTypeAliasDeclaration(t){u(t.id.name,n)&&e.report({node:t,message:`业务逻辑泄漏嫌疑: 技术层发现业务相关类型 '${t.id.name}'。业务类型应该放在 domains/ 层。`})}}}}),f={types:0,utils:1,"shared-kernel":2,contract:3,engine:4,platform:5,infrastructure:6,implementation:6,domain:6,capability:7,ui:7,"electron-shared":7,"electron-main":8,"electron-preload":8,"electron-renderer":9},p={types:`类型层 (Types)`,utils:`工具层 (Utils)`,"shared-kernel":`共享内核层 (Shared Kernel)`,contract:`契约层 (Contract)`,engine:`引擎层 (Engine)`,platform:`平台层 (Platform)`,infrastructure:`基础设施层 (Infrastructure)`,implementation:`实现层 (Implementation)`,domain:`门面层 (Domain Facade)`,capability:`能力装配层 (Capability)`,ui:`UI 表现层`,"electron-shared":`Electron 共享层 (Shared)`,"electron-main":`Electron 主进程 (Main)`,"electron-preload":`Electron 预加载层 (Preload)`,"electron-renderer":`Electron 渲染进程 (Renderer)`},te=new Set(Object.keys(f)),m={pathPatterns:[{pattern:/\/apps\/capabilities\//,layer:`capability`},{pattern:/\/domains\/[^/]+\/implementation\//,layer:`implementation`},{pattern:/\/domains\/[^/]+\/contract\//,layer:`contract`},{pattern:/\/domains\/[^/]+\/ui\//,layer:`ui`},{pattern:/\/domains\/shared-kernel\//,layer:`shared-kernel`},{pattern:/\/infrastructure\//,layer:`infrastructure`}],packagePrefix:``,specialPackageMappings:{},packageSuffixMappings:[{suffix:`-contract`,layer:`contract`},{suffix:`-implementation`,layer:`implementation`},{suffix:`-infrastructure`,layer:`infrastructure`},{suffix:`-domain`,layer:`domain`},{suffix:`-ui`,layer:`ui`},{suffix:`-engine`,layer:`engine`},{suffix:`-runtime`,layer:`engine`}]},ne=[{from:`capability`,to:`implementation`},{from:`capability`,to:`infrastructure`},{from:`ui`,to:`domain`,hint:`UI 面禁止经门面包取件,域外依赖唯一指向生成式 sdk 客户端,ADR 0127。`},{from:`ui`,to:`contract`,hint:`contract 为 DDD 主体对 UI 全封闭,类型正源为 sdk 生成契约推断类型,ADR 0127。`},{from:`ui`,to:`implementation`,hint:`UI 面禁止直连实现层,域外依赖唯一指向生成式 sdk 客户端,ADR 0127。`},{from:`ui`,to:`infrastructure`,hint:`UI 面禁止直连基础设施层,域外依赖唯一指向生成式 sdk 客户端,ADR 0127。`}],re=[`-main`],h=[{pattern:/\/src\/domain\//,layer:`contract`},{pattern:/\/src\/engine\//,layer:`engine`},{pattern:/\/src\/platform\//,layer:`platform`},{pattern:/\/src\/infrastructure\//,layer:`infrastructure`},{pattern:/\/src\/shared\//,layer:`shared-kernel`}];function g(e){return te.has(e)}function ie(e,t){return f[e]>=f[t]}function _(e){return p[e]}function ae(e,t){let n=e.replace(/\\/g,`/`),r=t??m.pathPatterns;if(r){for(let{pattern:e,layer:t}of r)if(e.test(n))return{layer:t,priority:f[t]}}if(n.includes(`/apps/`)){for(let{pattern:e,layer:t}of h)if(e.test(n))return{layer:t,priority:f[t]}}if(n.includes(`/packages/`)){for(let{pattern:e,layer:t}of h)if(e.test(n))return{layer:t,priority:f[t]}}return null}function v(e,t){if(!e.startsWith(t))return null;let n=e.split(`/`),r=n[0]===``?n.slice(1):n;return(r[0]?.startsWith(`@`)?r[1]:r[0])??null}function oe(e,t){for(let n of t)if(e.endsWith(n))return n;return null}function y(e,t){let n=t.packagePrefix??``,r=v(e,n);if(!r)return null;let i=(t.specialPackageMappings??{})[`${n}${r}`];if(i)return{layer:i,priority:f[i]};let a=t.packageSuffixMappings??[];for(let{suffix:e,layer:t}of a)if(r.endsWith(e))return{layer:t,priority:f[t]};return null}function b(e,t,n){return e.find(e=>e.from===t&&e.to===n)??null}function x(e){let t=e.match(/^\/(.+)\/([gimsuvy]*)$/);return t?.[1]?new RegExp(t[1],t[2]??``):new RegExp(e)}function S(e){let t={};if(e.pathPatterns&&(t.pathPatterns=e.pathPatterns.reduce((e,{pattern:t,layer:n})=>(g(n)&&e.push({pattern:x(t),layer:n}),e),[])),e.packagePrefix&&(t.packagePrefix=e.packagePrefix),e.specialPackageMappings){let n={};for(let[t,r]of Object.entries(e.specialPackageMappings))g(r)&&(n[t]=r);t.specialPackageMappings=Object.keys(n).length>0?n:void 0}if(e.packageSuffixMappings){let n=[];for(let{suffix:t,layer:r}of e.packageSuffixMappings)g(r)&&n.push({suffix:t,layer:r});t.packageSuffixMappings=n.length>0?n:void 0}if(e.forbiddenLayerPairs){let n=[];for(let{from:t,to:r}of e.forbiddenLayerPairs)g(t)&&g(r)&&n.push({from:t,to:r});t.forbiddenLayerPairs=n.length>0?n:void 0}if(e.forbiddenPackageSuffixes){let n=e.forbiddenPackageSuffixes.filter(e=>e.length>0);t.forbiddenPackageSuffixes=n.length>0?n:void 0}return t}function C(e){return typeof e==`object`&&!!e}function w(e){let t=S(C(e)?e:{});return{pathPatterns:t.pathPatterns??m.pathPatterns,packagePrefix:t.packagePrefix??m.packagePrefix,specialPackageMappings:t.specialPackageMappings??m.specialPackageMappings,packageSuffixMappings:t.packageSuffixMappings??m.packageSuffixMappings,forbiddenLayerPairs:[...ne,...t.forbiddenLayerPairs??[]],forbiddenPackageSuffixes:[...re,...t.forbiddenPackageSuffixes??[]]}}const T=e({meta:{type:`problem`,docs:{description:`强制执行 Clean Architecture 的依赖方向规则`},schema:[{type:`object`,properties:{pathPatterns:{type:`array`,items:{type:`object`,properties:{pattern:{type:`string`},layer:{type:`string`}},required:[`pattern`,`layer`],additionalProperties:!1}},packagePrefix:{type:`string`},specialPackageMappings:{type:`object`,additionalProperties:{type:`string`}},packageSuffixMappings:{type:`array`,items:{type:`object`,properties:{suffix:{type:`string`},layer:{type:`string`}},required:[`suffix`,`layer`],additionalProperties:!1}},forbiddenLayerPairs:{type:`array`,items:{type:`object`,properties:{from:{type:`string`},to:{type:`string`}},required:[`from`,`to`],additionalProperties:!1}},forbiddenPackageSuffixes:{type:`array`,items:{type:`string`}}},additionalProperties:!1}]},createOnce(e){let t=null,n=null;return{before(){return t===null&&(t=w(e.options?.[0])),n=ae(e.filename,t.pathPatterns),n!==null},ImportDeclaration(r){if(!n||!t)return;let i=r.source.type===`Literal`&&typeof r.source.value==`string`?r.source.value:null;if(!i)return;let a=v(i,t.packagePrefix??``);if(a){let n=oe(a,t.forbiddenPackageSuffixes??[]);if(n){e.report({node:r,message:`禁止导入已禁用的 ${n} 门面包,门面包统一使用 -domain 后缀。`});return}}let o=y(i,t);if(!o)return;let s=b(t.forbiddenLayerPairs??[],n.layer,o.layer);s?e.report({node:r,message:`依赖方向违规: ${n.layer} (${_(n.layer)}) 不能导入 ${o.layer} (${_(o.layer)})。${s.hint??`域内取件必须经 -domain 门面包。`}`}):ie(n.layer,o.layer)||e.report({node:r,message:`依赖方向违规: ${n.layer} (${_(n.layer)}) 不能导入 ${o.layer} (${_(o.layer)})。依赖应该从外层指向内层。`})}}}});function E(e){let t=e.replace(/\\/g,`/`);return t.includes(`/domains/`)&&t.includes(`/implementation/src/index.ts`)}function D(e){return e.endsWith(`Entity`)}function O(e){return e.endsWith(`Repository`)&&!e.startsWith(`I`)}function k(e){return e.endsWith(`OrmEntity`)}function A(e){return e.endsWith(`_ORM_ENTITIES`)}function j(e){return e.endsWith(`OrmMapper`)}function M(e){return e.endsWith(`Schema`)}function N(e){return`exportKind`in e&&typeof e.exportKind==`string`?e.exportKind===`value`:!0}function P(e){return e.exported.type===`Identifier`?e.exported.name:e.exported.type===`Literal`&&typeof e.exported.value==`string`?e.exported.value:null}function F(e,t,n){D(e)&&N(t)?n.report({node:t,message:`禁止从主入口值导出 Entity 类 "${e}"。请改为类型导出或移除导出。`}):O(e)?n.report({node:t,message:`禁止导出 Repository 类型 "${e}"。Repository 是内部实现,不应暴露给外部。`}):k(e)?n.report({node:t,message:`禁止从主入口导出 ORM 实体 "${e}"。请创建 src/typeorm.ts 入口文件,从该入口导出。`}):A(e)?n.report({node:t,message:`禁止从主入口导出 ORM 实体集合 "${e}"。请创建 src/typeorm.ts 入口文件,从该入口导出。`}):j(e)?n.report({node:t,message:`禁止导出 ORM Mapper "${e}"。ORM Mapper 是基础设施实现细节,不应暴露给外部。`}):M(e)&&n.report({node:t,message:`禁止导出 Schema 定义 "${e}"。Schema 是实现细节,应仅导出推断类型。`})}const I=e({meta:{type:`problem`,docs:{description:`检查 Domain Implementation 包的导出是否符合规范`}},createOnce(e){return{before(){let t=e.filename;if(!E(t))return!1},ExportNamedDeclaration(t){if(t.source&&t.source.type===`Literal`&&typeof t.source.value==`string`)for(let n of t.specifiers){if(n.type!==`ExportSpecifier`)continue;let t=P(n);t&&F(t,n,e)}}}}});function L(e){let t=e.replace(/\\/g,`/`);return t.includes(`/domains/`)&&t.includes(`/implementation/src/index.ts`)}const R=[/\.\/typeorm/,/\.\/typeorm'/,/\.\/typeorm"/],z=[{pattern:/OrmEntity$/,message:`禁止从主入口导出 ORM 实体。应使用 typeorm 入口导出。`},{pattern:/Entity$/,message:`禁止从主入口导出 Entity 类。Entity 是内部实现,应使用 contract 包的 DTO。`},{pattern:/UseCase$/,message:`禁止导出 UseCase 类。UseCase 是内部实现,应通过 Port 接口访问。`},{pattern:/^I[A-Z].*Repository$/,message:`禁止导出 Repository 接口。Repository 是内部接口,外部应通过 Port 访问。`},{pattern:/^create[A-Za-z]+Repository$/,message:`禁止导出 Repository 工厂函数。外部应使用 PortAdapter 工厂函数。`},{pattern:/Repository/,message:`禁止导出 Repository 实现类。应使用工厂函数或 Port 接口。`},{pattern:/Schema$/,message:`禁止导出 Schema 定义。Schema 是实现细节,应导出推断类型。`},{pattern:/_ORM_ENTITIES$/,message:`禁止从主入口导出实体集合。应使用 typeorm 入口导出。`},{pattern:/OrmMapper$/,message:`禁止导出 ORM Mapper。这是基础设施实现细节。`},{pattern:/Table$/,message:`禁止导出数据库表定义。这是数据存储细节。`}];function B(e){return R.some(t=>t.test(e))}function V(e){for(let{pattern:t,message:n}of z)if(t.test(e))return n;return null}function H(e,t){if(e.source){let n=e.source.type===`Literal`&&typeof e.source.value==`string`?e.source.value:null;if(n&&B(n)){t.report({node:e,message:`禁止从主入口重新导出 typeorm.ts 的内容。typeorm.ts 是专门的持久化入口。`});return}}if(e.specifiers){for(let n of e.specifiers)if(n.type===`ExportSpecifier`){let e=n.exported.type===`Identifier`?n.exported.name:n.exported.type===`Literal`&&typeof n.exported.value==`string`?n.exported.value:null;if(e){let r=V(e);r&&t.report({node:n,message:`${e}: ${r}`})}}}e.declaration&&G(e.declaration,t.report)}function U(e,t){if(e.source){let n=e.source.type===`Literal`&&typeof e.source.value==`string`?e.source.value:null;if(n&&B(n)){t.report({node:e,message:`禁止从主入口重新导出 typeorm.ts 的内容。typeorm.ts 是专门的持久化入口。`});return}}t.report({node:e,message:`禁止使用 export *。请使用显式导出,明确导出内容。`})}function W(e,t){K(e.declaration)&&G(e.declaration,t.report)}function G(e,t){if(e.type===`ClassDeclaration`&&e.id){let n=e.id.name,r=V(n);r&&t({node:e,message:`${n}: ${r}`})}else if(e.type===`VariableDeclaration`){for(let n of e.declarations)if(n.id.type===`Identifier`){let e=n.id.name,r=V(e);r&&t({node:n,message:`${e}: ${r}`})}}else if(e.type===`FunctionDeclaration`&&e.id){let n=e.id.name,r=V(n);r&&t({node:e,message:`${n}: ${r}`})}}function K(e){return e.type===`ClassDeclaration`||e.type===`FunctionDeclaration`||e.type===`VariableDeclaration`}const q=e({meta:{type:`problem`,docs:{description:`Implementation 包导出控制:禁止从主入口导出内部实现`}},createOnce(e){return{before(){let t=e.filename;if(!L(t))return!1},ExportNamedDeclaration(t){H(t,e)},ExportAllDeclaration(t){U(t,e)},ExportDefaultDeclaration(t){W(t,e)}}}}),se=[{alias:`@main`,pathPattern:`src/main`},{alias:`@preload`,pathPattern:`src/preload`},{alias:`@renderer`,pathPattern:`src/renderer/src`},{alias:`@shared`,pathPattern:`src/shared`}];function ce(e){return typeof e==`object`&&!!e&&`packageBoundaries`in e}function J(e,t){let n=e.replace(/\\/g,`/`);for(let e of t){let t=e.pathPattern.replace(/\\/g,`/`);if(n.includes(`/${t}/`)||n.endsWith(`/${t}`)||n.includes(`/${t}\\`))return e.alias}return null}function le(e,t,n){if(!t.startsWith(`.`))return null;let r=e.replace(/\\/g,`/`),i=r.lastIndexOf(`/`);return J(ue(i>0?r.substring(0,i):``,t),n)}function ue(e,t){let n=(e+`/`+t).split(`/`),r=[];for(let e of n)e===`..`?r.pop():e!==`.`&&e!==``&&r.push(e);return`/`+r.join(`/`)}function Y(e){return e.startsWith(`./`)||e.startsWith(`../`)}const de=e({meta:{type:`problem`,docs:{description:`禁止使用相对路径穿越不同包范围,强制使用别名导入`}},createOnce(e){let t=null;function n(){if(t===null){let n=e.options?.[0],r=n&&typeof n==`object`?n:{};t=ce(r)&&Array.isArray(r.packageBoundaries)?r.packageBoundaries:se}return t}return{before(){n()},ImportDeclaration(t){let r=e.filename,i=t.source.type===`Literal`&&typeof t.source.value==`string`?t.source.value:null;if(!i||!Y(i))return;let a=J(r,n());if(!a)return;let o=le(r,i,n());o&&a!==o&&e.report({node:t,message:`禁止使用相对路径跨包导入。当前文件位于 "${a}" 包,目标位于 "${o}" 包。请使用别名导入:${o}/...`})}}}});function fe(e){return e.replace(/\\/g,`/`).includes(`/apps/`)}function pe(e){return/-implementation(\/|$)/.test(e)}function me(e){return e.endsWith(`Entity`)}function he(e){return e.endsWith(`UseCase`)}function ge(e){return[/PortAdapter$/,/_TOKEN$/,/^create\w+Repository$/,/^create\w+Adapter$/].some(t=>t.test(e))}const _e=e({meta:{type:`problem`,docs:{description:`禁止在 apps 层导入 implementation 包的 Entity 和 UseCase`}},createOnce(e){return{before(){let t=e.filename;if(!fe(t))return!1},ImportDeclaration(t){let n=t.source.type===`Literal`&&typeof t.source.value==`string`?t.source.value:null;if(n&&pe(n))for(let n of t.specifiers){if(n.type!==`ImportSpecifier`)continue;let t=n.imported.type===`Identifier`?n.imported.name:null;t&&(me(t)?e.report({node:n,message:`禁止在 apps 层导入 Entity 类 "${t}"。请使用 contract 包的 Port 接口进行操作。`}):he(t)?e.report({node:n,message:`禁止在 apps 层导入 UseCase 类 "${t}"。请使用 contract 包的 Port 接口进行操作。`}):ge(t)||e.report({node:n,message:`禁止在 apps 层从 implementation 包导入 "${t}"。请使用 contract 包的 Port 接口。`}))}}}}}),ve=n.object({functionName:n.string(),forbiddenPaths:n.array(n.string()),message:n.optional(n.string())}),ye=n.object({restrictions:n.array(ve)});function X(e,t){let n=e.replace(/\\/g,`/`),r=t.replace(/\\/g,`/`);if(!r.includes(`*`))return n.includes(r);let i=r.split(`*`).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)).join(`[^/]+`);return new RegExp(i).test(n)}function be(e,t){return t.some(t=>X(e,t))}function xe(e){let t=ye.safeParse(e);return t.success?t.data.restrictions:[]}function Se(e){return e.type===`Identifier`?e.name:e.type===`MemberExpression`&&e.property&&!e.computed&&e.property.type===`Identifier`?e.property.name:null}const Ce=e({meta:{type:`problem`,docs:{description:`禁止在指定路径中调用特定函数,强制架构约束`}},createOnce(e){let t=[],n=/* @__PURE__ */ new Set;return{before(){let r=e.options?.[0];if(t=xe(r),t.length===0)return!1;n=/* @__PURE__ */ new Set;for(let r=0;r<t.length;r++){let i=t[r];i&&be(e.filename,i.forbiddenPaths)&&n.add(r)}if(n.size===0)return!1},CallExpression(r){let i=Se(r.callee);if(i)for(let a of n){let n=t[a];if(!n||i!==n.functionName)continue;let o=n.message??`禁止在当前路径中调用 "${n.functionName}"`;e.report({node:r,message:o})}}}}}),we=[`fetch`,`axios`],Te=[`XMLHttpRequest`,`EventSource`,`WebSocket`],Ee=[`domains/*/ui/`,`packages/ui/`,`packages/*-ui/`],De=new Set(Te),Oe=[`__tests__/`,`.test.`,`.spec.`,`__mocks__/`],ke=n.object({uiPathPatterns:n.optional(n.array(n.string())),extraFunctionNames:n.optional(n.array(n.string())),ignoreTests:n.optional(n.boolean())});function Ae(e,t){return t.some(t=>X(e,t))}function je(e){let t=e.replace(/\\/g,`/`);return Oe.some(e=>t.includes(e))}function Me(e){let t=ke.safeParse(e),n=t.success?t.data:{};return{uiPathPatterns:n.uiPathPatterns??[...Ee],rawCallNames:/* @__PURE__ */ new Set([...we,...n.extraFunctionNames??[]]),ignoreTests:n.ignoreTests??!0}}function Ne(e,t){return e.type===`Identifier`?t.has(e.name)?e.name:null:e.type===`MemberExpression`&&!e.computed&&e.object.type===`Identifier`&&t.has(e.object.name)?e.property.type===`Identifier`?`${e.object.name}.${e.property.name}`:e.object.name:null}function Pe(e){return e.type===`Identifier`&&De.has(e.name)?e.name:null}function Z(e){return`UI 层禁止手写网络调用 "${e}"。网络消费统一经生成式 sdk 客户端端点加 transport 通道注入,正线见 ADR 0127 与 sdk 治理台账工作流 002。`}const Fe=e({meta:{type:`problem`,docs:{description:`禁止 UI 层手写网络调用,消费面统一经生成式 sdk 客户端`}},createOnce(e){let t=null,n=!1;return{before(){if(t=Me(e.options?.[0]),n=!(t.ignoreTests&&je(e.filename))&&Ae(e.filename,t.uiPathPatterns),!n)return!1},CallExpression(r){if(!t||!n)return;let i=Ne(r.callee,t.rawCallNames);i&&e.report({node:r,message:Z(i)})},NewExpression(r){if(!t||!n)return;let i=Pe(r.callee);i&&e.report({node:r,message:Z(i)})}}}}),Ie=n.object({importSource:n.string(),importNames:n.optional(n.array(n.string())),forbiddenPaths:n.array(n.string()),message:n.optional(n.string())}),Le=n.object({restrictions:n.array(Ie)});function Re(e,t,n){return e===n.importSource?!n.importNames||n.importNames.length===0?t.length>0:t.some(e=>n.importNames?.includes(e)??!1):!1}function ze(e){let t=[];for(let n of e)n.type===`ImportSpecifier`&&n.imported.type===`Identifier`?t.push(n.imported.name):n.type===`ImportDefaultSpecifier`&&t.push(n.local.name);return t}function Be(e){let t=Le.safeParse(e);return t.success?t.data.restrictions:[]}function Ve(e,t){return t.some(t=>X(e,t))}const Q={"dependency-direction":T,"business-leak":ee,"no-cross-package-relative-import":de,"domain-export-compliance":I,"implementation-export-control":q,"no-implementation-import-in-apps":_e,"no-restricted-call-in-paths":Ce,"no-restricted-import-in-paths":e({meta:{type:`problem`,docs:{description:`禁止在指定路径中导入特定模块,强制架构约束`},schema:[{type:`object`,properties:{restrictions:{type:`array`,items:{type:`object`,properties:{importSource:{type:`string`},importNames:{type:`array`,items:{type:`string`}},forbiddenPaths:{type:`array`,items:{type:`string`}},message:{type:`string`}},required:[`importSource`,`forbiddenPaths`],additionalProperties:!1}}},additionalProperties:!1}]},createOnce(e){let t=[],n=/* @__PURE__ */ new Set;return{before(){let r=e.options?.[0];if(t=Be(r),t.length===0)return!1;n=/* @__PURE__ */ new Set;for(let r=0;r<t.length;r++){let i=t[r];i&&Ve(e.filename,i.forbiddenPaths)&&n.add(r)}if(n.size===0)return!1},ImportDeclaration(r){let i=r.source.value;if(typeof i!=`string`)return;let a=ze(r.specifiers??[]);for(let o of n){let n=t[o];if(!n||!Re(i,a,n))continue;let s=n.importNames?a.filter(e=>n.importNames?.includes(e)??!1):a;for(let t of s){let i=n.message??`禁止在当前路径中导入 "${t}"(来源:${n.importSource})`;e.report({node:r,message:i})}}}}}}),"no-raw-network-call-in-ui":Fe},$=t({meta:{name:`@longzai-intelligence-oxlint/architecture-core-plugin`},rules:Q});export{$ as architectureCorePlugin,$ as default,Q as coreRules};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longzai-intelligence-oxlint/architecture-core-plugin",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Longzai Intelligence oxlint 核心架构规则插件",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@longzai-intelligence-oxlint/core": "0.1
|
|
46
|
+
"@longzai-intelligence-oxlint/core": "0.2.1",
|
|
47
47
|
"@oxlint/plugins": "^1.74.0",
|
|
48
48
|
"zod": "^4.4.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@longzai-intelligence-oxlint/testing": "0.1
|
|
51
|
+
"@longzai-intelligence-oxlint/testing": "0.2.1",
|
|
52
52
|
"@vitest/coverage-v8": "^4.1.10",
|
|
53
53
|
"typescript": "^6.0.3"
|
|
54
54
|
},
|