homegraph 1.5.10 → 1.6.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/CHANGELOG.md +18 -0
- package/dist/extraction/grammars.d.ts +19 -0
- package/dist/extraction/grammars.js +44 -1
- package/dist/mcp/arkts-evidence-packs.js +1 -0
- package/dist/mcp/daemon.d.ts +21 -3
- package/dist/mcp/daemon.js +60 -6
- package/dist/mcp/engine.d.ts +26 -0
- package/dist/mcp/engine.js +143 -7
- package/dist/mcp/index-availability.d.ts +22 -0
- package/dist/mcp/index-availability.js +40 -1
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/indexable-root.d.ts +22 -0
- package/dist/mcp/indexable-root.js +140 -0
- package/dist/mcp/liveness-watchdog.d.ts +6 -1
- package/dist/mcp/liveness-watchdog.js +17 -5
- package/dist/mcp/locate-contract.d.ts +50 -0
- package/dist/mcp/locate-contract.js +146 -0
- package/dist/mcp/server-instructions.d.ts +2 -2
- package/dist/mcp/server-instructions.js +7 -4
- package/dist/mcp/session.js +15 -0
- package/dist/mcp/tools.d.ts +49 -2
- package/dist/mcp/tools.js +714 -64
- package/dist/project-map/index.d.ts +45 -0
- package/dist/project-map/index.js +371 -3
- package/dist/resolution/callback-synthesizer.js +251 -0
- package/dist/resolution/frameworks/arkts-entry.d.ts +35 -6
- package/dist/resolution/frameworks/arkts-entry.js +513 -30
- package/dist/resolution/index.js +25 -21
- package/dist/runtime-log.d.ts +52 -0
- package/dist/runtime-log.js +199 -0
- package/dist/search/query-plan-provider.js +3 -2
- package/dist/search/query-plan.js +21 -19
- package/dist/search/query-utils.d.ts +13 -0
- package/dist/search/query-utils.js +64 -0
- package/package.json +1 -1
|
@@ -1,14 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.arktsEntryResolver = void 0;
|
|
4
|
+
exports.harmonyModuleRootFromProfile = harmonyModuleRootFromProfile;
|
|
5
|
+
exports.resolveHarmonyPageSourcePath = resolveHarmonyPageSourcePath;
|
|
6
|
+
exports.parseHarmonyRouterMap = parseHarmonyRouterMap;
|
|
7
|
+
exports.parseHarmonyMainPages = parseHarmonyMainPages;
|
|
8
|
+
exports.parseHarmonyStringResources = parseHarmonyStringResources;
|
|
9
|
+
exports.parseHarmonyFormConfig = parseHarmonyFormConfig;
|
|
10
|
+
exports.parseHarmonyShortcutsConfig = parseHarmonyShortcutsConfig;
|
|
4
11
|
exports.findArktsEntryPageComponent = findArktsEntryPageComponent;
|
|
5
12
|
exports.findArktsLifecycleMethod = findArktsLifecycleMethod;
|
|
13
|
+
/**
|
|
14
|
+
* ArkTS entry / module manifest + Harmony route profile resolver (Spec 0039)
|
|
15
|
+
* + element/string.json lightweight constants (Spec 0041, no edges)
|
|
16
|
+
* + form_config / shortcuts_config capability profiles (Spec 0048, no UI edges).
|
|
17
|
+
*
|
|
18
|
+
* Parses:
|
|
19
|
+
* - `module.json5` pages → @Entry components, loadContent(url) → lifecycle
|
|
20
|
+
* - `route_map.json` / `router_map.json` routerMap[] → page / buildFunction
|
|
21
|
+
* - `main_pages.json` src[] → @Entry (same as module pages)
|
|
22
|
+
* - resources/.../element/string.json → constant nodes for FTS (no references)
|
|
23
|
+
* - form_config.json / shortcuts_config.json → capability constants (Spec 0048)
|
|
24
|
+
* - module.json5 form extensionAbilities + shortcuts/form metadata (Spec 0048)
|
|
25
|
+
*/
|
|
26
|
+
const jsonc_parser_1 = require("jsonc-parser");
|
|
27
|
+
const grammars_1 = require("../../extraction/grammars");
|
|
6
28
|
const LOAD_CONTENT_PAGE_RE = /loadContent\s*\(\s*['"]([^'"]+)['"]/g;
|
|
29
|
+
/** Cap string.json entries per file (Spec 0041). */
|
|
30
|
+
const STRING_JSON_ENTRY_CAP = 2000;
|
|
31
|
+
const STRING_JSON_VALUE_CAP = 200;
|
|
32
|
+
const CAPABILITY_ENTRY_CAP = 64;
|
|
7
33
|
function pageStem(pagePath) {
|
|
8
34
|
const normalized = pagePath.replace(/\\/g, '/');
|
|
9
35
|
const base = normalized.split('/').pop() ?? normalized;
|
|
10
36
|
return base.replace(/\.ets$/i, '');
|
|
11
37
|
}
|
|
38
|
+
function lineOfNeedle(content, needle) {
|
|
39
|
+
const idx = content.indexOf(needle);
|
|
40
|
+
if (idx < 0)
|
|
41
|
+
return 1;
|
|
42
|
+
return content.slice(0, idx).split('\n').length;
|
|
43
|
+
}
|
|
44
|
+
/** Module root = path prefix before `/src/main/` (Harmony HAP/HAR layout). */
|
|
45
|
+
function harmonyModuleRootFromProfile(profileRel) {
|
|
46
|
+
const n = profileRel.replace(/\\/g, '/');
|
|
47
|
+
const marker = '/src/main/';
|
|
48
|
+
const i = n.indexOf(marker);
|
|
49
|
+
if (i < 0)
|
|
50
|
+
return '';
|
|
51
|
+
return n.slice(0, i);
|
|
52
|
+
}
|
|
53
|
+
/** Resolve `pageSourceFile` (module-relative) against the profile's module root. */
|
|
54
|
+
function resolveHarmonyPageSourcePath(profileRel, pageSourceFile) {
|
|
55
|
+
const modRoot = harmonyModuleRootFromProfile(profileRel);
|
|
56
|
+
const page = pageSourceFile.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
57
|
+
if (!modRoot)
|
|
58
|
+
return page;
|
|
59
|
+
return `${modRoot}/${page}`.replace(/\/+/g, '/');
|
|
60
|
+
}
|
|
12
61
|
function parseModuleJson5Pages(content) {
|
|
13
62
|
const pages = [];
|
|
14
63
|
const seen = new Set();
|
|
@@ -37,6 +86,63 @@ function parseModuleAbilities(content) {
|
|
|
37
86
|
}
|
|
38
87
|
return abilities;
|
|
39
88
|
}
|
|
89
|
+
/** Parse standard Harmony `routerMap` JSON (route_map / router_map). */
|
|
90
|
+
function parseHarmonyRouterMap(content) {
|
|
91
|
+
let data;
|
|
92
|
+
try {
|
|
93
|
+
data = JSON.parse(content);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
if (!data || typeof data !== 'object')
|
|
99
|
+
return [];
|
|
100
|
+
const arr = data.routerMap;
|
|
101
|
+
if (!Array.isArray(arr))
|
|
102
|
+
return [];
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const item of arr) {
|
|
105
|
+
if (!item || typeof item !== 'object')
|
|
106
|
+
continue;
|
|
107
|
+
const rec = item;
|
|
108
|
+
const name = typeof rec.name === 'string' ? rec.name.trim() : '';
|
|
109
|
+
const pageSourceFile = typeof rec.pageSourceFile === 'string' ? rec.pageSourceFile.trim() : '';
|
|
110
|
+
if (!name || !pageSourceFile)
|
|
111
|
+
continue;
|
|
112
|
+
const buildFunction = typeof rec.buildFunction === 'string' && rec.buildFunction.trim()
|
|
113
|
+
? rec.buildFunction.trim()
|
|
114
|
+
: undefined;
|
|
115
|
+
out.push({ name, pageSourceFile, buildFunction });
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
/** Parse `main_pages.json` `{ "src": ["pages/Index", ...] }`. */
|
|
120
|
+
function parseHarmonyMainPages(content) {
|
|
121
|
+
let data;
|
|
122
|
+
try {
|
|
123
|
+
data = JSON.parse(content);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
if (!data || typeof data !== 'object')
|
|
129
|
+
return [];
|
|
130
|
+
const src = data.src;
|
|
131
|
+
if (!Array.isArray(src))
|
|
132
|
+
return [];
|
|
133
|
+
const out = [];
|
|
134
|
+
const seen = new Set();
|
|
135
|
+
for (const p of src) {
|
|
136
|
+
if (typeof p !== 'string')
|
|
137
|
+
continue;
|
|
138
|
+
const t = p.trim();
|
|
139
|
+
if (!t || t.includes('$') || seen.has(t))
|
|
140
|
+
continue;
|
|
141
|
+
seen.add(t);
|
|
142
|
+
out.push(t);
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
40
146
|
function findArktsComponentByName(context, name) {
|
|
41
147
|
for (const kind of ['component', 'struct', 'class']) {
|
|
42
148
|
const iter = context.iterateNodesByKind?.(kind) ?? context.getNodesByKind(kind);
|
|
@@ -67,6 +173,33 @@ function findArktsPageComponent(context, pagePath) {
|
|
|
67
173
|
}
|
|
68
174
|
return null;
|
|
69
175
|
}
|
|
176
|
+
function findArktsSymbolInFile(context, fileRel, name) {
|
|
177
|
+
const norm = fileRel.replace(/\\/g, '/');
|
|
178
|
+
const nodes = context.getNodesInFile(norm);
|
|
179
|
+
for (const n of nodes) {
|
|
180
|
+
if (n.name !== name)
|
|
181
|
+
continue;
|
|
182
|
+
if (n.kind === 'component' ||
|
|
183
|
+
n.kind === 'struct' ||
|
|
184
|
+
n.kind === 'class' ||
|
|
185
|
+
n.kind === 'function' ||
|
|
186
|
+
n.kind === 'method') {
|
|
187
|
+
return n;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
function findArktsBuilderByName(context, name) {
|
|
193
|
+
for (const kind of ['function', 'method']) {
|
|
194
|
+
const iter = context.iterateNodesByKind?.(kind) ?? context.getNodesByKind(kind);
|
|
195
|
+
for (const n of iter) {
|
|
196
|
+
if (n.language !== 'arkts' || n.name !== name)
|
|
197
|
+
continue;
|
|
198
|
+
return n;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
70
203
|
function findArktsMethodInComponent(context, component, methodName) {
|
|
71
204
|
let owner = component;
|
|
72
205
|
if (component.kind === 'component') {
|
|
@@ -86,12 +219,341 @@ function findArktsMethodInComponent(context, component, methodName) {
|
|
|
86
219
|
}
|
|
87
220
|
return null;
|
|
88
221
|
}
|
|
222
|
+
function pushPageRoute(nodes, references, filePath, content, page, now) {
|
|
223
|
+
const line = lineOfNeedle(content, page);
|
|
224
|
+
const routeId = `arkts-route:${filePath}:${page}`;
|
|
225
|
+
nodes.push({
|
|
226
|
+
id: routeId,
|
|
227
|
+
kind: 'route',
|
|
228
|
+
name: page,
|
|
229
|
+
qualifiedName: `${filePath}::${page}`,
|
|
230
|
+
filePath,
|
|
231
|
+
language: 'yaml',
|
|
232
|
+
startLine: line,
|
|
233
|
+
endLine: line,
|
|
234
|
+
startColumn: 0,
|
|
235
|
+
endColumn: 0,
|
|
236
|
+
isExported: false,
|
|
237
|
+
updatedAt: now,
|
|
238
|
+
});
|
|
239
|
+
references.push({
|
|
240
|
+
fromNodeId: routeId,
|
|
241
|
+
referenceName: pageStem(page),
|
|
242
|
+
referenceKind: 'references',
|
|
243
|
+
line,
|
|
244
|
+
column: 0,
|
|
245
|
+
filePath,
|
|
246
|
+
language: 'yaml',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function extractRouterMapProfile(filePath, content, now) {
|
|
250
|
+
const nodes = [];
|
|
251
|
+
const references = [];
|
|
252
|
+
for (const entry of parseHarmonyRouterMap(content)) {
|
|
253
|
+
const line = lineOfNeedle(content, `"name": "${entry.name}"`) || lineOfNeedle(content, entry.name);
|
|
254
|
+
const pageRel = resolveHarmonyPageSourcePath(filePath, entry.pageSourceFile);
|
|
255
|
+
const routeId = `arkts-route:${filePath}:${entry.name}`;
|
|
256
|
+
const sigParts = [`pageSourceFile=${entry.pageSourceFile}`];
|
|
257
|
+
if (entry.buildFunction)
|
|
258
|
+
sigParts.push(`buildFunction=${entry.buildFunction}`);
|
|
259
|
+
nodes.push({
|
|
260
|
+
id: routeId,
|
|
261
|
+
kind: 'route',
|
|
262
|
+
name: entry.name,
|
|
263
|
+
qualifiedName: `${filePath}::${entry.name}`,
|
|
264
|
+
filePath,
|
|
265
|
+
language: 'yaml',
|
|
266
|
+
startLine: line,
|
|
267
|
+
endLine: line,
|
|
268
|
+
startColumn: 0,
|
|
269
|
+
endColumn: 0,
|
|
270
|
+
isExported: false,
|
|
271
|
+
signature: sigParts.join('; '),
|
|
272
|
+
updatedAt: now,
|
|
273
|
+
});
|
|
274
|
+
references.push({
|
|
275
|
+
fromNodeId: routeId,
|
|
276
|
+
referenceName: pageStem(entry.pageSourceFile),
|
|
277
|
+
referenceKind: 'references',
|
|
278
|
+
line,
|
|
279
|
+
column: 0,
|
|
280
|
+
filePath,
|
|
281
|
+
language: 'yaml',
|
|
282
|
+
candidates: [pageRel],
|
|
283
|
+
});
|
|
284
|
+
if (entry.buildFunction) {
|
|
285
|
+
references.push({
|
|
286
|
+
fromNodeId: routeId,
|
|
287
|
+
referenceName: entry.buildFunction,
|
|
288
|
+
referenceKind: 'references',
|
|
289
|
+
line,
|
|
290
|
+
column: 0,
|
|
291
|
+
filePath,
|
|
292
|
+
language: 'yaml',
|
|
293
|
+
candidates: [pageRel],
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return { nodes, references };
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Spec 0041 — collect `{ name, value }` string resource entries (no edges).
|
|
301
|
+
* Same shape as literal-evidence: walk objects; skip illegal keys.
|
|
302
|
+
*/
|
|
303
|
+
function parseHarmonyStringResources(content) {
|
|
304
|
+
const data = (0, jsonc_parser_1.parse)(content, undefined, { allowTrailingComma: true });
|
|
305
|
+
if (data === undefined || data === null)
|
|
306
|
+
return [];
|
|
307
|
+
const out = [];
|
|
308
|
+
let visited = 0;
|
|
309
|
+
const visit = (value, depth) => {
|
|
310
|
+
if (++visited > 10000 || depth > 12 || !value || typeof value !== 'object')
|
|
311
|
+
return;
|
|
312
|
+
if (Array.isArray(value)) {
|
|
313
|
+
for (const item of value)
|
|
314
|
+
visit(item, depth + 1);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const entry = value;
|
|
318
|
+
if (typeof entry.name === 'string'
|
|
319
|
+
&& typeof entry.value === 'string'
|
|
320
|
+
&& /^[A-Za-z_][\w]*$/.test(entry.name)
|
|
321
|
+
&& out.length < STRING_JSON_ENTRY_CAP) {
|
|
322
|
+
const needle = JSON.stringify(entry.name);
|
|
323
|
+
const idx = content.indexOf(needle);
|
|
324
|
+
const line = idx < 0 ? 1 : content.slice(0, idx).split('\n').length;
|
|
325
|
+
out.push({
|
|
326
|
+
name: entry.name,
|
|
327
|
+
value: entry.value.slice(0, STRING_JSON_VALUE_CAP),
|
|
328
|
+
line,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
for (const child of Object.values(entry))
|
|
332
|
+
visit(child, depth + 1);
|
|
333
|
+
};
|
|
334
|
+
visit(data, 0);
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
function extractHarmonyStringJson(filePath, content, now) {
|
|
338
|
+
const nodes = [];
|
|
339
|
+
for (const entry of parseHarmonyStringResources(content)) {
|
|
340
|
+
nodes.push({
|
|
341
|
+
id: `harmony-string:${filePath}:${entry.name}`,
|
|
342
|
+
kind: 'constant',
|
|
343
|
+
name: entry.name,
|
|
344
|
+
qualifiedName: `app.string.${entry.name}`,
|
|
345
|
+
filePath,
|
|
346
|
+
language: 'yaml',
|
|
347
|
+
startLine: entry.line,
|
|
348
|
+
endLine: entry.line,
|
|
349
|
+
startColumn: 0,
|
|
350
|
+
endColumn: 0,
|
|
351
|
+
isExported: false,
|
|
352
|
+
docstring: entry.value,
|
|
353
|
+
signature: `$r('app.string.${entry.name}')`,
|
|
354
|
+
updatedAt: now,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return { nodes, references: [] };
|
|
358
|
+
}
|
|
359
|
+
/** Spec 0048 — form_config.json `forms[].name`. */
|
|
360
|
+
function parseHarmonyFormConfig(content) {
|
|
361
|
+
let data;
|
|
362
|
+
try {
|
|
363
|
+
data = JSON.parse(content);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
try {
|
|
367
|
+
data = (0, jsonc_parser_1.parse)(content, undefined, { allowTrailingComma: true });
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!data || typeof data !== 'object')
|
|
374
|
+
return [];
|
|
375
|
+
const forms = data.forms;
|
|
376
|
+
if (!Array.isArray(forms))
|
|
377
|
+
return [];
|
|
378
|
+
const out = [];
|
|
379
|
+
for (const item of forms) {
|
|
380
|
+
if (out.length >= CAPABILITY_ENTRY_CAP)
|
|
381
|
+
break;
|
|
382
|
+
if (!item || typeof item !== 'object')
|
|
383
|
+
continue;
|
|
384
|
+
const name = typeof item.name === 'string'
|
|
385
|
+
? item.name.trim()
|
|
386
|
+
: '';
|
|
387
|
+
if (!name)
|
|
388
|
+
continue;
|
|
389
|
+
const needle = JSON.stringify(name);
|
|
390
|
+
const idx = content.indexOf(needle);
|
|
391
|
+
const line = idx < 0 ? 1 : content.slice(0, idx).split('\n').length;
|
|
392
|
+
out.push({ name, line });
|
|
393
|
+
}
|
|
394
|
+
return out;
|
|
395
|
+
}
|
|
396
|
+
/** Spec 0048 — shortcuts_config.json `shortcuts[]`. */
|
|
397
|
+
function parseHarmonyShortcutsConfig(content) {
|
|
398
|
+
let data;
|
|
399
|
+
try {
|
|
400
|
+
data = JSON.parse(content);
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
try {
|
|
404
|
+
data = (0, jsonc_parser_1.parse)(content, undefined, { allowTrailingComma: true });
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (!data || typeof data !== 'object')
|
|
411
|
+
return [];
|
|
412
|
+
const shortcuts = data.shortcuts;
|
|
413
|
+
if (!Array.isArray(shortcuts))
|
|
414
|
+
return [];
|
|
415
|
+
const out = [];
|
|
416
|
+
for (const item of shortcuts) {
|
|
417
|
+
if (out.length >= CAPABILITY_ENTRY_CAP)
|
|
418
|
+
break;
|
|
419
|
+
if (!item || typeof item !== 'object')
|
|
420
|
+
continue;
|
|
421
|
+
const rec = item;
|
|
422
|
+
const shortcutId = typeof rec.shortcutId === 'string' ? rec.shortcutId.trim() : '';
|
|
423
|
+
if (!shortcutId)
|
|
424
|
+
continue;
|
|
425
|
+
const label = typeof rec.label === 'string' ? rec.label.trim() : '';
|
|
426
|
+
let abilityName;
|
|
427
|
+
if (Array.isArray(rec.wants) && rec.wants[0] && typeof rec.wants[0] === 'object') {
|
|
428
|
+
const w = rec.wants[0];
|
|
429
|
+
if (typeof w.abilityName === 'string' && w.abilityName.trim()) {
|
|
430
|
+
abilityName = w.abilityName.trim();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const needle = JSON.stringify(shortcutId);
|
|
434
|
+
const idx = content.indexOf(needle);
|
|
435
|
+
const line = idx < 0 ? 1 : content.slice(0, idx).split('\n').length;
|
|
436
|
+
out.push({ shortcutId, label, abilityName, line });
|
|
437
|
+
}
|
|
438
|
+
return out;
|
|
439
|
+
}
|
|
440
|
+
function extractHarmonyFormConfig(filePath, content, now) {
|
|
441
|
+
const nodes = [];
|
|
442
|
+
for (const entry of parseHarmonyFormConfig(content)) {
|
|
443
|
+
nodes.push({
|
|
444
|
+
id: `harmony-form:${filePath}:${entry.name}`,
|
|
445
|
+
kind: 'constant',
|
|
446
|
+
name: entry.name,
|
|
447
|
+
qualifiedName: `harmony.form.${entry.name}`,
|
|
448
|
+
filePath,
|
|
449
|
+
language: 'yaml',
|
|
450
|
+
startLine: entry.line,
|
|
451
|
+
endLine: entry.line,
|
|
452
|
+
startColumn: 0,
|
|
453
|
+
endColumn: 0,
|
|
454
|
+
isExported: false,
|
|
455
|
+
docstring: 'form_config',
|
|
456
|
+
signature: `form_config name=${entry.name}`,
|
|
457
|
+
updatedAt: now,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return { nodes, references: [] };
|
|
461
|
+
}
|
|
462
|
+
function extractHarmonyShortcutsConfig(filePath, content, now) {
|
|
463
|
+
const nodes = [];
|
|
464
|
+
for (const entry of parseHarmonyShortcutsConfig(content)) {
|
|
465
|
+
const sigParts = [`shortcuts_config id=${entry.shortcutId}`];
|
|
466
|
+
if (entry.label)
|
|
467
|
+
sigParts.push(`label=${entry.label}`);
|
|
468
|
+
if (entry.abilityName)
|
|
469
|
+
sigParts.push(`ability=${entry.abilityName}`);
|
|
470
|
+
nodes.push({
|
|
471
|
+
id: `harmony-shortcut:${filePath}:${entry.shortcutId}`,
|
|
472
|
+
kind: 'constant',
|
|
473
|
+
name: entry.shortcutId,
|
|
474
|
+
qualifiedName: `harmony.shortcut.${entry.shortcutId}`,
|
|
475
|
+
filePath,
|
|
476
|
+
language: 'yaml',
|
|
477
|
+
startLine: entry.line,
|
|
478
|
+
endLine: entry.line,
|
|
479
|
+
startColumn: 0,
|
|
480
|
+
endColumn: 0,
|
|
481
|
+
isExported: false,
|
|
482
|
+
docstring: entry.label || 'shortcut',
|
|
483
|
+
signature: sigParts.join('; '),
|
|
484
|
+
updatedAt: now,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
return { nodes, references: [] };
|
|
488
|
+
}
|
|
489
|
+
/** Spec 0048 — module.json5 form extensions + shortcuts/form metadata. */
|
|
490
|
+
function extractModuleCapabilityWiring(filePath, content, now, nodes) {
|
|
491
|
+
// Locate `"type": "form"` then take the nearest preceding `"name"` in a 500-char window.
|
|
492
|
+
for (const m of content.matchAll(/"type"\s*:\s*"form"/g)) {
|
|
493
|
+
const start = Math.max(0, (m.index ?? 0) - 500);
|
|
494
|
+
const window = content.slice(start, (m.index ?? 0) + m[0].length + 200);
|
|
495
|
+
const names = [...window.matchAll(/"name"\s*:\s*"([^"]+)"/g)];
|
|
496
|
+
const name = names.length ? names[names.length - 1][1] : '';
|
|
497
|
+
if (!name || name.startsWith('ohos.'))
|
|
498
|
+
continue;
|
|
499
|
+
const src = /"srcEntry"\s*:\s*"([^"]+)"/.exec(window)?.[1] ?? '';
|
|
500
|
+
const line = lineOfNeedle(content, name);
|
|
501
|
+
const id = `harmony-form-ability:${filePath}:${name}`;
|
|
502
|
+
if (nodes.some((n) => n.id === id))
|
|
503
|
+
continue;
|
|
504
|
+
nodes.push({
|
|
505
|
+
id,
|
|
506
|
+
kind: 'route',
|
|
507
|
+
name: `formAbility:${name}`,
|
|
508
|
+
qualifiedName: `${filePath}::formAbility::${name}`,
|
|
509
|
+
filePath,
|
|
510
|
+
language: 'yaml',
|
|
511
|
+
startLine: line,
|
|
512
|
+
endLine: line,
|
|
513
|
+
startColumn: 0,
|
|
514
|
+
endColumn: 0,
|
|
515
|
+
isExported: false,
|
|
516
|
+
docstring: 'FormExtensionAbility',
|
|
517
|
+
signature: `type=form; srcEntry=${src}`,
|
|
518
|
+
updatedAt: now,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
for (const m of content.matchAll(/\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"resource"\s*:\s*"(\$profile:[^"]+)"\s*\}/g)) {
|
|
522
|
+
const metaName = m[1];
|
|
523
|
+
const resource = m[2];
|
|
524
|
+
const isShortcut = /shortcut/i.test(metaName) || /shortcuts_config/i.test(resource);
|
|
525
|
+
const isForm = /ohos\.extension\.form/i.test(metaName) || /form_config/i.test(resource);
|
|
526
|
+
if (!isShortcut && !isForm)
|
|
527
|
+
continue;
|
|
528
|
+
const kind = isForm ? 'form' : 'shortcut';
|
|
529
|
+
const line = content.slice(0, m.index ?? 0).split('\n').length;
|
|
530
|
+
nodes.push({
|
|
531
|
+
id: `harmony-capability-meta:${filePath}:${metaName}:${resource}`,
|
|
532
|
+
kind: 'constant',
|
|
533
|
+
name: metaName,
|
|
534
|
+
qualifiedName: `harmony.capability.${kind}.${metaName}`,
|
|
535
|
+
filePath,
|
|
536
|
+
language: 'yaml',
|
|
537
|
+
startLine: line,
|
|
538
|
+
endLine: line,
|
|
539
|
+
startColumn: 0,
|
|
540
|
+
endColumn: 0,
|
|
541
|
+
isExported: false,
|
|
542
|
+
docstring: kind,
|
|
543
|
+
signature: `metadata ${metaName} → ${resource}`,
|
|
544
|
+
updatedAt: now,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
89
548
|
exports.arktsEntryResolver = {
|
|
90
549
|
name: 'arkts-entry',
|
|
91
550
|
languages: ['arkts', 'yaml'],
|
|
92
551
|
detect(context) {
|
|
93
552
|
for (const file of context.getAllFiles()) {
|
|
94
|
-
if (file.endsWith('module.json5')
|
|
553
|
+
if (file.endsWith('module.json5')
|
|
554
|
+
|| (0, grammars_1.isHarmonyRouteProfileJson)(file)
|
|
555
|
+
|| (0, grammars_1.isHarmonyElementStringJson)(file)
|
|
556
|
+
|| (0, grammars_1.isHarmonyCapabilityProfileJson)(file))
|
|
95
557
|
return true;
|
|
96
558
|
if (!file.endsWith('.ets'))
|
|
97
559
|
continue;
|
|
@@ -102,43 +564,22 @@ exports.arktsEntryResolver = {
|
|
|
102
564
|
return false;
|
|
103
565
|
},
|
|
104
566
|
claimsReference(name) {
|
|
105
|
-
return name.startsWith('pages/') ||
|
|
567
|
+
return (name.startsWith('pages/') ||
|
|
568
|
+
/^[A-Z][A-Za-z0-9]*$/.test(name) ||
|
|
569
|
+
/Builder$/.test(name));
|
|
106
570
|
},
|
|
107
571
|
extract(filePath, content) {
|
|
108
572
|
const nodes = [];
|
|
109
573
|
const references = [];
|
|
110
574
|
const now = Date.now();
|
|
575
|
+
const base = filePath.replace(/\\/g, '/').split('/').pop()?.toLowerCase() ?? '';
|
|
111
576
|
if (filePath.endsWith('module.json5')) {
|
|
112
577
|
const pages = parseModuleJson5Pages(content);
|
|
113
578
|
for (const page of pages) {
|
|
114
|
-
|
|
115
|
-
const routeId = `arkts-route:${filePath}:${page}`;
|
|
116
|
-
nodes.push({
|
|
117
|
-
id: routeId,
|
|
118
|
-
kind: 'route',
|
|
119
|
-
name: page,
|
|
120
|
-
qualifiedName: `${filePath}::${page}`,
|
|
121
|
-
filePath,
|
|
122
|
-
language: 'yaml',
|
|
123
|
-
startLine: line > 0 ? line : 1,
|
|
124
|
-
endLine: line > 0 ? line : 1,
|
|
125
|
-
startColumn: 0,
|
|
126
|
-
endColumn: 0,
|
|
127
|
-
isExported: false,
|
|
128
|
-
updatedAt: now,
|
|
129
|
-
});
|
|
130
|
-
references.push({
|
|
131
|
-
fromNodeId: routeId,
|
|
132
|
-
referenceName: pageStem(page),
|
|
133
|
-
referenceKind: 'references',
|
|
134
|
-
line: line > 0 ? line : 1,
|
|
135
|
-
column: 0,
|
|
136
|
-
filePath,
|
|
137
|
-
language: 'yaml',
|
|
138
|
-
});
|
|
579
|
+
pushPageRoute(nodes, references, filePath, content, page, now);
|
|
139
580
|
}
|
|
140
581
|
for (const ability of parseModuleAbilities(content)) {
|
|
141
|
-
const line = content
|
|
582
|
+
const line = lineOfNeedle(content, ability.name);
|
|
142
583
|
const abilityId = `arkts-ability:${filePath}:${ability.name}`;
|
|
143
584
|
nodes.push({
|
|
144
585
|
id: abilityId,
|
|
@@ -147,8 +588,8 @@ exports.arktsEntryResolver = {
|
|
|
147
588
|
qualifiedName: `${filePath}::ability::${ability.name}`,
|
|
148
589
|
filePath,
|
|
149
590
|
language: 'yaml',
|
|
150
|
-
startLine: line
|
|
151
|
-
endLine: line
|
|
591
|
+
startLine: line,
|
|
592
|
+
endLine: line,
|
|
152
593
|
startColumn: 0,
|
|
153
594
|
endColumn: 0,
|
|
154
595
|
isExported: false,
|
|
@@ -156,8 +597,27 @@ exports.arktsEntryResolver = {
|
|
|
156
597
|
updatedAt: now,
|
|
157
598
|
});
|
|
158
599
|
}
|
|
600
|
+
extractModuleCapabilityWiring(filePath, content, now, nodes);
|
|
601
|
+
return { nodes, references };
|
|
602
|
+
}
|
|
603
|
+
if (base === 'route_map.json' || base === 'router_map.json') {
|
|
604
|
+
return extractRouterMapProfile(filePath, content, now);
|
|
605
|
+
}
|
|
606
|
+
if (base === 'main_pages.json') {
|
|
607
|
+
for (const page of parseHarmonyMainPages(content)) {
|
|
608
|
+
pushPageRoute(nodes, references, filePath, content, page, now);
|
|
609
|
+
}
|
|
159
610
|
return { nodes, references };
|
|
160
611
|
}
|
|
612
|
+
if ((0, grammars_1.isHarmonyElementStringJson)(filePath)) {
|
|
613
|
+
return extractHarmonyStringJson(filePath, content, now);
|
|
614
|
+
}
|
|
615
|
+
if (base === 'form_config.json') {
|
|
616
|
+
return extractHarmonyFormConfig(filePath, content, now);
|
|
617
|
+
}
|
|
618
|
+
if (base === 'shortcuts_config.json') {
|
|
619
|
+
return extractHarmonyShortcutsConfig(filePath, content, now);
|
|
620
|
+
}
|
|
161
621
|
if (!filePath.endsWith('.ets')) {
|
|
162
622
|
return { nodes, references };
|
|
163
623
|
}
|
|
@@ -181,6 +641,18 @@ exports.arktsEntryResolver = {
|
|
|
181
641
|
resolve(ref, context) {
|
|
182
642
|
if (ref.referenceKind !== 'references')
|
|
183
643
|
return null;
|
|
644
|
+
const preferredFile = ref.candidates?.[0]?.replace(/\\/g, '/');
|
|
645
|
+
if (preferredFile) {
|
|
646
|
+
const inFile = findArktsSymbolInFile(context, preferredFile, ref.referenceName);
|
|
647
|
+
if (inFile) {
|
|
648
|
+
return {
|
|
649
|
+
original: ref,
|
|
650
|
+
targetNodeId: inFile.id,
|
|
651
|
+
confidence: 0.92,
|
|
652
|
+
resolvedBy: 'framework',
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
}
|
|
184
656
|
if (ref.referenceName.startsWith('pages/')) {
|
|
185
657
|
const routesIter = context.iterateNodesByKind?.('route') ?? context.getNodesByKind('route');
|
|
186
658
|
for (const n of routesIter) {
|
|
@@ -204,6 +676,17 @@ exports.arktsEntryResolver = {
|
|
|
204
676
|
}
|
|
205
677
|
return null;
|
|
206
678
|
}
|
|
679
|
+
if (/Builder$/.test(ref.referenceName)) {
|
|
680
|
+
const builder = findArktsBuilderByName(context, ref.referenceName);
|
|
681
|
+
if (builder) {
|
|
682
|
+
return {
|
|
683
|
+
original: ref,
|
|
684
|
+
targetNodeId: builder.id,
|
|
685
|
+
confidence: 0.85,
|
|
686
|
+
resolvedBy: 'framework',
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
}
|
|
207
690
|
const component = findArktsComponentByName(context, ref.referenceName);
|
|
208
691
|
if (component) {
|
|
209
692
|
return {
|
package/dist/resolution/index.js
CHANGED
|
@@ -56,6 +56,7 @@ const go_module_1 = require("./go-module");
|
|
|
56
56
|
const workspace_packages_1 = require("./workspace-packages");
|
|
57
57
|
const errors_1 = require("../errors");
|
|
58
58
|
const lru_cache_1 = require("./lru-cache");
|
|
59
|
+
const grammars_1 = require("../extraction/grammars");
|
|
59
60
|
/** Node kinds that can declare supertypes (extends/implements). */
|
|
60
61
|
const SUPERTYPE_BEARING_KINDS = new Set([
|
|
61
62
|
'class', 'struct', 'interface', 'trait', 'protocol', 'enum',
|
|
@@ -1087,27 +1088,30 @@ class ReferenceResolver {
|
|
|
1087
1088
|
kind,
|
|
1088
1089
|
line: ref.original.line,
|
|
1089
1090
|
column: ref.original.column,
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1091
|
+
// Spec 0039: Harmony route configs → page/builder edges need heuristic
|
|
1092
|
+
// provenance + registeredAt so explore/trail show the JSON wiring site.
|
|
1093
|
+
...(() => {
|
|
1094
|
+
const sourceNode = this.queries.getNodeById(ref.original.fromNodeId);
|
|
1095
|
+
const isRouteMap = sourceNode?.kind === 'route' &&
|
|
1096
|
+
(0, grammars_1.isHarmonyRouteConfigFile)(sourceNode.filePath);
|
|
1097
|
+
const baseMeta = {
|
|
1098
|
+
confidence: ref.confidence,
|
|
1099
|
+
resolvedBy: ref.resolvedBy,
|
|
1100
|
+
refName: ref.original.referenceName,
|
|
1101
|
+
...(ref.original.referenceKind !== kind ? { refKind: ref.original.referenceKind } : {}),
|
|
1102
|
+
...(ref.original.referenceKind === 'function_ref' ? { fnRef: true } : {}),
|
|
1103
|
+
};
|
|
1104
|
+
if (!isRouteMap)
|
|
1105
|
+
return { metadata: baseMeta };
|
|
1106
|
+
return {
|
|
1107
|
+
provenance: 'heuristic',
|
|
1108
|
+
metadata: {
|
|
1109
|
+
...baseMeta,
|
|
1110
|
+
synthesizedBy: 'arkts-route-map',
|
|
1111
|
+
registeredAt: `${sourceNode.filePath.replace(/\\/g, '/')}:${ref.original.line}`,
|
|
1112
|
+
},
|
|
1113
|
+
};
|
|
1114
|
+
})(),
|
|
1111
1115
|
};
|
|
1112
1116
|
});
|
|
1113
1117
|
}
|