homegraph 1.5.2 → 1.5.4
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 +156 -0
- package/README.md +63 -10
- package/dist/addons/dynamic-import.d.ts +18 -0
- package/dist/addons/dynamic-import.d.ts.map +1 -0
- package/dist/addons/dynamic-import.js +25 -0
- package/dist/addons/dynamic-import.js.map +1 -0
- package/dist/addons/init-template.d.ts +15 -0
- package/dist/addons/init-template.d.ts.map +1 -0
- package/dist/addons/init-template.js +290 -0
- package/dist/addons/init-template.js.map +1 -0
- package/dist/addons/loader.d.ts +18 -0
- package/dist/addons/loader.d.ts.map +1 -0
- package/dist/addons/loader.js +88 -0
- package/dist/addons/loader.js.map +1 -0
- package/dist/addons/manager.d.ts +106 -0
- package/dist/addons/manager.d.ts.map +1 -0
- package/dist/addons/manager.js +437 -0
- package/dist/addons/manager.js.map +1 -0
- package/dist/addons/paths.d.ts +22 -0
- package/dist/addons/paths.d.ts.map +1 -0
- package/dist/addons/paths.js +65 -0
- package/dist/addons/paths.js.map +1 -0
- package/dist/addons/registry.d.ts +38 -0
- package/dist/addons/registry.d.ts.map +1 -0
- package/dist/addons/registry.js +160 -0
- package/dist/addons/registry.js.map +1 -0
- package/dist/addons/semver.d.ts +21 -0
- package/dist/addons/semver.d.ts.map +1 -0
- package/dist/addons/semver.js +79 -0
- package/dist/addons/semver.js.map +1 -0
- package/dist/addons/types.d.ts +51 -0
- package/dist/addons/types.d.ts.map +1 -0
- package/dist/addons/types.js +14 -0
- package/dist/addons/types.js.map +1 -0
- package/dist/addons/validate.d.ts +38 -0
- package/dist/addons/validate.d.ts.map +1 -0
- package/dist/addons/validate.js +165 -0
- package/dist/addons/validate.js.map +1 -0
- 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 +364 -0
- package/dist/arkui/migrate-snapshot.js.map +1 -0
- package/dist/bin/addon-commands.d.ts +27 -0
- package/dist/bin/addon-commands.d.ts.map +1 -0
- package/dist/bin/addon-commands.js +254 -0
- package/dist/bin/addon-commands.js.map +1 -0
- package/dist/bin/fatal-handler.js +2 -2
- package/dist/bin/homegraph.js +111 -157
- 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 +7 -3
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +44 -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 +33 -2
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +525 -129
- 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 +20 -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-repeat-guard.d.ts +123 -0
- package/dist/mcp/explore-repeat-guard.d.ts.map +1 -0
- package/dist/mcp/explore-repeat-guard.js +503 -0
- package/dist/mcp/explore-repeat-guard.js.map +1 -0
- package/dist/mcp/explore-session-state.d.ts +237 -0
- package/dist/mcp/explore-session-state.d.ts.map +1 -0
- package/dist/mcp/explore-session-state.js +353 -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 +20 -18
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/query-pool.d.ts.map +1 -1
- package/dist/mcp/query-pool.js +10 -13
- package/dist/mcp/query-pool.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 +5 -2
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +43 -32
- 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 +149 -8
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +4265 -575
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +166 -0
- package/dist/resolution/callback-synthesizer.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 +157 -5
- package/dist/search/query-utils.d.ts.map +1 -1
- package/dist/search/query-utils.js +1105 -70
- package/dist/search/query-utils.js.map +1 -1
- package/dist/spec/build/scan.js +1 -1
- package/dist/spec/build/scan.js.map +1 -1
- package/dist/spec/build/scope-resolver.d.ts +24 -5
- package/dist/spec/build/scope-resolver.d.ts.map +1 -1
- package/dist/spec/build/scope-resolver.js +76 -10
- package/dist/spec/build/scope-resolver.js.map +1 -1
- package/dist/spec/config.d.ts +8 -0
- package/dist/spec/config.d.ts.map +1 -1
- package/dist/spec/config.js +3 -0
- package/dist/spec/config.js.map +1 -1
- package/dist/spec/evolve/pipeline.d.ts.map +1 -1
- package/dist/spec/evolve/pipeline.js +2 -1
- package/dist/spec/evolve/pipeline.js.map +1 -1
- package/dist/spec/git/commits.d.ts +8 -3
- package/dist/spec/git/commits.d.ts.map +1 -1
- package/dist/spec/git/commits.js +12 -11
- package/dist/spec/git/commits.js.map +1 -1
- package/dist/spec/mine/addon/adapter.d.ts +45 -0
- package/dist/spec/mine/addon/adapter.d.ts.map +1 -0
- package/dist/spec/mine/addon/adapter.js +118 -0
- package/dist/spec/mine/addon/adapter.js.map +1 -0
- package/dist/spec/mine/addon/render.d.ts +20 -0
- package/dist/spec/mine/addon/render.d.ts.map +1 -0
- package/dist/spec/mine/addon/render.js +41 -0
- package/dist/spec/mine/addon/render.js.map +1 -0
- package/dist/spec/mine/addon/types.d.ts +70 -0
- package/dist/spec/mine/addon/types.d.ts.map +1 -0
- package/dist/spec/mine/addon/types.js +14 -0
- package/dist/spec/mine/addon/types.js.map +1 -0
- package/dist/spec/mine/generator.d.ts +5 -1
- package/dist/spec/mine/generator.d.ts.map +1 -1
- package/dist/spec/mine/generator.js +69 -7
- package/dist/spec/mine/generator.js.map +1 -1
- package/dist/spec/mine/pipeline.d.ts.map +1 -1
- package/dist/spec/mine/pipeline.js +5 -1
- package/dist/spec/mine/pipeline.js.map +1 -1
- package/dist/spec/mine/scanner.d.ts +1 -0
- package/dist/spec/mine/scanner.d.ts.map +1 -1
- package/dist/spec/mine/scanner.js +1 -1
- package/dist/spec/mine/scanner.js.map +1 -1
- package/dist/spec/types.d.ts +1 -1
- package/dist/spec/types.d.ts.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 +13 -9
- 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
|
@@ -78,8 +78,11 @@ exports.isArkTSBatchPersisted = isArkTSBatchPersisted;
|
|
|
78
78
|
exports.isArkTSBatchCommitted = isArkTSBatchCommitted;
|
|
79
79
|
exports.resetArkTSBatch = resetArkTSBatch;
|
|
80
80
|
exports.shrinkArkTSBatchPostParse = shrinkArkTSBatchPostParse;
|
|
81
|
+
exports.createArkRelPathNormalizer = createArkRelPathNormalizer;
|
|
81
82
|
exports.collectExportedRtaEntryPointsFromFiles = collectExportedRtaEntryPointsFromFiles;
|
|
82
83
|
exports.stateDecoratorKinds = stateDecoratorKinds;
|
|
84
|
+
exports.fieldDecoratorEntries = fieldDecoratorEntries;
|
|
85
|
+
exports.fieldDecoratorsEncoded = fieldDecoratorsEncoded;
|
|
83
86
|
exports.stateTransferViaForField = stateTransferViaForField;
|
|
84
87
|
exports.isUnclearArkClassSignature = isUnclearArkClassSignature;
|
|
85
88
|
exports.isUnclearArkMethodSignature = isUnclearArkMethodSignature;
|
|
@@ -105,6 +108,7 @@ exports.readOhosSdkPlatformVersion = readOhosSdkPlatformVersion;
|
|
|
105
108
|
exports.discoverLocalOhosSdkCandidates = discoverLocalOhosSdkCandidates;
|
|
106
109
|
exports.findLocalOhosSdkForVersion = findLocalOhosSdkForVersion;
|
|
107
110
|
exports.resolveOhosApiVersionForProject = resolveOhosApiVersionForProject;
|
|
111
|
+
exports.tryDownloadOhosApiDbFromNpm = tryDownloadOhosApiDbFromNpm;
|
|
108
112
|
exports.ensureOhosApiDb = ensureOhosApiDb;
|
|
109
113
|
exports.attachExistingOhosApiDbForProject = attachExistingOhosApiDbForProject;
|
|
110
114
|
exports.bindOhosApiDbForProject = bindOhosApiDbForProject;
|
|
@@ -122,9 +126,12 @@ const directory_1 = require("../../directory");
|
|
|
122
126
|
const project_config_1 = require("../../project-config");
|
|
123
127
|
const context_1 = require("../context");
|
|
124
128
|
const default_ignore_1 = require("../default-ignore");
|
|
129
|
+
const generated_detection_1 = require("../generated-detection");
|
|
125
130
|
const extraction_version_1 = require("../extraction-version");
|
|
126
131
|
const version_1 = require("../../mcp/version");
|
|
127
132
|
const liveness_watchdog_1 = require("../../mcp/liveness-watchdog");
|
|
133
|
+
const migrate_semantics_1 = require("../../arkui/migrate-semantics");
|
|
134
|
+
const migrate_passage_1 = require("../../arkui/migrate-passage");
|
|
128
135
|
const tree_sitter_helpers_1 = require("../tree-sitter-helpers");
|
|
129
136
|
const wasm_runtime_flags_1 = require("../wasm-runtime-flags");
|
|
130
137
|
const ignore_1 = __importDefault(require("ignore"));
|
|
@@ -480,11 +487,16 @@ function hasHarmonyProjectMarkers(rootDir) {
|
|
|
480
487
|
function normIndexPath(filePath) {
|
|
481
488
|
return filePath.replace(/\\/g, '/');
|
|
482
489
|
}
|
|
483
|
-
/**
|
|
490
|
+
/** Photos-style member calls: `sdk.Asset_setCropRect(`. */
|
|
484
491
|
const NAPI_MEMBER_CALL_RE = /\.\s*([A-Z][A-Za-z0-9_]*(?:_[A-Za-z0-9_]+)+)\s*\(/g;
|
|
492
|
+
/** `libFoo.so` module path from ArkAnalyzer import infos. */
|
|
493
|
+
const LIB_SO_MODULE_RE = /^lib[A-Za-z0-9_]+\.so$/;
|
|
485
494
|
function isNapiSymbolName(name) {
|
|
486
495
|
return /^[A-Z][A-Za-z0-9_]*_[A-Za-z0-9_]+$/.test(name);
|
|
487
496
|
}
|
|
497
|
+
function escapeRegExp(s) {
|
|
498
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
499
|
+
}
|
|
488
500
|
let persistedBatch = null;
|
|
489
501
|
/** File that triggered the most recent batch persist (for indexAll edge stats). */
|
|
490
502
|
let batchTriggerFile = null;
|
|
@@ -635,6 +647,7 @@ function persistFileResult(queries, filePath, content, stats, result, options) {
|
|
|
635
647
|
indexedAt: Date.now(),
|
|
636
648
|
nodeCount: result.nodes.length,
|
|
637
649
|
errors: result.errors.length > 0 ? result.errors : undefined,
|
|
650
|
+
generated: (0, generated_detection_1.detectGeneratedFile)(filePath, content),
|
|
638
651
|
};
|
|
639
652
|
queries.upsertFile(fileRecord);
|
|
640
653
|
}
|
|
@@ -888,7 +901,9 @@ function runArkTSBatchFullCore(rootDir, queries, triggerFile, etsFiles, batchKey
|
|
|
888
901
|
const index = buildArkTSIndex(rootDir, etsFiles);
|
|
889
902
|
if (index.fileResults.size === 0) {
|
|
890
903
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
891
|
-
|
|
904
|
+
const detail = index.errors.map((e) => `[${e.severity}] ${e.message}`).join(' | ');
|
|
905
|
+
throw new Error(fatal?.message ??
|
|
906
|
+
`ArkTS batch produced no indexed files${detail ? ` (${detail})` : ' (no Scene/adapter errors recorded)'}`);
|
|
892
907
|
}
|
|
893
908
|
queries.deleteArkTSCrossFileCallEdges();
|
|
894
909
|
persistBatchResultsSync(rootDir, queries, index);
|
|
@@ -1025,7 +1040,9 @@ async function primeArkTSBatch(rootDir, queries, triggerFile, options) {
|
|
|
1025
1040
|
const streamed = index.streamedPersistedPaths?.size ?? 0;
|
|
1026
1041
|
if (index.fileResults.size === 0 && streamed === 0) {
|
|
1027
1042
|
const fatal = index.errors.find((e) => e.severity === 'error');
|
|
1028
|
-
|
|
1043
|
+
const detail = index.errors.map((e) => `[${e.severity}] ${e.message}`).join(' | ');
|
|
1044
|
+
throw new Error(fatal?.message ??
|
|
1045
|
+
`ArkTS batch produced no indexed files${detail ? ` (${detail})` : ' (no Scene/adapter errors recorded)'}`);
|
|
1029
1046
|
}
|
|
1030
1047
|
if (!streamModular) {
|
|
1031
1048
|
queries.deleteArkTSCrossFileCallEdges();
|
|
@@ -1198,8 +1215,72 @@ const FIELD_CATEGORY = {
|
|
|
1198
1215
|
};
|
|
1199
1216
|
/** {@link ModifierType.CONST} — not re-exported from the arkanalyzer npm entry. */
|
|
1200
1217
|
const MODIFIER_CONST = 128;
|
|
1201
|
-
|
|
1202
|
-
|
|
1218
|
+
/** `realpathSync` when the path (or its dirname) exists; otherwise `path.resolve`. */
|
|
1219
|
+
function realpathExisting(p) {
|
|
1220
|
+
try {
|
|
1221
|
+
return fs.realpathSync(p);
|
|
1222
|
+
}
|
|
1223
|
+
catch {
|
|
1224
|
+
try {
|
|
1225
|
+
return path.join(fs.realpathSync(path.dirname(p)), path.basename(p));
|
|
1226
|
+
}
|
|
1227
|
+
catch {
|
|
1228
|
+
return path.resolve(p);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
function toPosixPath(p) {
|
|
1233
|
+
return p.replace(/\\/g, '/');
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* If `absolutePath` is under `canonicalRoot`, return the project-relative posix
|
|
1237
|
+
* path; otherwise null. Windows compares case-insensitively.
|
|
1238
|
+
*/
|
|
1239
|
+
function tryStripRootPrefix(canonicalRoot, absolutePath) {
|
|
1240
|
+
const root = toPosixPath(canonicalRoot);
|
|
1241
|
+
const abs = toPosixPath(absolutePath);
|
|
1242
|
+
const rootCmp = process.platform === 'win32' ? root.toLowerCase() : root;
|
|
1243
|
+
const absCmp = process.platform === 'win32' ? abs.toLowerCase() : abs;
|
|
1244
|
+
if (absCmp === rootCmp)
|
|
1245
|
+
return '';
|
|
1246
|
+
const prefix = rootCmp.endsWith('/') ? rootCmp : `${rootCmp}/`;
|
|
1247
|
+
if (!absCmp.startsWith(prefix))
|
|
1248
|
+
return null;
|
|
1249
|
+
const sliceFrom = root.endsWith('/') ? root.length : root.length + 1;
|
|
1250
|
+
return abs.slice(sliceFrom);
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Project-relative paths for ArkAnalyzer absolutes.
|
|
1254
|
+
*
|
|
1255
|
+
* ArkAnalyzer often returns realpath'd absolutes (macOS: `/private/var/...`),
|
|
1256
|
+
* while HomeGraph's `rootDir` from `mkdtemp` / callers may be unresolved
|
|
1257
|
+
* (`/var/...`). We realpath the root **once**, then strip by prefix on the hot
|
|
1258
|
+
* path; only prefix misses pay a per-path `realpath` (cached for the batch).
|
|
1259
|
+
*/
|
|
1260
|
+
function createArkRelPathNormalizer(rootDir) {
|
|
1261
|
+
const canonicalRoot = realpathExisting(rootDir);
|
|
1262
|
+
const cache = new Map();
|
|
1263
|
+
const normalizeOnce = (absolutePath) => {
|
|
1264
|
+
const fast = tryStripRootPrefix(canonicalRoot, absolutePath);
|
|
1265
|
+
if (fast !== null)
|
|
1266
|
+
return fast;
|
|
1267
|
+
const abs = realpathExisting(absolutePath);
|
|
1268
|
+
const after = tryStripRootPrefix(canonicalRoot, abs);
|
|
1269
|
+
if (after !== null)
|
|
1270
|
+
return after;
|
|
1271
|
+
return toPosixPath(path.relative(canonicalRoot, abs));
|
|
1272
|
+
};
|
|
1273
|
+
return {
|
|
1274
|
+
rootDir: canonicalRoot,
|
|
1275
|
+
normalize(absolutePath) {
|
|
1276
|
+
const hit = cache.get(absolutePath);
|
|
1277
|
+
if (hit !== undefined)
|
|
1278
|
+
return hit;
|
|
1279
|
+
const rel = normalizeOnce(absolutePath);
|
|
1280
|
+
cache.set(absolutePath, rel);
|
|
1281
|
+
return rel;
|
|
1282
|
+
},
|
|
1283
|
+
};
|
|
1203
1284
|
}
|
|
1204
1285
|
/** Map ArkAnalyzer synthetic files (e.g. @dummyFile) to our virtual indexed path. */
|
|
1205
1286
|
function resolveArkanalyzerVirtualPath(arkFile) {
|
|
@@ -1209,9 +1290,6 @@ function resolveArkanalyzerVirtualPath(arkFile) {
|
|
|
1209
1290
|
}
|
|
1210
1291
|
return null;
|
|
1211
1292
|
}
|
|
1212
|
-
function relPathForArkFile(rootDir, arkFile) {
|
|
1213
|
-
return resolveArkanalyzerVirtualPath(arkFile) ?? normalizeRelPath(rootDir, arkFile.getFilePath());
|
|
1214
|
-
}
|
|
1215
1293
|
function buildQualifiedName(parts) {
|
|
1216
1294
|
return parts.filter(Boolean).join('::');
|
|
1217
1295
|
}
|
|
@@ -1655,6 +1733,52 @@ function stateDecoratorKinds(field) {
|
|
|
1655
1733
|
return [];
|
|
1656
1734
|
return [...decs].map((d) => d.getKind()).filter(Boolean);
|
|
1657
1735
|
}
|
|
1736
|
+
/** All field decorators with optional string args (Provide('theme') → theme). */
|
|
1737
|
+
function fieldDecoratorEntries(field) {
|
|
1738
|
+
const out = [];
|
|
1739
|
+
const seen = new Set();
|
|
1740
|
+
const push = (kind, arg) => {
|
|
1741
|
+
if (!kind || seen.has(kind))
|
|
1742
|
+
return;
|
|
1743
|
+
seen.add(kind);
|
|
1744
|
+
out.push(arg ? { kind, arg } : { kind });
|
|
1745
|
+
};
|
|
1746
|
+
try {
|
|
1747
|
+
for (const d of field.getDecorators?.() ?? []) {
|
|
1748
|
+
push(d.getKind(), (0, migrate_semantics_1.parseDecoratorArgFromContent)(d.getContent?.() ?? ''));
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
catch {
|
|
1752
|
+
/* ignore */
|
|
1753
|
+
}
|
|
1754
|
+
if (out.length === 0) {
|
|
1755
|
+
for (const kind of stateDecoratorKinds(field))
|
|
1756
|
+
push(kind);
|
|
1757
|
+
}
|
|
1758
|
+
return out;
|
|
1759
|
+
}
|
|
1760
|
+
/** Encoded decorators list for node.decorators (bare kinds + Kind@arg). */
|
|
1761
|
+
function fieldDecoratorsEncoded(field) {
|
|
1762
|
+
return (0, migrate_semantics_1.encodeDecoratorEntries)(fieldDecoratorEntries(field));
|
|
1763
|
+
}
|
|
1764
|
+
function modelDecoratorEntries(model) {
|
|
1765
|
+
const out = [];
|
|
1766
|
+
const seen = new Set();
|
|
1767
|
+
try {
|
|
1768
|
+
for (const d of model.getDecorators?.() ?? []) {
|
|
1769
|
+
const kind = d.getKind();
|
|
1770
|
+
if (!kind || seen.has(kind))
|
|
1771
|
+
continue;
|
|
1772
|
+
seen.add(kind);
|
|
1773
|
+
const arg = (0, migrate_semantics_1.parseDecoratorArgFromContent)(d.getContent?.() ?? '');
|
|
1774
|
+
out.push(arg ? { kind, arg } : { kind });
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
catch {
|
|
1778
|
+
/* ignore */
|
|
1779
|
+
}
|
|
1780
|
+
return out;
|
|
1781
|
+
}
|
|
1658
1782
|
/** Child @Prop / @Link — edge `via` uses the decorator name as-is. */
|
|
1659
1783
|
function stateTransferViaForField(field) {
|
|
1660
1784
|
for (const kind of stateDecoratorKinds(field)) {
|
|
@@ -2001,12 +2125,12 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
2001
2125
|
if (!buildId)
|
|
2002
2126
|
return;
|
|
2003
2127
|
const seen = new Set();
|
|
2004
|
-
const link = (source, target, kind, via, line) => {
|
|
2128
|
+
const link = (source, target, kind, via, line, extraMeta) => {
|
|
2005
2129
|
const key = `${source}>${target}>${kind}>${via}`;
|
|
2006
2130
|
if (seen.has(key))
|
|
2007
2131
|
return;
|
|
2008
2132
|
seen.add(key);
|
|
2009
|
-
ctx.addEdge(result, source, target, kind, relativePath, via, line);
|
|
2133
|
+
ctx.addEdge(result, source, target, kind, relativePath, via, line, extraMeta);
|
|
2010
2134
|
};
|
|
2011
2135
|
for (const [field] of viewTree.getStateValues()) {
|
|
2012
2136
|
const fieldId = ctx.resolveFieldNodeId(field);
|
|
@@ -2074,32 +2198,62 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
2074
2198
|
// Skip this node's signature edge; keep walking siblings/children.
|
|
2075
2199
|
}
|
|
2076
2200
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2201
|
+
// Data passages for migrate snapshot (+ Prop/Link edges for explore).
|
|
2202
|
+
try {
|
|
2203
|
+
const passages = (0, migrate_passage_1.collectPassagesForViewTreeNode)(node, ctx.scene, cls);
|
|
2204
|
+
for (const p of passages) {
|
|
2205
|
+
const toId = ctx.resolveFieldNodeId(p.toField);
|
|
2206
|
+
if (!toId)
|
|
2207
|
+
continue;
|
|
2208
|
+
ctx.markFieldStateDecorators?.(p.toField, toId, result);
|
|
2209
|
+
let fromId = null;
|
|
2210
|
+
if (p.fromField) {
|
|
2211
|
+
fromId = ctx.resolveFieldNodeId(p.fromField);
|
|
2212
|
+
if (fromId)
|
|
2213
|
+
ctx.markFieldStateDecorators?.(p.fromField, fromId, result);
|
|
2090
2214
|
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2215
|
+
if (!fromId && p.fromIsParentComponent)
|
|
2216
|
+
fromId = classNodeId;
|
|
2217
|
+
if (!fromId)
|
|
2218
|
+
continue;
|
|
2219
|
+
const line = p.toField.getOriginFullPosition()?.getFirstLine() ?? 1;
|
|
2220
|
+
link(fromId, toId, 'references', p.via || 'data-passage', line, {
|
|
2221
|
+
passageType: p.passageType,
|
|
2222
|
+
valueType: p.valueType,
|
|
2223
|
+
forcesMigration: p.forcesMigration,
|
|
2224
|
+
parentExpression: p.parentExpression,
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
catch {
|
|
2229
|
+
// Fall back to legacy Prop/Link-only transfer below.
|
|
2230
|
+
if (node.stateValuesTransfer) {
|
|
2231
|
+
for (const [childField, parentValue] of node.stateValuesTransfer) {
|
|
2232
|
+
const childFieldId = ctx.resolveFieldNodeId(childField);
|
|
2233
|
+
if (parentValue instanceof arkanalyzer_1.ArkField) {
|
|
2234
|
+
const via = stateTransferViaForField(childField);
|
|
2235
|
+
if (!via || !childFieldId)
|
|
2236
|
+
continue;
|
|
2237
|
+
ctx.markFieldStateDecorators?.(childField, childFieldId, result);
|
|
2238
|
+
const parentFieldId = ctx.resolveFieldNodeId(parentValue);
|
|
2239
|
+
if (parentFieldId) {
|
|
2240
|
+
ctx.markFieldStateDecorators?.(parentValue, parentFieldId, result);
|
|
2241
|
+
link(parentFieldId, childFieldId, 'references', via, childField.getOriginFullPosition()?.getFirstLine() ?? 1);
|
|
2099
2242
|
}
|
|
2100
2243
|
}
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2244
|
+
else if (parentValue instanceof arkanalyzer_1.ArkMethod) {
|
|
2245
|
+
let builderId = ctx.ensureMethodNode(parentValue, relativePath, result, classNodeId);
|
|
2246
|
+
if (!builderId) {
|
|
2247
|
+
try {
|
|
2248
|
+
builderId = ctx.resolveMethodIdBySig(parentValue.getSignature());
|
|
2249
|
+
}
|
|
2250
|
+
catch {
|
|
2251
|
+
builderId = null;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
if (childFieldId && builderId) {
|
|
2255
|
+
link(builderId, childFieldId, 'references', 'builder-param', childField.getOriginFullPosition()?.getFirstLine() ?? 1);
|
|
2256
|
+
}
|
|
2103
2257
|
}
|
|
2104
2258
|
}
|
|
2105
2259
|
}
|
|
@@ -2126,6 +2280,7 @@ function indexViewTreeForClass(ctx, cls, result, relativePath) {
|
|
|
2126
2280
|
}
|
|
2127
2281
|
class ArkTSAdapter {
|
|
2128
2282
|
rootDir;
|
|
2283
|
+
pathNorm;
|
|
2129
2284
|
scanned;
|
|
2130
2285
|
scene = null;
|
|
2131
2286
|
methodToId = new Map();
|
|
@@ -2155,10 +2310,24 @@ class ArkTSAdapter {
|
|
|
2155
2310
|
crossFileEdges = [];
|
|
2156
2311
|
/** Per-module `@dummyMain@…` created during analyseByModule (for scene aggregator). */
|
|
2157
2312
|
moduleDummyMains = [];
|
|
2313
|
+
/**
|
|
2314
|
+
* Local import binding names from `lib*.so` per relative file path.
|
|
2315
|
+
* Filled before method bodies are scanned so `multimodalinput.getTidByName(`
|
|
2316
|
+
* can emit NAPI call refs (not only photos-style `Class_method` names).
|
|
2317
|
+
*/
|
|
2318
|
+
soImportBindingsByFile = new Map();
|
|
2158
2319
|
constructor(rootDir, scannedFiles) {
|
|
2159
|
-
this.
|
|
2320
|
+
this.pathNorm = createArkRelPathNormalizer(rootDir);
|
|
2321
|
+
this.rootDir = this.pathNorm.rootDir;
|
|
2160
2322
|
this.scanned = new Set(scannedFiles);
|
|
2161
2323
|
}
|
|
2324
|
+
/** ArkAnalyzer absolute → project-relative (shared batch cache). */
|
|
2325
|
+
normalizeRelPath(absolutePath) {
|
|
2326
|
+
return this.pathNorm.normalize(absolutePath);
|
|
2327
|
+
}
|
|
2328
|
+
relPathForArkFile(arkFile) {
|
|
2329
|
+
return resolveArkanalyzerVirtualPath(arkFile) ?? this.normalizeRelPath(arkFile.getFilePath());
|
|
2330
|
+
}
|
|
2162
2331
|
build(scene) {
|
|
2163
2332
|
this.scene = scene;
|
|
2164
2333
|
for (const arkFile of scene.getFiles()) {
|
|
@@ -2176,7 +2345,7 @@ class ArkTSAdapter {
|
|
|
2176
2345
|
indexModule(module, scene) {
|
|
2177
2346
|
this.scene = scene;
|
|
2178
2347
|
const files = [...module.getFilesMap().values()];
|
|
2179
|
-
const moduleFiles = new Set(files.map((f) => normalizeRelPath(
|
|
2348
|
+
const moduleFiles = new Set(files.map((f) => this.normalizeRelPath(f.getFilePath())).filter(Boolean));
|
|
2180
2349
|
for (const arkFile of files) {
|
|
2181
2350
|
this.indexScannedFile(arkFile);
|
|
2182
2351
|
}
|
|
@@ -2211,7 +2380,7 @@ class ArkTSAdapter {
|
|
|
2211
2380
|
continue;
|
|
2212
2381
|
}
|
|
2213
2382
|
for (const arkFile of filesMap.values()) {
|
|
2214
|
-
const rel = normalizeRelPath(
|
|
2383
|
+
const rel = this.normalizeRelPath(arkFile.getFilePath());
|
|
2215
2384
|
if (!rel || excludeFiles.has(rel) || this.fileResults.has(rel))
|
|
2216
2385
|
continue;
|
|
2217
2386
|
if (!this.scanned.has(rel) || !isArkAnalyzerSourcePath(rel))
|
|
@@ -2244,7 +2413,7 @@ class ArkTSAdapter {
|
|
|
2244
2413
|
/** Re-attach live Ark* objects to already-indexed HomeGraph node ids (sig maps). */
|
|
2245
2414
|
bindLiveMapsFromFiles(files) {
|
|
2246
2415
|
for (const arkFile of files) {
|
|
2247
|
-
const relativePath = normalizeRelPath(
|
|
2416
|
+
const relativePath = this.normalizeRelPath(arkFile.getFilePath());
|
|
2248
2417
|
if (!this.scanned.has(relativePath) || !isArkAnalyzerSourcePath(relativePath))
|
|
2249
2418
|
continue;
|
|
2250
2419
|
this.bindLiveClass(arkFile.getDefaultClass());
|
|
@@ -2396,13 +2565,13 @@ class ArkTSAdapter {
|
|
|
2396
2565
|
!callerArk.isGenerated()) {
|
|
2397
2566
|
continue;
|
|
2398
2567
|
}
|
|
2399
|
-
const callerFile = (callerArk ? relPathForArkFile(
|
|
2568
|
+
const callerFile = (callerArk ? this.relPathForArkFile(callerArk.getDeclaringArkFile()) : null) ??
|
|
2400
2569
|
(callerSig ? this.relPathFromMethodSignature(callerSig) : null) ??
|
|
2401
2570
|
'';
|
|
2402
2571
|
if (moduleFiles && callerFile && !moduleFiles.has(callerFile)) {
|
|
2403
2572
|
continue;
|
|
2404
2573
|
}
|
|
2405
|
-
const calleeFile = (calleeArk ? relPathForArkFile(
|
|
2574
|
+
const calleeFile = (calleeArk ? this.relPathForArkFile(calleeArk.getDeclaringArkFile()) : null) ??
|
|
2406
2575
|
(calleeSig ? this.relPathFromMethodSignature(calleeSig) : null);
|
|
2407
2576
|
const callerId = (callerArk ? this.resolveMethodNodeId(callerArk) : null) ??
|
|
2408
2577
|
(callerSig ? this.resolveMethodIdBySigExact(callerSig) : null);
|
|
@@ -2441,7 +2610,7 @@ class ArkTSAdapter {
|
|
|
2441
2610
|
*/
|
|
2442
2611
|
stitchModuleCfgCallsAndRefs(files, _moduleFiles) {
|
|
2443
2612
|
for (const arkFile of files) {
|
|
2444
|
-
const callerFile = normalizeRelPath(
|
|
2613
|
+
const callerFile = this.normalizeRelPath(arkFile.getFilePath());
|
|
2445
2614
|
const visitClass = (cls) => {
|
|
2446
2615
|
for (const method of cls.getMethods(true)) {
|
|
2447
2616
|
if (shouldSkipArkMethod(method) &&
|
|
@@ -2612,7 +2781,7 @@ class ArkTSAdapter {
|
|
|
2612
2781
|
const fileName = sig.getDeclaringClassSignature().getDeclaringFileSignature().getFileName();
|
|
2613
2782
|
if (!fileName || fileName === '%unk' || fileName.includes('%unk'))
|
|
2614
2783
|
return null;
|
|
2615
|
-
return normalizeRelPath(
|
|
2784
|
+
return this.normalizeRelPath(fileName);
|
|
2616
2785
|
}
|
|
2617
2786
|
catch {
|
|
2618
2787
|
return null;
|
|
@@ -2657,7 +2826,7 @@ class ArkTSAdapter {
|
|
|
2657
2826
|
return [...this.fileResults.keys()];
|
|
2658
2827
|
}
|
|
2659
2828
|
indexScannedFile(arkFile) {
|
|
2660
|
-
const relativePath = normalizeRelPath(
|
|
2829
|
+
const relativePath = this.normalizeRelPath(arkFile.getFilePath());
|
|
2661
2830
|
if (!this.scanned.has(relativePath))
|
|
2662
2831
|
return;
|
|
2663
2832
|
if (!isArkAnalyzerSourcePath(relativePath))
|
|
@@ -2666,7 +2835,7 @@ class ArkTSAdapter {
|
|
|
2666
2835
|
}
|
|
2667
2836
|
indexViewTreesFromFiles(files) {
|
|
2668
2837
|
for (const arkFile of files) {
|
|
2669
|
-
const relativePath = normalizeRelPath(
|
|
2838
|
+
const relativePath = this.normalizeRelPath(arkFile.getFilePath());
|
|
2670
2839
|
if (!this.scanned.has(relativePath) || !isArkAnalyzerSourcePath(relativePath))
|
|
2671
2840
|
continue;
|
|
2672
2841
|
const result = this.fileResults.get(relativePath);
|
|
@@ -2688,6 +2857,70 @@ class ArkTSAdapter {
|
|
|
2688
2857
|
}
|
|
2689
2858
|
}
|
|
2690
2859
|
}
|
|
2860
|
+
this.indexObservedClassRefs();
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* State / Param fields whose type names an @Observed / @ObservedV2 class →
|
|
2864
|
+
* `references` via `observed-ref` (arkui-migrate).
|
|
2865
|
+
*/
|
|
2866
|
+
indexObservedClassRefs() {
|
|
2867
|
+
const observedByName = new Map();
|
|
2868
|
+
for (const [, result] of this.fileResults) {
|
|
2869
|
+
for (const n of result.nodes) {
|
|
2870
|
+
if (n.kind !== 'class' && n.kind !== 'struct')
|
|
2871
|
+
continue;
|
|
2872
|
+
const decs = n.decorators ?? [];
|
|
2873
|
+
if (!decs.some((d) => migrate_semantics_1.OBSERVATION_DECORATORS.has(d.split('@')[0])))
|
|
2874
|
+
continue;
|
|
2875
|
+
const arr = observedByName.get(n.name) ?? [];
|
|
2876
|
+
arr.push(n.id);
|
|
2877
|
+
observedByName.set(n.name, arr);
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
if (observedByName.size === 0)
|
|
2881
|
+
return;
|
|
2882
|
+
for (const [relativePath, result] of this.fileResults) {
|
|
2883
|
+
for (const field of result.nodes) {
|
|
2884
|
+
if (field.kind !== 'property' && field.kind !== 'field')
|
|
2885
|
+
continue;
|
|
2886
|
+
const typeStr = field.signature ?? '';
|
|
2887
|
+
const bare = typeStr.replace(/<.*>/, '').split(/[.\s|]/).filter(Boolean).pop();
|
|
2888
|
+
if (!bare)
|
|
2889
|
+
continue;
|
|
2890
|
+
const targets = observedByName.get(bare);
|
|
2891
|
+
if (!targets || targets.length === 0)
|
|
2892
|
+
continue;
|
|
2893
|
+
// Prefer unique name; if ambiguous, same-file first
|
|
2894
|
+
let targetId = targets[0];
|
|
2895
|
+
if (targets.length > 1) {
|
|
2896
|
+
const sameFile = targets.find((id) => {
|
|
2897
|
+
const n = result.nodes.find((x) => x.id === id);
|
|
2898
|
+
return n?.filePath === relativePath;
|
|
2899
|
+
});
|
|
2900
|
+
if (sameFile)
|
|
2901
|
+
targetId = sameFile;
|
|
2902
|
+
else
|
|
2903
|
+
continue; // ambiguous cross-file — skip (precision > recall)
|
|
2904
|
+
}
|
|
2905
|
+
const edgeKey = `${field.id}\0${targetId}\0observed-ref`;
|
|
2906
|
+
if (this.emittedEdgeKeys.has(edgeKey))
|
|
2907
|
+
continue;
|
|
2908
|
+
this.emittedEdgeKeys.add(edgeKey);
|
|
2909
|
+
const edge = arkEdge(field.id, targetId, 'references', {
|
|
2910
|
+
metadata: {
|
|
2911
|
+
synthesizedBy: 'arkui-migrate',
|
|
2912
|
+
via: 'observed-ref',
|
|
2913
|
+
registeredAt: `${relativePath}:${field.startLine}`,
|
|
2914
|
+
},
|
|
2915
|
+
});
|
|
2916
|
+
if (this.nodeInFile(targetId, relativePath)) {
|
|
2917
|
+
result.edges.push(edge);
|
|
2918
|
+
}
|
|
2919
|
+
else {
|
|
2920
|
+
this.crossFileEdges.push(edge);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2691
2924
|
}
|
|
2692
2925
|
/** Index ArkAnalyzer's in-scene @dummyMain (DummyMainCreater output), not a HomeGraph synthetic node. */
|
|
2693
2926
|
indexArkanalyzerDummyMain(dummyMain) {
|
|
@@ -2726,7 +2959,7 @@ class ArkTSAdapter {
|
|
|
2726
2959
|
this.nodeIds.add(node.id);
|
|
2727
2960
|
}
|
|
2728
2961
|
indexFile(arkFile) {
|
|
2729
|
-
const relativePath = normalizeRelPath(
|
|
2962
|
+
const relativePath = this.normalizeRelPath(arkFile.getFilePath());
|
|
2730
2963
|
const language = 'arkts';
|
|
2731
2964
|
let lineCount = 1;
|
|
2732
2965
|
try {
|
|
@@ -2737,6 +2970,8 @@ class ArkTSAdapter {
|
|
|
2737
2970
|
}
|
|
2738
2971
|
const result = this.ensureFileResult(relativePath, lineCount);
|
|
2739
2972
|
const fileId = `file:${relativePath}`;
|
|
2973
|
+
// Before methods: record `lib*.so` bindings so body NAPI call scan can see them.
|
|
2974
|
+
this.collectSoImportBindings(relativePath, arkFile);
|
|
2740
2975
|
for (const ns of arkFile.getNamespaces()) {
|
|
2741
2976
|
this.indexNamespace(relativePath, language, result, ns, fileId);
|
|
2742
2977
|
}
|
|
@@ -2745,6 +2980,59 @@ class ArkTSAdapter {
|
|
|
2745
2980
|
this.indexTypeAliases(relativePath, language, result, arkFile, undefined, fileId);
|
|
2746
2981
|
this.indexModuleLocals(relativePath, language, result, arkFile, undefined, fileId);
|
|
2747
2982
|
this.indexImports(relativePath, language, result, arkFile, fileId);
|
|
2983
|
+
this.indexStorageApiKeys(relativePath, arkFile, result);
|
|
2984
|
+
}
|
|
2985
|
+
/** AppStorage / LocalStorage / … literal keys → arkui-migrate edges from each component. */
|
|
2986
|
+
indexStorageApiKeys(relativePath, arkFile, result) {
|
|
2987
|
+
let code = '';
|
|
2988
|
+
try {
|
|
2989
|
+
code = arkFile.getCode() ?? '';
|
|
2990
|
+
}
|
|
2991
|
+
catch {
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
if (!code)
|
|
2995
|
+
return;
|
|
2996
|
+
const hits = (0, migrate_semantics_1.scanStorageApiKeys)(code);
|
|
2997
|
+
if (hits.length === 0)
|
|
2998
|
+
return;
|
|
2999
|
+
const componentIds = result.nodes
|
|
3000
|
+
.filter((n) => n.kind === 'component' && n.filePath === relativePath)
|
|
3001
|
+
.map((n) => n.id);
|
|
3002
|
+
if (componentIds.length === 0)
|
|
3003
|
+
return;
|
|
3004
|
+
for (const hit of hits) {
|
|
3005
|
+
const line = (0, migrate_semantics_1.lineOfOffset)(code, hit.index);
|
|
3006
|
+
for (const compId of componentIds) {
|
|
3007
|
+
const edgeKey = `${compId}\0storage-api\0${hit.channel}\0${hit.key}`;
|
|
3008
|
+
if (this.emittedEdgeKeys.has(edgeKey))
|
|
3009
|
+
continue;
|
|
3010
|
+
this.emittedEdgeKeys.add(edgeKey);
|
|
3011
|
+
result.edges.push(arkEdge(compId, compId, 'references', {
|
|
3012
|
+
metadata: {
|
|
3013
|
+
synthesizedBy: 'arkui-migrate',
|
|
3014
|
+
via: 'storage-api',
|
|
3015
|
+
channel: hit.channel,
|
|
3016
|
+
key: hit.key,
|
|
3017
|
+
method: hit.method,
|
|
3018
|
+
registeredAt: `${relativePath}:${line}`,
|
|
3019
|
+
},
|
|
3020
|
+
}));
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
collectSoImportBindings(relativePath, arkFile) {
|
|
3025
|
+
const set = new Set();
|
|
3026
|
+
for (const importInfo of arkFile.getImportInfos()) {
|
|
3027
|
+
const modulePath = importInfo.getFrom();
|
|
3028
|
+
if (!modulePath || !LIB_SO_MODULE_RE.test(modulePath))
|
|
3029
|
+
continue;
|
|
3030
|
+
const clause = importInfo.getImportClauseName();
|
|
3031
|
+
if (clause)
|
|
3032
|
+
set.add(clause);
|
|
3033
|
+
}
|
|
3034
|
+
if (set.size > 0)
|
|
3035
|
+
this.soImportBindingsByFile.set(relativePath, set);
|
|
2748
3036
|
}
|
|
2749
3037
|
indexModuleLocals(relativePath, language, result, arkFile, ns, parentId) {
|
|
2750
3038
|
const declaringClass = ns ? ns.getDefaultClass() : arkFile.getDefaultClass();
|
|
@@ -2838,8 +3126,10 @@ class ArkTSAdapter {
|
|
|
2838
3126
|
const qn = buildClassQualifiedName(cls);
|
|
2839
3127
|
const kind = classNodeKind(cls);
|
|
2840
3128
|
const { line, endLine, col } = linesFromPosition(cls.getOriginFullPosition());
|
|
3129
|
+
const classDecs = (0, migrate_semantics_1.encodeDecoratorEntries)(modelDecoratorEntries(cls));
|
|
2841
3130
|
const classNode = makeNode(relativePath, language, kind, displayName, qn, line, endLine, col, {
|
|
2842
3131
|
...modelModifiersToNodeExtras(cls),
|
|
3132
|
+
...(classDecs.length > 0 ? { decorators: classDecs } : {}),
|
|
2843
3133
|
});
|
|
2844
3134
|
this.addNode(result, classNode);
|
|
2845
3135
|
this.classToId.set(cls, classNode.id);
|
|
@@ -2867,7 +3157,10 @@ class ArkTSAdapter {
|
|
|
2867
3157
|
}
|
|
2868
3158
|
}
|
|
2869
3159
|
if (cls.hasComponentDecorator()) {
|
|
2870
|
-
const componentNode = makeNode(relativePath, language, 'component', displayName, qn, line, endLine, col, {
|
|
3160
|
+
const componentNode = makeNode(relativePath, language, 'component', displayName, qn, line, endLine, col, {
|
|
3161
|
+
...modelModifiersToNodeExtras(cls),
|
|
3162
|
+
...(classDecs.length > 0 ? { decorators: classDecs } : {}),
|
|
3163
|
+
});
|
|
2871
3164
|
this.addNode(result, componentNode);
|
|
2872
3165
|
this.componentToId.set(cls, componentNode.id);
|
|
2873
3166
|
try {
|
|
@@ -2953,10 +3246,10 @@ class ArkTSAdapter {
|
|
|
2953
3246
|
return;
|
|
2954
3247
|
const { line, endLine, col } = linesFromPosition(field.getOriginFullPosition());
|
|
2955
3248
|
const qn = buildFieldQualifiedName(field);
|
|
2956
|
-
const
|
|
3249
|
+
const encodedDecs = fieldDecoratorsEncoded(field);
|
|
2957
3250
|
const fieldNode = makeNode(relativePath, language, kind, name, qn, line, endLine, col, {
|
|
2958
3251
|
signature: field.getType()?.toString(),
|
|
2959
|
-
...(
|
|
3252
|
+
...(encodedDecs.length > 0 ? { decorators: encodedDecs } : {}),
|
|
2960
3253
|
...modelModifiersToNodeExtras(field),
|
|
2961
3254
|
});
|
|
2962
3255
|
this.addNode(result, fieldNode);
|
|
@@ -2982,19 +3275,19 @@ class ArkTSAdapter {
|
|
|
2982
3275
|
classToId: this.classToId,
|
|
2983
3276
|
fieldToId: this.fieldToId,
|
|
2984
3277
|
nodeIds: this.nodeIds,
|
|
2985
|
-
addEdge: (result, source, target, kind, callerFile, via, line) => this.addViewTreeEdge(result, source, target, kind, callerFile, via, line),
|
|
3278
|
+
addEdge: (result, source, target, kind, callerFile, via, line, extraMeta) => this.addViewTreeEdge(result, source, target, kind, callerFile, via, line, extraMeta),
|
|
2986
3279
|
ensureMethodNode: (method, relativePath, result, parentId) => this.ensureMethodNode(method, relativePath, result, parentId),
|
|
2987
3280
|
resolveClassNodeId: (cls) => this.resolveClassNodeId(cls),
|
|
2988
3281
|
resolveClassIdBySig: (sig, importer) => this.resolveClassIdBySig(sig, importer),
|
|
2989
3282
|
resolveMethodIdBySig: (sig, importer) => this.resolveMethodIdBySig(sig, importer),
|
|
2990
3283
|
resolveFieldNodeId: (field) => this.resolveFieldNodeId(field),
|
|
2991
3284
|
markFieldStateDecorators: (field, fieldNodeId, result) => {
|
|
2992
|
-
const
|
|
2993
|
-
if (
|
|
3285
|
+
const encoded = fieldDecoratorsEncoded(field);
|
|
3286
|
+
if (encoded.length === 0)
|
|
2994
3287
|
return;
|
|
2995
3288
|
const node = result.nodes.find((n) => n.id === fieldNodeId);
|
|
2996
3289
|
if (node)
|
|
2997
|
-
node.decorators =
|
|
3290
|
+
node.decorators = encoded;
|
|
2998
3291
|
},
|
|
2999
3292
|
};
|
|
3000
3293
|
}
|
|
@@ -3058,7 +3351,7 @@ class ArkTSAdapter {
|
|
|
3058
3351
|
// prefer ids from the importer's file before treating the name as ambiguous.
|
|
3059
3352
|
if (importer) {
|
|
3060
3353
|
try {
|
|
3061
|
-
const importerPath = normalizeRelPath(
|
|
3354
|
+
const importerPath = this.normalizeRelPath(importer.getDeclaringArkFile().getFilePath());
|
|
3062
3355
|
const sameFile = candidates.filter((id) => this.classIdFilePath.get(id) === importerPath);
|
|
3063
3356
|
if (sameFile.length > 0)
|
|
3064
3357
|
candidates = sameFile;
|
|
@@ -3201,7 +3494,7 @@ class ArkTSAdapter {
|
|
|
3201
3494
|
if (!displayName)
|
|
3202
3495
|
return null;
|
|
3203
3496
|
const arkFile = method.getDeclaringArkFile();
|
|
3204
|
-
const relativePath = normalizeRelPath(
|
|
3497
|
+
const relativePath = this.normalizeRelPath(arkFile.getFilePath());
|
|
3205
3498
|
if (!this.scanned.has(relativePath) || !isArkAnalyzerSourcePath(relativePath))
|
|
3206
3499
|
return null;
|
|
3207
3500
|
let lineCount = 1;
|
|
@@ -3218,7 +3511,7 @@ class ArkTSAdapter {
|
|
|
3218
3511
|
this.indexMethod(relativePath, 'arkts', result, method, parentId, kind, displayName);
|
|
3219
3512
|
return this.methodToId.get(method) ?? null;
|
|
3220
3513
|
}
|
|
3221
|
-
addViewTreeEdge(result, source, target, kind, callerFile, via, line) {
|
|
3514
|
+
addViewTreeEdge(result, source, target, kind, callerFile, via, line, extraMeta) {
|
|
3222
3515
|
const edgeKey = `${source}\0${target}\0${kind}\0${via}`;
|
|
3223
3516
|
if (this.emittedEdgeKeys.has(edgeKey))
|
|
3224
3517
|
return;
|
|
@@ -3228,6 +3521,7 @@ class ArkTSAdapter {
|
|
|
3228
3521
|
synthesizedBy: 'viewtree',
|
|
3229
3522
|
via,
|
|
3230
3523
|
registeredAt: `${callerFile}:${line}`,
|
|
3524
|
+
...(extraMeta ?? {}),
|
|
3231
3525
|
},
|
|
3232
3526
|
});
|
|
3233
3527
|
if (this.nodeInFile(target, callerFile)) {
|
|
@@ -3305,17 +3599,14 @@ class ArkTSAdapter {
|
|
|
3305
3599
|
return;
|
|
3306
3600
|
const baseLine = method.getImplOriginFullPosition()?.getFirstLine() ?? 1;
|
|
3307
3601
|
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;
|
|
3602
|
+
const pushCall = (symbol, matchIndex) => {
|
|
3603
|
+
if (!symbol)
|
|
3604
|
+
return;
|
|
3605
|
+
const lineOffset = code.slice(0, matchIndex).split('\n').length - 1;
|
|
3315
3606
|
const line = baseLine + lineOffset;
|
|
3316
3607
|
const dedupKey = `${symbol}:${line}`;
|
|
3317
3608
|
if (seenAtLine.has(dedupKey))
|
|
3318
|
-
|
|
3609
|
+
return;
|
|
3319
3610
|
seenAtLine.add(dedupKey);
|
|
3320
3611
|
result.unresolvedReferences.push({
|
|
3321
3612
|
fromNodeId,
|
|
@@ -3326,6 +3617,35 @@ class ArkTSAdapter {
|
|
|
3326
3617
|
filePath: relativePath,
|
|
3327
3618
|
language,
|
|
3328
3619
|
});
|
|
3620
|
+
};
|
|
3621
|
+
// Photos-style `Class_method` on any receiver.
|
|
3622
|
+
NAPI_MEMBER_CALL_RE.lastIndex = 0;
|
|
3623
|
+
let m;
|
|
3624
|
+
while ((m = NAPI_MEMBER_CALL_RE.exec(code)) !== null) {
|
|
3625
|
+
const symbol = m[1];
|
|
3626
|
+
if (!isNapiSymbolName(symbol))
|
|
3627
|
+
continue;
|
|
3628
|
+
pushCall(symbol, m.index);
|
|
3629
|
+
}
|
|
3630
|
+
// `import x from 'libFoo.so'` → `x.draw(` / `x.getTidByName(` (camelCase OK).
|
|
3631
|
+
const soBindings = this.soImportBindingsByFile.get(relativePath);
|
|
3632
|
+
if (soBindings) {
|
|
3633
|
+
for (const binding of soBindings) {
|
|
3634
|
+
const re = new RegExp(`\\b${escapeRegExp(binding)}\\s*\\.\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(`, 'g');
|
|
3635
|
+
while ((m = re.exec(code)) !== null) {
|
|
3636
|
+
pushCall(m[1], m.index);
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
// define_class / instance path: `this.inst?.addVerticalRuler(` — receiver is
|
|
3640
|
+
// not the import binding. Only in files that already import a lib*.so.
|
|
3641
|
+
const anyMemberRe = /\?\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(|\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
3642
|
+
while ((m = anyMemberRe.exec(code)) !== null) {
|
|
3643
|
+
const symbol = m[1] ?? m[2];
|
|
3644
|
+
if (!symbol)
|
|
3645
|
+
continue;
|
|
3646
|
+
// Skip photos-style names already handled above; keep camelCase / plain ids.
|
|
3647
|
+
pushCall(symbol, m.index);
|
|
3648
|
+
}
|
|
3329
3649
|
}
|
|
3330
3650
|
}
|
|
3331
3651
|
indexImports(relativePath, language, result, arkFile, fileId) {
|
|
@@ -3340,7 +3660,7 @@ class ArkTSAdapter {
|
|
|
3340
3660
|
if (exportInfo) {
|
|
3341
3661
|
const targetFile = exportInfo.getDeclaringArkFile();
|
|
3342
3662
|
if (targetFile) {
|
|
3343
|
-
const targetRel = normalizeRelPath(
|
|
3663
|
+
const targetRel = this.normalizeRelPath(targetFile.getFilePath());
|
|
3344
3664
|
const targetFileId = `file:${targetRel}`;
|
|
3345
3665
|
if (this.nodeIds.has(targetFileId)) {
|
|
3346
3666
|
result.edges.push(arkEdge(importNode.id, targetFileId, 'imports', {
|
|
@@ -3481,7 +3801,7 @@ function buildArkTSIndexByModuleInner(rootDir, scannedFiles, options) {
|
|
|
3481
3801
|
}
|
|
3482
3802
|
scene.analyseByModule((module, scn) => {
|
|
3483
3803
|
const moduleFileRels = [...module.getFilesMap().values()]
|
|
3484
|
-
.map((f) => normalizeRelPath(
|
|
3804
|
+
.map((f) => adapter.normalizeRelPath(f.getFilePath()))
|
|
3485
3805
|
.filter((p) => p && isArkAnalyzerSourcePath(p))
|
|
3486
3806
|
.map(normIndexPath);
|
|
3487
3807
|
adapter.indexModule(module, scn);
|
|
@@ -4237,13 +4557,81 @@ function attachOhosApiDbBinding(queries, binding) {
|
|
|
4237
4557
|
};
|
|
4238
4558
|
}
|
|
4239
4559
|
}
|
|
4560
|
+
/** Last-resort prebuilt npm package when the project's API version cannot be obtained. */
|
|
4561
|
+
const OHOS_API_FALLBACK_VERSION = '6.1.0';
|
|
4562
|
+
function runNpmPack(args, cwd) {
|
|
4563
|
+
const inv = process.platform === 'win32'
|
|
4564
|
+
? { cmd: 'cmd.exe', args: ['/d', '/s', '/c', ['npm', ...args].join(' ')] }
|
|
4565
|
+
: { cmd: 'npm', args };
|
|
4566
|
+
(0, child_process_1.execFileSync)(inv.cmd, inv.args, {
|
|
4567
|
+
cwd,
|
|
4568
|
+
stdio: 'pipe',
|
|
4569
|
+
timeout: 180_000,
|
|
4570
|
+
windowsHide: true,
|
|
4571
|
+
env: process.env,
|
|
4572
|
+
});
|
|
4573
|
+
}
|
|
4240
4574
|
/**
|
|
4241
|
-
*
|
|
4242
|
-
*
|
|
4575
|
+
* Fetch `homegraph-ohos-api-db-<version>` from npm and copy the db into
|
|
4576
|
+
* `~/.homegraph/api/`. Returns null when downloads are disabled, the package
|
|
4577
|
+
* is missing, or install fails. Opt out: HOMEGRAPH_OHOS_API_NO_DOWNLOAD=1.
|
|
4578
|
+
*/
|
|
4579
|
+
function tryDownloadOhosApiDbFromNpm(version) {
|
|
4580
|
+
if (process.env.HOMEGRAPH_OHOS_API_NO_DOWNLOAD === '1')
|
|
4581
|
+
return null;
|
|
4582
|
+
const dbPath = ohosApiDbInstallPath(version);
|
|
4583
|
+
const packageName = ohosApiDbPackageName(version);
|
|
4584
|
+
if (fs.existsSync(dbPath)) {
|
|
4585
|
+
return { version, dbPath, packageName, installed: false };
|
|
4586
|
+
}
|
|
4587
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'hg-ohos-api-npm-'));
|
|
4588
|
+
try {
|
|
4589
|
+
runNpmPack(['pack', packageName, '--silent'], tmp);
|
|
4590
|
+
const tgz = fs.readdirSync(tmp).find((f) => f.endsWith('.tgz'));
|
|
4591
|
+
if (!tgz)
|
|
4592
|
+
return null;
|
|
4593
|
+
const extractDir = path.join(tmp, 'extract');
|
|
4594
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
4595
|
+
(0, child_process_1.execFileSync)('tar', ['-xzf', path.join(tmp, tgz), '-C', extractDir], {
|
|
4596
|
+
stdio: 'pipe',
|
|
4597
|
+
timeout: 60_000,
|
|
4598
|
+
windowsHide: true,
|
|
4599
|
+
});
|
|
4600
|
+
const dbName = ohosApiDbFilename(version);
|
|
4601
|
+
const src = [
|
|
4602
|
+
path.join(extractDir, 'package', dbName),
|
|
4603
|
+
path.join(extractDir, dbName),
|
|
4604
|
+
].find((p) => fs.existsSync(p)) ?? null;
|
|
4605
|
+
if (!src)
|
|
4606
|
+
return null;
|
|
4607
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4608
|
+
fs.copyFileSync(src, dbPath);
|
|
4609
|
+
return { version, dbPath, packageName, installed: true };
|
|
4610
|
+
}
|
|
4611
|
+
catch {
|
|
4612
|
+
return null;
|
|
4613
|
+
}
|
|
4614
|
+
finally {
|
|
4615
|
+
try {
|
|
4616
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
4617
|
+
}
|
|
4618
|
+
catch {
|
|
4619
|
+
/* ignore */
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
/**
|
|
4624
|
+
* Build (or reuse) ~/.homegraph/api/ohos-api-<version>.db from a local SDK,
|
|
4625
|
+
* then npm prebuilts. Never throws — missing SDK / build / download failure
|
|
4626
|
+
* returns a warning.
|
|
4627
|
+
*
|
|
4628
|
+
* Order: existing db → local SDK build → npm pack for that version →
|
|
4629
|
+
* npm pack homegraph-ohos-api-db-6.1.0 fallback.
|
|
4243
4630
|
*/
|
|
4244
4631
|
async function ensureOhosApiDb(version, options = {}) {
|
|
4245
4632
|
const dbPath = ohosApiDbInstallPath(version);
|
|
4246
4633
|
const packageName = ohosApiDbPackageName(version);
|
|
4634
|
+
// 1. Local db for the requested version
|
|
4247
4635
|
if (fs.existsSync(dbPath)) {
|
|
4248
4636
|
return { version, dbPath, packageName, installed: false };
|
|
4249
4637
|
}
|
|
@@ -4255,6 +4643,7 @@ async function ensureOhosApiDb(version, options = {}) {
|
|
|
4255
4643
|
'Explore will use project code only.',
|
|
4256
4644
|
};
|
|
4257
4645
|
}
|
|
4646
|
+
// 2. Build from a local DevEco / OpenHarmony SDK
|
|
4258
4647
|
let sdkHome = null;
|
|
4259
4648
|
if (options.sdkHomeHint) {
|
|
4260
4649
|
const hintHomes = expandLocalOhosSdkHomes(options.sdkHomeHint);
|
|
@@ -4265,80 +4654,87 @@ async function ensureOhosApiDb(version, options = {}) {
|
|
|
4265
4654
|
if (!sdkHome) {
|
|
4266
4655
|
sdkHome = findLocalOhosSdkForVersion(version)?.sdkHome ?? null;
|
|
4267
4656
|
}
|
|
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
|
-
}
|
|
4657
|
+
if (sdkHome) {
|
|
4658
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
4319
4659
|
options.onProgress?.({
|
|
4320
4660
|
phase: 'ohos-api',
|
|
4321
|
-
current:
|
|
4661
|
+
current: 0,
|
|
4322
4662
|
total: 1,
|
|
4323
4663
|
currentFile: version,
|
|
4324
4664
|
});
|
|
4325
|
-
return { version, dbPath, packageName, installed: true };
|
|
4326
|
-
}
|
|
4327
|
-
catch (err) {
|
|
4328
4665
|
try {
|
|
4329
|
-
|
|
4666
|
+
const result = await indexOhosApiDb({
|
|
4667
|
+
sdkHome,
|
|
4668
|
+
version,
|
|
4669
|
+
outputPath: dbPath,
|
|
4670
|
+
onProgress: options.onProgress
|
|
4671
|
+
? (progress) => {
|
|
4672
|
+
options.onProgress?.({
|
|
4673
|
+
phase: 'ohos-api',
|
|
4674
|
+
current: progress.current,
|
|
4675
|
+
total: progress.total || 1,
|
|
4676
|
+
currentFile: progress.currentFile
|
|
4677
|
+
? `${version} ${path.basename(progress.currentFile)}`
|
|
4678
|
+
: version,
|
|
4679
|
+
subphase: progress.subphase,
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
: undefined,
|
|
4683
|
+
});
|
|
4684
|
+
if (result.success && fs.existsSync(dbPath)) {
|
|
4685
|
+
options.onProgress?.({
|
|
4686
|
+
phase: 'ohos-api',
|
|
4687
|
+
current: 1,
|
|
4688
|
+
total: 1,
|
|
4689
|
+
currentFile: version,
|
|
4690
|
+
});
|
|
4691
|
+
return { version, dbPath, packageName, installed: true };
|
|
4692
|
+
}
|
|
4693
|
+
try {
|
|
4330
4694
|
fs.unlinkSync(dbPath);
|
|
4695
|
+
}
|
|
4696
|
+
catch {
|
|
4697
|
+
/* ignore */
|
|
4698
|
+
}
|
|
4331
4699
|
}
|
|
4332
4700
|
catch {
|
|
4333
|
-
|
|
4701
|
+
try {
|
|
4702
|
+
if (fs.existsSync(dbPath))
|
|
4703
|
+
fs.unlinkSync(dbPath);
|
|
4704
|
+
}
|
|
4705
|
+
catch {
|
|
4706
|
+
/* ignore */
|
|
4707
|
+
}
|
|
4334
4708
|
}
|
|
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
4709
|
}
|
|
4710
|
+
// 3. Download prebuilt for the requested version from npm
|
|
4711
|
+
const fromNpm = tryDownloadOhosApiDbFromNpm(version);
|
|
4712
|
+
if (fromNpm)
|
|
4713
|
+
return fromNpm;
|
|
4714
|
+
// 4. Last resort: homegraph-ohos-api-db-6.1.0
|
|
4715
|
+
if (version !== OHOS_API_FALLBACK_VERSION) {
|
|
4716
|
+
const fallbackPath = ohosApiDbInstallPath(OHOS_API_FALLBACK_VERSION);
|
|
4717
|
+
if (fs.existsSync(fallbackPath)) {
|
|
4718
|
+
return {
|
|
4719
|
+
version: OHOS_API_FALLBACK_VERSION,
|
|
4720
|
+
dbPath: fallbackPath,
|
|
4721
|
+
packageName: ohosApiDbPackageName(OHOS_API_FALLBACK_VERSION),
|
|
4722
|
+
installed: false,
|
|
4723
|
+
};
|
|
4724
|
+
}
|
|
4725
|
+
const fallback = tryDownloadOhosApiDbFromNpm(OHOS_API_FALLBACK_VERSION);
|
|
4726
|
+
if (fallback)
|
|
4727
|
+
return fallback;
|
|
4728
|
+
}
|
|
4729
|
+
return {
|
|
4730
|
+
code: 'ohos_api_sdk_missing',
|
|
4731
|
+
message: `OHOS API db for ${version} unavailable (no local SDK build, npm ${packageName} failed` +
|
|
4732
|
+
(version !== OHOS_API_FALLBACK_VERSION
|
|
4733
|
+
? `, and fallback ${ohosApiDbPackageName(OHOS_API_FALLBACK_VERSION)} failed`
|
|
4734
|
+
: '') +
|
|
4735
|
+
'). Set OHOS_SDK_HOME / DEVECO_SDK_HOME or install the matching DevEco SDK. ' +
|
|
4736
|
+
'SDK API lookup disabled; project indexing continues normally.',
|
|
4737
|
+
};
|
|
4342
4738
|
}
|
|
4343
4739
|
/**
|
|
4344
4740
|
* Attach a prebuilt API db if present — never builds. Used on open / wireLayers.
|