homegraph 1.5.2 → 1.5.3
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 +131 -0
- package/README.md +15 -8
- package/dist/arkui/index.d.ts +4 -0
- package/dist/arkui/index.d.ts.map +1 -0
- package/dist/arkui/index.js +23 -0
- package/dist/arkui/index.js.map +1 -0
- package/dist/arkui/migrate-passage.d.ts +28 -0
- package/dist/arkui/migrate-passage.d.ts.map +1 -0
- package/dist/arkui/migrate-passage.js +310 -0
- package/dist/arkui/migrate-passage.js.map +1 -0
- package/dist/arkui/migrate-semantics.d.ts +47 -0
- package/dist/arkui/migrate-semantics.d.ts.map +1 -0
- package/dist/arkui/migrate-semantics.js +229 -0
- package/dist/arkui/migrate-semantics.js.map +1 -0
- package/dist/arkui/migrate-snapshot.d.ts +79 -0
- package/dist/arkui/migrate-snapshot.d.ts.map +1 -0
- package/dist/arkui/migrate-snapshot.js +340 -0
- package/dist/arkui/migrate-snapshot.js.map +1 -0
- package/dist/bin/fatal-handler.js +2 -2
- package/dist/bin/homegraph.js +105 -156
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/bin/node-version-check.d.ts +6 -15
- package/dist/bin/node-version-check.d.ts.map +1 -1
- package/dist/bin/node-version-check.js +8 -37
- package/dist/bin/node-version-check.js.map +1 -1
- package/dist/db/index.d.ts +36 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +92 -6
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +15 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +42 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +177 -10
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +6 -1
- package/dist/db/sqlite-adapter.d.ts +8 -3
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +45 -3
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/extraction/extraction-version.d.ts +1 -1
- package/dist/extraction/extraction-version.js +1 -1
- package/dist/extraction/generated-detection.d.ts +42 -11
- package/dist/extraction/generated-detection.d.ts.map +1 -1
- package/dist/extraction/generated-detection.js +141 -20
- package/dist/extraction/generated-detection.js.map +1 -1
- package/dist/extraction/index.d.ts +42 -0
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +96 -0
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +19 -2
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +460 -110
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
- package/dist/extraction/wasm-runtime-flags.js +9 -6
- package/dist/extraction/wasm-runtime-flags.js.map +1 -1
- package/dist/graph-sources.d.ts +41 -0
- package/dist/graph-sources.d.ts.map +1 -0
- package/dist/graph-sources.js +82 -0
- package/dist/graph-sources.js.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -9
- package/dist/index.js.map +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +0 -48
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/targets/antigravity.js +1 -1
- package/dist/installer/targets/antigravity.js.map +1 -1
- package/dist/installer/targets/deveco.js +1 -1
- package/dist/installer/targets/deveco.js.map +1 -1
- package/dist/installer/targets/hermes.d.ts +1 -1
- package/dist/installer/targets/hermes.js +2 -2
- package/dist/installer/targets/hermes.js.map +1 -1
- package/dist/installer/targets/opencode.js +1 -1
- package/dist/installer/targets/opencode.js.map +1 -1
- package/dist/installer/targets/shared.js +1 -1
- package/dist/installer/targets/shared.js.map +1 -1
- package/dist/mcp/daemon-paths.d.ts +5 -4
- package/dist/mcp/daemon-paths.d.ts.map +1 -1
- package/dist/mcp/daemon-paths.js +28 -13
- package/dist/mcp/daemon-paths.js.map +1 -1
- package/dist/mcp/daemon-registry.d.ts.map +1 -1
- package/dist/mcp/daemon-registry.js +4 -1
- package/dist/mcp/daemon-registry.js.map +1 -1
- package/dist/mcp/daemon.d.ts +1 -1
- package/dist/mcp/daemon.js +1 -1
- package/dist/mcp/engine.d.ts.map +1 -1
- package/dist/mcp/engine.js +12 -3
- package/dist/mcp/engine.js.map +1 -1
- package/dist/mcp/explore-dedup.d.ts +137 -0
- package/dist/mcp/explore-dedup.d.ts.map +1 -0
- package/dist/mcp/explore-dedup.js +236 -0
- package/dist/mcp/explore-dedup.js.map +1 -0
- package/dist/mcp/explore-session-state.d.ts +217 -0
- package/dist/mcp/explore-session-state.d.ts.map +1 -0
- package/dist/mcp/explore-session-state.js +322 -0
- package/dist/mcp/explore-session-state.js.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +4 -7
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/proxy.d.ts.map +1 -1
- package/dist/mcp/proxy.js +5 -14
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/query-worker.d.ts +1 -1
- package/dist/mcp/query-worker.js +1 -1
- package/dist/mcp/server-instructions.d.ts +7 -2
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +104 -28
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/session.d.ts +14 -3
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +17 -14
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/startup-handshake.d.ts +1 -1
- package/dist/mcp/startup-handshake.js +1 -1
- package/dist/mcp/tools.d.ts +117 -7
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +3056 -503
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resolution/frameworks/arkts-napi.d.ts +14 -0
- package/dist/resolution/frameworks/arkts-napi.d.ts.map +1 -1
- package/dist/resolution/frameworks/arkts-napi.js +169 -49
- package/dist/resolution/frameworks/arkts-napi.js.map +1 -1
- package/dist/search/query-utils.d.ts +116 -4
- package/dist/search/query-utils.d.ts.map +1 -1
- package/dist/search/query-utils.js +869 -60
- package/dist/search/query-utils.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/upgrade/index.d.ts +14 -26
- package/dist/upgrade/index.d.ts.map +1 -1
- package/dist/upgrade/index.js +28 -123
- package/dist/upgrade/index.js.map +1 -1
- package/dist/upgrade/remove-binary.d.ts +19 -19
- package/dist/upgrade/remove-binary.d.ts.map +1 -1
- package/dist/upgrade/remove-binary.js +20 -21
- package/dist/upgrade/remove-binary.js.map +1 -1
- package/dist/upgrade/update-check.d.ts +4 -3
- package/dist/upgrade/update-check.d.ts.map +1 -1
- package/dist/upgrade/update-check.js +4 -3
- package/dist/upgrade/update-check.js.map +1 -1
- package/package.json +12 -8
- package/dist/telemetry/index.d.ts +0 -143
- package/dist/telemetry/index.d.ts.map +0 -1
- package/dist/telemetry/index.js +0 -541
- package/dist/telemetry/index.js.map +0 -1
|
@@ -80,6 +80,8 @@ exports.resetArkTSBatch = resetArkTSBatch;
|
|
|
80
80
|
exports.shrinkArkTSBatchPostParse = shrinkArkTSBatchPostParse;
|
|
81
81
|
exports.collectExportedRtaEntryPointsFromFiles = collectExportedRtaEntryPointsFromFiles;
|
|
82
82
|
exports.stateDecoratorKinds = stateDecoratorKinds;
|
|
83
|
+
exports.fieldDecoratorEntries = fieldDecoratorEntries;
|
|
84
|
+
exports.fieldDecoratorsEncoded = fieldDecoratorsEncoded;
|
|
83
85
|
exports.stateTransferViaForField = stateTransferViaForField;
|
|
84
86
|
exports.isUnclearArkClassSignature = isUnclearArkClassSignature;
|
|
85
87
|
exports.isUnclearArkMethodSignature = isUnclearArkMethodSignature;
|
|
@@ -105,6 +107,7 @@ exports.readOhosSdkPlatformVersion = readOhosSdkPlatformVersion;
|
|
|
105
107
|
exports.discoverLocalOhosSdkCandidates = discoverLocalOhosSdkCandidates;
|
|
106
108
|
exports.findLocalOhosSdkForVersion = findLocalOhosSdkForVersion;
|
|
107
109
|
exports.resolveOhosApiVersionForProject = resolveOhosApiVersionForProject;
|
|
110
|
+
exports.tryDownloadOhosApiDbFromNpm = tryDownloadOhosApiDbFromNpm;
|
|
108
111
|
exports.ensureOhosApiDb = ensureOhosApiDb;
|
|
109
112
|
exports.attachExistingOhosApiDbForProject = attachExistingOhosApiDbForProject;
|
|
110
113
|
exports.bindOhosApiDbForProject = bindOhosApiDbForProject;
|
|
@@ -122,9 +125,12 @@ const directory_1 = require("../../directory");
|
|
|
122
125
|
const project_config_1 = require("../../project-config");
|
|
123
126
|
const context_1 = require("../context");
|
|
124
127
|
const default_ignore_1 = require("../default-ignore");
|
|
128
|
+
const generated_detection_1 = require("../generated-detection");
|
|
125
129
|
const extraction_version_1 = require("../extraction-version");
|
|
126
130
|
const version_1 = require("../../mcp/version");
|
|
127
131
|
const liveness_watchdog_1 = require("../../mcp/liveness-watchdog");
|
|
132
|
+
const migrate_semantics_1 = require("../../arkui/migrate-semantics");
|
|
133
|
+
const migrate_passage_1 = require("../../arkui/migrate-passage");
|
|
128
134
|
const tree_sitter_helpers_1 = require("../tree-sitter-helpers");
|
|
129
135
|
const wasm_runtime_flags_1 = require("../wasm-runtime-flags");
|
|
130
136
|
const ignore_1 = __importDefault(require("ignore"));
|
|
@@ -480,11 +486,16 @@ function hasHarmonyProjectMarkers(rootDir) {
|
|
|
480
486
|
function normIndexPath(filePath) {
|
|
481
487
|
return filePath.replace(/\\/g, '/');
|
|
482
488
|
}
|
|
483
|
-
/**
|
|
489
|
+
/** Photos-style member calls: `sdk.Asset_setCropRect(`. */
|
|
484
490
|
const NAPI_MEMBER_CALL_RE = /\.\s*([A-Z][A-Za-z0-9_]*(?:_[A-Za-z0-9_]+)+)\s*\(/g;
|
|
491
|
+
/** `libFoo.so` module path from ArkAnalyzer import infos. */
|
|
492
|
+
const LIB_SO_MODULE_RE = /^lib[A-Za-z0-9_]+\.so$/;
|
|
485
493
|
function isNapiSymbolName(name) {
|
|
486
494
|
return /^[A-Z][A-Za-z0-9_]*_[A-Za-z0-9_]+$/.test(name);
|
|
487
495
|
}
|
|
496
|
+
function escapeRegExp(s) {
|
|
497
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
498
|
+
}
|
|
488
499
|
let persistedBatch = null;
|
|
489
500
|
/** File that triggered the most recent batch persist (for indexAll edge stats). */
|
|
490
501
|
let batchTriggerFile = null;
|
|
@@ -635,6 +646,7 @@ function persistFileResult(queries, filePath, content, stats, result, options) {
|
|
|
635
646
|
indexedAt: Date.now(),
|
|
636
647
|
nodeCount: result.nodes.length,
|
|
637
648
|
errors: result.errors.length > 0 ? result.errors : undefined,
|
|
649
|
+
generated: (0, generated_detection_1.detectGeneratedFile)(filePath, content),
|
|
638
650
|
};
|
|
639
651
|
queries.upsertFile(fileRecord);
|
|
640
652
|
}
|
|
@@ -888,7 +900,9 @@ function runArkTSBatchFullCore(rootDir, queries, triggerFile, etsFiles, batchKey
|
|
|
888
900
|
const index = buildArkTSIndex(rootDir, etsFiles);
|
|
889
901
|
if (index.fileResults.size === 0) {
|
|
890
902
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
891
|
-
|
|
903
|
+
const detail = index.errors.map((e) => `[${e.severity}] ${e.message}`).join(' | ');
|
|
904
|
+
throw new Error(fatal?.message ??
|
|
905
|
+
`ArkTS batch produced no indexed files${detail ? ` (${detail})` : ' (no Scene/adapter errors recorded)'}`);
|
|
892
906
|
}
|
|
893
907
|
queries.deleteArkTSCrossFileCallEdges();
|
|
894
908
|
persistBatchResultsSync(rootDir, queries, index);
|
|
@@ -1025,7 +1039,9 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
1025
1039
|
const streamed = index.streamedPersistedPaths?.size ?? 0;
|
|
1026
1040
|
if (index.fileResults.size === 0 && streamed === 0) {
|
|
1027
1041
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
1028
|
-
|
|
1042
|
+
const detail = index.errors.map((e) => `[${e.severity}] ${e.message}`).join(' | ');
|
|
1043
|
+
throw new Error(fatal?.message ??
|
|
1044
|
+
`ArkTS batch produced no indexed files${detail ? ` (${detail})` : ' (no Scene/adapter errors recorded)'}`);
|
|
1029
1045
|
}
|
|
1030
1046
|
if (!streamModular) {
|
|
1031
1047
|
queries.deleteArkTSCrossFileCallEdges();
|
|
@@ -1198,8 +1214,33 @@ const FIELD_CATEGORY = {
|
|
|
1198
1214
|
};
|
|
1199
1215
|
/** {@link ModifierType.CONST} — not re-exported from the arkanalyzer npm entry. */
|
|
1200
1216
|
const MODIFIER_CONST = 128;
|
|
1217
|
+
/**
|
|
1218
|
+
* Project-relative path for an ArkAnalyzer absolute file path.
|
|
1219
|
+
*
|
|
1220
|
+
* ArkAnalyzer returns realpath'd absolutes (macOS: `/private/var/...`), while
|
|
1221
|
+
* HomeGraph's `rootDir` from `mkdtemp` / callers is often the unresolved form
|
|
1222
|
+
* (`/var/...`). Without canonicalizing both sides, `path.relative` escapes the
|
|
1223
|
+
* project (`../../../../private/var/.../file.ets`) and every file is skipped
|
|
1224
|
+
* by the `scanned` set → empty batch / "no indexed files".
|
|
1225
|
+
*/
|
|
1201
1226
|
function normalizeRelPath(rootDir, absolutePath) {
|
|
1202
|
-
|
|
1227
|
+
const root = realpathExisting(rootDir);
|
|
1228
|
+
const abs = realpathExisting(absolutePath);
|
|
1229
|
+
return path.relative(root, abs).replace(/\\/g, '/');
|
|
1230
|
+
}
|
|
1231
|
+
/** `realpathSync` when the path (or its dirname) exists; otherwise `path.resolve`. */
|
|
1232
|
+
function realpathExisting(p) {
|
|
1233
|
+
try {
|
|
1234
|
+
return fs.realpathSync(p);
|
|
1235
|
+
}
|
|
1236
|
+
catch {
|
|
1237
|
+
try {
|
|
1238
|
+
return path.join(fs.realpathSync(path.dirname(p)), path.basename(p));
|
|
1239
|
+
}
|
|
1240
|
+
catch {
|
|
1241
|
+
return path.resolve(p);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1203
1244
|
}
|
|
1204
1245
|
/** Map ArkAnalyzer synthetic files (e.g. @dummyFile) to our virtual indexed path. */
|
|
1205
1246
|
function resolveArkanalyzerVirtualPath(arkFile) {
|
|
@@ -1655,6 +1696,52 @@ function stateDecoratorKinds(field) {
|
|
|
1655
1696
|
return [];
|
|
1656
1697
|
return [...decs].map((d) => d.getKind()).filter(Boolean);
|
|
1657
1698
|
}
|
|
1699
|
+
/** All field decorators with optional string args (Provide('theme') → theme). */
|
|
1700
|
+
function fieldDecoratorEntries(field) {
|
|
1701
|
+
const out = [];
|
|
1702
|
+
const seen = new Set();
|
|
1703
|
+
const push = (kind, arg) => {
|
|
1704
|
+
if (!kind || seen.has(kind))
|
|
1705
|
+
return;
|
|
1706
|
+
seen.add(kind);
|
|
1707
|
+
out.push(arg ? { kind, arg } : { kind });
|
|
1708
|
+
};
|
|
1709
|
+
try {
|
|
1710
|
+
for (const d of field.getDecorators?.() ?? []) {
|
|
1711
|
+
push(d.getKind(), (0, migrate_semantics_1.parseDecoratorArgFromContent)(d.getContent?.() ?? ''));
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
catch {
|
|
1715
|
+
/* ignore */
|
|
1716
|
+
}
|
|
1717
|
+
if (out.length === 0) {
|
|
1718
|
+
for (const kind of stateDecoratorKinds(field))
|
|
1719
|
+
push(kind);
|
|
1720
|
+
}
|
|
1721
|
+
return out;
|
|
1722
|
+
}
|
|
1723
|
+
/** Encoded decorators list for node.decorators (bare kinds + Kind@arg). */
|
|
1724
|
+
function fieldDecoratorsEncoded(field) {
|
|
1725
|
+
return (0, migrate_semantics_1.encodeDecoratorEntries)(fieldDecoratorEntries(field));
|
|
1726
|
+
}
|
|
1727
|
+
function modelDecoratorEntries(model) {
|
|
1728
|
+
const out = [];
|
|
1729
|
+
const seen = new Set();
|
|
1730
|
+
try {
|
|
1731
|
+
for (const d of model.getDecorators?.() ?? []) {
|
|
1732
|
+
const kind = d.getKind();
|
|
1733
|
+
if (!kind || seen.has(kind))
|
|
1734
|
+
continue;
|
|
1735
|
+
seen.add(kind);
|
|
1736
|
+
const arg = (0, migrate_semantics_1.parseDecoratorArgFromContent)(d.getContent?.() ?? '');
|
|
1737
|
+
out.push(arg ? { kind, arg } : { kind });
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
catch {
|
|
1741
|
+
/* ignore */
|
|
1742
|
+
}
|
|
1743
|
+
return out;
|
|
1744
|
+
}
|
|
1658
1745
|
/** Child @Prop / @Link — edge `via` uses the decorator name as-is. */
|
|
1659
1746
|
function stateTransferViaForField(field) {
|
|
1660
1747
|
for (const kind of stateDecoratorKinds(field)) {
|
|
@@ -2001,12 +2088,12 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
2001
2088
|
if (!buildId)
|
|
2002
2089
|
return;
|
|
2003
2090
|
const seen = new Set();
|
|
2004
|
-
const link = (source, target, kind, via, line) => {
|
|
2091
|
+
const link = (source, target, kind, via, line, extraMeta) => {
|
|
2005
2092
|
const key = `${source}>${target}>${kind}>${via}`;
|
|
2006
2093
|
if (seen.has(key))
|
|
2007
2094
|
return;
|
|
2008
2095
|
seen.add(key);
|
|
2009
|
-
ctx.addEdge(result, source, target, kind, relativePath, via, line);
|
|
2096
|
+
ctx.addEdge(result, source, target, kind, relativePath, via, line, extraMeta);
|
|
2010
2097
|
};
|
|
2011
2098
|
for (const [field] of viewTree.getStateValues()) {
|
|
2012
2099
|
const fieldId = ctx.resolveFieldNodeId(field);
|
|
@@ -2074,32 +2161,62 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
2074
2161
|
// Skip this node's signature edge; keep walking siblings/children.
|
|
2075
2162
|
}
|
|
2076
2163
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2164
|
+
// Data passages for migrate snapshot (+ Prop/Link edges for explore).
|
|
2165
|
+
try {
|
|
2166
|
+
const passages = (0, migrate_passage_1.collectPassagesForViewTreeNode)(node, ctx.scene, cls);
|
|
2167
|
+
for (const p of passages) {
|
|
2168
|
+
const toId = ctx.resolveFieldNodeId(p.toField);
|
|
2169
|
+
if (!toId)
|
|
2170
|
+
continue;
|
|
2171
|
+
ctx.markFieldStateDecorators?.(p.toField, toId, result);
|
|
2172
|
+
let fromId = null;
|
|
2173
|
+
if (p.fromField) {
|
|
2174
|
+
fromId = ctx.resolveFieldNodeId(p.fromField);
|
|
2175
|
+
if (fromId)
|
|
2176
|
+
ctx.markFieldStateDecorators?.(p.fromField, fromId, result);
|
|
2090
2177
|
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2178
|
+
if (!fromId && p.fromIsParentComponent)
|
|
2179
|
+
fromId = classNodeId;
|
|
2180
|
+
if (!fromId)
|
|
2181
|
+
continue;
|
|
2182
|
+
const line = p.toField.getOriginFullPosition()?.getFirstLine() ?? 1;
|
|
2183
|
+
link(fromId, toId, 'references', p.via || 'data-passage', line, {
|
|
2184
|
+
passageType: p.passageType,
|
|
2185
|
+
valueType: p.valueType,
|
|
2186
|
+
forcesMigration: p.forcesMigration,
|
|
2187
|
+
parentExpression: p.parentExpression,
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
catch {
|
|
2192
|
+
// Fall back to legacy Prop/Link-only transfer below.
|
|
2193
|
+
if (node.stateValuesTransfer) {
|
|
2194
|
+
for (const [childField, parentValue] of node.stateValuesTransfer) {
|
|
2195
|
+
const childFieldId = ctx.resolveFieldNodeId(childField);
|
|
2196
|
+
if (parentValue instanceof arkanalyzer_1.ArkField) {
|
|
2197
|
+
const via = stateTransferViaForField(childField);
|
|
2198
|
+
if (!via || !childFieldId)
|
|
2199
|
+
continue;
|
|
2200
|
+
ctx.markFieldStateDecorators?.(childField, childFieldId, result);
|
|
2201
|
+
const parentFieldId = ctx.resolveFieldNodeId(parentValue);
|
|
2202
|
+
if (parentFieldId) {
|
|
2203
|
+
ctx.markFieldStateDecorators?.(parentValue, parentFieldId, result);
|
|
2204
|
+
link(parentFieldId, childFieldId, 'references', via, childField.getOriginFullPosition()?.getFirstLine() ?? 1);
|
|
2099
2205
|
}
|
|
2100
2206
|
}
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2207
|
+
else if (parentValue instanceof arkanalyzer_1.ArkMethod) {
|
|
2208
|
+
let builderId = ctx.ensureMethodNode(parentValue, relativePath, result, classNodeId);
|
|
2209
|
+
if (!builderId) {
|
|
2210
|
+
try {
|
|
2211
|
+
builderId = ctx.resolveMethodIdBySig(parentValue.getSignature());
|
|
2212
|
+
}
|
|
2213
|
+
catch {
|
|
2214
|
+
builderId = null;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
if (childFieldId && builderId) {
|
|
2218
|
+
link(builderId, childFieldId, 'references', 'builder-param', childField.getOriginFullPosition()?.getFirstLine() ?? 1);
|
|
2219
|
+
}
|
|
2103
2220
|
}
|
|
2104
2221
|
}
|
|
2105
2222
|
}
|
|
@@ -2155,6 +2272,12 @@ class ArkTSAdapter {
|
|
|
2155
2272
|
crossFileEdges = [];
|
|
2156
2273
|
/** Per-module `@dummyMain@…` created during analyseByModule (for scene aggregator). */
|
|
2157
2274
|
moduleDummyMains = [];
|
|
2275
|
+
/**
|
|
2276
|
+
* Local import binding names from `lib*.so` per relative file path.
|
|
2277
|
+
* Filled before method bodies are scanned so `multimodalinput.getTidByName(`
|
|
2278
|
+
* can emit NAPI call refs (not only photos-style `Class_method` names).
|
|
2279
|
+
*/
|
|
2280
|
+
soImportBindingsByFile = new Map();
|
|
2158
2281
|
constructor(rootDir, scannedFiles) {
|
|
2159
2282
|
this.rootDir = rootDir;
|
|
2160
2283
|
this.scanned = new Set(scannedFiles);
|
|
@@ -2688,6 +2811,70 @@ class ArkTSAdapter {
|
|
|
2688
2811
|
}
|
|
2689
2812
|
}
|
|
2690
2813
|
}
|
|
2814
|
+
this.indexObservedClassRefs();
|
|
2815
|
+
}
|
|
2816
|
+
/**
|
|
2817
|
+
* State / Param fields whose type names an @Observed / @ObservedV2 class →
|
|
2818
|
+
* `references` via `observed-ref` (arkui-migrate).
|
|
2819
|
+
*/
|
|
2820
|
+
indexObservedClassRefs() {
|
|
2821
|
+
const observedByName = new Map();
|
|
2822
|
+
for (const [, result] of this.fileResults) {
|
|
2823
|
+
for (const n of result.nodes) {
|
|
2824
|
+
if (n.kind !== 'class' && n.kind !== 'struct')
|
|
2825
|
+
continue;
|
|
2826
|
+
const decs = n.decorators ?? [];
|
|
2827
|
+
if (!decs.some((d) => migrate_semantics_1.OBSERVATION_DECORATORS.has(d.split('@')[0])))
|
|
2828
|
+
continue;
|
|
2829
|
+
const arr = observedByName.get(n.name) ?? [];
|
|
2830
|
+
arr.push(n.id);
|
|
2831
|
+
observedByName.set(n.name, arr);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
if (observedByName.size === 0)
|
|
2835
|
+
return;
|
|
2836
|
+
for (const [relativePath, result] of this.fileResults) {
|
|
2837
|
+
for (const field of result.nodes) {
|
|
2838
|
+
if (field.kind !== 'property' && field.kind !== 'field')
|
|
2839
|
+
continue;
|
|
2840
|
+
const typeStr = field.signature ?? '';
|
|
2841
|
+
const bare = typeStr.replace(/<.*>/, '').split(/[.\s|]/).filter(Boolean).pop();
|
|
2842
|
+
if (!bare)
|
|
2843
|
+
continue;
|
|
2844
|
+
const targets = observedByName.get(bare);
|
|
2845
|
+
if (!targets || targets.length === 0)
|
|
2846
|
+
continue;
|
|
2847
|
+
// Prefer unique name; if ambiguous, same-file first
|
|
2848
|
+
let targetId = targets[0];
|
|
2849
|
+
if (targets.length > 1) {
|
|
2850
|
+
const sameFile = targets.find((id) => {
|
|
2851
|
+
const n = result.nodes.find((x) => x.id === id);
|
|
2852
|
+
return n?.filePath === relativePath;
|
|
2853
|
+
});
|
|
2854
|
+
if (sameFile)
|
|
2855
|
+
targetId = sameFile;
|
|
2856
|
+
else
|
|
2857
|
+
continue; // ambiguous cross-file — skip (precision > recall)
|
|
2858
|
+
}
|
|
2859
|
+
const edgeKey = `${field.id}\0${targetId}\0observed-ref`;
|
|
2860
|
+
if (this.emittedEdgeKeys.has(edgeKey))
|
|
2861
|
+
continue;
|
|
2862
|
+
this.emittedEdgeKeys.add(edgeKey);
|
|
2863
|
+
const edge = arkEdge(field.id, targetId, 'references', {
|
|
2864
|
+
metadata: {
|
|
2865
|
+
synthesizedBy: 'arkui-migrate',
|
|
2866
|
+
via: 'observed-ref',
|
|
2867
|
+
registeredAt: `${relativePath}:${field.startLine}`,
|
|
2868
|
+
},
|
|
2869
|
+
});
|
|
2870
|
+
if (this.nodeInFile(targetId, relativePath)) {
|
|
2871
|
+
result.edges.push(edge);
|
|
2872
|
+
}
|
|
2873
|
+
else {
|
|
2874
|
+
this.crossFileEdges.push(edge);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2691
2878
|
}
|
|
2692
2879
|
/** Index ArkAnalyzer's in-scene @dummyMain (DummyMainCreater output), not a HomeGraph synthetic node. */
|
|
2693
2880
|
indexArkanalyzerDummyMain(dummyMain) {
|
|
@@ -2737,6 +2924,8 @@ class ArkTSAdapter {
|
|
|
2737
2924
|
}
|
|
2738
2925
|
const result = this.ensureFileResult(relativePath, lineCount);
|
|
2739
2926
|
const fileId = `file:${relativePath}`;
|
|
2927
|
+
// Before methods: record `lib*.so` bindings so body NAPI call scan can see them.
|
|
2928
|
+
this.collectSoImportBindings(relativePath, arkFile);
|
|
2740
2929
|
for (const ns of arkFile.getNamespaces()) {
|
|
2741
2930
|
this.indexNamespace(relativePath, language, result, ns, fileId);
|
|
2742
2931
|
}
|
|
@@ -2745,6 +2934,59 @@ class ArkTSAdapter {
|
|
|
2745
2934
|
this.indexTypeAliases(relativePath, language, result, arkFile, undefined, fileId);
|
|
2746
2935
|
this.indexModuleLocals(relativePath, language, result, arkFile, undefined, fileId);
|
|
2747
2936
|
this.indexImports(relativePath, language, result, arkFile, fileId);
|
|
2937
|
+
this.indexStorageApiKeys(relativePath, arkFile, result);
|
|
2938
|
+
}
|
|
2939
|
+
/** AppStorage / LocalStorage / … literal keys → arkui-migrate edges from each component. */
|
|
2940
|
+
indexStorageApiKeys(relativePath, arkFile, result) {
|
|
2941
|
+
let code = '';
|
|
2942
|
+
try {
|
|
2943
|
+
code = arkFile.getCode() ?? '';
|
|
2944
|
+
}
|
|
2945
|
+
catch {
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
if (!code)
|
|
2949
|
+
return;
|
|
2950
|
+
const hits = (0, migrate_semantics_1.scanStorageApiKeys)(code);
|
|
2951
|
+
if (hits.length === 0)
|
|
2952
|
+
return;
|
|
2953
|
+
const componentIds = result.nodes
|
|
2954
|
+
.filter((n) => n.kind === 'component' && n.filePath === relativePath)
|
|
2955
|
+
.map((n) => n.id);
|
|
2956
|
+
if (componentIds.length === 0)
|
|
2957
|
+
return;
|
|
2958
|
+
for (const hit of hits) {
|
|
2959
|
+
const line = (0, migrate_semantics_1.lineOfOffset)(code, hit.index);
|
|
2960
|
+
for (const compId of componentIds) {
|
|
2961
|
+
const edgeKey = `${compId}\0storage-api\0${hit.channel}\0${hit.key}`;
|
|
2962
|
+
if (this.emittedEdgeKeys.has(edgeKey))
|
|
2963
|
+
continue;
|
|
2964
|
+
this.emittedEdgeKeys.add(edgeKey);
|
|
2965
|
+
result.edges.push(arkEdge(compId, compId, 'references', {
|
|
2966
|
+
metadata: {
|
|
2967
|
+
synthesizedBy: 'arkui-migrate',
|
|
2968
|
+
via: 'storage-api',
|
|
2969
|
+
channel: hit.channel,
|
|
2970
|
+
key: hit.key,
|
|
2971
|
+
method: hit.method,
|
|
2972
|
+
registeredAt: `${relativePath}:${line}`,
|
|
2973
|
+
},
|
|
2974
|
+
}));
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
collectSoImportBindings(relativePath, arkFile) {
|
|
2979
|
+
const set = new Set();
|
|
2980
|
+
for (const importInfo of arkFile.getImportInfos()) {
|
|
2981
|
+
const modulePath = importInfo.getFrom();
|
|
2982
|
+
if (!modulePath || !LIB_SO_MODULE_RE.test(modulePath))
|
|
2983
|
+
continue;
|
|
2984
|
+
const clause = importInfo.getImportClauseName();
|
|
2985
|
+
if (clause)
|
|
2986
|
+
set.add(clause);
|
|
2987
|
+
}
|
|
2988
|
+
if (set.size > 0)
|
|
2989
|
+
this.soImportBindingsByFile.set(relativePath, set);
|
|
2748
2990
|
}
|
|
2749
2991
|
indexModuleLocals(relativePath, language, result, arkFile, ns, parentId) {
|
|
2750
2992
|
const declaringClass = ns ? ns.getDefaultClass() : arkFile.getDefaultClass();
|
|
@@ -2838,8 +3080,10 @@ class ArkTSAdapter {
|
|
|
2838
3080
|
const qn = buildClassQualifiedName(cls);
|
|
2839
3081
|
const kind = classNodeKind(cls);
|
|
2840
3082
|
const { line, endLine, col } = linesFromPosition(cls.getOriginFullPosition());
|
|
3083
|
+
const classDecs = (0, migrate_semantics_1.encodeDecoratorEntries)(modelDecoratorEntries(cls));
|
|
2841
3084
|
const classNode = makeNode(relativePath, language, kind, displayName, qn, line, endLine, col, {
|
|
2842
3085
|
...modelModifiersToNodeExtras(cls),
|
|
3086
|
+
...(classDecs.length > 0 ? { decorators: classDecs } : {}),
|
|
2843
3087
|
});
|
|
2844
3088
|
this.addNode(result, classNode);
|
|
2845
3089
|
this.classToId.set(cls, classNode.id);
|
|
@@ -2867,7 +3111,10 @@ class ArkTSAdapter {
|
|
|
2867
3111
|
}
|
|
2868
3112
|
}
|
|
2869
3113
|
if (cls.hasComponentDecorator()) {
|
|
2870
|
-
const componentNode = makeNode(relativePath, language, 'component', displayName, qn, line, endLine, col, {
|
|
3114
|
+
const componentNode = makeNode(relativePath, language, 'component', displayName, qn, line, endLine, col, {
|
|
3115
|
+
...modelModifiersToNodeExtras(cls),
|
|
3116
|
+
...(classDecs.length > 0 ? { decorators: classDecs } : {}),
|
|
3117
|
+
});
|
|
2871
3118
|
this.addNode(result, componentNode);
|
|
2872
3119
|
this.componentToId.set(cls, componentNode.id);
|
|
2873
3120
|
try {
|
|
@@ -2953,10 +3200,10 @@ class ArkTSAdapter {
|
|
|
2953
3200
|
return;
|
|
2954
3201
|
const { line, endLine, col } = linesFromPosition(field.getOriginFullPosition());
|
|
2955
3202
|
const qn = buildFieldQualifiedName(field);
|
|
2956
|
-
const
|
|
3203
|
+
const encodedDecs = fieldDecoratorsEncoded(field);
|
|
2957
3204
|
const fieldNode = makeNode(relativePath, language, kind, name, qn, line, endLine, col, {
|
|
2958
3205
|
signature: field.getType()?.toString(),
|
|
2959
|
-
...(
|
|
3206
|
+
...(encodedDecs.length > 0 ? { decorators: encodedDecs } : {}),
|
|
2960
3207
|
...modelModifiersToNodeExtras(field),
|
|
2961
3208
|
});
|
|
2962
3209
|
this.addNode(result, fieldNode);
|
|
@@ -2982,19 +3229,19 @@ class ArkTSAdapter {
|
|
|
2982
3229
|
classToId: this.classToId,
|
|
2983
3230
|
fieldToId: this.fieldToId,
|
|
2984
3231
|
nodeIds: this.nodeIds,
|
|
2985
|
-
addEdge: (result, source, target, kind, callerFile, via, line) => this.addViewTreeEdge(result, source, target, kind, callerFile, via, line),
|
|
3232
|
+
addEdge: (result, source, target, kind, callerFile, via, line, extraMeta) => this.addViewTreeEdge(result, source, target, kind, callerFile, via, line, extraMeta),
|
|
2986
3233
|
ensureMethodNode: (method, relativePath, result, parentId) => this.ensureMethodNode(method, relativePath, result, parentId),
|
|
2987
3234
|
resolveClassNodeId: (cls) => this.resolveClassNodeId(cls),
|
|
2988
3235
|
resolveClassIdBySig: (sig, importer) => this.resolveClassIdBySig(sig, importer),
|
|
2989
3236
|
resolveMethodIdBySig: (sig, importer) => this.resolveMethodIdBySig(sig, importer),
|
|
2990
3237
|
resolveFieldNodeId: (field) => this.resolveFieldNodeId(field),
|
|
2991
3238
|
markFieldStateDecorators: (field, fieldNodeId, result) => {
|
|
2992
|
-
const
|
|
2993
|
-
if (
|
|
3239
|
+
const encoded = fieldDecoratorsEncoded(field);
|
|
3240
|
+
if (encoded.length === 0)
|
|
2994
3241
|
return;
|
|
2995
3242
|
const node = result.nodes.find((n) => n.id === fieldNodeId);
|
|
2996
3243
|
if (node)
|
|
2997
|
-
node.decorators =
|
|
3244
|
+
node.decorators = encoded;
|
|
2998
3245
|
},
|
|
2999
3246
|
};
|
|
3000
3247
|
}
|
|
@@ -3218,7 +3465,7 @@ class ArkTSAdapter {
|
|
|
3218
3465
|
this.indexMethod(relativePath, 'arkts', result, method, parentId, kind, displayName);
|
|
3219
3466
|
return this.methodToId.get(method) ?? null;
|
|
3220
3467
|
}
|
|
3221
|
-
addViewTreeEdge(result, source, target, kind, callerFile, via, line) {
|
|
3468
|
+
addViewTreeEdge(result, source, target, kind, callerFile, via, line, extraMeta) {
|
|
3222
3469
|
const edgeKey = `${source}\0${target}\0${kind}\0${via}`;
|
|
3223
3470
|
if (this.emittedEdgeKeys.has(edgeKey))
|
|
3224
3471
|
return;
|
|
@@ -3228,6 +3475,7 @@ class ArkTSAdapter {
|
|
|
3228
3475
|
synthesizedBy: 'viewtree',
|
|
3229
3476
|
via,
|
|
3230
3477
|
registeredAt: `${callerFile}:${line}`,
|
|
3478
|
+
...(extraMeta ?? {}),
|
|
3231
3479
|
},
|
|
3232
3480
|
});
|
|
3233
3481
|
if (this.nodeInFile(target, callerFile)) {
|
|
@@ -3305,17 +3553,14 @@ class ArkTSAdapter {
|
|
|
3305
3553
|
return;
|
|
3306
3554
|
const baseLine = method.getImplOriginFullPosition()?.getFirstLine() ?? 1;
|
|
3307
3555
|
const seenAtLine = new Set();
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
const
|
|
3312
|
-
if (!isNapiSymbolName(symbol))
|
|
3313
|
-
continue;
|
|
3314
|
-
const lineOffset = code.slice(0, m.index).split('\n').length - 1;
|
|
3556
|
+
const pushCall = (symbol, matchIndex) => {
|
|
3557
|
+
if (!symbol)
|
|
3558
|
+
return;
|
|
3559
|
+
const lineOffset = code.slice(0, matchIndex).split('\n').length - 1;
|
|
3315
3560
|
const line = baseLine + lineOffset;
|
|
3316
3561
|
const dedupKey = `${symbol}:${line}`;
|
|
3317
3562
|
if (seenAtLine.has(dedupKey))
|
|
3318
|
-
|
|
3563
|
+
return;
|
|
3319
3564
|
seenAtLine.add(dedupKey);
|
|
3320
3565
|
result.unresolvedReferences.push({
|
|
3321
3566
|
fromNodeId,
|
|
@@ -3326,6 +3571,35 @@ class ArkTSAdapter {
|
|
|
3326
3571
|
filePath: relativePath,
|
|
3327
3572
|
language,
|
|
3328
3573
|
});
|
|
3574
|
+
};
|
|
3575
|
+
// Photos-style `Class_method` on any receiver.
|
|
3576
|
+
NAPI_MEMBER_CALL_RE.lastIndex = 0;
|
|
3577
|
+
let m;
|
|
3578
|
+
while ((m = NAPI_MEMBER_CALL_RE.exec(code)) !== null) {
|
|
3579
|
+
const symbol = m[1];
|
|
3580
|
+
if (!isNapiSymbolName(symbol))
|
|
3581
|
+
continue;
|
|
3582
|
+
pushCall(symbol, m.index);
|
|
3583
|
+
}
|
|
3584
|
+
// `import x from 'libFoo.so'` → `x.draw(` / `x.getTidByName(` (camelCase OK).
|
|
3585
|
+
const soBindings = this.soImportBindingsByFile.get(relativePath);
|
|
3586
|
+
if (soBindings) {
|
|
3587
|
+
for (const binding of soBindings) {
|
|
3588
|
+
const re = new RegExp(`\\b${escapeRegExp(binding)}\\s*\\.\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(`, 'g');
|
|
3589
|
+
while ((m = re.exec(code)) !== null) {
|
|
3590
|
+
pushCall(m[1], m.index);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
// define_class / instance path: `this.inst?.addVerticalRuler(` — receiver is
|
|
3594
|
+
// not the import binding. Only in files that already import a lib*.so.
|
|
3595
|
+
const anyMemberRe = /\?\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(|\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
3596
|
+
while ((m = anyMemberRe.exec(code)) !== null) {
|
|
3597
|
+
const symbol = m[1] ?? m[2];
|
|
3598
|
+
if (!symbol)
|
|
3599
|
+
continue;
|
|
3600
|
+
// Skip photos-style names already handled above; keep camelCase / plain ids.
|
|
3601
|
+
pushCall(symbol, m.index);
|
|
3602
|
+
}
|
|
3329
3603
|
}
|
|
3330
3604
|
}
|
|
3331
3605
|
indexImports(relativePath, language, result, arkFile, fileId) {
|
|
@@ -4237,13 +4511,81 @@ function attachOhosApiDbBinding(queries, binding) {
|
|
|
4237
4511
|
};
|
|
4238
4512
|
}
|
|
4239
4513
|
}
|
|
4514
|
+
/** Last-resort prebuilt npm package when the project's API version cannot be obtained. */
|
|
4515
|
+
const OHOS_API_FALLBACK_VERSION = '6.1.0';
|
|
4516
|
+
function runNpmPack(args, cwd) {
|
|
4517
|
+
const inv = process.platform === 'win32'
|
|
4518
|
+
? { cmd: 'cmd.exe', args: ['/d', '/s', '/c', ['npm', ...args].join(' ')] }
|
|
4519
|
+
: { cmd: 'npm', args };
|
|
4520
|
+
(0, child_process_1.execFileSync)(inv.cmd, inv.args, {
|
|
4521
|
+
cwd,
|
|
4522
|
+
stdio: 'pipe',
|
|
4523
|
+
timeout: 180_000,
|
|
4524
|
+
windowsHide: true,
|
|
4525
|
+
env: process.env,
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4240
4528
|
/**
|
|
4241
|
-
*
|
|
4242
|
-
*
|
|
4529
|
+
* Fetch `homegraph-ohos-api-db-<version>` from npm and copy the db into
|
|
4530
|
+
* `~/.homegraph/api/`. Returns null when downloads are disabled, the package
|
|
4531
|
+
* is missing, or install fails. Opt out: HOMEGRAPH_OHOS_API_NO_DOWNLOAD=1.
|
|
4532
|
+
*/
|
|
4533
|
+
function tryDownloadOhosApiDbFromNpm(version) {
|
|
4534
|
+
if (process.env.HOMEGRAPH_OHOS_API_NO_DOWNLOAD === '1')
|
|
4535
|
+
return null;
|
|
4536
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
4537
|
+
const packageName = ohosApiDbPackageName(version);
|
|
4538
|
+
if (fs.existsSync(dbPath)) {
|
|
4539
|
+
return { version, dbPath, packageName, installed: false };
|
|
4540
|
+
}
|
|
4541
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'hg-ohos-api-npm-'));
|
|
4542
|
+
try {
|
|
4543
|
+
runNpmPack(['pack', packageName, '--silent'], tmp);
|
|
4544
|
+
const tgz = fs.readdirSync(tmp).find((f) => f.endsWith('.tgz'));
|
|
4545
|
+
if (!tgz)
|
|
4546
|
+
return null;
|
|
4547
|
+
const extractDir = path.join(tmp, 'extract');
|
|
4548
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
4549
|
+
(0, child_process_1.execFileSync)('tar', ['-xzf', path.join(tmp, tgz), '-C', extractDir], {
|
|
4550
|
+
stdio: 'pipe',
|
|
4551
|
+
timeout: 60_000,
|
|
4552
|
+
windowsHide: true,
|
|
4553
|
+
});
|
|
4554
|
+
const dbName = ohosApiDbFilename(version);
|
|
4555
|
+
const src = [
|
|
4556
|
+
path.join(extractDir, 'package', dbName),
|
|
4557
|
+
path.join(extractDir, dbName),
|
|
4558
|
+
].find((p) => fs.existsSync(p)) ?? null;
|
|
4559
|
+
if (!src)
|
|
4560
|
+
return null;
|
|
4561
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4562
|
+
fs.copyFileSync(src, dbPath);
|
|
4563
|
+
return { version, dbPath, packageName, installed: true };
|
|
4564
|
+
}
|
|
4565
|
+
catch {
|
|
4566
|
+
return null;
|
|
4567
|
+
}
|
|
4568
|
+
finally {
|
|
4569
|
+
try {
|
|
4570
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
4571
|
+
}
|
|
4572
|
+
catch {
|
|
4573
|
+
/* ignore */
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Build (or reuse) ~/.homegraph/api/ohos-api-<version>.db from a local SDK,
|
|
4579
|
+
* then npm prebuilts. Never throws — missing SDK / build / download failure
|
|
4580
|
+
* returns a warning.
|
|
4581
|
+
*
|
|
4582
|
+
* Order: existing db → local SDK build → npm pack for that version →
|
|
4583
|
+
* npm pack homegraph-ohos-api-db-6.1.0 fallback.
|
|
4243
4584
|
*/
|
|
4244
4585
|
async function ensureOhosApiDb(version, options = {}) {
|
|
4245
4586
|
const dbPath = ohosApiDbInstallPath(version);
|
|
4246
4587
|
const packageName = ohosApiDbPackageName(version);
|
|
4588
|
+
// 1. Local db for the requested version
|
|
4247
4589
|
if (fs.existsSync(dbPath)) {
|
|
4248
4590
|
return { version, dbPath, packageName, installed: false };
|
|
4249
4591
|
}
|
|
@@ -4255,6 +4597,7 @@ async function ensureOhosApiDb(version, options = {}) {
|
|
|
4255
4597
|
'Explore will use project code only.',
|
|
4256
4598
|
};
|
|
4257
4599
|
}
|
|
4600
|
+
// 2. Build from a local DevEco / OpenHarmony SDK
|
|
4258
4601
|
let sdkHome = null;
|
|
4259
4602
|
if (options.sdkHomeHint) {
|
|
4260
4603
|
const hintHomes = expandLocalOhosSdkHomes(options.sdkHomeHint);
|
|
@@ -4265,80 +4608,87 @@ async function ensureOhosApiDb(version, options = {}) {
|
|
|
4265
4608
|
if (!sdkHome) {
|
|
4266
4609
|
sdkHome = findLocalOhosSdkForVersion(version)?.sdkHome ?? null;
|
|
4267
4610
|
}
|
|
4268
|
-
if (
|
|
4269
|
-
|
|
4270
|
-
code: 'ohos_api_sdk_missing',
|
|
4271
|
-
message: `Local OHOS SDK for API ${version} not found (set OHOS_SDK_HOME / DEVECO_SDK_HOME / HOMEGRAPH_OHOS_SDK, ` +
|
|
4272
|
-
'or install the matching DevEco SDK). SDK API lookup disabled; project indexing continues normally.',
|
|
4273
|
-
};
|
|
4274
|
-
}
|
|
4275
|
-
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4276
|
-
options.onProgress?.({
|
|
4277
|
-
phase: 'ohos-api',
|
|
4278
|
-
current: 0,
|
|
4279
|
-
total: 1,
|
|
4280
|
-
currentFile: version,
|
|
4281
|
-
});
|
|
4282
|
-
try {
|
|
4283
|
-
const result = await indexOhosApiDb({
|
|
4284
|
-
sdkHome,
|
|
4285
|
-
version,
|
|
4286
|
-
outputPath: dbPath,
|
|
4287
|
-
onProgress: options.onProgress
|
|
4288
|
-
? (progress) => {
|
|
4289
|
-
options.onProgress?.({
|
|
4290
|
-
phase: 'ohos-api',
|
|
4291
|
-
current: progress.current,
|
|
4292
|
-
total: progress.total || 1,
|
|
4293
|
-
currentFile: progress.currentFile
|
|
4294
|
-
? `${version} ${path.basename(progress.currentFile)}`
|
|
4295
|
-
: version,
|
|
4296
|
-
subphase: progress.subphase,
|
|
4297
|
-
});
|
|
4298
|
-
}
|
|
4299
|
-
: undefined,
|
|
4300
|
-
});
|
|
4301
|
-
if (!result.success || !fs.existsSync(dbPath)) {
|
|
4302
|
-
try {
|
|
4303
|
-
fs.unlinkSync(dbPath);
|
|
4304
|
-
}
|
|
4305
|
-
catch {
|
|
4306
|
-
/* ignore */
|
|
4307
|
-
}
|
|
4308
|
-
const detail = result.errors
|
|
4309
|
-
.filter((e) => e.severity === 'error')
|
|
4310
|
-
.map((e) => e.message)
|
|
4311
|
-
.join('; ');
|
|
4312
|
-
return {
|
|
4313
|
-
code: 'ohos_api_build_failed',
|
|
4314
|
-
message: `Failed to build OHOS API db for ${version} from ${sdkHome}` +
|
|
4315
|
-
(detail ? ` (${detail})` : '') +
|
|
4316
|
-
'. SDK API lookup disabled; project indexing continues normally.',
|
|
4317
|
-
};
|
|
4318
|
-
}
|
|
4611
|
+
if (sdkHome) {
|
|
4612
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4319
4613
|
options.onProgress?.({
|
|
4320
4614
|
phase: 'ohos-api',
|
|
4321
|
-
current:
|
|
4615
|
+
current: 0,
|
|
4322
4616
|
total: 1,
|
|
4323
4617
|
currentFile: version,
|
|
4324
4618
|
});
|
|
4325
|
-
return { version, dbPath, packageName, installed: true };
|
|
4326
|
-
}
|
|
4327
|
-
catch (err) {
|
|
4328
4619
|
try {
|
|
4329
|
-
|
|
4620
|
+
const result = await indexOhosApiDb({
|
|
4621
|
+
sdkHome,
|
|
4622
|
+
version,
|
|
4623
|
+
outputPath: dbPath,
|
|
4624
|
+
onProgress: options.onProgress
|
|
4625
|
+
? (progress) => {
|
|
4626
|
+
options.onProgress?.({
|
|
4627
|
+
phase: 'ohos-api',
|
|
4628
|
+
current: progress.current,
|
|
4629
|
+
total: progress.total || 1,
|
|
4630
|
+
currentFile: progress.currentFile
|
|
4631
|
+
? `${version} ${path.basename(progress.currentFile)}`
|
|
4632
|
+
: version,
|
|
4633
|
+
subphase: progress.subphase,
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
: undefined,
|
|
4637
|
+
});
|
|
4638
|
+
if (result.success && fs.existsSync(dbPath)) {
|
|
4639
|
+
options.onProgress?.({
|
|
4640
|
+
phase: 'ohos-api',
|
|
4641
|
+
current: 1,
|
|
4642
|
+
total: 1,
|
|
4643
|
+
currentFile: version,
|
|
4644
|
+
});
|
|
4645
|
+
return { version, dbPath, packageName, installed: true };
|
|
4646
|
+
}
|
|
4647
|
+
try {
|
|
4330
4648
|
fs.unlinkSync(dbPath);
|
|
4649
|
+
}
|
|
4650
|
+
catch {
|
|
4651
|
+
/* ignore */
|
|
4652
|
+
}
|
|
4331
4653
|
}
|
|
4332
4654
|
catch {
|
|
4333
|
-
|
|
4655
|
+
try {
|
|
4656
|
+
if (fs.existsSync(dbPath))
|
|
4657
|
+
fs.unlinkSync(dbPath);
|
|
4658
|
+
}
|
|
4659
|
+
catch {
|
|
4660
|
+
/* ignore */
|
|
4661
|
+
}
|
|
4334
4662
|
}
|
|
4335
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
4336
|
-
return {
|
|
4337
|
-
code: 'ohos_api_build_failed',
|
|
4338
|
-
message: `Failed to build OHOS API db for ${version} from ${sdkHome} (${msg}). ` +
|
|
4339
|
-
'SDK API lookup disabled; project indexing continues normally.',
|
|
4340
|
-
};
|
|
4341
4663
|
}
|
|
4664
|
+
// 3. Download prebuilt for the requested version from npm
|
|
4665
|
+
const fromNpm = tryDownloadOhosApiDbFromNpm(version);
|
|
4666
|
+
if (fromNpm)
|
|
4667
|
+
return fromNpm;
|
|
4668
|
+
// 4. Last resort: homegraph-ohos-api-db-6.1.0
|
|
4669
|
+
if (version !== OHOS_API_FALLBACK_VERSION) {
|
|
4670
|
+
const fallbackPath = ohosApiDbInstallPath(OHOS_API_FALLBACK_VERSION);
|
|
4671
|
+
if (fs.existsSync(fallbackPath)) {
|
|
4672
|
+
return {
|
|
4673
|
+
version: OHOS_API_FALLBACK_VERSION,
|
|
4674
|
+
dbPath: fallbackPath,
|
|
4675
|
+
packageName: ohosApiDbPackageName(OHOS_API_FALLBACK_VERSION),
|
|
4676
|
+
installed: false,
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
const fallback = tryDownloadOhosApiDbFromNpm(OHOS_API_FALLBACK_VERSION);
|
|
4680
|
+
if (fallback)
|
|
4681
|
+
return fallback;
|
|
4682
|
+
}
|
|
4683
|
+
return {
|
|
4684
|
+
code: 'ohos_api_sdk_missing',
|
|
4685
|
+
message: `OHOS API db for ${version} unavailable (no local SDK build, npm ${packageName} failed` +
|
|
4686
|
+
(version !== OHOS_API_FALLBACK_VERSION
|
|
4687
|
+
? `, and fallback ${ohosApiDbPackageName(OHOS_API_FALLBACK_VERSION)} failed`
|
|
4688
|
+
: '') +
|
|
4689
|
+
'). Set OHOS_SDK_HOME / DEVECO_SDK_HOME or install the matching DevEco SDK. ' +
|
|
4690
|
+
'SDK API lookup disabled; project indexing continues normally.',
|
|
4691
|
+
};
|
|
4342
4692
|
}
|
|
4343
4693
|
/**
|
|
4344
4694
|
* Attach a prebuilt API db if present — never builds. Used on open / wireLayers.
|