dsh-code-index 0.9.1 → 0.10.1
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/CHANGELOG.md +26 -0
- package/README.md +49 -8
- package/README.zh.md +41 -8
- package/dist/index.d.ts +25 -6
- package/dist/index.js +205 -28
- package/dist/index.js.map +1 -1
- package/grammars/ARKTS-LICENSE +43 -0
- package/grammars/README.md +25 -0
- package/grammars/tree-sitter-arkts.wasm +0 -0
- package/package.json +3 -2
- package/scripts/release-smoke.mjs +81 -8
package/dist/index.js
CHANGED
|
@@ -6,10 +6,12 @@ import path4 from "path";
|
|
|
6
6
|
import { readFile } from "fs/promises";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import path from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
9
10
|
import { Language, Parser, Query } from "web-tree-sitter";
|
|
10
11
|
var require2 = createRequire(import.meta.url);
|
|
11
12
|
var WASM_DIR = path.dirname(require2.resolve("tree-sitter-wasms/out/tree-sitter-typescript.wasm"));
|
|
12
13
|
var GRAMMAR_NAMES = {
|
|
14
|
+
arkts: "tree-sitter-arkts",
|
|
13
15
|
typescript: "tree-sitter-typescript",
|
|
14
16
|
javascript: "tree-sitter-javascript",
|
|
15
17
|
python: "tree-sitter-python",
|
|
@@ -20,6 +22,7 @@ var GRAMMAR_NAMES = {
|
|
|
20
22
|
c: "tree-sitter-c"
|
|
21
23
|
};
|
|
22
24
|
var EXT_TO_LANG = {
|
|
25
|
+
".ets": "arkts",
|
|
23
26
|
".ts": "typescript",
|
|
24
27
|
".tsx": "typescript",
|
|
25
28
|
".mts": "typescript",
|
|
@@ -51,6 +54,19 @@ function languageForFile(filePath) {
|
|
|
51
54
|
return EXT_TO_LANG[ext] ?? null;
|
|
52
55
|
}
|
|
53
56
|
var QUERIES = {
|
|
57
|
+
arkts: `
|
|
58
|
+
(struct_declaration) @class
|
|
59
|
+
(function_declaration) @function
|
|
60
|
+
(generator_function_declaration) @function
|
|
61
|
+
(method_definition) @method
|
|
62
|
+
(class_declaration) @class
|
|
63
|
+
(abstract_class_declaration) @class
|
|
64
|
+
(interface_declaration) @interface
|
|
65
|
+
(type_alias_declaration) @type
|
|
66
|
+
(enum_declaration) @enum
|
|
67
|
+
(variable_declarator) @variable
|
|
68
|
+
(public_field_definition) @field
|
|
69
|
+
`,
|
|
54
70
|
typescript: `
|
|
55
71
|
(function_declaration) @function
|
|
56
72
|
(generator_function_declaration) @function
|
|
@@ -132,6 +148,7 @@ var CAPTURE_KINDS = {
|
|
|
132
148
|
module: { kind: "module" }
|
|
133
149
|
};
|
|
134
150
|
var IMPORT_QUERIES = {
|
|
151
|
+
arkts: `(import_statement) @import (export_statement) @reexport`,
|
|
135
152
|
typescript: `
|
|
136
153
|
(import_statement) @import
|
|
137
154
|
(export_statement) @reexport
|
|
@@ -163,6 +180,12 @@ var IMPORT_QUERIES = {
|
|
|
163
180
|
`
|
|
164
181
|
};
|
|
165
182
|
var CALL_QUERIES = {
|
|
183
|
+
arkts: `
|
|
184
|
+
(call_expression function: (identifier) @call)
|
|
185
|
+
(call_expression function: (member_expression property: (property_identifier) @call))
|
|
186
|
+
(arkui_component_expression function: (identifier) @call)
|
|
187
|
+
(new_expression constructor: (identifier) @call)
|
|
188
|
+
`,
|
|
166
189
|
typescript: `
|
|
167
190
|
(call_expression function: (identifier) @call)
|
|
168
191
|
(call_expression function: (member_expression property: (property_identifier) @call))
|
|
@@ -201,6 +224,7 @@ var CALL_QUERIES = {
|
|
|
201
224
|
`
|
|
202
225
|
};
|
|
203
226
|
var CALLABLE_NODES = {
|
|
227
|
+
arkts: /* @__PURE__ */ new Set(["function_declaration", "generator_function_declaration", "method_definition", "function_expression", "arrow_function"]),
|
|
204
228
|
typescript: /* @__PURE__ */ new Set([
|
|
205
229
|
"function_declaration",
|
|
206
230
|
"generator_function_declaration",
|
|
@@ -239,7 +263,7 @@ function getLanguage(id) {
|
|
|
239
263
|
let entry = languageCache.get(id);
|
|
240
264
|
if (!entry) {
|
|
241
265
|
entry = getParser().then(async () => {
|
|
242
|
-
const grammarPath = path.join(WASM_DIR, `${GRAMMAR_NAMES[id]}.wasm`);
|
|
266
|
+
const grammarPath = id === "arkts" ? fileURLToPath(new URL("../grammars/tree-sitter-arkts.wasm", import.meta.url)) : path.join(WASM_DIR, `${GRAMMAR_NAMES[id]}.wasm`);
|
|
243
267
|
const bytes = await readFile(grammarPath);
|
|
244
268
|
const lang = await Language.load(bytes);
|
|
245
269
|
new Query(lang, QUERIES[id]).delete();
|
|
@@ -264,6 +288,7 @@ async function extractAll(code, id, file = "") {
|
|
|
264
288
|
const tree = parser.parse(code);
|
|
265
289
|
if (!tree) throw new Error(`tree-sitter parse returned null for a ${id} source`);
|
|
266
290
|
try {
|
|
291
|
+
const diagnostic = parseDiagnostic(tree.rootNode);
|
|
267
292
|
const drafts = [];
|
|
268
293
|
const symbolQuery = new Query(lang, QUERIES[id]);
|
|
269
294
|
try {
|
|
@@ -272,6 +297,7 @@ async function extractAll(code, id, file = "") {
|
|
|
272
297
|
const def = CAPTURE_KINDS[cap.name];
|
|
273
298
|
if (!def) continue;
|
|
274
299
|
const node = cap.node;
|
|
300
|
+
if (id === "arkts" && !reliableArktsNode(node)) continue;
|
|
275
301
|
if (def.kind === "variable" && !isModuleLevelVariable(id, node)) continue;
|
|
276
302
|
const name2 = nameOf(id, node);
|
|
277
303
|
if (!name2) continue;
|
|
@@ -279,10 +305,11 @@ async function extractAll(code, id, file = "") {
|
|
|
279
305
|
symbol: {
|
|
280
306
|
name: name2,
|
|
281
307
|
kind: kindFor(id, node, def.kind),
|
|
282
|
-
line: node.startPosition.row + 1,
|
|
308
|
+
line: id === "arkts" ? arktsDeclarationStart(node) : node.startPosition.row + 1,
|
|
283
309
|
endLine: node.endPosition.row + 1,
|
|
284
310
|
exported: isExported(id, node),
|
|
285
|
-
signature: signatureFor(id, node)
|
|
311
|
+
signature: signatureFor(id, node),
|
|
312
|
+
...id === "arkts" ? { decorators: arktsDecorators(node) } : {}
|
|
286
313
|
},
|
|
287
314
|
node
|
|
288
315
|
});
|
|
@@ -298,6 +325,7 @@ async function extractAll(code, id, file = "") {
|
|
|
298
325
|
const importQuery = new Query(lang, IMPORT_QUERIES[id]);
|
|
299
326
|
try {
|
|
300
327
|
for (const cap of importQuery.captures(tree.rootNode)) {
|
|
328
|
+
if (id === "arkts" && !reliableArktsNode(cap.node)) continue;
|
|
301
329
|
for (const info of importInfoFor(id, cap.node)) importDetails.push(info);
|
|
302
330
|
}
|
|
303
331
|
} finally {
|
|
@@ -309,6 +337,7 @@ async function extractAll(code, id, file = "") {
|
|
|
309
337
|
try {
|
|
310
338
|
const seen = /* @__PURE__ */ new Set();
|
|
311
339
|
for (const cap of callQuery.captures(tree.rootNode)) {
|
|
340
|
+
if (id === "arkts" && (!reliableArktsNode(cap.node) || isArkuiModifier(cap.node) || isArktsMetadataCall(cap.node))) continue;
|
|
312
341
|
const name2 = cap.node.text.trim();
|
|
313
342
|
if (!name2) continue;
|
|
314
343
|
const line = cap.node.startPosition.row + 1;
|
|
@@ -318,7 +347,14 @@ async function extractAll(code, id, file = "") {
|
|
|
318
347
|
const key = `${from}|${name2}|${line}`;
|
|
319
348
|
if (seen.has(key)) continue;
|
|
320
349
|
seen.add(key);
|
|
321
|
-
calls.push({
|
|
350
|
+
calls.push({
|
|
351
|
+
name: name2,
|
|
352
|
+
line,
|
|
353
|
+
from,
|
|
354
|
+
fromId,
|
|
355
|
+
qualifier: qualifierOf(cap.node),
|
|
356
|
+
...id === "arkts" && isArkuiCall(cap.node) ? { context: "arkui" } : {}
|
|
357
|
+
});
|
|
322
358
|
}
|
|
323
359
|
} finally {
|
|
324
360
|
callQuery.delete();
|
|
@@ -327,11 +363,85 @@ async function extractAll(code, id, file = "") {
|
|
|
327
363
|
symbols.sort(
|
|
328
364
|
(a, b) => a.line - b.line || a.endLine - b.endLine || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name)
|
|
329
365
|
);
|
|
330
|
-
|
|
366
|
+
if (diagnostic.status === "partial" && !symbols.length && !importDetails.length) diagnostic.status = "failed";
|
|
367
|
+
return { symbols, imports, importDetails, calls, diagnostic };
|
|
331
368
|
} finally {
|
|
332
369
|
tree.delete();
|
|
333
370
|
}
|
|
334
371
|
}
|
|
372
|
+
function parseDiagnostic(root) {
|
|
373
|
+
const diagnostic = { status: root.hasError ? "partial" : "parsed", errorCount: 0, missingCount: 0, issues: [] };
|
|
374
|
+
if (!root.hasError) return diagnostic;
|
|
375
|
+
const stack = [root];
|
|
376
|
+
while (stack.length) {
|
|
377
|
+
const node = stack.pop();
|
|
378
|
+
if (node.type === "ERROR" || node.isMissing) {
|
|
379
|
+
if (node.isMissing) diagnostic.missingCount++;
|
|
380
|
+
else diagnostic.errorCount++;
|
|
381
|
+
if (diagnostic.issues.length < 3) diagnostic.issues.push({ kind: node.isMissing ? "MISSING" : "ERROR", line: node.startPosition.row + 1 });
|
|
382
|
+
}
|
|
383
|
+
for (let i = node.childCount - 1; i >= 0; i--) {
|
|
384
|
+
const child = node.child(i);
|
|
385
|
+
if (child) stack.push(child);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return diagnostic;
|
|
389
|
+
}
|
|
390
|
+
function reliableArktsNode(node) {
|
|
391
|
+
if (node.hasError) return false;
|
|
392
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
393
|
+
if (parent.type === "ERROR") return false;
|
|
394
|
+
if (["struct_declaration", "class_declaration", "function_declaration", "method_definition"].includes(parent.type) && parent.hasError) return false;
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
function arktsDecoratorNodes(node) {
|
|
399
|
+
const nodes = node.namedChildren.filter((child) => child?.type === "decorator");
|
|
400
|
+
for (let prev = node.previousNamedSibling; prev?.type === "decorator"; prev = prev.previousNamedSibling) nodes.unshift(prev);
|
|
401
|
+
if (node.parent?.type === "export_statement") {
|
|
402
|
+
nodes.unshift(...node.parent.namedChildren.filter((child) => child?.type === "decorator"));
|
|
403
|
+
}
|
|
404
|
+
return [...new Map(nodes.map((decorator) => [decorator.id, decorator])).values()];
|
|
405
|
+
}
|
|
406
|
+
function arktsDecorators(node) {
|
|
407
|
+
return arktsDecoratorNodes(node).map((decorator) => collapseSpace(decorator.text));
|
|
408
|
+
}
|
|
409
|
+
function arktsDeclarationStart(node) {
|
|
410
|
+
return Math.min(node.startPosition.row, ...arktsDecoratorNodes(node).map((decorator) => decorator.startPosition.row)) + 1;
|
|
411
|
+
}
|
|
412
|
+
function isArkuiCall(node) {
|
|
413
|
+
if (node.parent?.type === "arkui_component_expression") return true;
|
|
414
|
+
if (node.parent?.type !== "call_expression") return false;
|
|
415
|
+
if (!/^[A-Z]/.test(node.text)) return false;
|
|
416
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
417
|
+
if (parent.type === "arkui_children") return true;
|
|
418
|
+
if (parent.type === "arrow_function" || parent.type === "function_expression") return false;
|
|
419
|
+
if (parent.type === "method_definition" || parent.type === "function_declaration") {
|
|
420
|
+
return parent.type === "method_definition" && nameOf("arkts", parent) === "build" && parent.parent?.type === "struct_body" || arktsDecorators(parent).some((text) => /^@Builder\b/.test(text));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
function isArkuiModifier(node) {
|
|
426
|
+
if (node.parent?.type !== "member_expression") return false;
|
|
427
|
+
let receiver = node.parent.childForFieldName("object");
|
|
428
|
+
while (receiver?.type === "call_expression") {
|
|
429
|
+
const callee = receiver.childForFieldName("function");
|
|
430
|
+
if (callee?.type === "identifier") return isArkuiCall(callee);
|
|
431
|
+
if (callee?.type !== "member_expression") return false;
|
|
432
|
+
receiver = callee.childForFieldName("object");
|
|
433
|
+
}
|
|
434
|
+
return receiver?.type === "arkui_component_expression" || receiver?.type === "leading_dot_expression";
|
|
435
|
+
}
|
|
436
|
+
function isArktsMetadataCall(node) {
|
|
437
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
438
|
+
if (parent.type === "decorator") return true;
|
|
439
|
+
if (parent.type === "arguments") return false;
|
|
440
|
+
if (parent.type === "leading_dot_expression") return true;
|
|
441
|
+
if (CALLABLE_NODES.arkts.has(parent.type)) return false;
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
335
445
|
function dedupeDrafts(drafts) {
|
|
336
446
|
const sorted = [...drafts].sort(
|
|
337
447
|
(a, b) => a.symbol.line - b.symbol.line || a.node.startIndex - b.node.startIndex
|
|
@@ -882,6 +992,10 @@ function signatureFor(id, node) {
|
|
|
882
992
|
}
|
|
883
993
|
function rawSignature(id, node) {
|
|
884
994
|
const name2 = nameOf(id, node);
|
|
995
|
+
if (id === "arkts" && node.type === "struct_declaration") return `struct ${name2}`;
|
|
996
|
+
if (id === "arkts" && node.type === "public_field_definition") {
|
|
997
|
+
return `${name2}${collapseSpace(node.childForFieldName("type")?.text ?? "")}`;
|
|
998
|
+
}
|
|
885
999
|
const params = node.childForFieldName?.("parameters") ?? node.namedChildren.find(
|
|
886
1000
|
(c) => c?.type === "formal_parameters" || c?.type === "method_parameters" || c?.type === "parameters" || c?.type === "parameter_list"
|
|
887
1001
|
);
|
|
@@ -939,7 +1053,7 @@ function isExported(id, node) {
|
|
|
939
1053
|
}
|
|
940
1054
|
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
941
1055
|
if (parent.type === "export_statement") return true;
|
|
942
|
-
if (parent.type === "statement_block" || parent.type === "class_body") return false;
|
|
1056
|
+
if (parent.type === "statement_block" || parent.type === "class_body" || parent.type === "struct_body") return false;
|
|
943
1057
|
if (parent.type === "program") return false;
|
|
944
1058
|
}
|
|
945
1059
|
return false;
|
|
@@ -1030,6 +1144,7 @@ var DEFAULT_EXCLUDED_DIRS = [
|
|
|
1030
1144
|
".dsh-code-index"
|
|
1031
1145
|
];
|
|
1032
1146
|
var SUPPORTED_EXTS = /* @__PURE__ */ new Set([
|
|
1147
|
+
".ets",
|
|
1033
1148
|
".ts",
|
|
1034
1149
|
".tsx",
|
|
1035
1150
|
".mts",
|
|
@@ -1134,7 +1249,7 @@ import path3 from "path";
|
|
|
1134
1249
|
import { createHash, randomUUID } from "crypto";
|
|
1135
1250
|
|
|
1136
1251
|
// src/types.ts
|
|
1137
|
-
var REPO_INDEX_SCHEMA_VERSION =
|
|
1252
|
+
var REPO_INDEX_SCHEMA_VERSION = 3;
|
|
1138
1253
|
function symbolCount(index) {
|
|
1139
1254
|
return index.files.reduce((n, f) => n + f.symbols.length, 0);
|
|
1140
1255
|
}
|
|
@@ -1198,7 +1313,7 @@ async function buildIndex(root, options = {}, previous = null, forcePaths = /* @
|
|
|
1198
1313
|
batch.map(async (f) => {
|
|
1199
1314
|
const lang = languageForFile(f.abs);
|
|
1200
1315
|
if (!lang) return null;
|
|
1201
|
-
if (prevMtime.get(f.rel) === f.mtimeMs && !forcePaths.has(f.rel) && !forcePaths.has("*")) {
|
|
1316
|
+
if (prevMtime.get(f.rel) === f.mtimeMs && prevByPath.get(f.rel)?.diagnostic?.status !== "failed" && !forcePaths.has(f.rel) && !forcePaths.has("*")) {
|
|
1202
1317
|
const cached = prevByPath.get(f.rel);
|
|
1203
1318
|
return {
|
|
1204
1319
|
...cached,
|
|
@@ -1210,9 +1325,33 @@ async function buildIndex(root, options = {}, previous = null, forcePaths = /* @
|
|
|
1210
1325
|
try {
|
|
1211
1326
|
code = await readFile4(f.abs, "utf8");
|
|
1212
1327
|
} catch {
|
|
1213
|
-
return
|
|
1328
|
+
return {
|
|
1329
|
+
path: f.rel,
|
|
1330
|
+
lang,
|
|
1331
|
+
mtimeMs: f.mtimeMs,
|
|
1332
|
+
symbols: [],
|
|
1333
|
+
imports: [],
|
|
1334
|
+
importDetails: [],
|
|
1335
|
+
calls: [],
|
|
1336
|
+
diagnostic: { status: "failed", errorCount: 0, missingCount: 0, issues: [], reason: "source-unavailable" }
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
let extracted;
|
|
1340
|
+
try {
|
|
1341
|
+
extracted = await extractAll(code, lang, f.rel);
|
|
1342
|
+
} catch {
|
|
1343
|
+
return {
|
|
1344
|
+
path: f.rel,
|
|
1345
|
+
lang,
|
|
1346
|
+
mtimeMs: f.mtimeMs,
|
|
1347
|
+
symbols: [],
|
|
1348
|
+
imports: [],
|
|
1349
|
+
importDetails: [],
|
|
1350
|
+
calls: [],
|
|
1351
|
+
diagnostic: { status: "failed", errorCount: 0, missingCount: 0, issues: [], reason: "parser-failed" }
|
|
1352
|
+
};
|
|
1214
1353
|
}
|
|
1215
|
-
const { symbols, imports, importDetails, calls } =
|
|
1354
|
+
const { symbols, imports, importDetails, calls, diagnostic } = extracted;
|
|
1216
1355
|
return {
|
|
1217
1356
|
path: f.rel,
|
|
1218
1357
|
lang,
|
|
@@ -1220,7 +1359,8 @@ async function buildIndex(root, options = {}, previous = null, forcePaths = /* @
|
|
|
1220
1359
|
symbols: symbols.map((s) => ({ ...s, file: f.rel })),
|
|
1221
1360
|
imports,
|
|
1222
1361
|
importDetails,
|
|
1223
|
-
calls
|
|
1362
|
+
calls,
|
|
1363
|
+
diagnostic
|
|
1224
1364
|
};
|
|
1225
1365
|
})
|
|
1226
1366
|
);
|
|
@@ -1268,11 +1408,12 @@ function indexesEqual(left, right) {
|
|
|
1268
1408
|
return false;
|
|
1269
1409
|
}
|
|
1270
1410
|
if (file.symbols.length !== other.symbols.length) return false;
|
|
1411
|
+
if (JSON.stringify(file.diagnostic) !== JSON.stringify(other.diagnostic)) return false;
|
|
1271
1412
|
if (!callsEqual(file.calls, other.calls)) return false;
|
|
1272
1413
|
if (!importsEqual(file.importDetails, other.importDetails)) return false;
|
|
1273
1414
|
return file.symbols.every((symbol, symbolIndex) => {
|
|
1274
1415
|
const candidate = other.symbols[symbolIndex];
|
|
1275
|
-
return candidate !== void 0 && symbol.id === candidate.id && symbol.name === candidate.name && symbol.kind === candidate.kind && symbol.file === candidate.file && symbol.line === candidate.line && symbol.endLine === candidate.endLine && symbol.exported === candidate.exported && symbol.signature === candidate.signature && symbol.ordinal === candidate.ordinal && scopesEqual(symbol.scope, candidate.scope);
|
|
1416
|
+
return candidate !== void 0 && symbol.id === candidate.id && symbol.name === candidate.name && symbol.kind === candidate.kind && symbol.file === candidate.file && symbol.line === candidate.line && symbol.endLine === candidate.endLine && symbol.exported === candidate.exported && symbol.signature === candidate.signature && symbol.ordinal === candidate.ordinal && JSON.stringify(symbol.decorators) === JSON.stringify(candidate.decorators) && scopesEqual(symbol.scope, candidate.scope);
|
|
1276
1417
|
});
|
|
1277
1418
|
});
|
|
1278
1419
|
}
|
|
@@ -1304,7 +1445,7 @@ function callsEqual(left, right) {
|
|
|
1304
1445
|
if (a.length !== b.length) return false;
|
|
1305
1446
|
return a.every((call, index) => {
|
|
1306
1447
|
const other = b[index];
|
|
1307
|
-
return other !== void 0 && call.name === other.name && call.line === other.line && call.from === other.from && call.fromId === other.fromId && call.qualifier === other.qualifier;
|
|
1448
|
+
return other !== void 0 && call.name === other.name && call.line === other.line && call.from === other.from && call.fromId === other.fromId && call.qualifier === other.qualifier && call.context === other.context;
|
|
1308
1449
|
});
|
|
1309
1450
|
}
|
|
1310
1451
|
async function findRepoRoot(startDir, maxLevels = 12) {
|
|
@@ -1322,6 +1463,21 @@ async function findRepoRoot(startDir, maxLevels = 12) {
|
|
|
1322
1463
|
return null;
|
|
1323
1464
|
}
|
|
1324
1465
|
|
|
1466
|
+
// src/integrity.ts
|
|
1467
|
+
function indexIntegrityWarnings(index) {
|
|
1468
|
+
const affected = index.files.filter((file) => file.diagnostic && file.diagnostic.status !== "parsed");
|
|
1469
|
+
if (!affected.length) return [];
|
|
1470
|
+
const partial = affected.filter((file) => file.diagnostic.status === "partial").length;
|
|
1471
|
+
return [
|
|
1472
|
+
`index incomplete: ${partial} partial, ${affected.length - partial} failed; inspect code_index`,
|
|
1473
|
+
...affected.slice(0, 3).map((file) => {
|
|
1474
|
+
const diagnostic = file.diagnostic;
|
|
1475
|
+
const location = diagnostic.issues[0]?.line;
|
|
1476
|
+
return `${file.path.slice(0, 100)}${location ? `:${location}` : ""}: ${diagnostic.status} (${diagnostic.reason ?? `${diagnostic.errorCount} ERROR, ${diagnostic.missingCount} MISSING`})`;
|
|
1477
|
+
})
|
|
1478
|
+
];
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1325
1481
|
// src/repomap.ts
|
|
1326
1482
|
import path5 from "path";
|
|
1327
1483
|
var KIND_WEIGHT = {
|
|
@@ -1336,7 +1492,7 @@ var KIND_WEIGHT = {
|
|
|
1336
1492
|
import: 0.05,
|
|
1337
1493
|
module: 0.3
|
|
1338
1494
|
};
|
|
1339
|
-
var TEST_PATH_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
1495
|
+
var TEST_PATH_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
1340
1496
|
function scoreFile(file) {
|
|
1341
1497
|
let score = 0;
|
|
1342
1498
|
for (const sym of file.symbols) {
|
|
@@ -1428,6 +1584,7 @@ function referencePageRank(files, density) {
|
|
|
1428
1584
|
}
|
|
1429
1585
|
function resolveImportDetailed(spec, fromPath, fileSet) {
|
|
1430
1586
|
if (!spec) return { target: null, suffixSkipped: false };
|
|
1587
|
+
if (/^@(ohos|kit)\./.test(spec)) return { target: null, suffixSkipped: false };
|
|
1431
1588
|
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
1432
1589
|
const base = path5.posix.normalize(path5.posix.join(path5.posix.dirname(fromPath), spec));
|
|
1433
1590
|
return { target: candidateFor(base, fileSet), suffixSkipped: false };
|
|
@@ -1444,11 +1601,13 @@ function resolveImport(spec, fromPath, fileSet) {
|
|
|
1444
1601
|
}
|
|
1445
1602
|
function candidateFor(base, fileSet) {
|
|
1446
1603
|
if (fileSet.has(base)) return base;
|
|
1604
|
+
if (fileSet.has(`${base}.ets`) && [...SUPPORTED_EXTS].some((ext) => ext !== ".ets" && fileSet.has(`${base}${ext}`))) return null;
|
|
1447
1605
|
for (const ext of SUPPORTED_EXTS) {
|
|
1448
1606
|
if (fileSet.has(`${base}${ext}`)) return `${base}${ext}`;
|
|
1449
1607
|
}
|
|
1450
1608
|
const aliased = tsSourceForJsSpecifier(base, fileSet);
|
|
1451
1609
|
if (aliased) return aliased;
|
|
1610
|
+
if (fileSet.has(`${base}/index.ets`) && [...SUPPORTED_EXTS].some((ext) => ext !== ".ets" && fileSet.has(`${base}/index${ext}`))) return null;
|
|
1452
1611
|
for (const ext of SUPPORTED_EXTS) {
|
|
1453
1612
|
if (fileSet.has(`${base}/index${ext}`)) return `${base}/index${ext}`;
|
|
1454
1613
|
if (fileSet.has(`${base}/__init__${ext}`)) return `${base}/__init__${ext}`;
|
|
@@ -1514,20 +1673,31 @@ function symbolRefs(index, symbol, limit = 200) {
|
|
|
1514
1673
|
const defs = table.get(symbol) ?? [];
|
|
1515
1674
|
const callers = [];
|
|
1516
1675
|
const calleeFirstLine = /* @__PURE__ */ new Map();
|
|
1676
|
+
const arktsTargets = /* @__PURE__ */ new Map();
|
|
1677
|
+
const graph = index.files.some((file) => file.lang === "arkts") ? buildReferenceGraph(index) : null;
|
|
1678
|
+
const arktsEdgesBySite = /* @__PURE__ */ new Map();
|
|
1679
|
+
if (graph) for (const edge of [...graph.outgoing.values()].flat().concat(graph.moduleLevelEdges)) {
|
|
1680
|
+
const key = `${edge.callSite.file}\0${edge.callSite.line}\0${edge.name}\0${edge.sourceId}`;
|
|
1681
|
+
const rows = arktsEdgesBySite.get(key) ?? [];
|
|
1682
|
+
rows.push(edge);
|
|
1683
|
+
arktsEdgesBySite.set(key, rows);
|
|
1684
|
+
}
|
|
1517
1685
|
for (const file of index.files) {
|
|
1518
1686
|
for (const call of file.calls ?? []) {
|
|
1519
|
-
|
|
1687
|
+
const resolved = file.lang === "arkts" && graph ? (arktsEdgesBySite.get(`${file.path}\0${call.line}\0${call.name}\0${call.fromId}`) ?? []).map((edge) => graph.symbolsById.get(edge.targetId)) : null;
|
|
1688
|
+
if (resolved ? resolved.some((target) => target.name === symbol) : call.name === symbol) {
|
|
1520
1689
|
callers.push({ file: file.path, line: call.line, from: call.from });
|
|
1521
1690
|
}
|
|
1522
1691
|
if (call.from === symbol && !calleeFirstLine.has(call.name)) {
|
|
1523
1692
|
calleeFirstLine.set(call.name, call.line);
|
|
1524
1693
|
}
|
|
1694
|
+
if (call.from === symbol) arktsTargets.set(call.name, dedupeSymbols([...arktsTargets.get(call.name) ?? [], ...resolved ?? table.get(call.name) ?? []]));
|
|
1525
1695
|
}
|
|
1526
1696
|
}
|
|
1527
1697
|
callers.sort(
|
|
1528
1698
|
(a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.from.localeCompare(b.from)
|
|
1529
1699
|
);
|
|
1530
|
-
const callees = [...calleeFirstLine.entries()].map(([name2, line]) => ({ name: name2, line, targets: table.get(name2) ?? [] })).sort((a, b) => a.line - b.line || a.name.localeCompare(b.name));
|
|
1700
|
+
const callees = [...calleeFirstLine.entries()].map(([name2, line]) => ({ name: name2, line, targets: arktsTargets.get(name2) ?? table.get(name2) ?? [] })).sort((a, b) => a.line - b.line || a.name.localeCompare(b.name));
|
|
1531
1701
|
return {
|
|
1532
1702
|
defs,
|
|
1533
1703
|
callers: callers.slice(0, limit),
|
|
@@ -1574,7 +1744,7 @@ function resolveCallTargetsFor(fileSet, file, call, symbolsByName, symbolsByFile
|
|
|
1574
1744
|
const qualified = call.qualifier !== null;
|
|
1575
1745
|
const imports = (file.importDetails ?? []).filter((info) => info.kind !== "reexport");
|
|
1576
1746
|
if (!qualified) {
|
|
1577
|
-
const locals = named.filter((s) => s.file === file.path && lexicallyVisible(s, call));
|
|
1747
|
+
const locals = named.filter((s) => s.file === file.path && lexicallyVisible(s, call) && (call.context !== "arkui" || s.signature.startsWith("struct ") || s.decorators?.some((text) => /^@Builder\b/.test(text))));
|
|
1578
1748
|
if (locals.length > 0) {
|
|
1579
1749
|
const unique2 = locals.length === 1;
|
|
1580
1750
|
const owned = unique2 && isStructuralOwner(call, locals[0]);
|
|
@@ -1604,7 +1774,7 @@ function resolveCallTargetsFor(fileSet, file, call, symbolsByName, symbolsByFile
|
|
|
1604
1774
|
}
|
|
1605
1775
|
if (!isExplicitBinding(binding)) continue;
|
|
1606
1776
|
for (const def of symbolsByName.get(binding.imported) ?? []) {
|
|
1607
|
-
if (def.file === target) bound.push(def);
|
|
1777
|
+
if (def.file === target && (file.lang !== "arkts" && !target.endsWith(".ets") || def.exported && def.scope.length === 0)) bound.push(def);
|
|
1608
1778
|
}
|
|
1609
1779
|
}
|
|
1610
1780
|
}
|
|
@@ -1612,6 +1782,7 @@ function resolveCallTargetsFor(fileSet, file, call, symbolsByName, symbolsByFile
|
|
|
1612
1782
|
const label = bound.length === 1 && directBinding && !defaultBinding ? "exact" : "import-scoped";
|
|
1613
1783
|
return toEdges(bound, call, file.path, "explicit-import-binding", label, maxCandidates);
|
|
1614
1784
|
}
|
|
1785
|
+
if (call.context === "arkui") return [];
|
|
1615
1786
|
const resolvedModules = /* @__PURE__ */ new Set();
|
|
1616
1787
|
for (const info of imports) {
|
|
1617
1788
|
const target = resolveImport(info.specifier, file.path, fileSet);
|
|
@@ -1826,7 +1997,7 @@ function findCycles(index) {
|
|
|
1826
1997
|
return components;
|
|
1827
1998
|
}
|
|
1828
1999
|
var ENTRY_RE = /(^|\/)(index|main|cli|app|server|mod|lib|__init__)\.[A-Za-z0-9]+$|(^|\/)(bin|cmd|scripts)\/|\.config\.[A-Za-z0-9]+$/;
|
|
1829
|
-
var TEST_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
2000
|
+
var TEST_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
1830
2001
|
function findOrphanModules(index) {
|
|
1831
2002
|
const { nodes, edges } = buildModuleGraph(index);
|
|
1832
2003
|
const incoming = /* @__PURE__ */ new Map();
|
|
@@ -2138,7 +2309,7 @@ var LABEL_STRENGTH = {
|
|
|
2138
2309
|
};
|
|
2139
2310
|
var ENTRY_KINDS = /* @__PURE__ */ new Set(["function", "method", "class"]);
|
|
2140
2311
|
var ENTRY_FILE_RE = /(^|\/)(main|index|cli|server|app)\.[^./]+$/;
|
|
2141
|
-
var TEST_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
2312
|
+
var TEST_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
2142
2313
|
function changeContextInputFromArgs(args, root) {
|
|
2143
2314
|
const hasDiff = typeof args.diff === "string" && args.diff.trim().length > 0;
|
|
2144
2315
|
const hasFiles = Array.isArray(args.files) && args.files.length > 0;
|
|
@@ -2188,7 +2359,7 @@ async function buildChangeContext(index, input, options = {}) {
|
|
|
2188
2359
|
const fileSet = new Set(filesByPath.keys());
|
|
2189
2360
|
const incomingCount = /* @__PURE__ */ new Map();
|
|
2190
2361
|
for (const [id, edges] of graph.incoming) incomingCount.set(id, edges.length);
|
|
2191
|
-
const warnings =
|
|
2362
|
+
const warnings = indexIntegrityWarnings(index);
|
|
2192
2363
|
const changedByKey = /* @__PURE__ */ new Map();
|
|
2193
2364
|
const unmapped = [];
|
|
2194
2365
|
const changedFiles = /* @__PURE__ */ new Set();
|
|
@@ -2265,6 +2436,7 @@ async function buildChangeContext(index, input, options = {}) {
|
|
|
2265
2436
|
const lang = languageForFile(filePath);
|
|
2266
2437
|
if (!lang) return null;
|
|
2267
2438
|
const extracted = await extractAll(content, lang, filePath);
|
|
2439
|
+
if (extracted.diagnostic.status !== "parsed") warnings.push(`base ${filePath.slice(0, 100)}: ${extracted.diagnostic.status} parse; change mapping incomplete`);
|
|
2268
2440
|
return extracted.symbols;
|
|
2269
2441
|
})();
|
|
2270
2442
|
baseSymbolsCache.set(filePath, pending);
|
|
@@ -2930,7 +3102,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
2930
3102
|
const graph = buildReferenceGraph(index);
|
|
2931
3103
|
const candidates = [];
|
|
2932
3104
|
const seen = /* @__PURE__ */ new Set();
|
|
2933
|
-
const gaps = [];
|
|
3105
|
+
const gaps = [...context.warnings];
|
|
2934
3106
|
const unavailable = /* @__PURE__ */ new Set();
|
|
2935
3107
|
const source = async (symbol, kind, reason, extra = {}, focus) => {
|
|
2936
3108
|
const side = extra.side ?? "current";
|
|
@@ -3046,7 +3218,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3046
3218
|
task: context.task.slice(0, 120),
|
|
3047
3219
|
route: context.route.kind,
|
|
3048
3220
|
items: [],
|
|
3049
|
-
gaps: ["budget omitted some evidence"],
|
|
3221
|
+
gaps: context.warnings.some((warning) => warning.startsWith("index incomplete:")) ? ["index incomplete; inspect code_index"] : ["budget omitted some evidence"],
|
|
3050
3222
|
budget: { budgetChars, usedChars: 0, packChars: 0, truncated: context.task.length > 120 }
|
|
3051
3223
|
};
|
|
3052
3224
|
const fits = () => {
|
|
@@ -3055,7 +3227,10 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3055
3227
|
};
|
|
3056
3228
|
if (!fits()) {
|
|
3057
3229
|
pack.task = "";
|
|
3058
|
-
|
|
3230
|
+
fits();
|
|
3231
|
+
}
|
|
3232
|
+
if (!fits()) {
|
|
3233
|
+
pack.gaps = pack.gaps.filter((gap) => gap.startsWith("index incomplete"));
|
|
3059
3234
|
fits();
|
|
3060
3235
|
}
|
|
3061
3236
|
if (!fits()) throw new Error("budget too small for repository identity");
|
|
@@ -3075,7 +3250,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3075
3250
|
}
|
|
3076
3251
|
if (!added) pack.budget.truncated = true;
|
|
3077
3252
|
}
|
|
3078
|
-
if (!pack.budget.truncated) pack.gaps =
|
|
3253
|
+
if (!pack.budget.truncated) pack.gaps = pack.gaps.filter((gap) => gap.startsWith("index incomplete"));
|
|
3079
3254
|
for (const item of pack.items) if (item.file && unavailable.has(`${item.side}:${item.file}`)) {
|
|
3080
3255
|
gaps.push(`source unavailable or changed after indexing: ${item.file} (${item.side})`);
|
|
3081
3256
|
}
|
|
@@ -3093,7 +3268,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3093
3268
|
}
|
|
3094
3269
|
|
|
3095
3270
|
// src/context.ts
|
|
3096
|
-
var TEST_PATH_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
3271
|
+
var TEST_PATH_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/i;
|
|
3097
3272
|
var FILE_TOKEN_RE = /[A-Za-z0-9_./\\-]+\.[A-Za-z0-9]+/g;
|
|
3098
3273
|
var CODE_TOKEN_RE = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)?/g;
|
|
3099
3274
|
var STOP_WORDS = /* @__PURE__ */ new Set(
|
|
@@ -3359,7 +3534,7 @@ async function buildTaskContext(index, task, options = {}) {
|
|
|
3359
3534
|
const maxSymbols = clamp2(options.maxSymbols, 1, 50, 10);
|
|
3360
3535
|
const route = routeTask(task, index);
|
|
3361
3536
|
let changeContext = null;
|
|
3362
|
-
const warnings =
|
|
3537
|
+
const warnings = indexIntegrityWarnings(index);
|
|
3363
3538
|
if (route.includeChangeContext) {
|
|
3364
3539
|
try {
|
|
3365
3540
|
changeContext = await buildChangeContext(index, {
|
|
@@ -3786,7 +3961,8 @@ var tools = [
|
|
|
3786
3961
|
`files indexed: ${index.files.length}`,
|
|
3787
3962
|
`symbols: ${total}`,
|
|
3788
3963
|
`languages: ${[...langs].join(", ")}`,
|
|
3789
|
-
index.files.length === 0 ? "no supported files found" : "index up to date"
|
|
3964
|
+
index.files.length === 0 ? "no supported files found" : "index up to date",
|
|
3965
|
+
...indexIntegrityWarnings(index)
|
|
3790
3966
|
].join("\n");
|
|
3791
3967
|
} catch (error) {
|
|
3792
3968
|
return `code_index: ${error.message ?? String(error)}`;
|
|
@@ -4324,6 +4500,7 @@ export {
|
|
|
4324
4500
|
findCycles,
|
|
4325
4501
|
findOrphanModules,
|
|
4326
4502
|
findRepoRoot,
|
|
4503
|
+
indexIntegrityWarnings,
|
|
4327
4504
|
inject,
|
|
4328
4505
|
languageForFile,
|
|
4329
4506
|
loadIndex,
|