@longzai-intelligence-oxlint/composer-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.
package/README.md CHANGED
@@ -1,27 +1,13 @@
1
1
  # @longzai-intelligence-oxlint/composer-plugin
2
2
 
3
- Composer 能力声明的 oxlint 静态校验规则。
3
+ Longzai Intelligence Oxlint 的能力声明校验插件,在静态检查阶段守护 Composer 能力声明的结构合法性与依赖无环。
4
4
 
5
- 结构性匹配 `defineCapability` / `createServiceToken` 调用点,运行时不依赖 `@longzai-intelligence-composer/core`。规则:
5
+ ## 简介
6
6
 
7
- - `define-capability-validity`:校验声明结构——`provides`/`inject` 必须是 ServiceToken(禁止裸字符串),`factory` 必须是函数。
8
- - `inject-graph-acyclic`:检测同文件内 `defineCapability` 的 inject 依赖环(跨文件环由运行时容器兜底)。
7
+ 治理能力声明违规的问题:`provides`/`inject` 的 token 语义、`factory` 的函数约束与同文件内 inject 依赖环在静态检查阶段拦截,防止非法声明与循环注入流入运行时;跨文件依赖环不在本插件治理范围,由运行时容器兜底。
9
8
 
10
- ## 示例
9
+ ## 能力
11
10
 
