@fieldwangai/agentflow 0.1.25
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/LICENSE +21 -0
- package/README.md +201 -0
- package/README.zh-CN.md +201 -0
- package/agents/agentflow-node-executor-code.md +32 -0
- package/agents/agentflow-node-executor-planning.md +32 -0
- package/agents/agentflow-node-executor-requirement.md +32 -0
- package/agents/agentflow-node-executor-test.md +32 -0
- package/agents/agentflow-node-executor-ui.md +32 -0
- package/agents/agentflow-node-executor.md +32 -0
- package/agents/agents.json +8 -0
- package/agents/en/agentflow-node-executor.md +32 -0
- package/agents/zh/agentflow-node-executor.md +32 -0
- package/bin/agentflow.mjs +52 -0
- package/bin/ensure-workspace-reference.mjs +35 -0
- package/bin/lib/agent-runners.mjs +1199 -0
- package/bin/lib/agents-path.mjs +61 -0
- package/bin/lib/api-runner.mjs +361 -0
- package/bin/lib/apply.mjs +852 -0
- package/bin/lib/catalog-agents.mjs +300 -0
- package/bin/lib/catalog-flows.mjs +532 -0
- package/bin/lib/composer-agent.mjs +884 -0
- package/bin/lib/composer-flow-instances.mjs +68 -0
- package/bin/lib/composer-flow-skeleton.mjs +334 -0
- package/bin/lib/composer-flow-validate.mjs +47 -0
- package/bin/lib/composer-log.mjs +197 -0
- package/bin/lib/composer-model-router.mjs +160 -0
- package/bin/lib/composer-node-schema.mjs +299 -0
- package/bin/lib/composer-planner.mjs +749 -0
- package/bin/lib/composer-script-ops.mjs +233 -0
- package/bin/lib/composer-skill-router.mjs +384 -0
- package/bin/lib/flow-import.mjs +305 -0
- package/bin/lib/flow-normalize.mjs +71 -0
- package/bin/lib/flow-write.mjs +395 -0
- package/bin/lib/help.mjs +139 -0
- package/bin/lib/hub-login.mjs +54 -0
- package/bin/lib/hub-publish.mjs +159 -0
- package/bin/lib/hub-remote.mjs +189 -0
- package/bin/lib/hub.mjs +299 -0
- package/bin/lib/i18n.mjs +233 -0
- package/bin/lib/locales/en.json +344 -0
- package/bin/lib/locales/zh.json +344 -0
- package/bin/lib/log.mjs +37 -0
- package/bin/lib/main.mjs +611 -0
- package/bin/lib/model-config.mjs +118 -0
- package/bin/lib/model-lists.mjs +188 -0
- package/bin/lib/node-exec-context.mjs +336 -0
- package/bin/lib/node-execute.mjs +513 -0
- package/bin/lib/normalize-node-tool-command.mjs +97 -0
- package/bin/lib/paths.mjs +216 -0
- package/bin/lib/pipeline-scripts.mjs +41 -0
- package/bin/lib/recent-runs.mjs +173 -0
- package/bin/lib/run-apply-active-lock.mjs +82 -0
- package/bin/lib/run-events.mjs +85 -0
- package/bin/lib/run-node-statuses-from-disk.mjs +85 -0
- package/bin/lib/schedule-config.mjs +227 -0
- package/bin/lib/scheduler.mjs +312 -0
- package/bin/lib/table.mjs +4 -0
- package/bin/lib/terminal.mjs +42 -0
- package/bin/lib/ui-print.mjs +94 -0
- package/bin/lib/ui-server.mjs +2113 -0
- package/bin/lib/workspace-tree.mjs +266 -0
- package/bin/lib/workspace.mjs +180 -0
- package/bin/pipeline/build-node-prompt.mjs +179 -0
- package/bin/pipeline/check-cache.mjs +191 -0
- package/bin/pipeline/check-flow.mjs +543 -0
- package/bin/pipeline/collect-nodes.mjs +212 -0
- package/bin/pipeline/compute-cache-md5.mjs +177 -0
- package/bin/pipeline/ensure-run-dir.mjs +71 -0
- package/bin/pipeline/extract-thinking.mjs +308 -0
- package/bin/pipeline/gc.mjs +129 -0
- package/bin/pipeline/get-env.mjs +83 -0
- package/bin/pipeline/get-exec-id.mjs +145 -0
- package/bin/pipeline/get-ready-nodes.mjs +435 -0
- package/bin/pipeline/get-resolved-values.mjs +337 -0
- package/bin/pipeline/load-key.mjs +62 -0
- package/bin/pipeline/parse-bool.mjs +33 -0
- package/bin/pipeline/parse-flow.mjs +698 -0
- package/bin/pipeline/post-process-control-if.mjs +23 -0
- package/bin/pipeline/post-process-node.mjs +490 -0
- package/bin/pipeline/pre-process-node.mjs +449 -0
- package/bin/pipeline/resolve-inputs.mjs +201 -0
- package/bin/pipeline/run-log.mjs +34 -0
- package/bin/pipeline/run-tool-nodejs.mjs +160 -0
- package/bin/pipeline/save-key.mjs +93 -0
- package/bin/pipeline/snapshot-prior-round.mjs +70 -0
- package/bin/pipeline/validate-flow.mjs +825 -0
- package/bin/pipeline/validate-for-ui.mjs +226 -0
- package/bin/pipeline/validate-script-output.mjs +130 -0
- package/bin/pipeline/write-result.mjs +182 -0
- package/builtin/nodes/agent_subAgent.md +14 -0
- package/builtin/nodes/control_agent_toBool.md +20 -0
- package/builtin/nodes/control_anyOne.md +17 -0
- package/builtin/nodes/control_end.md +11 -0
- package/builtin/nodes/control_if.md +20 -0
- package/builtin/nodes/control_start.md +11 -0
- package/builtin/nodes/control_toBool.md +21 -0
- package/builtin/nodes/provide_file.md +11 -0
- package/builtin/nodes/provide_str.md +11 -0
- package/builtin/nodes/tool_get_env.md +14 -0
- package/builtin/nodes/tool_load_key.md +20 -0
- package/builtin/nodes/tool_nodejs.md +40 -0
- package/builtin/nodes/tool_print.md +14 -0
- package/builtin/nodes/tool_save_key.md +20 -0
- package/builtin/nodes/tool_user_ask.md +23 -0
- package/builtin/nodes/tool_user_check.md +22 -0
- package/builtin/pipelines/module-migrate/flow.yaml +819 -0
- package/builtin/pipelines/module-migrate/scripts/check_imports.mjs +700 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Makefile +362 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/bindings/node/binding.o.d +17 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/src/parser.o.d +5 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/src/scanner.o.d +8 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/tree_sitter_kotlin_binding.node.d +1 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/bindings/node/binding.o +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/src/parser.o +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/src/scanner.o +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/tree_sitter_kotlin_binding.node +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/binding.Makefile +6 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/gyp-mac-tool +768 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/tree_sitter_kotlin_binding.target.mk +203 -0
- package/builtin/pipelines/new/flow.yaml +545 -0
- package/builtin/pipelines/new/scripts/check-flow.mjs +9 -0
- package/builtin/pipelines/new/scripts/collect-nodes.mjs +211 -0
- package/builtin/pipelines/scripts/adjust-node-positions.mjs +113 -0
- package/builtin/web-ui/dist/agentflow-icon.svg +23 -0
- package/builtin/web-ui/dist/assets/index-CZkUPcXE.css +1 -0
- package/builtin/web-ui/dist/assets/index-DkkhNESc.js +190 -0
- package/builtin/web-ui/dist/index.html +24 -0
- package/package.json +67 -0
- package/reference/flow-control-capabilities.md +274 -0
- package/reference/flow-layout.md +84 -0
- package/reference/flow-prompt-handler-check.md +12 -0
- package/reference/flow-result-semantics.md +14 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 轻量级 import 预检查(不跑 Gradle);主模块引用输出(需改为 API 调用的位置)。
|
|
4
|
+
* 供 AgentFlow tool_nodejs 或 CLI 调用;stdout 单行 JSON:{ "err_code": 0|1, "message": { "result": "..." } }。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* # 主模块引用输出(迁移后需改 API 的 import/引用位置)
|
|
8
|
+
* node check_imports.mjs --after-list <path> [--before-list <path>] [--root /path/to/repo]
|
|
9
|
+
*
|
|
10
|
+
* # 原有:按模块或文件列表检查 import 是否存在
|
|
11
|
+
* node check_imports.mjs --module modules/multiline [--root /path/to/repo]
|
|
12
|
+
* node check_imports.mjs --files - [--root /path/to/repo] # 从 stdin 读文件路径,每行一个
|
|
13
|
+
*
|
|
14
|
+
* 主模块引用模式依赖 tree-sitter、tree-sitter-kotlin、tree-sitter-java(scripts 目录 npm install)。
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { createRequire } from "node:module";
|
|
21
|
+
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const PIPELINE_DIR = path.resolve(__dirname, "..");
|
|
24
|
+
// scripts -> module-migrate -> pipelines -> AgentFlow 包根(传入 --root 可指向业务仓库)
|
|
25
|
+
const REPO_ROOT_DEFAULT = path.resolve(PIPELINE_DIR, "..", "..", "..");
|
|
26
|
+
|
|
27
|
+
const PRIVACY_GLOBS = [
|
|
28
|
+
"**/sg/bigo/live/accountAuth/**",
|
|
29
|
+
"**/sg/bigo/live/login/**",
|
|
30
|
+
"**/sg/bigo/like/socialib/LoginManager.kt",
|
|
31
|
+
"**/sg/bigo/like/mengma/TokenProperty.kt",
|
|
32
|
+
"**/jsMethod/extend/JSMethodGetToken.java",
|
|
33
|
+
"**/jsMethod/extend/JSMethodRefreshToken.java",
|
|
34
|
+
"**/jsMethod/extend/JSMethodSSOAuthSuccess.kt",
|
|
35
|
+
"**/sg/bigo/live/pay/**",
|
|
36
|
+
"**/sg/bigo/live/profit/**",
|
|
37
|
+
"**/googlebilling/**",
|
|
38
|
+
"**/huawei/*Pay*.java",
|
|
39
|
+
"**/huawei/*Pay*.kt",
|
|
40
|
+
"**/samsung/*Pay*.kt",
|
|
41
|
+
"**/xiaomi/*Billing*.kt",
|
|
42
|
+
"**/rustore/*Billing*.kt",
|
|
43
|
+
"**/jsMethod/biz/like/JSMethodHalfScreenRecharge.kt",
|
|
44
|
+
"**/jsMethod/biz/like/JSMethodGoAlterPayEntry.kt",
|
|
45
|
+
"**/jsMethod/biz/like/JSMethodOnlyAlterPayEntry.kt",
|
|
46
|
+
"**/jsMethod/biz/like/JSMethodWalletEntryConfig.kt",
|
|
47
|
+
"**/jsMethod/biz/other/JSMethodGotoPay.java",
|
|
48
|
+
"**/jsMethod/biz/like/JSMethodGetSecurityCode.kt",
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const IN_REPO_PREFIXES = ["sg.bigo.", "com.yy.iheima.", "com.yy."];
|
|
52
|
+
|
|
53
|
+
function pathMatchesGlobs(relPath, globs) {
|
|
54
|
+
const normalized = relPath.replace(/\\/g, "/");
|
|
55
|
+
for (const g of globs) {
|
|
56
|
+
const re = globToRegExp(g);
|
|
57
|
+
if (re.test(normalized)) return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function globToRegExp(glob) {
|
|
63
|
+
const s = glob.replace(/\\/g, "/");
|
|
64
|
+
let re = "";
|
|
65
|
+
for (let i = 0; i < s.length; i++) {
|
|
66
|
+
const c = s[i];
|
|
67
|
+
if (c === "*") {
|
|
68
|
+
if (s[i + 1] === "*" && (s[i + 2] === "/" || s[i + 2] === undefined)) {
|
|
69
|
+
re += ".*";
|
|
70
|
+
i += 1;
|
|
71
|
+
if (s[i + 1] === "/") i += 1;
|
|
72
|
+
} else {
|
|
73
|
+
re += "[^/]*";
|
|
74
|
+
}
|
|
75
|
+
} else if (/[.+^${}()|[\]\\]/.test(c)) {
|
|
76
|
+
re += "\\" + c;
|
|
77
|
+
} else {
|
|
78
|
+
re += c;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return new RegExp("^" + re + "$");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function sourceDirs(root) {
|
|
85
|
+
const dirs = [];
|
|
86
|
+
const candidates = [
|
|
87
|
+
path.join(root, "bigovlog"),
|
|
88
|
+
path.join(root, "iHeima"),
|
|
89
|
+
path.join(root, "modules"),
|
|
90
|
+
path.join(root, "effectone-api"),
|
|
91
|
+
path.join(root, "effectone_impl"),
|
|
92
|
+
];
|
|
93
|
+
for (const candidate of candidates) {
|
|
94
|
+
if (!fs.existsSync(candidate)) continue;
|
|
95
|
+
if (path.basename(candidate) === "modules") {
|
|
96
|
+
try {
|
|
97
|
+
const subs = fs.readdirSync(candidate, { withFileTypes: true });
|
|
98
|
+
for (const d of subs) {
|
|
99
|
+
if (!d.isDirectory()) continue;
|
|
100
|
+
for (const sub of ["src/main/java", "src/main/kotlin"]) {
|
|
101
|
+
const p = path.join(candidate, d.name, sub);
|
|
102
|
+
if (fs.existsSync(p)) dirs.push(p);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (_) {}
|
|
106
|
+
} else {
|
|
107
|
+
for (const sub of ["src/main/java", "src/main/kotlin"]) {
|
|
108
|
+
const p = path.join(candidate, sub);
|
|
109
|
+
if (fs.existsSync(p)) dirs.push(p);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return dirs;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function buildClassIndex(root) {
|
|
117
|
+
const index = Object.create(null);
|
|
118
|
+
const dirs = sourceDirs(root);
|
|
119
|
+
const exts = [".kt", ".java"];
|
|
120
|
+
for (const src of dirs) {
|
|
121
|
+
try {
|
|
122
|
+
for (const ext of exts) {
|
|
123
|
+
const files = walkRel(src, ext);
|
|
124
|
+
for (const { rel, full } of files) {
|
|
125
|
+
const rootRel = path.relative(root, full).replace(/\\/g, "/");
|
|
126
|
+
if (pathMatchesGlobs(rootRel, PRIVACY_GLOBS)) continue;
|
|
127
|
+
const qual = rel.replace(/\.(kt|java)$/, "").replace(/\//g, ".");
|
|
128
|
+
index[qual] = rootRel;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (_) {}
|
|
132
|
+
}
|
|
133
|
+
return index;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function walkRel(dir, ext, base = dir, acc = []) {
|
|
137
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
138
|
+
for (const e of entries) {
|
|
139
|
+
const full = path.join(dir, e.name);
|
|
140
|
+
const rel = path.relative(base, full).replace(/\\/g, "/");
|
|
141
|
+
if (e.isDirectory()) {
|
|
142
|
+
walkRel(full, ext, base, acc);
|
|
143
|
+
} else if (e.name.endsWith(ext)) {
|
|
144
|
+
acc.push({ rel, full });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return acc;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const IMPORT_RE = /^\s*import\s+(?:static\s+)?([\w.]+)(?:\s+as\s+\w+)?\s*(?:$|\/\/)/;
|
|
151
|
+
|
|
152
|
+
function extractImports(filePath, root) {
|
|
153
|
+
let text;
|
|
154
|
+
try {
|
|
155
|
+
text = fs.readFileSync(filePath, "utf-8");
|
|
156
|
+
} catch {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const lines = text.split(/\r?\n/);
|
|
160
|
+
const imports = [];
|
|
161
|
+
for (let i = 0; i < lines.length; i++) {
|
|
162
|
+
const m = lines[i].trim().match(IMPORT_RE);
|
|
163
|
+
if (m) imports.push({ imp: m[1], line: i + 1 });
|
|
164
|
+
}
|
|
165
|
+
return imports;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function isInRepoImport(imp) {
|
|
169
|
+
const pkg = imp.endsWith(".*") ? imp.slice(0, -2) : imp.includes(".") ? imp.replace(/\.[^.]*$/, "") : imp;
|
|
170
|
+
return IN_REPO_PREFIXES.some((prefix) => pkg.startsWith(prefix));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function checkFilesExist(root, files, classIndex) {
|
|
174
|
+
const missing = [];
|
|
175
|
+
for (const f of files) {
|
|
176
|
+
if (!fs.existsSync(f) || !fs.statSync(f).isFile()) continue;
|
|
177
|
+
const imports = extractImports(f, root);
|
|
178
|
+
for (const { imp, line } of imports) {
|
|
179
|
+
if (!isInRepoImport(imp)) continue;
|
|
180
|
+
if (imp.endsWith(".*")) {
|
|
181
|
+
const pkg = imp.slice(0, -2);
|
|
182
|
+
const has = Object.keys(classIndex).some((k) => k === pkg || k.startsWith(pkg + "."));
|
|
183
|
+
if (!has) missing.push({ imp, file: path.relative(root, f).replace(/\\/g, "/"), line, reason: "no_class_in_package" });
|
|
184
|
+
} else {
|
|
185
|
+
if (classIndex[imp]) continue;
|
|
186
|
+
const outer = imp.replace(/\.[^.]*$/, "");
|
|
187
|
+
if (classIndex[outer]) continue;
|
|
188
|
+
missing.push({ imp, file: path.relative(root, f).replace(/\\/g, "/"), line, reason: "class_not_found" });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return missing;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function collectKtJavaUnder(root, modulePath) {
|
|
196
|
+
const base = path.join(root, modulePath);
|
|
197
|
+
if (!fs.existsSync(base)) return [];
|
|
198
|
+
const out = [];
|
|
199
|
+
const subdirs = ["src/main/java", "src/main/kotlin", "src/debug/java", "src/release/java"];
|
|
200
|
+
for (const sub of subdirs) {
|
|
201
|
+
const dPath = path.join(base, sub);
|
|
202
|
+
if (!fs.existsSync(dPath)) continue;
|
|
203
|
+
for (const ext of [".kt", ".java"]) {
|
|
204
|
+
const files = walkRel(dPath, ext, root);
|
|
205
|
+
for (const { rel } of files) {
|
|
206
|
+
if (pathMatchesGlobs(rel, PRIVACY_GLOBS)) continue;
|
|
207
|
+
out.push(path.join(root, rel));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parseArgs() {
|
|
215
|
+
const args = process.argv.slice(2);
|
|
216
|
+
let modulePath = null;
|
|
217
|
+
let filesFromStdin = false;
|
|
218
|
+
let root = REPO_ROOT_DEFAULT;
|
|
219
|
+
let beforeListPath = null;
|
|
220
|
+
let afterListPath = null;
|
|
221
|
+
for (let i = 0; i < args.length; i++) {
|
|
222
|
+
if (args[i] === "--module" && args[i + 1]) {
|
|
223
|
+
modulePath = args[++i];
|
|
224
|
+
} else if (args[i] === "--files" && args[i + 1] === "-") {
|
|
225
|
+
filesFromStdin = true;
|
|
226
|
+
i++;
|
|
227
|
+
} else if (args[i] === "--root" && args[i + 1]) {
|
|
228
|
+
root = path.resolve(args[++i]);
|
|
229
|
+
} else if (args[i] === "--before-list" && args[i + 1]) {
|
|
230
|
+
beforeListPath = args[++i];
|
|
231
|
+
} else if (args[i] === "--after-list" && args[i + 1]) {
|
|
232
|
+
afterListPath = args[++i];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return { modulePath, filesFromStdin, root, beforeListPath, afterListPath };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** 从列表文件读取路径,每行一个,相对 path 则基于 root 解析为绝对路径。 */
|
|
239
|
+
function readListFile(listPath, root) {
|
|
240
|
+
const resolved = path.isAbsolute(listPath) ? listPath : path.join(root, listPath);
|
|
241
|
+
let content;
|
|
242
|
+
try {
|
|
243
|
+
content = fs.readFileSync(resolved, "utf-8");
|
|
244
|
+
} catch (e) {
|
|
245
|
+
return { ok: false, error: e.message, paths: [] };
|
|
246
|
+
}
|
|
247
|
+
const paths = content
|
|
248
|
+
.split(/\r?\n/)
|
|
249
|
+
.map((s) => s.trim())
|
|
250
|
+
.filter(Boolean)
|
|
251
|
+
.map((p) => (path.isAbsolute(p) ? p : path.join(root, p)));
|
|
252
|
+
return { ok: true, paths };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** 动态加载 Tree-sitter 与 Kotlin/Java 语法(从脚本所在目录 node_modules);失败返回 null。 */
|
|
256
|
+
function loadTreeSitterSync() {
|
|
257
|
+
const require = createRequire(import.meta.url);
|
|
258
|
+
try {
|
|
259
|
+
const Parser = require("tree-sitter");
|
|
260
|
+
const Kotlin = require("tree-sitter-kotlin");
|
|
261
|
+
const Java = require("tree-sitter-java");
|
|
262
|
+
const parser = new Parser();
|
|
263
|
+
return { Parser, Kotlin, Java, parser };
|
|
264
|
+
} catch (_) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function nodeText(node, src) {
|
|
270
|
+
return src.slice(node.startIndex, node.endIndex);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** 从 Kotlin AST 取 package 名(不含 keyword)。 */
|
|
274
|
+
function getPackageFromKotlin(rootNode, src) {
|
|
275
|
+
for (let i = 0; i < rootNode.childCount; i++) {
|
|
276
|
+
const c = rootNode.child(i);
|
|
277
|
+
if (c.type === "package_header") {
|
|
278
|
+
const raw = nodeText(c, src);
|
|
279
|
+
const m = raw.match(/package\s+(.+?)(?:\s*$|\s*[;\n])/s);
|
|
280
|
+
if (m) return m[1].replace(/\s+/g, "").trim();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return "";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** 从 Java AST 取 package 名。 */
|
|
287
|
+
function getPackageFromJava(rootNode, src) {
|
|
288
|
+
for (let i = 0; i < rootNode.childCount; i++) {
|
|
289
|
+
const c = rootNode.child(i);
|
|
290
|
+
if (c.type === "package_declaration") {
|
|
291
|
+
const raw = nodeText(c, src);
|
|
292
|
+
const m = raw.match(/package\s+(.+?)\s*;/s);
|
|
293
|
+
if (m) return m[1].replace(/\s+/g, "").trim();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return "";
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** 递归找 simple_identifier 或 type_identifier 的文本(首个)。 */
|
|
300
|
+
function firstIdentifierText(node, src) {
|
|
301
|
+
if (!node) return "";
|
|
302
|
+
if (node.type === "simple_identifier" || node.type === "identifier" || node.type === "type_identifier") return nodeText(node, src).trim();
|
|
303
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
304
|
+
const t = firstIdentifierText(node.namedChild(i), src);
|
|
305
|
+
if (t) return t;
|
|
306
|
+
}
|
|
307
|
+
return "";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Kotlin:顶层 class/object/interface 名。 */
|
|
311
|
+
function getTopLevelDeclNamesFromKotlin(rootNode, src) {
|
|
312
|
+
const names = [];
|
|
313
|
+
function walk(n) {
|
|
314
|
+
if (!n) return;
|
|
315
|
+
const type = n.type;
|
|
316
|
+
if (type === "class_declaration" || type === "object_declaration" || type === "interface_declaration") {
|
|
317
|
+
const name = n.namedChild(0) ? nodeText(n.namedChild(0), src).trim() : "";
|
|
318
|
+
if (name) names.push(name);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
for (let i = 0; i < n.childCount; i++) walk(n.child(i));
|
|
322
|
+
}
|
|
323
|
+
walk(rootNode);
|
|
324
|
+
return names;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Java:顶层 class/interface 名。 */
|
|
328
|
+
function getTopLevelDeclNamesFromJava(rootNode, src) {
|
|
329
|
+
const names = [];
|
|
330
|
+
for (let i = 0; i < rootNode.childCount; i++) {
|
|
331
|
+
const c = rootNode.child(i);
|
|
332
|
+
if (c.type === "class_declaration" || c.type === "interface_declaration") {
|
|
333
|
+
const nameNode = c.childForFieldName("name");
|
|
334
|
+
if (nameNode) names.push(nodeText(nameNode, src).trim());
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return names;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** 从迁移后文件列表解析出已迁移 FQCN 集合与包集合(用于 wildcard import)。 */
|
|
341
|
+
function extractFqcnsFromAfterList(root, afterPaths, ts) {
|
|
342
|
+
const movedFqcns = new Set();
|
|
343
|
+
const movedPackages = new Set();
|
|
344
|
+
const parser = ts.parser;
|
|
345
|
+
|
|
346
|
+
for (const filePath of afterPaths) {
|
|
347
|
+
if (!/\.(kt|java)$/i.test(filePath) || !fs.existsSync(filePath)) continue;
|
|
348
|
+
const rootRel = path.relative(root, filePath).replace(/\\/g, "/");
|
|
349
|
+
if (pathMatchesGlobs(rootRel, PRIVACY_GLOBS)) continue;
|
|
350
|
+
|
|
351
|
+
let src;
|
|
352
|
+
try {
|
|
353
|
+
src = fs.readFileSync(filePath, "utf-8");
|
|
354
|
+
} catch {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
359
|
+
if (ext === ".kt") {
|
|
360
|
+
parser.setLanguage(ts.Kotlin);
|
|
361
|
+
const tree = parser.parse(src);
|
|
362
|
+
const rootNode = tree.rootNode;
|
|
363
|
+
const pkg = getPackageFromKotlin(rootNode, src);
|
|
364
|
+
if (pkg) movedPackages.add(pkg);
|
|
365
|
+
const names = getTopLevelDeclNamesFromKotlin(rootNode, src);
|
|
366
|
+
for (const name of names) movedFqcns.add(pkg ? `${pkg}.${name}` : name);
|
|
367
|
+
} else {
|
|
368
|
+
parser.setLanguage(ts.Java);
|
|
369
|
+
const tree = parser.parse(src);
|
|
370
|
+
const rootNode = tree.rootNode;
|
|
371
|
+
const pkg = getPackageFromJava(rootNode, src);
|
|
372
|
+
if (pkg) movedPackages.add(pkg);
|
|
373
|
+
const names = getTopLevelDeclNamesFromJava(rootNode, src);
|
|
374
|
+
for (const name of names) movedFqcns.add(pkg ? `${pkg}.${name}` : name);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return { movedFqcns, movedPackages };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** 无 Tree-sitter 时从文件路径推导 FQCN(java/kotlin 目录下的包路径 + 文件名)。 */
|
|
382
|
+
function extractFqcnsFromPathsFallback(root, afterPaths) {
|
|
383
|
+
const movedFqcns = new Set();
|
|
384
|
+
const movedPackages = new Set();
|
|
385
|
+
const javaKotlin = /[/\\]src[/\\](?:main|debug|release)[/\\](?:java|kotlin)[/\\]/i;
|
|
386
|
+
for (const filePath of afterPaths) {
|
|
387
|
+
const rootRel = path.relative(root, filePath).replace(/\\/g, "/");
|
|
388
|
+
const match = rootRel.match(javaKotlin);
|
|
389
|
+
if (!match) continue;
|
|
390
|
+
const after = rootRel.indexOf(match[0]) + match[0].length;
|
|
391
|
+
const rest = rootRel.slice(after).replace(/\.(kt|java)$/i, "").replace(/\//g, ".");
|
|
392
|
+
if (rest) {
|
|
393
|
+
movedFqcns.add(rest);
|
|
394
|
+
const pkg = rest.replace(/\.[^.]+$/, "");
|
|
395
|
+
if (pkg !== rest) movedPackages.add(pkg);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return { movedFqcns, movedPackages };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** 仅主模块(iHeima)的源码目录。 */
|
|
402
|
+
function mainModuleSourceDirs(root) {
|
|
403
|
+
const iHeima = path.join(root, "iHeima");
|
|
404
|
+
if (!fs.existsSync(iHeima)) return [];
|
|
405
|
+
const dirs = [];
|
|
406
|
+
for (const sub of ["src/main/java", "src/main/kotlin", "src/debug/java", "src/release/java"]) {
|
|
407
|
+
const p = path.join(iHeima, sub);
|
|
408
|
+
if (fs.existsSync(p)) dirs.push(p);
|
|
409
|
+
}
|
|
410
|
+
return dirs;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** 收集主模块内所有 .kt/.java 文件路径。 */
|
|
414
|
+
function collectMainModuleFiles(root) {
|
|
415
|
+
const dirs = mainModuleSourceDirs(root);
|
|
416
|
+
const out = [];
|
|
417
|
+
for (const d of dirs) {
|
|
418
|
+
for (const ext of [".kt", ".java"]) {
|
|
419
|
+
for (const { full } of walkRel(d, ext)) {
|
|
420
|
+
const rootRel = path.relative(root, full).replace(/\\/g, "/");
|
|
421
|
+
if (pathMatchesGlobs(rootRel, PRIVACY_GLOBS)) continue;
|
|
422
|
+
out.push(full);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** 从 Kotlin/Java AST 中收集 import 的 FQCN 或包(wildcard),带行号列号。 */
|
|
430
|
+
function collectImportsFromAst(rootNode, src, ext) {
|
|
431
|
+
const imports = [];
|
|
432
|
+
function walk(n) {
|
|
433
|
+
if (!n) return;
|
|
434
|
+
if (n.type === "import_list") {
|
|
435
|
+
for (let i = 0; i < n.childCount; i++) walk(n.child(i));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (n.type === "import_header") {
|
|
439
|
+
const full = nodeText(n, src).replace(/\s+/g, " ").trim();
|
|
440
|
+
const m = full.match(/import\s+(?:static\s+)?([\w.]+)(?:\s+as\s+\w+)?/);
|
|
441
|
+
if (m) {
|
|
442
|
+
const line = n.startPosition.row + 1;
|
|
443
|
+
const column = n.startPosition.column + 1;
|
|
444
|
+
imports.push({ fqcnOrPkg: m[1], isWildcard: m[1].endsWith(".*"), line, column });
|
|
445
|
+
}
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (ext === ".java" && n.type === "import_declaration") {
|
|
449
|
+
const full = nodeText(n, src).replace(/\s+/g, " ").trim();
|
|
450
|
+
const m = full.match(/import\s+(?:static\s+)?([\w.]+)\s*;/);
|
|
451
|
+
if (m) {
|
|
452
|
+
const line = n.startPosition.row + 1;
|
|
453
|
+
const column = n.startPosition.column + 1;
|
|
454
|
+
imports.push({ fqcnOrPkg: m[1], isWildcard: m[1].endsWith(".*"), line, column });
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
for (let i = 0; i < n.childCount; i++) walk(n.child(i));
|
|
459
|
+
}
|
|
460
|
+
walk(rootNode);
|
|
461
|
+
return imports;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** 单文件中找出命中 movedFqcns/movedPackages 的 import 与引用位置。 */
|
|
465
|
+
function findImportsAndRefsInFile(filePath, root, movedFqcns, movedPackages, ts) {
|
|
466
|
+
const refs = [];
|
|
467
|
+
const rootRel = path.relative(root, filePath).replace(/\\/g, "/");
|
|
468
|
+
let src;
|
|
469
|
+
try {
|
|
470
|
+
src = fs.readFileSync(filePath, "utf-8");
|
|
471
|
+
} catch {
|
|
472
|
+
return refs;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
476
|
+
const parser = ts.parser;
|
|
477
|
+
|
|
478
|
+
if (ext === ".kt") {
|
|
479
|
+
parser.setLanguage(ts.Kotlin);
|
|
480
|
+
} else {
|
|
481
|
+
parser.setLanguage(ts.Java);
|
|
482
|
+
}
|
|
483
|
+
const tree = parser.parse(src);
|
|
484
|
+
const rootNode = tree.rootNode;
|
|
485
|
+
|
|
486
|
+
const imports = collectImportsFromAst(rootNode, src, ext);
|
|
487
|
+
for (const { fqcnOrPkg, isWildcard, line, column } of imports) {
|
|
488
|
+
let hit = false;
|
|
489
|
+
if (isWildcard) {
|
|
490
|
+
const pkg = fqcnOrPkg.slice(0, -2);
|
|
491
|
+
if (movedPackages.has(pkg)) hit = true;
|
|
492
|
+
if (movedFqcns.has(pkg)) hit = true;
|
|
493
|
+
for (const f of movedFqcns) {
|
|
494
|
+
if (f === pkg || f.startsWith(pkg + ".")) {
|
|
495
|
+
hit = true;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
if (movedFqcns.has(fqcnOrPkg)) hit = true;
|
|
501
|
+
if (movedPackages.has(fqcnOrPkg)) hit = true;
|
|
502
|
+
}
|
|
503
|
+
if (hit) refs.push({ file: rootRel, line, column, type: "import", fqcn: fqcnOrPkg });
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const lines = src.split(/\r?\n/);
|
|
507
|
+
const importMap = new Map();
|
|
508
|
+
for (const { fqcnOrPkg, isWildcard } of imports) {
|
|
509
|
+
if (isWildcard) continue;
|
|
510
|
+
const short = fqcnOrPkg.replace(/^.*\./, "");
|
|
511
|
+
importMap.set(short, fqcnOrPkg);
|
|
512
|
+
}
|
|
513
|
+
for (let i = 0; i < lines.length; i++) {
|
|
514
|
+
const line = lines[i];
|
|
515
|
+
for (const [short, fqcn] of importMap) {
|
|
516
|
+
if (!movedFqcns.has(fqcn)) continue;
|
|
517
|
+
const re = new RegExp("\\b" + short.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b");
|
|
518
|
+
if (re.test(line)) {
|
|
519
|
+
refs.push({
|
|
520
|
+
file: rootRel,
|
|
521
|
+
line: i + 1,
|
|
522
|
+
column: 1,
|
|
523
|
+
type: "reference",
|
|
524
|
+
fqcn,
|
|
525
|
+
snippet: line.trim().slice(0, 80),
|
|
526
|
+
});
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return refs;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** 无 Tree-sitter 时仅用正则收集 import 行中命中 moved 的。 */
|
|
536
|
+
function findImportsAndRefsInFileFallback(filePath, root, movedFqcns, movedPackages) {
|
|
537
|
+
const refs = [];
|
|
538
|
+
const rootRel = path.relative(root, filePath).replace(/\\/g, "/");
|
|
539
|
+
let src;
|
|
540
|
+
try {
|
|
541
|
+
src = fs.readFileSync(filePath, "utf-8");
|
|
542
|
+
} catch {
|
|
543
|
+
return refs;
|
|
544
|
+
}
|
|
545
|
+
const importList = extractImports(filePath, root);
|
|
546
|
+
for (const { imp, line } of importList) {
|
|
547
|
+
let hit = false;
|
|
548
|
+
if (imp.endsWith(".*")) {
|
|
549
|
+
const pkg = imp.slice(0, -2);
|
|
550
|
+
if (movedPackages.has(pkg)) hit = true;
|
|
551
|
+
for (const f of movedFqcns) {
|
|
552
|
+
if (f === pkg || f.startsWith(pkg + ".")) {
|
|
553
|
+
hit = true;
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
if (movedFqcns.has(imp) || movedPackages.has(imp)) hit = true;
|
|
559
|
+
}
|
|
560
|
+
if (hit) refs.push({ file: rootRel, line, column: 1, type: "import", fqcn: imp });
|
|
561
|
+
}
|
|
562
|
+
return refs;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function findMainModuleRefs(root, movedFqcns, movedPackages, ts) {
|
|
566
|
+
const mainFiles = collectMainModuleFiles(root);
|
|
567
|
+
const allRefs = [];
|
|
568
|
+
const useFallback = !ts;
|
|
569
|
+
for (const filePath of mainFiles) {
|
|
570
|
+
const refs = useFallback
|
|
571
|
+
? findImportsAndRefsInFileFallback(filePath, root, movedFqcns, movedPackages)
|
|
572
|
+
: findImportsAndRefsInFile(filePath, root, movedFqcns, movedPackages, ts);
|
|
573
|
+
allRefs.push(...refs);
|
|
574
|
+
}
|
|
575
|
+
return allRefs;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function formatMainRefsResult(refs, usedTreeSitter) {
|
|
579
|
+
if (refs.length === 0) {
|
|
580
|
+
return "主模块中未发现对已迁移类的 import 或引用,无需改为 API。" + (usedTreeSitter ? "" : "\n(未使用 Tree-sitter,仅检查 import。)");
|
|
581
|
+
}
|
|
582
|
+
const header = "| 文件 | 行 | 类型 | FQCN/内容 |";
|
|
583
|
+
const sep = "| --- | --- | --- | --- |";
|
|
584
|
+
const rows = refs.map((r) => `| ${r.file} | ${r.line} | ${r.type} | ${(r.fqcn || r.snippet || "").replace(/\|/g, "\\|")} |`);
|
|
585
|
+
const note = usedTreeSitter ? "" : "\n\n(未使用 Tree-sitter,仅 import;安装 tree-sitter、tree-sitter-kotlin、tree-sitter-java 可得到引用位置。)";
|
|
586
|
+
return "主模块中需改为 API 调用的代码地址:\n\n" + [header, sep, ...rows].join("\n") + note;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function readStdinLines() {
|
|
590
|
+
return new Promise((resolve, reject) => {
|
|
591
|
+
const chunks = [];
|
|
592
|
+
process.stdin.setEncoding("utf8");
|
|
593
|
+
process.stdin.on("data", (c) => chunks.push(c));
|
|
594
|
+
process.stdin.on("end", () => {
|
|
595
|
+
const line = Buffer.concat(chunks).toString("utf8");
|
|
596
|
+
resolve(line.split(/\r?\n/).map((s) => s.trim()).filter(Boolean));
|
|
597
|
+
});
|
|
598
|
+
process.stdin.on("error", reject);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function outputResult(errCode, resultText) {
|
|
603
|
+
const out = JSON.stringify({ err_code: errCode, message: { result: resultText } });
|
|
604
|
+
if (process.stdout.write(out + "\n") === false) {
|
|
605
|
+
process.stdout.once("drain", () => process.exit(errCode));
|
|
606
|
+
} else {
|
|
607
|
+
process.exit(errCode);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async function main() {
|
|
612
|
+
const { modulePath, filesFromStdin, root, beforeListPath, afterListPath } = parseArgs();
|
|
613
|
+
|
|
614
|
+
if (afterListPath) {
|
|
615
|
+
const afterRes = readListFile(afterListPath, root);
|
|
616
|
+
if (!afterRes.ok) {
|
|
617
|
+
outputResult(1, "error: 无法读取迁移后列表 " + afterListPath + ": " + afterRes.error);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
const afterPaths = afterRes.paths.filter((p) => /\.(kt|java)$/i.test(p));
|
|
621
|
+
if (afterPaths.length === 0) {
|
|
622
|
+
outputResult(0, "主模块中未发现对已迁移类的 import 或引用,无需改为 API。(迁移后列表无 .kt/.java 文件)");
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
let ts = loadTreeSitterSync();
|
|
627
|
+
let movedFqcns, movedPackages;
|
|
628
|
+
if (ts) {
|
|
629
|
+
try {
|
|
630
|
+
const out = extractFqcnsFromAfterList(root, afterPaths, ts);
|
|
631
|
+
movedFqcns = out.movedFqcns;
|
|
632
|
+
movedPackages = out.movedPackages;
|
|
633
|
+
} catch (_) {
|
|
634
|
+
const out = extractFqcnsFromPathsFallback(root, afterPaths);
|
|
635
|
+
movedFqcns = out.movedFqcns;
|
|
636
|
+
movedPackages = out.movedPackages;
|
|
637
|
+
ts = null;
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
const out = extractFqcnsFromPathsFallback(root, afterPaths);
|
|
641
|
+
movedFqcns = out.movedFqcns;
|
|
642
|
+
movedPackages = out.movedPackages;
|
|
643
|
+
}
|
|
644
|
+
if (movedFqcns.size === 0 && movedPackages.size === 0) {
|
|
645
|
+
outputResult(0, "主模块中未发现对已迁移类的 import 或引用,无需改为 API。(未能从迁移后文件解析出类/包)");
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
let refs;
|
|
650
|
+
try {
|
|
651
|
+
refs = findMainModuleRefs(root, movedFqcns, movedPackages, ts);
|
|
652
|
+
} catch (_) {
|
|
653
|
+
refs = findMainModuleRefs(root, movedFqcns, movedPackages, null);
|
|
654
|
+
ts = null;
|
|
655
|
+
}
|
|
656
|
+
const result = formatMainRefsResult(refs, !!ts);
|
|
657
|
+
// 仅脚本自身失败返回 1;检查完成(无论是否发现需改引用)均返回 0,下游根据 result 报告内容分支
|
|
658
|
+
outputResult(0, result);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (!modulePath && !filesFromStdin) {
|
|
663
|
+
outputResult(1, "error: 请指定 --after-list <path>(主模块引用)或 --module <path> / --files -(import 检查)");
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (modulePath && filesFromStdin) {
|
|
667
|
+
outputResult(1, "error: 只能指定 --module 或 --files 其一");
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
let files = [];
|
|
672
|
+
if (filesFromStdin) {
|
|
673
|
+
const lines = await readStdinLines();
|
|
674
|
+
files = lines.map((p) => (path.isAbsolute(p) ? p : path.join(root, p)));
|
|
675
|
+
} else {
|
|
676
|
+
files = collectKtJavaUnder(root, modulePath);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
files = files.filter((f) => /\.(kt|java)$/i.test(f));
|
|
680
|
+
if (files.length === 0) {
|
|
681
|
+
outputResult(0, "ok: no .kt/.java files to check.");
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const classIndex = buildClassIndex(root);
|
|
686
|
+
const missing = checkFilesExist(root, files, classIndex);
|
|
687
|
+
|
|
688
|
+
if (missing.length === 0) {
|
|
689
|
+
outputResult(0, "ok: all checked imports exist in repo.");
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const lines = missing.map((m) => ` ${m.file}:${m.line} ${m.imp} [${m.reason}]`);
|
|
694
|
+
const result = "potential broken imports (run compile to confirm):\n" + lines.join("\n");
|
|
695
|
+
outputResult(1, result);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
main().catch((err) => {
|
|
699
|
+
outputResult(1, "error: " + (err && err.message ? err.message : String(err)));
|
|
700
|
+
});
|