@longzai-intelligence-oxlint/composer-plugin 0.1.18

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 ADDED
@@ -0,0 +1,27 @@
1
+ # @longzai-intelligence-oxlint/composer-plugin
2
+
3
+ Composer 能力声明的 oxlint 静态校验规则。
4
+
5
+ 结构性匹配 `defineCapability` / `createServiceToken` 调用点,运行时不依赖 `@longzai-intelligence-composer/core`。规则:
6
+
7
+ - `define-capability-validity`:校验声明结构——`provides`/`inject` 必须是 ServiceToken(禁止裸字符串),`factory` 必须是函数。
8
+ - `inject-graph-acyclic`:检测同文件内 `defineCapability` 的 inject 依赖环(跨文件环由运行时容器兜底)。
9
+
10
+ ## 示例
11
+
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)
@@ -0,0 +1,8 @@
1
+ import { OxlintRules } from "@longzai-intelligence-oxlint/core";
2
+ //#region src/configs/recommended-composer.config.d.ts
3
+ /**
4
+ * Composer 插件推荐规则配置对象
5
+ */
6
+ declare const recommended: OxlintRules;
7
+ //#endregion
8
+ export { recommended };
@@ -0,0 +1 @@
1
+ const e={"@longzai-intelligence-oxlint/composer-plugin/define-capability-validity":`error`,"@longzai-intelligence-oxlint/composer-plugin/inject-graph-acyclic":`error`};export{e as recommended};
@@ -0,0 +1,14 @@
1
+ //#region src/rules/index.d.ts
2
+ /**
3
+ * composer 规则集合
4
+ */
5
+ declare const composerRules: {
6
+ 'define-capability-validity': import("@oxlint/plugins").Rule;
7
+ 'inject-graph-acyclic': import("@oxlint/plugins").Rule;
8
+ };
9
+ /**
10
+ * composer 子插件
11
+ */
12
+ declare const composerPlugin: import("@oxlint/plugins").Plugin;
13
+ //#endregion
14
+ export { composerPlugin, composerPlugin as default, composerRules };
package/dist/index.js ADDED
@@ -0,0 +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};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@longzai-intelligence-oxlint/composer-plugin",
3
+ "version": "0.1.18",
4
+ "license": "UNLICENSED",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./configs/recommended-composer": {
17
+ "types": "./dist/configs/recommended-composer.d.ts",
18
+ "import": "./dist/configs/recommended-composer.js"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build": "lzi-builder",
23
+ "build:prod": "NODE_ENV=production bun run build",
24
+ "prepublishOnly": "bun run build:prod",
25
+ "dev": "lzi-builder --watch",
26
+ "clean": "lzi-dev-cli clean",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "test:coverage": "vitest run --coverage",
30
+ "lint": "oxlint && oxfmt --check",
31
+ "lint:fix": "oxlint --fix && oxfmt",
32
+ "typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
33
+ "typecheck:app": "lzi-tsgo typecheck tsconfig/app.json",
34
+ "typecheck:node": "lzi-tsgo typecheck tsconfig/node.json",
35
+ "typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
36
+ },
37
+ "dependencies": {
38
+ "@longzai-intelligence-oxlint/core": "0.1.18",
39
+ "@oxlint/plugins": "^1.74.0",
40
+ "zod": "^4.4.3"
41
+ },
42
+ "devDependencies": {
43
+ "@longzai-intelligence-oxlint/testing": "0.1.18",
44
+ "@vitest/coverage-v8": "^4.1.10",
45
+ "typescript": "^6.0.3"
46
+ },
47
+ "peerDependencies": {
48
+ "oxlint": ">=1.0.0"
49
+ }
50
+ }