@dyyz1993/create-agent 2.0.0 → 2.1.0
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/package.json +1 -1
- package/src/commands/create.ts +31 -31
- package/src/commands/workspace.ts +112 -105
- package/src/lib/copy.ts +24 -1
- package/templates/agent/electron/main.js +46 -0
- package/templates/agent/electron/preload.js +5 -0
- package/templates/agent/electron-builder.json +40 -0
- package/templates/agent/eslint.config.mjs +17 -2
- package/templates/agent/package.json +69 -3
- package/templates/agent/src/__tests__/hybrid-mode.test.ts +126 -0
- package/templates/agent/src/__tests__/server-config-security.test.ts +55 -0
- package/templates/agent/src/bun/index.ts +96 -41
- package/templates/agent/src/bun/three.d.ts +1 -0
- package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +118 -0
- package/templates/agent/src/gateway/http-routes.ts +1 -1
- package/templates/agent/src/gateway/ws-handler.ts +84 -56
- package/templates/agent/src/mainview/App.tsx +29 -26
- package/templates/agent/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
- package/templates/agent/src/mainview/__tests__/setup.ts +32 -29
- package/templates/agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
- package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +88 -88
- package/templates/agent/src/mainview/components/common/ErrorBoundary.tsx +1 -58
- package/templates/agent/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
- package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +1 -0
- package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +232 -210
- package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +105 -81
- package/templates/agent/src/mainview/components/search/SearchPanel.tsx +427 -378
- package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +149 -122
- package/templates/agent/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/agent/src/mainview/lib/api-client.ts +179 -172
- package/templates/agent/src/mainview/main.tsx +4 -10
- package/templates/agent/src/mainview/stores/use-feed-store.ts +107 -107
- package/templates/agent/src/mainview/utils/drop-handler.ts +114 -115
- package/templates/agent/src/server-config.ts +34 -32
- package/templates/agent/src/server.ts +42 -54
- package/templates/agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
- package/templates/agent/src/shared/handlers/__tests__/handler-isolation.test.ts +244 -0
- package/templates/agent/src/shared/handlers/bash.ts +65 -65
- package/templates/agent/src/shared/handlers/chat.ts +189 -155
- package/templates/agent/src/shared/handlers/debug.ts +12 -0
- package/templates/agent/src/shared/handlers/feed.ts +31 -32
- package/templates/agent/src/shared/handlers/git.ts +286 -243
- package/templates/agent/src/shared/handlers/index.ts +1 -0
- package/templates/agent/src/shared/handlers/rules.ts +31 -31
- package/templates/agent/src/shared/handlers/todo.ts +31 -31
- package/templates/agent/src/shared/http-routes.ts +250 -0
- package/templates/agent/src/shared/lib/__tests__/web-server.test.ts +190 -0
- package/templates/agent/src/shared/lib/bash-security.ts +43 -43
- package/templates/agent/src/shared/lib/logger.ts +117 -95
- package/templates/agent/src/shared/lib/web-server.ts +128 -0
- package/templates/agent/src/shared/modules/debug.ts +8 -0
- package/templates/agent/src/shared/rpc-schema.ts +16 -3
- package/templates/agent/test-upload.txt +0 -0
- package/templates/agent/tsconfig.ipc.json +14 -0
- package/templates/agent/tsconfig.json +13 -3
- package/templates/chat/eslint.config.mjs +15 -2
- package/templates/chat/package.json +13 -2
- package/templates/chat/src/__tests__/hybrid-mode.test.ts +125 -0
- package/templates/chat/src/__tests__/server-config-security.test.ts +55 -0
- package/templates/chat/src/__tests__/server-config.test.ts +46 -44
- package/templates/chat/src/bun/index.ts +94 -44
- package/templates/chat/src/bun/three.d.ts +1 -0
- package/templates/chat/src/gateway/http-routes.ts +1 -1
- package/templates/chat/src/gateway/ws-handler.ts +84 -56
- package/templates/chat/src/mainview/App.tsx +6 -0
- package/templates/chat/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
- package/templates/chat/src/mainview/__tests__/theme-variables.test.ts +36 -0
- package/templates/chat/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
- package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +1 -0
- package/templates/chat/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/chat/src/mainview/lib/api-client.ts +180 -156
- package/templates/chat/src/mainview/lib/rpc-cache.ts +84 -0
- package/templates/chat/src/mainview/main.tsx +10 -6
- package/templates/chat/src/server-config.ts +33 -31
- package/templates/chat/src/server.ts +42 -60
- package/templates/chat/src/shared/handlers/chat.ts +196 -162
- package/templates/chat/src/shared/handlers/debug.ts +12 -0
- package/templates/chat/src/shared/handlers/index.ts +1 -0
- package/templates/chat/src/shared/http-routes.ts +250 -0
- package/templates/chat/src/shared/lib/__tests__/logger.test.ts +92 -0
- package/templates/chat/src/shared/lib/logger.ts +108 -95
- package/templates/chat/src/shared/lib/web-server.ts +128 -0
- package/templates/chat/src/shared/modules/debug.ts +8 -0
- package/templates/chat/src/shared/rpc-schema.ts +3 -2
- package/templates/chat/test-upload.txt +0 -0
- package/templates/chat/tsconfig.ipc.json +14 -0
- package/templates/chat/tsconfig.json +12 -2
- package/templates/general/eslint.config.mjs +15 -2
- package/templates/general/package.json +13 -2
- package/templates/general/src/__tests__/hybrid-mode.test.ts +125 -0
- package/templates/general/src/__tests__/server-config-security.test.ts +55 -0
- package/templates/general/src/__tests__/server-config.test.ts +46 -44
- package/templates/general/src/bun/index.ts +94 -44
- package/templates/general/src/bun/three.d.ts +1 -0
- package/templates/general/src/gateway/http-routes.ts +1 -1
- package/templates/general/src/gateway/ws-handler.ts +84 -56
- package/templates/general/src/mainview/__tests__/i18n/i18n.test.ts +1 -0
- package/templates/general/src/mainview/__tests__/theme-variables.test.ts +36 -0
- package/templates/general/src/mainview/components/common/LanguageSwitcher.tsx +1 -0
- package/templates/general/src/mainview/components/common/ThemeToggle.tsx +1 -0
- package/templates/general/src/mainview/components/feed/FeedPanel.tsx +241 -219
- package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +101 -81
- package/templates/general/src/mainview/components/layout/AppLayout.tsx +6 -0
- package/templates/general/src/mainview/components/search/SearchPanel.tsx +429 -378
- package/templates/general/src/mainview/hooks/use-input-history.ts +70 -61
- package/templates/general/src/mainview/lib/api-client.ts +180 -156
- package/templates/general/src/mainview/lib/rpc-cache.ts +84 -0
- package/templates/general/src/mainview/main.tsx +10 -6
- package/templates/general/src/mainview/stores/use-feed-store.ts +107 -107
- package/templates/general/src/mainview/utils/drop-handler.ts +114 -115
- package/templates/general/src/server-config.ts +33 -31
- package/templates/general/src/server.ts +42 -60
- package/templates/general/src/shared/handlers/__tests__/chat.test.ts +165 -160
- package/templates/general/src/shared/handlers/chat.ts +189 -155
- package/templates/general/src/shared/handlers/debug.ts +12 -0
- package/templates/general/src/shared/handlers/feed.ts +38 -40
- package/templates/general/src/shared/handlers/git.ts +286 -243
- package/templates/general/src/shared/handlers/index.ts +1 -0
- package/templates/general/src/shared/http-routes.ts +250 -0
- package/templates/general/src/shared/lib/__tests__/logger.test.ts +92 -0
- package/templates/general/src/shared/lib/logger.ts +117 -95
- package/templates/general/src/shared/lib/web-server.ts +128 -0
- package/templates/general/src/shared/modules/debug.ts +8 -0
- package/templates/general/src/shared/rpc-schema.ts +12 -2
- package/templates/general/test-upload.txt +0 -0
- package/templates/general/tsconfig.ipc.json +14 -0
- package/templates/general/tsconfig.json +12 -2
- package/templates/shared/__tests__/cors-security.test.ts +165 -0
- package/templates/shared/__tests__/file-path-security.test.ts +260 -0
- package/templates/shared/__tests__/http-routes.test.ts +174 -0
- package/templates/shared/__tests__/logger.test.ts +186 -0
- package/templates/shared/components/ErrorBoundary.tsx +59 -0
- package/templates/shared/env.d.ts +11 -0
- package/templates/shared/http-routes.ts +250 -0
- package/templates/shared/logger.ts +129 -0
- package/templates/shared/test-upload.txt +0 -0
- package/templates/shared/vite-base.config.ts +92 -0
- package/templates/shared/vitest-base.config.ts +42 -0
- package/templates/agent/eslint-plugin-rpc/index.js +0 -50
- package/templates/agent/eslint-plugin-rpc/package.json +0 -6
- package/templates/agent/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
- package/templates/agent/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
- package/templates/agent/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
- package/templates/agent/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
- package/templates/agent/eslint-plugin-rpc/rules/require-api-client.js +0 -58
- package/templates/agent/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
- package/templates/agent/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
- package/templates/chat/eslint-plugin-rpc/index.js +0 -50
- package/templates/chat/eslint-plugin-rpc/package.json +0 -6
- package/templates/chat/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
- package/templates/chat/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
- package/templates/chat/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
- package/templates/chat/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
- package/templates/chat/eslint-plugin-rpc/rules/require-api-client.js +0 -58
- package/templates/chat/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
- package/templates/chat/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
- package/templates/general/eslint-plugin-rpc/index.js +0 -50
- package/templates/general/eslint-plugin-rpc/package.json +0 -6
- package/templates/general/eslint-plugin-rpc/rules/module-file-naming.js +0 -99
- package/templates/general/eslint-plugin-rpc/rules/no-bare-method.js +0 -78
- package/templates/general/eslint-plugin-rpc/rules/no-direct-register.js +0 -65
- package/templates/general/eslint-plugin-rpc/rules/no-hardcoded-strings.js +0 -69
- package/templates/general/eslint-plugin-rpc/rules/require-api-client.js +0 -58
- package/templates/general/eslint-plugin-rpc/rules/require-typed-register.js +0 -78
- package/templates/general/eslint-plugin-rpc/rules/schema-merge-only.js +0 -55
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview 入口文件必须导入 registerAllHandlers
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "入口文件必须导入并调用 registerAllHandlers",
|
|
11
|
-
category: "RPC Conventions",
|
|
12
|
-
recommended: "error",
|
|
13
|
-
},
|
|
14
|
-
messages: {
|
|
15
|
-
missingRegisterAllHandlersImport:
|
|
16
|
-
'入口文件必须导入 registerAllHandlers。请添加:import { registerAllHandlers } from "./shared/handlers/register-all-handlers";',
|
|
17
|
-
missingRegisterAllHandlersCall:
|
|
18
|
-
"已导入 registerAllHandlers 但未调用。请在创建 server 后调用 registerAllHandlers(server)。",
|
|
19
|
-
},
|
|
20
|
-
schema: [],
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
create(context) {
|
|
24
|
-
const filename = context.getFilename();
|
|
25
|
-
|
|
26
|
-
const isEntryPoint =
|
|
27
|
-
filename.endsWith("bun/index.ts") || filename.endsWith("gateway/ws-handler.ts");
|
|
28
|
-
|
|
29
|
-
if (!isEntryPoint) return {};
|
|
30
|
-
|
|
31
|
-
let hasRegisterAllHandlersImport = false;
|
|
32
|
-
let hasRegisterAllHandlersCall = false;
|
|
33
|
-
let importNode = null;
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
ImportDeclaration(node) {
|
|
37
|
-
if (
|
|
38
|
-
node.source.type === "Literal" &&
|
|
39
|
-
typeof node.source.value === "string" &&
|
|
40
|
-
node.source.value.includes("register-all-handlers")
|
|
41
|
-
) {
|
|
42
|
-
for (const specifier of node.specifiers) {
|
|
43
|
-
if (
|
|
44
|
-
specifier.type === "ImportSpecifier" &&
|
|
45
|
-
specifier.imported.name === "registerAllHandlers"
|
|
46
|
-
) {
|
|
47
|
-
hasRegisterAllHandlersImport = true;
|
|
48
|
-
importNode = node;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
CallExpression(node) {
|
|
55
|
-
if (
|
|
56
|
-
node.callee.type === "Identifier" &&
|
|
57
|
-
node.callee.name === "registerAllHandlers"
|
|
58
|
-
) {
|
|
59
|
-
hasRegisterAllHandlersCall = true;
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
"Program:exit"() {
|
|
64
|
-
if (!hasRegisterAllHandlersImport) {
|
|
65
|
-
context.report({
|
|
66
|
-
loc: { line: 1, column: 0 },
|
|
67
|
-
messageId: "missingRegisterAllHandlersImport",
|
|
68
|
-
});
|
|
69
|
-
} else if (!hasRegisterAllHandlersCall) {
|
|
70
|
-
context.report({
|
|
71
|
-
node: importNode,
|
|
72
|
-
messageId: "missingRegisterAllHandlersCall",
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview rpc-schema.ts 只允许合并,禁止直接定义方法
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "rpc-schema.ts 禁止直接定义方法,只允许 extends 合并",
|
|
11
|
-
category: "RPC Conventions",
|
|
12
|
-
recommended: "error",
|
|
13
|
-
},
|
|
14
|
-
messages: {
|
|
15
|
-
noDirectDefinition:
|
|
16
|
-
'rpc-schema.ts 中禁止直接定义方法 "{{key}}"。请在 src/shared/modules/ 下对应模块文件中定义,然后在此处 extends 合并。',
|
|
17
|
-
},
|
|
18
|
-
schema: [],
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
create(context) {
|
|
22
|
-
const filename = context.getFilename();
|
|
23
|
-
|
|
24
|
-
if (!filename.endsWith("rpc-schema.ts") && !filename.endsWith("rpc-schema.tsx")) return {};
|
|
25
|
-
|
|
26
|
-
const schemaInterfaces = new Set(["RPCMethods", "RPCEvents"]);
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
TSInterfaceDeclaration(node) {
|
|
30
|
-
if (!schemaInterfaces.has(node.id.name)) return;
|
|
31
|
-
|
|
32
|
-
if (node.body && node.body.body) {
|
|
33
|
-
for (const member of node.body.body) {
|
|
34
|
-
if (member.type === "TSPropertySignature" && member.key) {
|
|
35
|
-
const keyName =
|
|
36
|
-
member.key.type === "Identifier"
|
|
37
|
-
? member.key.name
|
|
38
|
-
: member.key.type === "Literal"
|
|
39
|
-
? String(member.key.value)
|
|
40
|
-
: null;
|
|
41
|
-
|
|
42
|
-
if (keyName) {
|
|
43
|
-
context.report({
|
|
44
|
-
node: member,
|
|
45
|
-
messageId: "noDirectDefinition",
|
|
46
|
-
data: { key: keyName },
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* eslint-plugin-rpc — RPC 模块化规范 ESLint 插件(单注册架构)
|
|
3
|
-
*
|
|
4
|
-
* 规则列表:
|
|
5
|
-
* - rpc/no-bare-method : 方法名必须使用 module.action 格式
|
|
6
|
-
* - rpc/no-direct-register : server.register() 只允许在 handlers/ 目录内使用
|
|
7
|
-
* - rpc/schema-merge-only : rpc-schema.ts 禁止直接定义方法
|
|
8
|
-
* - rpc/module-file-naming : 模块文件命名、导出、方法前缀强制规范
|
|
9
|
-
* - rpc/require-typed-register : 入口文件必须导入 registerAllHandlers
|
|
10
|
-
* - rpc/require-api-client : 前端必须通过 apiClient 调用 RPC
|
|
11
|
-
*/
|
|
12
|
-
"use strict";
|
|
13
|
-
|
|
14
|
-
const noBareMethod = require("./rules/no-bare-method");
|
|
15
|
-
const noDirectRegister = require("./rules/no-direct-register");
|
|
16
|
-
const schemaMergeOnly = require("./rules/schema-merge-only");
|
|
17
|
-
const moduleFileNaming = require("./rules/module-file-naming");
|
|
18
|
-
const requireTypedRegister = require("./rules/require-typed-register");
|
|
19
|
-
const requireApiClient = require("./rules/require-api-client");
|
|
20
|
-
const noHardcodedStrings = require("./rules/no-hardcoded-strings");
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
meta: {
|
|
24
|
-
name: "eslint-plugin-rpc",
|
|
25
|
-
version: "1.0.0",
|
|
26
|
-
},
|
|
27
|
-
rules: {
|
|
28
|
-
"no-bare-method": noBareMethod,
|
|
29
|
-
"no-direct-register": noDirectRegister,
|
|
30
|
-
"schema-merge-only": schemaMergeOnly,
|
|
31
|
-
"module-file-naming": moduleFileNaming,
|
|
32
|
-
"require-typed-register": requireTypedRegister,
|
|
33
|
-
"require-api-client": requireApiClient,
|
|
34
|
-
"no-hardcoded-strings": noHardcodedStrings,
|
|
35
|
-
},
|
|
36
|
-
configs: {
|
|
37
|
-
recommended: {
|
|
38
|
-
plugins: ["rpc"],
|
|
39
|
-
rules: {
|
|
40
|
-
"rpc/no-bare-method": "error",
|
|
41
|
-
"rpc/no-direct-register": "error",
|
|
42
|
-
"rpc/schema-merge-only": "error",
|
|
43
|
-
"rpc/module-file-naming": "error",
|
|
44
|
-
"rpc/require-typed-register": "error",
|
|
45
|
-
"rpc/require-api-client": "error",
|
|
46
|
-
"rpc/no-hardcoded-strings": "warn",
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview 强制 RPC 模块文件的命名和导出规范
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
function getModuleName(filename) {
|
|
7
|
-
const match = filename.match(/\/modules\/([^/]+)\.[jt]sx?$/);
|
|
8
|
-
return match ? match[1] : null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module.exports = {
|
|
12
|
-
meta: {
|
|
13
|
-
type: "problem",
|
|
14
|
-
docs: {
|
|
15
|
-
description: "RPC 模块文件必须遵循命名和导出规范",
|
|
16
|
-
category: "RPC Conventions",
|
|
17
|
-
recommended: "error",
|
|
18
|
-
},
|
|
19
|
-
messages: {
|
|
20
|
-
missingMethodsExport:
|
|
21
|
-
'模块文件 "{{file}}" 必须导出 "{{ModuleName}}Methods" 接口。',
|
|
22
|
-
methodPrefixMismatch:
|
|
23
|
-
'方法 "{{method}}" 的前缀不匹配模块 "{{module}}"。必须以 "{{module}}." 开头。',
|
|
24
|
-
eventPrefixMismatch:
|
|
25
|
-
'事件 "{{event}}" 的前缀不匹配模块 "{{module}}"。必须以 "{{module}}." 开头。',
|
|
26
|
-
},
|
|
27
|
-
schema: [],
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
create(context) {
|
|
31
|
-
const filename = context.getFilename();
|
|
32
|
-
const moduleName = getModuleName(filename);
|
|
33
|
-
if (!moduleName) return {};
|
|
34
|
-
|
|
35
|
-
const moduleNamePascal = moduleName
|
|
36
|
-
.split(/[-_]/)
|
|
37
|
-
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
38
|
-
.join("");
|
|
39
|
-
|
|
40
|
-
let hasMethodsExport = false;
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
TSInterfaceDeclaration(node) {
|
|
44
|
-
const name = node.id.name;
|
|
45
|
-
|
|
46
|
-
if (name === `${moduleNamePascal}Methods`) {
|
|
47
|
-
hasMethodsExport = true;
|
|
48
|
-
|
|
49
|
-
if (node.body && node.body.body) {
|
|
50
|
-
for (const member of node.body.body) {
|
|
51
|
-
if (member.type === "TSPropertySignature" && member.key) {
|
|
52
|
-
const key =
|
|
53
|
-
member.key.type === "Literal" ? String(member.key.value) : null;
|
|
54
|
-
if (key && !key.startsWith(`${moduleName}.`)) {
|
|
55
|
-
context.report({
|
|
56
|
-
node: member,
|
|
57
|
-
messageId: "methodPrefixMismatch",
|
|
58
|
-
data: { method: key, module: moduleName },
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (name === `${moduleNamePascal}Events`) {
|
|
67
|
-
if (node.body && node.body.body) {
|
|
68
|
-
for (const member of node.body.body) {
|
|
69
|
-
if (member.type === "TSPropertySignature" && member.key) {
|
|
70
|
-
const key =
|
|
71
|
-
member.key.type === "Literal" ? String(member.key.value) : null;
|
|
72
|
-
if (key && !key.startsWith(`${moduleName}.`)) {
|
|
73
|
-
context.report({
|
|
74
|
-
node: member,
|
|
75
|
-
messageId: "eventPrefixMismatch",
|
|
76
|
-
data: { event: key, module: moduleName },
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
"Program:exit"() {
|
|
86
|
-
if (!hasMethodsExport) {
|
|
87
|
-
context.report({
|
|
88
|
-
loc: { line: 1, column: 0 },
|
|
89
|
-
messageId: "missingMethodsExport",
|
|
90
|
-
data: {
|
|
91
|
-
file: filename.split("/").pop(),
|
|
92
|
-
ModuleName: moduleNamePascal,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
},
|
|
99
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview 禁止使用裸方法名(无模块前缀)
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
const METHOD_CALL_PATTERNS = [
|
|
7
|
-
{ calleePattern: /^register$/, argIndex: 0 },
|
|
8
|
-
{ calleePattern: /\.call$/, argIndex: 0 },
|
|
9
|
-
{ calleePattern: /\.subscribe$/, argIndex: 0 },
|
|
10
|
-
{ calleePattern: /\.emitEvent$/, argIndex: 0 },
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
function isModuleMethodName(name) {
|
|
14
|
-
const parts = name.split(".");
|
|
15
|
-
return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = {
|
|
19
|
-
meta: {
|
|
20
|
-
type: "problem",
|
|
21
|
-
docs: {
|
|
22
|
-
description: "RPC 方法名必须使用 module.action 格式(禁止裸方法名)",
|
|
23
|
-
category: "RPC Conventions",
|
|
24
|
-
recommended: "error",
|
|
25
|
-
},
|
|
26
|
-
messages: {
|
|
27
|
-
bareMethod:
|
|
28
|
-
'RPC 方法名 "{{name}}" 缺少模块前缀。必须使用 "module.action" 格式,如 "{{suggestion}}"。',
|
|
29
|
-
invalidFormat:
|
|
30
|
-
'RPC 方法名 "{{name}}" 格式错误。必须使用 "module.action" 格式(单一 "." 分隔)。',
|
|
31
|
-
},
|
|
32
|
-
schema: [],
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
create(context) {
|
|
36
|
-
const filename = context.getFilename();
|
|
37
|
-
|
|
38
|
-
if (!/\.[jt]sx?$/.test(filename)) return {};
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
CallExpression(node) {
|
|
42
|
-
const { callee, arguments: args } = node;
|
|
43
|
-
|
|
44
|
-
let calleeText = "";
|
|
45
|
-
if (callee.type === "Identifier") {
|
|
46
|
-
calleeText = callee.name;
|
|
47
|
-
} else if (callee.type === "MemberExpression" && callee.property.type === "Identifier") {
|
|
48
|
-
calleeText = `.${callee.property.name}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
for (const pattern of METHOD_CALL_PATTERNS) {
|
|
52
|
-
if (!pattern.calleePattern.test(calleeText)) continue;
|
|
53
|
-
if (args.length <= pattern.argIndex) continue;
|
|
54
|
-
|
|
55
|
-
const arg = args[pattern.argIndex];
|
|
56
|
-
if (arg.type !== "Literal" || typeof arg.value !== "string") continue;
|
|
57
|
-
|
|
58
|
-
const methodName = arg.value;
|
|
59
|
-
|
|
60
|
-
if (!methodName.includes(".")) {
|
|
61
|
-
const suggestion = `module.${methodName}`;
|
|
62
|
-
context.report({
|
|
63
|
-
node: arg,
|
|
64
|
-
messageId: "bareMethod",
|
|
65
|
-
data: { name: methodName, suggestion },
|
|
66
|
-
});
|
|
67
|
-
} else if (!isModuleMethodName(methodName)) {
|
|
68
|
-
context.report({
|
|
69
|
-
node: arg,
|
|
70
|
-
messageId: "invalidFormat",
|
|
71
|
-
data: { name: methodName },
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
};
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview server.register() 只允许在 shared/handlers/ 目录内使用
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description:
|
|
11
|
-
"server.register() 只允许在 shared/handlers/ 目录内使用,入口文件和其他文件禁止直接调用",
|
|
12
|
-
category: "RPC Conventions",
|
|
13
|
-
recommended: "error",
|
|
14
|
-
},
|
|
15
|
-
messages: {
|
|
16
|
-
noDirectRegisterInEntry:
|
|
17
|
-
"入口文件禁止直接调用 server.register()。请导入并使用 registerAllHandlers() 统一注册 handler。",
|
|
18
|
-
noDirectRegisterGeneral:
|
|
19
|
-
"禁止直接调用 server.register()。Handler 注册只能在 shared/handlers/ 目录内的文件中进行。",
|
|
20
|
-
},
|
|
21
|
-
schema: [],
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
create(context) {
|
|
25
|
-
const filename = context.getFilename();
|
|
26
|
-
|
|
27
|
-
if (!/src\/.*\.[jt]sx?$/.test(filename)) return {};
|
|
28
|
-
|
|
29
|
-
if (filename.includes("shared/handlers/")) return {};
|
|
30
|
-
|
|
31
|
-
const isEntryPoint =
|
|
32
|
-
filename.endsWith("bun/index.ts") || filename.endsWith("server.ts");
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
CallExpression(node) {
|
|
36
|
-
const { callee } = node;
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
callee.type === "MemberExpression" &&
|
|
40
|
-
callee.property.type === "Identifier" &&
|
|
41
|
-
callee.property.name === "register" &&
|
|
42
|
-
callee.object.type === "Identifier"
|
|
43
|
-
) {
|
|
44
|
-
const objectName = callee.object.name;
|
|
45
|
-
|
|
46
|
-
const serverVarNames = [
|
|
47
|
-
"server",
|
|
48
|
-
"rpcServer",
|
|
49
|
-
"rpc",
|
|
50
|
-
"rpc_server",
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
if (serverVarNames.includes(objectName)) {
|
|
54
|
-
context.report({
|
|
55
|
-
node,
|
|
56
|
-
messageId: isEntryPoint
|
|
57
|
-
? "noDirectRegisterInEntry"
|
|
58
|
-
: "noDirectRegisterGeneral",
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
};
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
meta: {
|
|
5
|
-
type: "suggestion",
|
|
6
|
-
docs: {
|
|
7
|
-
description: "disallow hardcoded strings in JSX (use i18n t() instead)",
|
|
8
|
-
category: "Best Practices",
|
|
9
|
-
recommended: false,
|
|
10
|
-
},
|
|
11
|
-
fixable: null,
|
|
12
|
-
schema: [
|
|
13
|
-
{
|
|
14
|
-
type: "object",
|
|
15
|
-
properties: {
|
|
16
|
-
ignoreAttributes: {
|
|
17
|
-
type: "array",
|
|
18
|
-
items: { type: "string" },
|
|
19
|
-
},
|
|
20
|
-
ignoreComponents: {
|
|
21
|
-
type: "array",
|
|
22
|
-
items: { type: "string" },
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
additionalProperties: false,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
messages: {
|
|
29
|
-
hardcodedString:
|
|
30
|
-
'Hardcoded string "{{text}}" should use i18n translation (t()).',
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
create(context) {
|
|
35
|
-
const options = context.options[0] || {};
|
|
36
|
-
const ignoreAttributes = new Set(options.ignoreAttributes || ["className", "style", "id", "key", "ref", "type", "name", "data-testid", "viewBox", "d", "points", "x1", "x2", "y1", "y2", "strokeWidth", "strokeLinecap", "strokeLinejoin", "fill", "stroke", "xmlns"]);
|
|
37
|
-
new Set(options.ignoreComponents || []);
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
JSXText(node) {
|
|
41
|
-
const text = node.value.trim();
|
|
42
|
-
if (text.length > 1 && /[a-zA-Z]{2,}/.test(text)) {
|
|
43
|
-
context.report({
|
|
44
|
-
node,
|
|
45
|
-
messageId: "hardcodedString",
|
|
46
|
-
data: { text },
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
JSXAttribute(node) {
|
|
51
|
-
if (ignoreAttributes.has(node.name.name)) return;
|
|
52
|
-
if (
|
|
53
|
-
node.value &&
|
|
54
|
-
node.value.type === "Literal" &&
|
|
55
|
-
typeof node.value.value === "string"
|
|
56
|
-
) {
|
|
57
|
-
const text = node.value.value.trim();
|
|
58
|
-
if (text.length > 3 && /[a-zA-Z]{3,}/.test(text) && !text.startsWith("var(") && !text.startsWith("bg-") && !text.startsWith("text-") && !text.startsWith("border-")) {
|
|
59
|
-
context.report({
|
|
60
|
-
node,
|
|
61
|
-
messageId: "hardcodedString",
|
|
62
|
-
data: { text },
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
},
|
|
69
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview 强制前端使用 apiClient 进行 RPC 调用
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "前端 RPC 调用必须通过 apiClient",
|
|
11
|
-
category: "RPC Conventions",
|
|
12
|
-
recommended: "error",
|
|
13
|
-
},
|
|
14
|
-
messages: {
|
|
15
|
-
useApiClient:
|
|
16
|
-
"前端 RPC 调用必须通过 apiClient.call() / apiClient.subscribe()。禁止直接操作 WebSocket 或其他传输层。",
|
|
17
|
-
},
|
|
18
|
-
schema: [],
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
create(context) {
|
|
22
|
-
const filename = context.getFilename();
|
|
23
|
-
|
|
24
|
-
if (!/mainview\/.*\.[jt]sx?$/.test(filename)) return {};
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
NewExpression(node) {
|
|
28
|
-
if (
|
|
29
|
-
node.callee.type === "Identifier" &&
|
|
30
|
-
node.callee.name === "WebSocket"
|
|
31
|
-
) {
|
|
32
|
-
context.report({
|
|
33
|
-
node,
|
|
34
|
-
messageId: "useApiClient",
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
CallExpression(node) {
|
|
40
|
-
if (
|
|
41
|
-
node.callee.type === "MemberExpression" &&
|
|
42
|
-
node.callee.property.type === "Identifier" &&
|
|
43
|
-
node.callee.property.name === "send"
|
|
44
|
-
) {
|
|
45
|
-
if (node.callee.object.type === "Identifier") {
|
|
46
|
-
const objName = node.callee.object.name.toLowerCase();
|
|
47
|
-
if (objName.includes("websocket") || objName.includes("ws")) {
|
|
48
|
-
context.report({
|
|
49
|
-
node,
|
|
50
|
-
messageId: "useApiClient",
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview 入口文件必须导入 registerAllHandlers
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "入口文件必须导入并调用 registerAllHandlers",
|
|
11
|
-
category: "RPC Conventions",
|
|
12
|
-
recommended: "error",
|
|
13
|
-
},
|
|
14
|
-
messages: {
|
|
15
|
-
missingRegisterAllHandlersImport:
|
|
16
|
-
'入口文件必须导入 registerAllHandlers。请添加:import { registerAllHandlers } from "./shared/handlers/register-all-handlers";',
|
|
17
|
-
missingRegisterAllHandlersCall:
|
|
18
|
-
"已导入 registerAllHandlers 但未调用。请在创建 server 后调用 registerAllHandlers(server)。",
|
|
19
|
-
},
|
|
20
|
-
schema: [],
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
create(context) {
|
|
24
|
-
const filename = context.getFilename();
|
|
25
|
-
|
|
26
|
-
const isEntryPoint =
|
|
27
|
-
filename.endsWith("bun/index.ts") || filename.endsWith("gateway/ws-handler.ts");
|
|
28
|
-
|
|
29
|
-
if (!isEntryPoint) return {};
|
|
30
|
-
|
|
31
|
-
let hasRegisterAllHandlersImport = false;
|
|
32
|
-
let hasRegisterAllHandlersCall = false;
|
|
33
|
-
let importNode = null;
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
ImportDeclaration(node) {
|
|
37
|
-
if (
|
|
38
|
-
node.source.type === "Literal" &&
|
|
39
|
-
typeof node.source.value === "string" &&
|
|
40
|
-
node.source.value.includes("register-all-handlers")
|
|
41
|
-
) {
|
|
42
|
-
for (const specifier of node.specifiers) {
|
|
43
|
-
if (
|
|
44
|
-
specifier.type === "ImportSpecifier" &&
|
|
45
|
-
specifier.imported.name === "registerAllHandlers"
|
|
46
|
-
) {
|
|
47
|
-
hasRegisterAllHandlersImport = true;
|
|
48
|
-
importNode = node;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
CallExpression(node) {
|
|
55
|
-
if (
|
|
56
|
-
node.callee.type === "Identifier" &&
|
|
57
|
-
node.callee.name === "registerAllHandlers"
|
|
58
|
-
) {
|
|
59
|
-
hasRegisterAllHandlersCall = true;
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
"Program:exit"() {
|
|
64
|
-
if (!hasRegisterAllHandlersImport) {
|
|
65
|
-
context.report({
|
|
66
|
-
loc: { line: 1, column: 0 },
|
|
67
|
-
messageId: "missingRegisterAllHandlersImport",
|
|
68
|
-
});
|
|
69
|
-
} else if (!hasRegisterAllHandlersCall) {
|
|
70
|
-
context.report({
|
|
71
|
-
node: importNode,
|
|
72
|
-
messageId: "missingRegisterAllHandlersCall",
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview rpc-schema.ts 只允许合并,禁止直接定义方法
|
|
3
|
-
*/
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description: "rpc-schema.ts 禁止直接定义方法,只允许 extends 合并",
|
|
11
|
-
category: "RPC Conventions",
|
|
12
|
-
recommended: "error",
|
|
13
|
-
},
|
|
14
|
-
messages: {
|
|
15
|
-
noDirectDefinition:
|
|
16
|
-
'rpc-schema.ts 中禁止直接定义方法 "{{key}}"。请在 src/shared/modules/ 下对应模块文件中定义,然后在此处 extends 合并。',
|
|
17
|
-
},
|
|
18
|
-
schema: [],
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
create(context) {
|
|
22
|
-
const filename = context.getFilename();
|
|
23
|
-
|
|
24
|
-
if (!filename.endsWith("rpc-schema.ts") && !filename.endsWith("rpc-schema.tsx")) return {};
|
|
25
|
-
|
|
26
|
-
const schemaInterfaces = new Set(["RPCMethods", "RPCEvents"]);
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
TSInterfaceDeclaration(node) {
|
|
30
|
-
if (!schemaInterfaces.has(node.id.name)) return;
|
|
31
|
-
|
|
32
|
-
if (node.body && node.body.body) {
|
|
33
|
-
for (const member of node.body.body) {
|
|
34
|
-
if (member.type === "TSPropertySignature" && member.key) {
|
|
35
|
-
const keyName =
|
|
36
|
-
member.key.type === "Identifier"
|
|
37
|
-
? member.key.name
|
|
38
|
-
: member.key.type === "Literal"
|
|
39
|
-
? String(member.key.value)
|
|
40
|
-
: null;
|
|
41
|
-
|
|
42
|
-
if (keyName) {
|
|
43
|
-
context.report({
|
|
44
|
-
node: member,
|
|
45
|
-
messageId: "noDirectDefinition",
|
|
46
|
-
data: { key: keyName },
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
};
|