@longzai-intelligence-oxlint/architecture-electron-plugin 0.1.11 → 0.1.13
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/dist/index.js +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";const n=`createTyped`;function r(e,t){let n=e.replace(/\\/g,`/`);return n.includes(t)&&!n.includes(`.test.`)}function i(e){let t=Array.isArray(e.options)?e.options[0]:void 0,r=t&&typeof t==`object`?t:{};return{preloadUtilsPath:`preloadUtilsPath`in r&&typeof r.preloadUtilsPath==`string`?r.preloadUtilsPath:`/src/preload/utils/`,ipcUtilityPrefix:`ipcUtilityPrefix`in r&&typeof r.ipcUtilityPrefix==`string`?r.ipcUtilityPrefix:n}}const a=e({meta:{type:`problem`,docs:{description:`禁止自定义 IPC 工具函数,应从标准库重新导出`}},createOnce(e){let t=!1,a=n;return{before(){let{preloadUtilsPath:n,ipcUtilityPrefix:o}=i(e);if(a=o,t=r(e.filename,n),!t)return!1},FunctionDeclaration(n){t&&n.id&&n.id.name.startsWith(a)&&e.report({node:n,message:`禁止自定义 IPC 工具函数 "${n.id.name}"。应从标准库重新导出。`})},VariableDeclaration(n){if(t)for(let t of n.declarations)t.id.type===`Identifier`&&t.id.name.startsWith(a)&&(t.init?.type===`FunctionExpression`||t.init?.type===`ArrowFunctionExpression`)&&e.report({node:t,message:`禁止自定义 IPC 工具函数 "${t.id.name}"。应从标准库重新导出。`})}}}});function o(e){let t=e.replace(/\\/g,`/`);return t.includes(`/src/preload/`)&&!t.includes(`/src/preload/utils/`)&&!t.includes(`.test.`)}const s=e({meta:{type:`problem`,docs:{description:`禁止在 Preload 中直接调用 ipcRenderer`}},createOnce(e){return{before(){if(!o(e.filename))return!1},CallExpression(t){t.callee.type===`MemberExpression`&&t.callee.object.type===`Identifier`&&t.callee.object.name===`ipcRenderer`&&t.callee.property.type===`Identifier`&&e.report({node:t,message:`禁止直接调用 ipcRenderer.${t.callee.property.name}()。请使用 createTypedAPI 或 invoke 封装。`})}}}}),c=new Set([`on`,`send`,`invoke`]);function l(e){let t=e.replace(/\\/g,`/`);return t.includes(`/src/preload/`)&&!t.includes(`.test.`)}function u(e){for(let t of e)if(typeof t==`object`&&t&&`key`in t&&t.key!==null&&typeof t.key==`object`&&`name`in t.key&&typeof t.key.name==`string`&&c.has(t.key.name))return!0;return!1}const d=e({meta:{type:`problem`,docs:{description:`禁止暴露包含通用 IPC 方法的桥接对象`}},createOnce(e){return{before(){if(!l(e.filename))return!1},Property(t){t.key.type
|
|
1
|
+
import{defineRule as e,eslintCompatPlugin as t}from"@oxlint/plugins";const n=`createTyped`;function r(e,t){let n=e.replace(/\\/g,`/`);return n.includes(t)&&!n.includes(`.test.`)}function i(e){let t=Array.isArray(e.options)?e.options[0]:void 0,r=t&&typeof t==`object`?t:{};return{preloadUtilsPath:`preloadUtilsPath`in r&&typeof r.preloadUtilsPath==`string`?r.preloadUtilsPath:`/src/preload/utils/`,ipcUtilityPrefix:`ipcUtilityPrefix`in r&&typeof r.ipcUtilityPrefix==`string`?r.ipcUtilityPrefix:n}}const a=e({meta:{type:`problem`,docs:{description:`禁止自定义 IPC 工具函数,应从标准库重新导出`}},createOnce(e){let t=!1,a=n;return{before(){let{preloadUtilsPath:n,ipcUtilityPrefix:o}=i(e);if(a=o,t=r(e.filename,n),!t)return!1},FunctionDeclaration(n){t&&n.id&&n.id.name.startsWith(a)&&e.report({node:n,message:`禁止自定义 IPC 工具函数 "${n.id.name}"。应从标准库重新导出。`})},VariableDeclaration(n){if(t)for(let t of n.declarations)t.id.type===`Identifier`&&t.id.name.startsWith(a)&&(t.init?.type===`FunctionExpression`||t.init?.type===`ArrowFunctionExpression`)&&e.report({node:t,message:`禁止自定义 IPC 工具函数 "${t.id.name}"。应从标准库重新导出。`})}}}});function o(e){let t=e.replace(/\\/g,`/`);return t.includes(`/src/preload/`)&&!t.includes(`/src/preload/utils/`)&&!t.includes(`.test.`)}const s=e({meta:{type:`problem`,docs:{description:`禁止在 Preload 中直接调用 ipcRenderer`}},createOnce(e){return{before(){if(!o(e.filename))return!1},CallExpression(t){t.callee.type===`MemberExpression`&&t.callee.object.type===`Identifier`&&t.callee.object.name===`ipcRenderer`&&t.callee.property.type===`Identifier`&&e.report({node:t,message:`禁止直接调用 ipcRenderer.${t.callee.property.name}()。请使用 createTypedAPI 或 invoke 封装。`})}}}}),c=new Set([`on`,`send`,`invoke`]);function l(e){let t=e.replace(/\\/g,`/`);return t.includes(`/src/preload/`)&&!t.includes(`.test.`)}function u(e){for(let t of e)if(typeof t==`object`&&t&&`key`in t&&t.key!==null&&typeof t.key==`object`&&`name`in t.key&&typeof t.key.name==`string`&&c.has(t.key.name))return!0;return!1}const d=e({meta:{type:`problem`,docs:{description:`禁止暴露包含通用 IPC 方法的桥接对象`}},createOnce(e){return{before(){if(!l(e.filename))return!1},Property(t){t.key.type===`Identifier`&&t.key.name===`ipc`&&t.value.type===`ObjectExpression`&&u(t.value.properties)&&e.report({node:t,message:`禁止暴露包含 on/send/invoke 方法的 ipc 对象。请使用领域 API 替代通用 IPC 方法。`})}}}}),f=[`/src/renderer/`];function p(e,t){let n=e.replace(/\\/g,`/`);return t.some(e=>n.includes(e))}function m(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function h(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function g(e,t,n){return m(e[t])?e[t]:n}function _(e,t,n){return typeof e[t]==`string`?e[t]:n}const v={},y=e({meta:{type:`problem`,docs:{description:`禁止在 Renderer 进程导入 Implementation 包`}},createOnce(e){let t=f,n=``,r=`-implementation`;return{before(){let i=e.options?.[0],a=h(i)?i:v;if(t=g(a,`rendererPatterns`,f),n=_(a,`packageScope`,``),r=_(a,`implementationSuffix`,`-implementation`),!p(e.filename,t))return!1},ImportDeclaration(t){let i=t.source.type===`Literal`&&typeof t.source.value==`string`?t.source.value:null;i&&(n&&!i.startsWith(n)||i.endsWith(r)&&e.report({node:t,message:`Renderer 进程禁止导入 Implementation 包 "${i}"。请使用 Contract 包替代。`}))}}}});function b(e,t){if(e.callee.type!==`MemberExpression`||e.callee.object.type!==`Identifier`||e.callee.object.name!==`contextBridge`||e.callee.property.type!==`Identifier`||e.callee.property.name!==`exposeInMainWorld`||e.arguments.length<2)return;let n=e.arguments[0];if(n?.type===`Literal`&&n.value===`electron`){let n=e.arguments[1];n?.type===`Identifier`&&n.name===`electronAPI`&&t(e,`禁止使用 contextBridge.exposeInMainWorld("electron", electronAPI) 暴露全部 Electron API。请使用领域 API 替代。`)}}function x(e,t){e.left.type===`MemberExpression`&&e.left.object.type===`Identifier`&&e.left.object.name===`window`&&e.left.property.type===`Identifier`&&e.left.property.name===`electron`&&e.right.type===`Identifier`&&e.right.name===`electronAPI`&&t(e,`禁止通过 window.electron = electronAPI 暴露全部 Electron API。请使用领域 API 替代。`)}const S=e({meta:{type:`problem`,docs:{description:`禁止直接暴露 electronAPI 给渲染进程`}},createOnce(e){return{ImportDeclaration(t){if(t.source.type===`Literal`&&t.source.value===`@electron-toolkit/preload`)for(let n of t.specifiers)n.type===`ImportSpecifier`&&n.imported.type===`Identifier`&&n.imported.name===`electronAPI`&&e.report({node:n,message:`禁止导入 electronAPI 并直接暴露给渲染进程。请使用领域 API 替代。`})},CallExpression(t){b(t,(t,n)=>{e.report({node:t,message:n})})},AssignmentExpression(t){x(t,(t,n)=>{e.report({node:t,message:n})})}}}}),C=new Set(`fs.path.os.crypto.stream.http.https.net.tls.child_process.cluster.dgram.dns.domain.events.punycode.querystring.readline.repl.string_decoder.timers.tty.url.util.v8.vm.worker_threads.zlib.fs/promises.stream/promises.stream/consumers.stream/web.node:fs.node:path.node:os.node:crypto.node:stream.node:http.node:https.node:net.node:tls.node:child_process.node:cluster.node:dgram.node:dns.node:events.node:punycode.node:querystring.node:readline.node:repl.node:string_decoder.node:timers.node:tty.node:url.node:util.node:v8.node:vm.node:worker_threads.node:zlib.node:fs/promises`.split(`.`)),w=new Set([`window`,`document`,`navigator`,`localStorage`,`sessionStorage`,`fetch`,`XMLHttpRequest`,`WebSocket`,`Worker`,`Blob`,`File`,`FileReader`,`URL`,`URLSearchParams`,`FormData`,`Headers`,`Request`,`Response`,`History`,`Location`,`Screen`]),T=new Set([`ipcRenderer`,`ipcMain`,`BrowserWindow`,`app`,`Menu`,`MenuItem`,`dialog`,`shell`,`clipboard`,`nativeImage`,`Tray`,`webContents`,`session`,`protocol`,`net`,`powerMonitor`,`powerSaveBlocker`,`screen`,`systemPreferences`,`TouchBar`,`crashReporter`]),E=[`/domains/shared-kernel/`];function D(e,t){let n=e.replace(/\\/g,`/`);return t.some(e=>n.includes(e))}function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k(e){return Array.isArray(e)&&e.every(e=>typeof e==`string`)}function A(e){if(O(e)&&`domainLayerPatterns`in e){let t=e.domainLayerPatterns;if(k(t))return t}return E}function j(e,t){let n=e.source.type===`Literal`&&typeof e.source.value==`string`?e.source.value:null;n&&(C.has(n)||n.startsWith(`node:`))&&t(e,`域层禁止导入 Node.js 核心模块 "${n}"。域层应保持平台无关性。`)}function M(e,t){if(e.object.type!==`Identifier`)return;let n=e.object.name;w.has(n)&&t(e,`域层禁止使用浏览器 API "${n}"。域层应保持平台无关性。`),T.has(n)&&t(e,`域层禁止使用 Electron API "${n}"。域层应保持平台无关性。`)}function N(e,t){(w.has(e.name)||T.has(e.name))&&(e.parent?.type!==`MemberExpression`||e.parent.object!==e)&&t(e,`域层禁止直接引用平台 API "${e.name}"。域层应保持平台无关性。`)}const P=e({meta:{type:`problem`,docs:{description:`禁止在业务域层使用平台特定 API(Node.js / Browser / Electron)`}},createOnce(e){let t=null;return{before(){if(t===null){let n=e.options?.[0];t=A(n)}let n=e.filename;if(!D(n,t))return!1},ImportDeclaration(t){j(t,(t,n)=>{e.report({node:t,message:n})})},MemberExpression(t){M(t,(t,n)=>{e.report({node:t,message:n})})},Identifier(t){N(t,(t,n)=>{e.report({node:t,message:n})})}}}});function F(e){let t=e.replace(/\\/g,`/`);return t.endsWith(`/src/preload/index.ts`)&&!t.includes(`.test.`)}const I=e({meta:{type:`problem`,docs:{description:`强制使用 exposeSafeAPI 暴露 API`}},createOnce(e){return{before(){if(!F(e.filename))return!1},CallExpression(t){t.callee.type===`MemberExpression`&&t.callee.object.type===`Identifier`&&t.callee.object.name===`contextBridge`&&t.callee.property.type===`Identifier`&&t.callee.property.name===`exposeInMainWorld`&&e.report({node:t,message:`禁止直接调用 contextBridge.exposeInMainWorld。请使用 exposeSafeAPI 进行 API 暴露。`})}}}});function L(e){let t=e.replace(/\\/g,`/`);return t.includes(`/src/preload/`)&&!t.includes(`.test.`)}const R=e({meta:{type:`problem`,docs:{description:`禁止使用字符串字面量作为 IPC 通道名`}},createOnce(e){return{before(){if(!L(e.filename))return!1},CallExpression(t){if(t.callee.type!==`Identifier`||t.callee.name!==`invoke`||t.arguments.length===0)return;let n=t.arguments[0];n?.type===`Literal`&&typeof n.value==`string`&&e.report({node:n,message:`禁止使用字符串字面量 "${n.value}" 作为 IPC 通道名。请使用常量引用。`})},ObjectExpression(t){if(t.parent?.type===`CallExpression`&&t.parent.callee.type===`Identifier`&&t.parent.callee.name===`createTypedAPI`)for(let n of t.properties)n.type===`Property`&&n.value.type===`Literal`&&typeof n.value.value==`string`&&e.report({node:n.value,message:`禁止在 createTypedAPI 配置中使用字符串字面量 "${n.value.value}" 作为 IPC 通道名。请使用常量引用。`})}}}});function z(e){let t=e.replace(/\\/g,`/`);return t.includes(`/src/renderer/`)&&!t.includes(`.test.`)}function B(e){return e.callee.type!==`MemberExpression`||e.callee.object.type!==`MemberExpression`?!1:e.callee.object.object.type===`Identifier`&&e.callee.object.object.name===`window`}function V(e){return typeof e==`object`&&!!e&&`type`in e&&`callee`in e}function H(e){return typeof e==`object`&&!!e&&`parent`in e}function U(e){let t=e;for(;t&&H(t);){let e=t.parent;if(V(e)&&e.callee){if(e.callee.type===`Identifier`&&(e.callee.name===`unwrap`||e.callee.name===`unwrapIPCResult`))return!0;if(e.callee.type===`Identifier`)return!1}t=e}return!1}const W={"platform-leak":P,"no-implementation-in-renderer":y,"no-unsafe-electron-api-exposure":S,"no-generic-ipc-bridge-exposure":d,"no-direct-ipcrenderer-in-preload":s,"require-typed-ipc-channel":R,"require-expose-safe-api":I,"no-custom-ipc-utility":a,"require-unwrap-ipc-result":e({meta:{type:`problem`,docs:{description:`渲染层调用 IPC API 时必须使用 unwrap 解包结果`}},createOnce(e){return{before(){if(!z(e.filename))return!1},CallExpression(t){B(t)&&(U(t)||e.report({node:t,message:`渲染层调用 IPC API 时必须使用 unwrap 或 unwrapIPCResult 解包结果。请使用 unwrap(window.xxx.method())。`}))}}}})},G=t({meta:{name:`@longzai-intelligence-oxlint/architecture-electron-plugin`},rules:W});export{G as architectureElectronPlugin,G as default,W as electronRules};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longzai-intelligence-oxlint/architecture-electron-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@longzai-intelligence-oxlint/core": "0.1.
|
|
34
|
+
"@longzai-intelligence-oxlint/core": "0.1.13",
|
|
35
35
|
"@oxlint/plugins": "^1.74.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@longzai-intelligence-oxlint/testing": "0.1.
|
|
38
|
+
"@longzai-intelligence-oxlint/testing": "0.1.13",
|
|
39
39
|
"@vitest/coverage-v8": "^4.1.10",
|
|
40
40
|
"typescript": "^6.0.3"
|
|
41
41
|
},
|