@longzai-intelligence-oxlint/architecture-library-plugin 0.1.18 → 0.1.20
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 +4 -6
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# @longzai-intelligence-oxlint/architecture-library-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Longzai Intelligence Oxlint 的库级子插件,在静态检查阶段约束测试目录结构。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 简介
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
治理库级测试目录结构失范的问题:测试文件放置规范、非测试文件不得混入测试目录、测试文件须与源文件匹配在静态检查阶段统一约束,防止测试目录结构失范。
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## 规则
|
|
9
|
+
## 能力
|
|
12
10
|
|
|
13
11
|
- `test-file-placement` — 测试文件放置规范
|
|
14
12
|
- `no-non-test-files-in-tests-directory` — 禁止非测试文件进入测试目录
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import n from"node:path";import r,{existsSync as i}from"node:fs";const a=[/\.test\.ts$/,/\.test\.tsx$/,/\.spec\.ts$/,/\.spec\.tsx$/],o=[`/node_modules/`,`/dist/`,`/out/`,`/.bun/`];function s(e){return e.replace(/\\/g,`/`)}function c(e){return a.some(t=>t.test(e))}function l(e){let t=s(e);return o.some(e=>t.includes(e))}const u=[{segment:`/src/main/`,process:`main`},{segment:`/src/renderer/`,process:`renderer`},{segment:`/src/preload/`,process:`preload`},{segment:`/src/shared/`,process:`shared`}];function d(e){let t=n.dirname(e);for(;;){let r=n.join(t,`package.json`);if(i(r))return t;let a=n.dirname(t);if(a===t)return n.dirname(e);t=a}}const f={name:`electron-process`,detect(e){let t=s(e);for(let n of u)if(t.includes(n.segment)){let r=s(d(e)),i=p(t,n.segment);return{type:`apps`,isElectron:!0,electronProcess:n.process,srcRoot:i,projectRoot:r}}return null}};function p(e,t){let n=e.indexOf(t);return e.slice(0,n+t.length-1)}const m={name:`package-json`,detect(e){let t=h(e);if(!t)return null;let n=s(t),r=s(g(t));return{type:_(n),isElectron:!1,srcRoot:r,projectRoot:n}}};function h(e){let t=n.dirname(e);for(;;){let e=n.join(t,`package.json`);if(i(e))return t;let r=n.dirname(t);if(r===t)return null;t=r}}function g(e){let t=n.join(e,`src`);return i(t)?t:e}function _(e){let t=s(e);return t.includes(`/packages/`)?`packages`:t.includes(`/domains/`)?`domains`:t.includes(`/apps/`)?`apps`:t.includes(`/infrastructure/`)?`infrastructure`:t.includes(`/templates/`)?`templates`:t.includes(`/database/`)?`database`:`unknown`}function v(){let e=[];return{detectors:e,addDetector(t){return e.push(t),this},detect(t){for(let n of e){let e=n.detect(t);if(e)return e}return null}}}const y=v().addDetector(f).addDetector(m),b=[/\.test\.ts$/,/\.test\.tsx$/,/\.spec\.ts$/,/\.spec\.tsx$/],x=[`.ts`,`.tsx`],S
|
|
1
|
+
import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";import n from"node:path";import r,{existsSync as i}from"node:fs";const a=[/\.test\.ts$/,/\.test\.tsx$/,/\.spec\.ts$/,/\.spec\.tsx$/],o=[`/node_modules/`,`/dist/`,`/out/`,`/.bun/`];function s(e){return e.replace(/\\/g,`/`)}function c(e){return a.some(t=>t.test(e))}function l(e){let t=s(e);return o.some(e=>t.includes(e))}const u=[{segment:`/src/main/`,process:`main`},{segment:`/src/renderer/`,process:`renderer`},{segment:`/src/preload/`,process:`preload`},{segment:`/src/shared/`,process:`shared`}];function d(e){let t=n.dirname(e);for(;;){let r=n.join(t,`package.json`);if(i(r))return t;let a=n.dirname(t);if(a===t)return n.dirname(e);t=a}}const f={name:`electron-process`,detect(e){let t=s(e);for(let n of u)if(t.includes(n.segment)){let r=s(d(e)),i=p(t,n.segment);return{type:`apps`,isElectron:!0,electronProcess:n.process,srcRoot:i,projectRoot:r}}return null}};function p(e,t){let n=e.indexOf(t);return e.slice(0,n+t.length-1)}const m={name:`package-json`,detect(e){let t=h(e);if(!t)return null;let n=s(t),r=s(g(t));return{type:_(n),isElectron:!1,srcRoot:r,projectRoot:n}}};function h(e){let t=n.dirname(e);for(;;){let e=n.join(t,`package.json`);if(i(e))return t;let r=n.dirname(t);if(r===t)return null;t=r}}function g(e){let t=n.join(e,`src`);return i(t)?t:e}function _(e){let t=s(e);return t.includes(`/packages/`)?`packages`:t.includes(`/domains/`)?`domains`:t.includes(`/apps/`)?`apps`:t.includes(`/infrastructure/`)?`infrastructure`:t.includes(`/templates/`)?`templates`:t.includes(`/database/`)?`database`:`unknown`}function v(){let e=[];return{detectors:e,addDetector(t){return e.push(t),this},detect(t){for(let n of e){let e=n.detect(t);if(e)return e}return null}}}const y=v().addDetector(f).addDetector(m),b=[/\.test\.ts$/,/\.test\.tsx$/,/\.spec\.ts$/,/\.spec\.tsx$/],x=[`.ts`,`.tsx`],S=/* @__PURE__ */ new Set([`node_modules`,`dist`,`out`,`.bun`,`.turbo`,`.cache`,`generated`]),C=/* @__PURE__ */ new Set([`bun`,`vitest`]),w=/* @__PURE__ */ new Set([`unit`,`integration`,`e2e`]),T=/* @__PURE__ */ new Map;function E(e,t){let i;try{i=r.readdirSync(e,{withFileTypes:!0})}catch{return}for(let r of i){let i=n.join(e,r.name);if(r.isDirectory()){if(S.has(r.name))continue;E(i,t)}else r.isFile()&&t(i)}}function D(e){for(let t of b)if(t.test(e))return e.replace(t,``);return e}function ee(e){let t=[],n=e;for(;n.includes(`.`);){let e=n.lastIndexOf(`.`);n=n.substring(0,e),t.push(n)}return t}function te(e){let t=s(e),r=t.indexOf(`/__tests__/`);return r===-1?n.dirname(e):t.substring(0,r)}function O(e,t,r){let i=s(e),a=i.indexOf(`/__tests__/`);if(a===-1)return null;let o=i.substring(a+11).split(`/`);if(o.length<2)return null;let c=o[0];if(!c)return null;let l=C.has(c),u=r?r.includes(c):w.has(c),d=l||u?o.slice(1,-1).join(`/`):o.slice(0,-1).join(`/`);return d?n.join(t,d):t}function k(e,t){for(let i of x){let a=n.join(t,e+i);if(r.existsSync(a))return a}return null}function A(e,t){let i=n.join(t,e);try{if(r.existsSync(i)&&r.statSync(i).isDirectory())return i}catch{return null}return null}function j(e,t){let i=n.join(t,e);try{if(!r.existsSync(i)||!r.statSync(i).isDirectory())return null}catch{return null}for(let e of x){let t=n.join(i,`index`+e);if(r.existsSync(t))return t}return null}function M(e){let t=T.get(e);if(t)return t;let n=[];return E(e,e=>n.push(e)),T.set(e,n),n}function N(e,t){for(let n of t){let t=k(e,n);if(t)return t}return null}function P(e,t){for(let n of t){let t=A(e,n);if(t)return t}return null}function F(e,t){for(let n of t){let t=j(e,n);if(t)return t}return null}function I(e,t,i,a){let o=[e,...t];for(let e of i){if(!r.existsSync(e))continue;let t=M(e);for(let e of t){let t=n.basename(e);if(!c(t)){for(let n of o)for(let r of x)if(t===n+r)return e;if(a){let n=t;for(let e of x)if(t.endsWith(e)){n=t.slice(0,-e.length);break}for(let t of o)if(t.includes(n)||n.includes(t))return e}}}}return null}function L(e,t,r,i){let a=n.dirname(t),o=n.dirname(a),s=[a,o],c=O(t,r,i?.testTypeSubdirs),l=c?[c,r,o]:[r,o];if(c){let t=k(e,c);if(t)return t;let n=A(e,c);if(n)return n;let r=j(e,c);if(r)return r}let u=N(e,s);if(u)return u;let d=P(e,l);if(d)return d;let f=F(e,l);if(f)return f;let p=ee(e),m=c?[c,...s]:s;for(let e of p){let t=N(e,m);if(t)return t;let n=P(e,l);if(n)return n;let r=F(e,l);if(r)return r}return i?.enableGlobalSearch?I(e,p,[r,a],i?.enableGlobalSearch):null}function R(e,t){return L(D(n.basename(e)),e,te(e),t)?{isMatch:!0}:{isMatch:!1}}function z(e){let t=n.basename(e);return c(t)&&!l(e)&&!t.endsWith(`.d.ts`)}const B=[`**/vitest.setup.ts`,`**/jest.setup.ts`,`**/fixtures/**`,`**/helpers/**`,`**/mocks/**`,`**/__mocks__/**`,`**/*.d.ts`,`**/bun/**`,`**/vitest/**`];function V(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function H(e){if(typeof e!=`object`||!e)return!1;if(!(`allowPatterns`in e))return!0;let t=e.allowPatterns;return Array.isArray(t)&&V(t)}function U(e){let t=Array.isArray(e)?e[0]:e;return!H(t)||!t.allowPatterns?{allowPatterns:[...B]}:{allowPatterns:[...B,...t.allowPatterns]}}function W(e){return a.some(t=>t.test(e))}function G(e,t){let n=t.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*\*/g,`{{DOUBLE_STAR}}`).replace(/\*/g,`[^/]*`).replace(/{{DOUBLE_STAR}}/g,`.*`);return RegExp(`^${n}$`).test(e)}function K(e,t){return t.some(t=>G(e,t))}const q=e({meta:{type:`problem`,docs:{description:`禁止在 __tests__ 目录下放置非测试文件`}},createOnce(e){return{before(){let t=U(e.options),r=e.filename,i=s(r);if(!i.includes(`/__tests__/`)||W(n.basename(r))||K(i,t.allowPatterns))return!1},Program(t){let r=e.filename,i=n.basename(r);e.report({node:t,message:`文件 "${i}" 位于 __tests__ 目录下但不是测试文件。测试目录应仅包含测试文件(*.test.ts, *.spec.ts)。如果是辅助文件,请移动到 src/test-helpers/ 或 src/testing/ 目录。`})}}}});function J(e){if(typeof e!=`object`||!e)return!1;if(`testTypeSubdirs`in e){let{testTypeSubdirs:t}=e;if(t!==void 0&&!Array.isArray(t))return!1}if(`enableGlobalSearch`in e){let{enableGlobalSearch:t}=e;if(t!==void 0&&typeof t!=`boolean`)return!1}return!0}const Y=e({meta:{type:`problem`,docs:{description:`确保测试文件集中放置于 __tests__/ 下,且文件名与被测源文件名或目录名保持一致`},schema:[{type:`object`,properties:{testTypeSubdirs:{type:`array`,items:{type:`string`}},enableGlobalSearch:{type:`boolean`}},additionalProperties:!1}]},createOnce(e){let t=J(e.options?.[0])?e.options[0]:void 0;return{before(){let t=e.filename;if(!z(t))return!1},Program(r){let i=e.filename,a=n.basename(i);R(i,t).isMatch||e.report({node:r,message:`测试文件 "${a}" 未找到匹配的源文件。测试文件应集中放置在 __tests__/ 目录下(可按 unit/integration/e2e 或 bun/vitest 分子目录组织),文件名与源文件名或目录名保持一致,例如 src/user.service.ts -> src/__tests__/user.service.test.ts、src/bridge/ -> src/__tests__/bridge.test.ts。同一源文件拆分多个测试时,用点号分隔拆分标识(如 src/comment.orm-repository.ts -> src/__tests__/comment.orm-repository.query.test.ts,而非 comment.orm-repository-query.test.ts)`})}}}});function X(e){return e.isElectron&&e.electronProcess?`src/${e.electronProcess}/__tests__`:`src/__tests__`}function Z(e,t){return s(e).startsWith(`${t.srcRoot}/__tests__/`)}function ne(e,t){let r=s(e),i=n.basename(e),a=r.indexOf(`/src/`);if(a===-1)return`${t.srcRoot}/__tests__/${i}`;let o=r.slice(a+5).split(`/`);if(o.length>1&&o.pop(),t.isElectron&&t.electronProcess){let e=o.indexOf(t.electronProcess);e!==-1&&o.splice(e,1)}let c=o.indexOf(`__tests__`);c!==-1&&o.splice(c,1);let l=o.join(`/`),u=X(t);return l?`${u}/${l}/${i}`:`${u}/${i}`}const Q={"test-file-placement":e({meta:{type:`problem`,docs:{description:`强制测试文件放置在 src/__tests__ 目录下`}},createOnce(e){return{before(){let t=e.filename,r=n.basename(t);if(!c(r)||l(t)||r.endsWith(`.d.ts`))return!1;let i=y.detect(t);if(!i||i.type===`unknown`||Z(t,i))return!1},Program(t){let r=e.filename,i=n.basename(r),a=y.detect(r);if(!a)return;let o=X(a),s=ne(r,a);e.report({node:t,message:`测试文件 "${i}" 必须放置在 ${o} 目录下。当前路径: "${r}"。建议路径: "${s}"`})}}}}),"no-non-test-files-in-tests-directory":q,"test-file-matches-source":Y},$=t({meta:{name:`@longzai-intelligence-oxlint/architecture-library-plugin`},rules:Q});export{$ as architectureLibraryPlugin,$ as default,Q as libraryRules};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longzai-intelligence-oxlint/architecture-library-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@longzai-intelligence-oxlint/core": "0.1.
|
|
38
|
+
"@longzai-intelligence-oxlint/core": "0.1.20",
|
|
39
39
|
"@oxlint/plugins": "^1.74.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@longzai-intelligence-oxlint/testing": "0.1.
|
|
42
|
+
"@longzai-intelligence-oxlint/testing": "0.1.20",
|
|
43
43
|
"@vitest/coverage-v8": "^4.1.10",
|
|
44
44
|
"typescript": "^6.0.3"
|
|
45
45
|
},
|