@optave/codegraph 3.9.3 → 3.9.5
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/README.md +10 -10
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +14 -0
- package/dist/ast-analysis/visitor.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +2 -0
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli.js +24 -1
- package/dist/cli.js.map +1 -1
- package/dist/domain/graph/builder/context.d.ts +17 -0
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js +7 -0
- package/dist/domain/graph/builder/context.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +13 -2
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +30 -4
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +221 -51
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +67 -6
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +2 -2
- package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +58 -26
- package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +105 -55
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +27 -4
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/run-analyses.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/run-analyses.js +5 -20
- package/dist/domain/graph/builder/stages/run-analyses.js.map +1 -1
- package/dist/domain/graph/journal.d.ts +15 -0
- package/dist/domain/graph/journal.d.ts.map +1 -1
- package/dist/domain/graph/journal.js +283 -28
- package/dist/domain/graph/journal.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +17 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +23 -7
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +13 -4
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +174 -80
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.d.ts.map +1 -1
- package/dist/domain/search/generator.js +28 -2
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/wasm-worker-entry.d.ts +24 -0
- package/dist/domain/wasm-worker-entry.d.ts.map +1 -0
- package/dist/domain/wasm-worker-entry.js +643 -0
- package/dist/domain/wasm-worker-entry.js.map +1 -0
- package/dist/domain/wasm-worker-pool.d.ts +59 -0
- package/dist/domain/wasm-worker-pool.d.ts.map +1 -0
- package/dist/domain/wasm-worker-pool.js +312 -0
- package/dist/domain/wasm-worker-pool.js.map +1 -0
- package/dist/domain/wasm-worker-protocol.d.ts +65 -0
- package/dist/domain/wasm-worker-protocol.d.ts.map +1 -0
- package/dist/domain/wasm-worker-protocol.js +13 -0
- package/dist/domain/wasm-worker-protocol.js.map +1 -0
- package/dist/extractors/javascript.js +265 -1
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/features/boundaries.d.ts +2 -2
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +2 -31
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/snapshot.d.ts.map +1 -1
- package/dist/features/snapshot.js +99 -13
- package/dist/features/snapshot.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +14 -1
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/louvain.d.ts.map +1 -1
- package/dist/graph/algorithms/louvain.js +2 -4
- package/dist/graph/algorithms/louvain.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +12 -2
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/shared/globs.d.ts +40 -0
- package/dist/shared/globs.d.ts.map +1 -0
- package/dist/shared/globs.js +126 -0
- package/dist/shared/globs.js.map +1 -0
- package/dist/types.d.ts +26 -1
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/package.json +7 -7
- package/src/ast-analysis/visitor.ts +15 -0
- package/src/cli/commands/watch.ts +2 -0
- package/src/cli.ts +31 -8
- package/src/domain/graph/builder/context.ts +19 -0
- package/src/domain/graph/builder/helpers.ts +53 -3
- package/src/domain/graph/builder/pipeline.ts +235 -49
- package/src/domain/graph/builder/stages/build-edges.ts +80 -6
- package/src/domain/graph/builder/stages/build-structure.ts +2 -2
- package/src/domain/graph/builder/stages/collect-files.ts +56 -26
- package/src/domain/graph/builder/stages/detect-changes.ts +118 -61
- package/src/domain/graph/builder/stages/finalize.ts +27 -4
- package/src/domain/graph/builder/stages/run-analyses.ts +5 -26
- package/src/domain/graph/journal.ts +284 -27
- package/src/domain/graph/watcher.ts +29 -9
- package/src/domain/parser.ts +166 -73
- package/src/domain/search/generator.ts +34 -2
- package/src/domain/wasm-worker-entry.ts +788 -0
- package/src/domain/wasm-worker-pool.ts +330 -0
- package/src/domain/wasm-worker-protocol.ts +81 -0
- package/src/extractors/javascript.ts +290 -1
- package/src/features/boundaries.ts +2 -27
- package/src/features/snapshot.ts +93 -14
- package/src/features/structure.ts +17 -1
- package/src/graph/algorithms/louvain.ts +2 -4
- package/src/infrastructure/config.ts +12 -2
- package/src/shared/globs.ts +121 -0
- package/src/types.ts +26 -1
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import { performance } from 'node:perf_hooks';
|
|
10
11
|
import { debug, info } from '../../../../infrastructure/logger.js';
|
|
11
12
|
import { normalizePath } from '../../../../shared/constants.js';
|
|
13
|
+
import { compileGlobs } from '../../../../shared/globs.js';
|
|
12
14
|
import { readJournal } from '../../journal.js';
|
|
13
15
|
import type { PipelineContext } from '../context.js';
|
|
14
|
-
import { collectFiles as collectFilesUtil } from '../helpers.js';
|
|
16
|
+
import { collectFiles as collectFilesUtil, passesIncludeExclude } from '../helpers.js';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Reconstruct allFiles from DB file_hashes + journal deltas.
|
|
@@ -20,7 +22,7 @@ import { collectFiles as collectFilesUtil } from '../helpers.js';
|
|
|
20
22
|
function tryFastCollect(
|
|
21
23
|
ctx: PipelineContext,
|
|
22
24
|
): { files: string[]; directories: Set<string> } | null {
|
|
23
|
-
const { db, rootDir } = ctx;
|
|
25
|
+
const { db, rootDir, config } = ctx;
|
|
24
26
|
const useNative = ctx.engineName === 'native' && !!ctx.nativeDb?.getCollectFilesData;
|
|
25
27
|
|
|
26
28
|
// 1. Check that file_hashes table exists and has entries
|
|
@@ -70,10 +72,20 @@ function tryFastCollect(
|
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
// 5. Convert to absolute paths and compute directories
|
|
75
|
+
// 5. Convert to absolute paths and compute directories, honoring
|
|
76
|
+
// config.include / config.exclude globs so incremental builds reflect
|
|
77
|
+
// config changes (paths from the DB were collected under older config).
|
|
78
|
+
const includeRegexes = compileGlobs(config?.include);
|
|
79
|
+
const excludeRegexes = compileGlobs(config?.exclude);
|
|
80
|
+
const hasGlobFilters = includeRegexes.length > 0 || excludeRegexes.length > 0;
|
|
81
|
+
|
|
74
82
|
const files: string[] = [];
|
|
75
83
|
const directories = new Set<string>();
|
|
76
84
|
for (const relPath of fileSet) {
|
|
85
|
+
if (hasGlobFilters) {
|
|
86
|
+
const normRel = normalizePath(relPath);
|
|
87
|
+
if (!passesIncludeExclude(normRel, includeRegexes, excludeRegexes)) continue;
|
|
88
|
+
}
|
|
77
89
|
const absPath = path.join(rootDir, relPath);
|
|
78
90
|
files.push(absPath);
|
|
79
91
|
directories.add(path.dirname(absPath));
|
|
@@ -89,42 +101,60 @@ export async function collectFiles(ctx: PipelineContext): Promise<void> {
|
|
|
89
101
|
const { rootDir, config, opts } = ctx;
|
|
90
102
|
|
|
91
103
|
if (opts.scope) {
|
|
92
|
-
// Scoped rebuild: rebuild only specified files
|
|
104
|
+
// Scoped rebuild: rebuild only specified files.
|
|
105
|
+
//
|
|
106
|
+
// Timer only wraps the filesystem-walk portion (existence checks + file
|
|
107
|
+
// list construction). Change-detection outputs (parseChanges, removed,
|
|
108
|
+
// isFullBuild) are attributed to detectMs for semantic consistency with
|
|
109
|
+
// the non-scoped path, even though this stage computes them.
|
|
110
|
+
const start = performance.now();
|
|
93
111
|
const scopedFiles = opts.scope.map((f: string) => normalizePath(f));
|
|
94
112
|
const existing: Array<{ file: string; relPath: string }> = [];
|
|
95
113
|
const missing: string[] = [];
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
try {
|
|
115
|
+
for (const rel of scopedFiles) {
|
|
116
|
+
const abs = path.join(rootDir, rel);
|
|
117
|
+
if (fs.existsSync(abs)) {
|
|
118
|
+
existing.push({ file: abs, relPath: rel });
|
|
119
|
+
} else {
|
|
120
|
+
missing.push(rel);
|
|
121
|
+
}
|
|
102
122
|
}
|
|
123
|
+
ctx.allFiles = existing.map((e) => e.file);
|
|
124
|
+
ctx.discoveredDirs = new Set(existing.map((e) => path.dirname(e.file)));
|
|
125
|
+
} finally {
|
|
126
|
+
ctx.timing.collectMs = performance.now() - start;
|
|
103
127
|
}
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
// Change-detection outputs — timed under detectMs for semantic parity.
|
|
129
|
+
const detectStart = performance.now();
|
|
106
130
|
ctx.parseChanges = existing;
|
|
107
131
|
ctx.metadataUpdates = [];
|
|
108
132
|
ctx.removed = missing;
|
|
109
133
|
ctx.isFullBuild = false;
|
|
134
|
+
ctx.timing.detectMs = (ctx.timing.detectMs ?? 0) + (performance.now() - detectStart);
|
|
110
135
|
info(`Scoped rebuild: ${existing.length} files to rebuild, ${missing.length} to purge`);
|
|
111
136
|
return;
|
|
112
137
|
}
|
|
113
138
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
const start = performance.now();
|
|
140
|
+
try {
|
|
141
|
+
// Incremental fast path: reconstruct file list from DB + journal deltas
|
|
142
|
+
// instead of full recursive filesystem scan (~8ms savings on 473 files).
|
|
143
|
+
if (ctx.incremental && !ctx.forceFullRebuild) {
|
|
144
|
+
const fast = tryFastCollect(ctx);
|
|
145
|
+
if (fast) {
|
|
146
|
+
ctx.allFiles = fast.files;
|
|
147
|
+
ctx.discoveredDirs = fast.directories;
|
|
148
|
+
info(`Found ${ctx.allFiles.length} files (cached)`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
123
151
|
}
|
|
124
|
-
}
|
|
125
152
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
153
|
+
const collected = collectFilesUtil(rootDir, [], config, new Set<string>());
|
|
154
|
+
ctx.allFiles = collected.files;
|
|
155
|
+
ctx.discoveredDirs = collected.directories;
|
|
156
|
+
info(`Found ${ctx.allFiles.length} files to parse`);
|
|
157
|
+
} finally {
|
|
158
|
+
ctx.timing.collectMs = performance.now() - start;
|
|
159
|
+
}
|
|
130
160
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import { performance } from 'node:perf_hooks';
|
|
10
11
|
import { closeDb } from '../../../../db/index.js';
|
|
11
12
|
import { debug, info } from '../../../../infrastructure/logger.js';
|
|
12
13
|
import { normalizePath } from '../../../../shared/constants.js';
|
|
@@ -374,24 +375,73 @@ function purgeAndAddReverseDeps(
|
|
|
374
375
|
// Prefer NativeDatabase: purge + reverse-dep edge deletion in one transaction (#670)
|
|
375
376
|
if (ctx.engineName === 'native' && ctx.nativeDb?.purgeFilesData) {
|
|
376
377
|
ctx.nativeDb.purgeFilesData(filesToPurge, false, hasReverseDeps ? reverseDepList : undefined);
|
|
378
|
+
// Native path still reparses reverse-deps (works correctly with native edge builder)
|
|
379
|
+
for (const relPath of reverseDeps) {
|
|
380
|
+
const absPath = path.join(rootDir, relPath);
|
|
381
|
+
ctx.parseChanges.push({ file: absPath, relPath, _reverseDepOnly: true });
|
|
382
|
+
}
|
|
377
383
|
} else {
|
|
384
|
+
// WASM/JS path: save edges from reverse-dep files → changed files BEFORE
|
|
385
|
+
// purge, then reconnect them to new node IDs after insertNodes (#932, #933).
|
|
386
|
+
//
|
|
387
|
+
// purgeFilesFromGraph deletes edges in BOTH directions for changed files,
|
|
388
|
+
// which already removes the reverse-dep → changed-file edges. The old
|
|
389
|
+
// approach then over-deleted ALL outgoing edges from reverse-dep files and
|
|
390
|
+
// reparsed them to rebuild everything — expensive (87 extra parses) and
|
|
391
|
+
// lossy (442 missing edges due to imperfect resolution on rebuild).
|
|
392
|
+
//
|
|
393
|
+
// New approach: save the edge topology, let purge handle deletion, then
|
|
394
|
+
// reconnect using new node IDs. No reparse needed.
|
|
395
|
+
if (hasReverseDeps && hasPurge) {
|
|
396
|
+
const changePathSet = new Set(changePaths);
|
|
397
|
+
const saveEdgesStmt = db.prepare(`
|
|
398
|
+
SELECT e.source_id, n_tgt.name AS tgt_name, n_tgt.kind AS tgt_kind,
|
|
399
|
+
n_tgt.file AS tgt_file, n_tgt.line AS tgt_line,
|
|
400
|
+
e.kind AS edge_kind, e.confidence, e.dynamic,
|
|
401
|
+
n_src.file AS src_file
|
|
402
|
+
FROM edges e
|
|
403
|
+
JOIN nodes n_src ON e.source_id = n_src.id
|
|
404
|
+
JOIN nodes n_tgt ON e.target_id = n_tgt.id
|
|
405
|
+
WHERE n_tgt.file = ? AND n_src.file != n_tgt.file
|
|
406
|
+
`);
|
|
407
|
+
for (const changedPath of changePaths) {
|
|
408
|
+
for (const row of saveEdgesStmt.all(changedPath) as Array<{
|
|
409
|
+
source_id: number;
|
|
410
|
+
tgt_name: string;
|
|
411
|
+
tgt_kind: string;
|
|
412
|
+
tgt_file: string;
|
|
413
|
+
tgt_line: number;
|
|
414
|
+
edge_kind: string;
|
|
415
|
+
confidence: number;
|
|
416
|
+
dynamic: number;
|
|
417
|
+
src_file: string;
|
|
418
|
+
}>) {
|
|
419
|
+
// Skip edges whose source is also being purged — buildEdges will
|
|
420
|
+
// re-create them with correct new IDs.
|
|
421
|
+
if (changePathSet.has(row.src_file)) continue;
|
|
422
|
+
ctx.savedReverseDepEdges.push({
|
|
423
|
+
sourceId: row.source_id,
|
|
424
|
+
tgtName: row.tgt_name,
|
|
425
|
+
tgtKind: row.tgt_kind,
|
|
426
|
+
tgtFile: row.tgt_file,
|
|
427
|
+
tgtLine: row.tgt_line,
|
|
428
|
+
edgeKind: row.edge_kind,
|
|
429
|
+
confidence: row.confidence,
|
|
430
|
+
dynamic: row.dynamic,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
debug(`Saved ${ctx.savedReverseDepEdges.length} reverse-dep edges for reconnection`);
|
|
435
|
+
}
|
|
436
|
+
|
|
378
437
|
if (hasPurge) {
|
|
379
438
|
purgeFilesFromGraph(db, filesToPurge, { purgeHashes: false });
|
|
380
439
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
);
|
|
385
|
-
for (const relPath of reverseDepList) {
|
|
386
|
-
deleteOutgoingEdgesForFile.run(relPath);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
440
|
+
// No outgoing-edge deletion for reverse-deps — purge already removed
|
|
441
|
+
// edges targeting the changed files, and other outgoing edges are valid.
|
|
442
|
+
// No reverse-deps added to parseChanges — no reparse needed.
|
|
389
443
|
}
|
|
390
444
|
}
|
|
391
|
-
for (const relPath of reverseDeps) {
|
|
392
|
-
const absPath = path.join(rootDir, relPath);
|
|
393
|
-
ctx.parseChanges.push({ file: absPath, relPath, _reverseDepOnly: true });
|
|
394
|
-
}
|
|
395
445
|
}
|
|
396
446
|
|
|
397
447
|
function detectHasEmbeddings(db: BetterSqlite3Database, nativeDb?: NativeDatabase): boolean {
|
|
@@ -463,59 +513,66 @@ function handleIncrementalBuild(ctx: PipelineContext): void {
|
|
|
463
513
|
}
|
|
464
514
|
|
|
465
515
|
export async function detectChanges(ctx: PipelineContext): Promise<void> {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
.
|
|
483
|
-
.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
if (
|
|
516
|
+
const start = performance.now();
|
|
517
|
+
try {
|
|
518
|
+
const { db, allFiles, rootDir, incremental, forceFullRebuild, opts } = ctx;
|
|
519
|
+
if ((opts as Record<string, unknown>).scope) {
|
|
520
|
+
handleScopedBuild(ctx);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const increResult =
|
|
524
|
+
incremental && !forceFullRebuild
|
|
525
|
+
? getChangedFiles(db, allFiles, rootDir)
|
|
526
|
+
: {
|
|
527
|
+
changed: allFiles.map((f): ChangedFile => ({ file: f })),
|
|
528
|
+
removed: [] as string[],
|
|
529
|
+
isFullBuild: true,
|
|
530
|
+
};
|
|
531
|
+
ctx.removed = increResult.removed;
|
|
532
|
+
ctx.isFullBuild = increResult.isFullBuild;
|
|
533
|
+
ctx.parseChanges = increResult.changed
|
|
534
|
+
.filter((c) => !c.metadataOnly)
|
|
535
|
+
.map((c) => ({
|
|
536
|
+
file: c.file,
|
|
537
|
+
relPath: c.relPath,
|
|
538
|
+
content: c.content,
|
|
539
|
+
hash: c.hash,
|
|
540
|
+
stat: c.stat ? { mtime: Math.floor(c.stat.mtimeMs), size: c.stat.size } : undefined,
|
|
541
|
+
_reverseDepOnly: c._reverseDepOnly,
|
|
542
|
+
}));
|
|
543
|
+
ctx.metadataUpdates = increResult.changed
|
|
544
|
+
.filter(
|
|
545
|
+
(c): c is ChangedFile & { relPath: string; hash: string; stat: FileStat } =>
|
|
546
|
+
!!c.metadataOnly && !!c.relPath && !!c.hash && !!c.stat,
|
|
547
|
+
)
|
|
548
|
+
.map((c) => ({
|
|
549
|
+
relPath: c.relPath,
|
|
550
|
+
hash: c.hash,
|
|
551
|
+
stat: { mtime: Math.floor(c.stat.mtimeMs), size: c.stat.size },
|
|
552
|
+
}));
|
|
553
|
+
if (!ctx.isFullBuild && ctx.parseChanges.length === 0 && ctx.removed.length === 0) {
|
|
554
|
+
const ranAnalysis = await runPendingAnalysis(ctx);
|
|
555
|
+
if (ranAnalysis) {
|
|
556
|
+
closeDb(db);
|
|
557
|
+
writeJournalHeader(rootDir, Date.now());
|
|
558
|
+
ctx.earlyExit = true;
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
healMetadata(ctx);
|
|
562
|
+
info('No changes detected. Graph is up to date.');
|
|
504
563
|
closeDb(db);
|
|
505
564
|
writeJournalHeader(rootDir, Date.now());
|
|
506
565
|
ctx.earlyExit = true;
|
|
507
566
|
return;
|
|
508
567
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
} else {
|
|
519
|
-
handleIncrementalBuild(ctx);
|
|
568
|
+
if (ctx.isFullBuild) {
|
|
569
|
+
handleFullBuild(ctx);
|
|
570
|
+
} else {
|
|
571
|
+
handleIncrementalBuild(ctx);
|
|
572
|
+
}
|
|
573
|
+
} finally {
|
|
574
|
+
// Additive to respect any partial detectMs contribution from collectFiles
|
|
575
|
+
// (scoped-rebuild path splits change-detection outputs across both stages).
|
|
576
|
+
ctx.timing.detectMs = (ctx.timing.detectMs ?? 0) + (performance.now() - start);
|
|
520
577
|
}
|
|
521
578
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* WASM cleanup, stats logging, drift detection, build metadata, registry, journal.
|
|
5
5
|
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
6
7
|
import { tmpdir } from 'node:os';
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
import { performance } from 'node:perf_hooks';
|
|
@@ -81,28 +82,50 @@ function persistBuildMetadata(
|
|
|
81
82
|
): void {
|
|
82
83
|
const useNativeDb = ctx.engineName === 'native' && !!ctx.nativeDb;
|
|
83
84
|
if (!ctx.isFullBuild && ctx.allSymbols.size <= 3) return;
|
|
85
|
+
// When the native engine is active, persist the Rust addon version so that
|
|
86
|
+
// checkEngineSchemaMismatch compares against the same value on the next build.
|
|
87
|
+
// Writing CODEGRAPH_VERSION (the npm package version) here would create a
|
|
88
|
+
// permanent mismatch whenever npm and crate versions diverge, forcing every
|
|
89
|
+
// subsequent build to be a full rebuild.
|
|
90
|
+
const codeVersionToWrite =
|
|
91
|
+
ctx.engineName === 'native' && ctx.engineVersion ? ctx.engineVersion : CODEGRAPH_VERSION;
|
|
92
|
+
// Persist the repo root so downstream commands (e.g. `codegraph embed`)
|
|
93
|
+
// can resolve relative file paths regardless of the invoking cwd.
|
|
94
|
+
// Use realpathSync (symlink-resolving) to match the Rust engine's
|
|
95
|
+
// std::fs::canonicalize — otherwise the JS write here would overwrite the
|
|
96
|
+
// canonical path Rust wrote for native full builds and could re-introduce
|
|
97
|
+
// a non-canonical path when the project root is behind a symlink.
|
|
98
|
+
const resolvedRootDir = path.resolve(ctx.rootDir);
|
|
99
|
+
let rootDirToWrite = resolvedRootDir;
|
|
100
|
+
try {
|
|
101
|
+
rootDirToWrite = fs.realpathSync(resolvedRootDir);
|
|
102
|
+
} catch {
|
|
103
|
+
/* realpath can fail (e.g. path no longer exists); fall back to resolve() */
|
|
104
|
+
}
|
|
84
105
|
try {
|
|
85
106
|
if (useNativeDb) {
|
|
86
107
|
ctx.nativeDb!.setBuildMeta(
|
|
87
108
|
Object.entries({
|
|
88
109
|
engine: ctx.engineName,
|
|
89
|
-
engine_version:
|
|
90
|
-
codegraph_version:
|
|
110
|
+
engine_version: codeVersionToWrite,
|
|
111
|
+
codegraph_version: codeVersionToWrite,
|
|
91
112
|
schema_version: String(ctx.schemaVersion),
|
|
92
113
|
built_at: buildNow.toISOString(),
|
|
93
114
|
node_count: String(nodeCount),
|
|
94
115
|
edge_count: String(actualEdgeCount),
|
|
116
|
+
root_dir: rootDirToWrite,
|
|
95
117
|
}).map(([key, value]) => ({ key, value: String(value) })),
|
|
96
118
|
);
|
|
97
119
|
} else {
|
|
98
120
|
setBuildMeta(ctx.db, {
|
|
99
121
|
engine: ctx.engineName,
|
|
100
|
-
engine_version:
|
|
101
|
-
codegraph_version:
|
|
122
|
+
engine_version: codeVersionToWrite,
|
|
123
|
+
codegraph_version: codeVersionToWrite,
|
|
102
124
|
schema_version: String(ctx.schemaVersion),
|
|
103
125
|
built_at: buildNow.toISOString(),
|
|
104
126
|
node_count: nodeCount,
|
|
105
127
|
edge_count: actualEdgeCount,
|
|
128
|
+
root_dir: rootDirToWrite,
|
|
106
129
|
});
|
|
107
130
|
}
|
|
108
131
|
} catch (err) {
|
|
@@ -2,39 +2,18 @@
|
|
|
2
2
|
* Stage: runAnalyses
|
|
3
3
|
*
|
|
4
4
|
* Dispatches to the unified AST analysis engine (AST nodes, complexity, CFG, dataflow).
|
|
5
|
-
*
|
|
5
|
+
* Reverse-dep files are no longer in allSymbols (they are not reparsed since #932/#933),
|
|
6
|
+
* so no filtering is needed here.
|
|
6
7
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import type { ExtractorOutput } from '../../../../types.js';
|
|
8
|
+
import { warn } from '../../../../infrastructure/logger.js';
|
|
9
9
|
import type { PipelineContext } from '../context.js';
|
|
10
10
|
|
|
11
11
|
export async function runAnalyses(ctx: PipelineContext): Promise<void> {
|
|
12
|
-
const { db, allSymbols, rootDir, opts, engineOpts
|
|
13
|
-
|
|
14
|
-
// For incremental builds, exclude reverse-dep-only files
|
|
15
|
-
let astComplexitySymbols: Map<string, ExtractorOutput> = allSymbols;
|
|
16
|
-
if (!isFullBuild) {
|
|
17
|
-
const reverseDepFiles = new Set(
|
|
18
|
-
filesToParse
|
|
19
|
-
.filter((item) => (item as { _reverseDepOnly?: boolean })._reverseDepOnly)
|
|
20
|
-
.map((item) => item.relPath),
|
|
21
|
-
);
|
|
22
|
-
if (reverseDepFiles.size > 0) {
|
|
23
|
-
astComplexitySymbols = new Map();
|
|
24
|
-
for (const [relPath, symbols] of allSymbols) {
|
|
25
|
-
if (!reverseDepFiles.has(relPath)) {
|
|
26
|
-
astComplexitySymbols.set(relPath, symbols);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
debug(
|
|
30
|
-
`AST/complexity/CFG/dataflow: processing ${astComplexitySymbols.size} changed files (skipping ${reverseDepFiles.size} reverse-deps)`,
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
12
|
+
const { db, allSymbols, rootDir, opts, engineOpts } = ctx;
|
|
34
13
|
|
|
35
14
|
const { runAnalyses: runAnalysesFn } = await import('../../../../ast-analysis/engine.js');
|
|
36
15
|
try {
|
|
37
|
-
const analysisTiming = await runAnalysesFn(db,
|
|
16
|
+
const analysisTiming = await runAnalysesFn(db, allSymbols, rootDir, opts, engineOpts);
|
|
38
17
|
ctx.timing.astMs = analysisTiming.astMs;
|
|
39
18
|
ctx.timing.complexityMs = analysisTiming.complexityMs;
|
|
40
19
|
ctx.timing.cfgMs = analysisTiming.cfgMs;
|