12
- ```ts
13
- // ✅ 合法
14
- const UserPort = createServiceToken<{ greet(): string }>('user');
15
- defineCapability({ provides: UserPort, factory: () => ({ greet: () => 'hi' }) });
16
-
17
- // ❌ provides 为裸字符串 → define-capability-validity 报告
18
- defineCapability({ provides: 'user', factory: () => ({}) });
19
-
20
- // ❌ 同文件环(x↔y)→ inject-graph-acyclic 报告
21
- defineCapability({ provides: TokenX, inject: { y: TokenY }, factory: () => 'x' });
22
- defineCapability({ provides: TokenY, inject: { x: TokenX }, factory: () => 'y' });
23
- ```
24
-
25
- ## 文档
26
-
27
- - [用户指南(规则说明、正反例、配置)](../../../docs/user-guides/composer-plugin.md)
11
+ - `define-capability-validity` — 校验声明结构:`provides`/`inject` 必须是 ServiceToken(禁止裸字符串),`factory` 必须是函数
12
+ - `inject-graph-acyclic` — 检测同文件内能力声明的 inject 依赖环
13
+ - 对 `defineCapability` / `createServiceToken` 调用点的结构性匹配,静态校验不引入运行时依赖
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";function n(e){return e.type===`Literal`}function r(e){return e.type===`FunctionExpression`||e.type===`ArrowFunctionExpression`}const i=e({meta:{type:`problem`,docs:{description:`校验 defineCapability 声明结构:provides/inject 必须是 ServiceToken,factory 必须是函数`}},createOnce(e){return{CallExpression(t){if(t.callee.type===`Identifier`&&t.callee.name===`defineCapability`){let i=t.arguments[0];if(!i||i.type!==`ObjectExpression`)e.report({node:t,message:`defineCapability 的首参必须是对象字面量`});else{let a=!1;for(let t of i.properties)if(t.type===`Property`&&t.key.type===`Identifier`){if(t.key.name===`provides`)a=!0,n(t.value)&&e.report({node:t,message:`provides 必须是 ServiceToken(createServiceToken 产物),禁止使用裸字符串`});else if(t.key.name===`factory`)r(t.value)||e.report({node:t,message:`factory 必须是函数`});else if(t.key.name===`inject`&&t.value.type===`ObjectExpression`)for(let r of t.value.properties)r.type===`Property`&&n(r.value)&&e.report({node:r,message:`inject 值必须是 ServiceToken,禁止使用裸字符串`})}a||e.report({node:t,message:`defineCapability 声明必须包含 provides`})}}}}}});function a(e,t){if(e.type===`Identifier`)return t.get(e.name)??e.name;if(e.type===`CallExpression`&&e.callee.type===`Identifier`&&e.callee.name===`createServiceToken`){let t=e.arguments[0];if(t&&t.type===`Literal`&&typeof t.value==`string`)return t.value}return null}function o(e){let t=new Map,n=[],r=null;for(let n of e.keys())t.set(n,0);let i=a=>{t.set(a,1),n.push(a);for(let o of e.get(a)??[]){let e=t.get(o);if(e===1){let e=n.indexOf(o);return r=[...n.slice(e),o],!0}if(e===0&&i(o))return!0}return n.pop(),t.set(a,2),!1};for(let n of e.keys())if(t.get(n)===0&&i(n))return r;return null}const s={"define-capability-validity":i,"inject-graph-acyclic":e({meta:{type:`problem`,docs:{description:`检测同文件内 defineCapability 的 inject 依赖环`}},createOnce(e){let t=new Map,n=[];return{Program(){t.clear(),n.length=0},VariableDeclaration(e){for(let n of e.declarations)if(n.id.type===`Identifier`&&n.init&&n.init.type===`CallExpression`&&n.init.callee.type===`Identifier`&&n.init.callee.name===`createServiceToken`){let e=n.init.arguments[0];e&&e.type===`Literal`&&typeof e.value==`string`&&t.set(n.id.name,e.value)}},CallExpression(e){if(e.callee.type===`Identifier`&&e.callee.name===`defineCapability`){let r=e.arguments[0];if(r&&r.type===`ObjectExpression`){let i=null,o=[];for(let e of r.properties)if(e.type===`Property`&&e.key.type===`Identifier`){if(e.key.name===`provides`)i=a(e.value,t);else if(e.key.name===`inject`&&e.value.type===`ObjectExpression`){for(let n of e.value.properties)if(n.type===`Property`){let e=a(n.value,t);e&&o.push(e)}}}i&&n.push({provideId:i,depIds:o,node:e})}}},"Program:exit"(){let t=new Set(n.map(e=>e.provideId)),r=new Map;for(let e of n)r.set(e.provideId,e.depIds.filter(e=>t.has(e)));let i=o(r);if(i){let t=n.find(e=>e.provideId===i[0]);t&&e.report({node:t.node,message:`检测到能力依赖环:${i.join(` → `)}`})}}}}})},c=t({meta:{name:`@longzai-intelligence-oxlint/composer-plugin`},rules:s});export{c as composerPlugin,c as default,s as composerRules};
1
+ import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";function n(e){return e.type===`Literal`}function r(e){return e.type===`FunctionExpression`||e.type===`ArrowFunctionExpression`}const i=e({meta:{type:`problem`,docs:{description:`校验 defineCapability 声明结构:provides/inject 必须是 ServiceToken,factory 必须是函数`}},createOnce(e){return{CallExpression(t){if(t.callee.type===`Identifier`&&t.callee.name===`defineCapability`){let i=t.arguments[0];if(!i||i.type!==`ObjectExpression`)e.report({node:t,message:`defineCapability 的首参必须是对象字面量`});else{let a=!1;for(let t of i.properties)if(t.type===`Property`&&t.key.type===`Identifier`){if(t.key.name===`provides`)a=!0,n(t.value)&&e.report({node:t,message:`provides 必须是 ServiceToken(createServiceToken 产物),禁止使用裸字符串`});else if(t.key.name===`factory`)r(t.value)||e.report({node:t,message:`factory 必须是函数`});else if(t.key.name===`inject`&&t.value.type===`ObjectExpression`)for(let r of t.value.properties)r.type===`Property`&&n(r.value)&&e.report({node:r,message:`inject 值必须是 ServiceToken,禁止使用裸字符串`})}a||e.report({node:t,message:`defineCapability 声明必须包含 provides`})}}}}}});function a(e,t){if(e.type===`Identifier`)return t.get(e.name)??e.name;if(e.type===`CallExpression`&&e.callee.type===`Identifier`&&e.callee.name===`createServiceToken`){let t=e.arguments[0];if(t&&t.type===`Literal`&&typeof t.value==`string`)return t.value}return null}function o(e){let t=/* @__PURE__ */ new Map,n=[],r=null;for(let n of e.keys())t.set(n,0);let i=a=>{t.set(a,1),n.push(a);for(let o of e.get(a)??[]){let e=t.get(o);if(e===1){let e=n.indexOf(o);return r=[...n.slice(e),o],!0}if(e===0&&i(o))return!0}return n.pop(),t.set(a,2),!1};for(let n of e.keys())if(t.get(n)===0&&i(n))return r;return null}const s={"define-capability-validity":i,"inject-graph-acyclic":e({meta:{type:`problem`,docs:{description:`检测同文件内 defineCapability 的 inject 依赖环`}},createOnce(e){let t=/* @__PURE__ */ new Map,n=[];return{Program(){t.clear(),n.length=0},VariableDeclaration(e){for(let n of e.declarations)if(n.id.type===`Identifier`&&n.init&&n.init.type===`CallExpression`&&n.init.callee.type===`Identifier`&&n.init.callee.name===`createServiceToken`){let e=n.init.arguments[0];e&&e.type===`Literal`&&typeof e.value==`string`&&t.set(n.id.name,e.value)}},CallExpression(e){if(e.callee.type===`Identifier`&&e.callee.name===`defineCapability`){let r=e.arguments[0];if(r&&r.type===`ObjectExpression`){let i=null,o=[];for(let e of r.properties)if(e.type===`Property`&&e.key.type===`Identifier`){if(e.key.name===`provides`)i=a(e.value,t);else if(e.key.name===`inject`&&e.value.type===`ObjectExpression`){for(let n of e.value.properties)if(n.type===`Property`){let e=a(n.value,t);e&&o.push(e)}}}i&&n.push({provideId:i,depIds:o,node:e})}}},"Program:exit"(){let t=new Set(n.map(e=>e.provideId)),r=/* @__PURE__ */ new Map;for(let e of n)r.set(e.provideId,e.depIds.filter(e=>t.has(e)));let i=o(r);if(i){let t=n.find(e=>e.provideId===i[0]);t&&e.report({node:t.node,message:`检测到能力依赖环:${i.join(` → `)}`})}}}}})},c=t({meta:{name:`@longzai-intelligence-oxlint/composer-plugin`},rules:s});export{c as composerPlugin,c as default,s as composerRules};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longzai-intelligence-oxlint/composer-plugin",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "license": "UNLICENSED",
5
5
  "files": [
6
6
  "dist"
@@ -35,12 +35,12 @@
35
35
  "typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
36
36
  },
37
37
  "dependencies": {
38
- "@longzai-intelligence-oxlint/core": "0.1.18",
38
+ "@longzai-intelligence-oxlint/core": "0.1.19",
39
39
  "@oxlint/plugins": "^1.74.0",
40
40
  "zod": "^4.4.3"
41
41
  },
42
42
  "devDependencies": {
43
- "@longzai-intelligence-oxlint/testing": "0.1.18",
43
+ "@longzai-intelligence-oxlint/testing": "0.1.19",
44
44
  "@vitest/coverage-v8": "^4.1.10",
45
45
  "typescript": "^6.0.3"
46
46
  },