@optave/codegraph 3.1.1 → 3.1.2
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/package.json +7 -7
- package/src/ast-analysis/engine.js +365 -0
- package/src/ast-analysis/metrics.js +118 -0
- package/src/ast-analysis/visitor-utils.js +176 -0
- package/src/ast-analysis/visitor.js +162 -0
- package/src/ast-analysis/visitors/ast-store-visitor.js +150 -0
- package/src/ast-analysis/visitors/cfg-visitor.js +792 -0
- package/src/ast-analysis/visitors/complexity-visitor.js +243 -0
- package/src/ast-analysis/visitors/dataflow-visitor.js +358 -0
- package/src/ast.js +13 -140
- package/src/audit.js +2 -87
- package/src/batch.js +0 -25
- package/src/boundaries.js +1 -1
- package/src/branch-compare.js +1 -96
- package/src/builder.js +48 -179
- package/src/cfg.js +89 -883
- package/src/check.js +1 -84
- package/src/cli.js +20 -19
- package/src/cochange.js +1 -39
- package/src/commands/audit.js +88 -0
- package/src/commands/batch.js +26 -0
- package/src/commands/branch-compare.js +97 -0
- package/src/commands/cfg.js +55 -0
- package/src/commands/check.js +82 -0
- package/src/commands/cochange.js +37 -0
- package/src/commands/communities.js +69 -0
- package/src/commands/complexity.js +77 -0
- package/src/commands/dataflow.js +110 -0
- package/src/commands/flow.js +70 -0
- package/src/commands/manifesto.js +77 -0
- package/src/commands/owners.js +52 -0
- package/src/commands/query.js +21 -0
- package/src/commands/sequence.js +33 -0
- package/src/commands/structure.js +64 -0
- package/src/commands/triage.js +49 -0
- package/src/communities.js +12 -83
- package/src/complexity.js +42 -356
- package/src/cycles.js +1 -1
- package/src/dataflow.js +12 -665
- package/src/db/repository/build-stmts.js +104 -0
- package/src/db/repository/cfg.js +83 -0
- package/src/db/repository/cochange.js +41 -0
- package/src/db/repository/complexity.js +15 -0
- package/src/db/repository/dataflow.js +12 -0
- package/src/db/repository/edges.js +259 -0
- package/src/db/repository/embeddings.js +40 -0
- package/src/db/repository/graph-read.js +39 -0
- package/src/db/repository/index.js +42 -0
- package/src/db/repository/nodes.js +236 -0
- package/src/db.js +40 -1
- package/src/embedder.js +14 -34
- package/src/export.js +1 -1
- package/src/extractors/javascript.js +130 -5
- package/src/flow.js +2 -70
- package/src/index.js +23 -19
- package/src/{result-formatter.js → infrastructure/result-formatter.js} +1 -1
- package/src/kinds.js +1 -0
- package/src/manifesto.js +0 -76
- package/src/owners.js +1 -56
- package/src/queries-cli.js +1 -1
- package/src/queries.js +79 -280
- package/src/sequence.js +5 -44
- package/src/structure.js +16 -75
- package/src/triage.js +1 -54
- package/src/viewer.js +1 -1
- package/src/watcher.js +7 -4
- package/src/db/repository.js +0 -134
- /package/src/{test-filter.js → infrastructure/test-filter.js} +0 -0
package/src/structure.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { normalizePath } from './constants.js';
|
|
3
|
-
import { openReadonlyOrFail, testFilterSQL } from './db.js';
|
|
3
|
+
import { getNodeId, openReadonlyOrFail, testFilterSQL } from './db.js';
|
|
4
|
+
import { isTestFile } from './infrastructure/test-filter.js';
|
|
4
5
|
import { debug } from './logger.js';
|
|
5
6
|
import { paginateResult } from './paginate.js';
|
|
6
|
-
import { isTestFile } from './test-filter.js';
|
|
7
7
|
|
|
8
8
|
// ─── Build-time: insert directory nodes, contains edges, and metrics ────
|
|
9
9
|
|
|
@@ -21,9 +21,12 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
21
21
|
const insertNode = db.prepare(
|
|
22
22
|
'INSERT OR IGNORE INTO nodes (name, kind, file, line, end_line) VALUES (?, ?, ?, ?, ?)',
|
|
23
23
|
);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const getNodeIdStmt = {
|
|
25
|
+
get: (name, kind, file, line) => {
|
|
26
|
+
const id = getNodeId(db, name, kind, file, line);
|
|
27
|
+
return id != null ? { id } : undefined;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
27
30
|
const insertEdge = db.prepare(
|
|
28
31
|
'INSERT INTO edges (source_id, target_id, kind, confidence, dynamic) VALUES (?, ?, ?, ?, ?)',
|
|
29
32
|
);
|
|
@@ -56,12 +59,12 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
56
59
|
}
|
|
57
60
|
// Delete metrics for changed files
|
|
58
61
|
for (const f of changedFiles) {
|
|
59
|
-
const fileRow =
|
|
62
|
+
const fileRow = getNodeIdStmt.get(f, 'file', f, 0);
|
|
60
63
|
if (fileRow) deleteMetricForNode.run(fileRow.id);
|
|
61
64
|
}
|
|
62
65
|
// Delete metrics for affected directories
|
|
63
66
|
for (const dir of affectedDirs) {
|
|
64
|
-
const dirRow =
|
|
67
|
+
const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
65
68
|
if (dirRow) deleteMetricForNode.run(dirRow.id);
|
|
66
69
|
}
|
|
67
70
|
})();
|
|
@@ -126,8 +129,8 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
126
129
|
if (!dir || dir === '.') continue;
|
|
127
130
|
// On incremental, skip dirs whose contains edges are intact
|
|
128
131
|
if (affectedDirs && !affectedDirs.has(dir)) continue;
|
|
129
|
-
const dirRow =
|
|
130
|
-
const fileRow =
|
|
132
|
+
const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
133
|
+
const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
|
|
131
134
|
if (dirRow && fileRow) {
|
|
132
135
|
insertEdge.run(dirRow.id, fileRow.id, 'contains', 1.0, 0);
|
|
133
136
|
}
|
|
@@ -138,8 +141,8 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
138
141
|
if (!parent || parent === '.' || parent === dir) continue;
|
|
139
142
|
// On incremental, skip parent dirs whose contains edges are intact
|
|
140
143
|
if (affectedDirs && !affectedDirs.has(parent)) continue;
|
|
141
|
-
const parentRow =
|
|
142
|
-
const childRow =
|
|
144
|
+
const parentRow = getNodeIdStmt.get(parent, 'directory', parent, 0);
|
|
145
|
+
const childRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
143
146
|
if (parentRow && childRow) {
|
|
144
147
|
insertEdge.run(parentRow.id, childRow.id, 'contains', 1.0, 0);
|
|
145
148
|
}
|
|
@@ -169,7 +172,7 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
169
172
|
|
|
170
173
|
const computeFileMetrics = db.transaction(() => {
|
|
171
174
|
for (const [relPath, symbols] of fileSymbols) {
|
|
172
|
-
const fileRow =
|
|
175
|
+
const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
|
|
173
176
|
if (!fileRow) continue;
|
|
174
177
|
|
|
175
178
|
const lineCount = lineCountMap.get(relPath) || 0;
|
|
@@ -263,7 +266,7 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
263
266
|
|
|
264
267
|
const computeDirMetrics = db.transaction(() => {
|
|
265
268
|
for (const [dir, files] of dirFiles) {
|
|
266
|
-
const dirRow =
|
|
269
|
+
const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
|
|
267
270
|
if (!dirRow) continue;
|
|
268
271
|
|
|
269
272
|
const fileCount = files.length;
|
|
@@ -640,68 +643,6 @@ export function moduleBoundariesData(customDbPath, opts = {}) {
|
|
|
640
643
|
}
|
|
641
644
|
}
|
|
642
645
|
|
|
643
|
-
// ─── Formatters ───────────────────────────────────────────────────────
|
|
644
|
-
|
|
645
|
-
export function formatStructure(data) {
|
|
646
|
-
if (data.count === 0) return 'No directory structure found. Run "codegraph build" first.';
|
|
647
|
-
|
|
648
|
-
const lines = [`\nProject structure (${data.count} directories):\n`];
|
|
649
|
-
for (const d of data.directories) {
|
|
650
|
-
const cohStr = d.cohesion !== null ? ` cohesion=${d.cohesion.toFixed(2)}` : '';
|
|
651
|
-
const depth = d.directory.split('/').length - 1;
|
|
652
|
-
const indent = ' '.repeat(depth);
|
|
653
|
-
lines.push(
|
|
654
|
-
`${indent}${d.directory}/ (${d.fileCount} files, ${d.symbolCount} symbols, <-${d.fanIn} ->${d.fanOut}${cohStr})`,
|
|
655
|
-
);
|
|
656
|
-
for (const f of d.files) {
|
|
657
|
-
lines.push(
|
|
658
|
-
`${indent} ${path.basename(f.file)} ${f.lineCount}L ${f.symbolCount}sym <-${f.fanIn} ->${f.fanOut}`,
|
|
659
|
-
);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
if (data.warning) {
|
|
663
|
-
lines.push('');
|
|
664
|
-
lines.push(`⚠ ${data.warning}`);
|
|
665
|
-
}
|
|
666
|
-
return lines.join('\n');
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
export function formatHotspots(data) {
|
|
670
|
-
if (data.hotspots.length === 0) return 'No hotspots found. Run "codegraph build" first.';
|
|
671
|
-
|
|
672
|
-
const lines = [`\nHotspots by ${data.metric} (${data.level}-level, top ${data.limit}):\n`];
|
|
673
|
-
let rank = 1;
|
|
674
|
-
for (const h of data.hotspots) {
|
|
675
|
-
const extra =
|
|
676
|
-
h.kind === 'directory'
|
|
677
|
-
? `${h.fileCount} files, cohesion=${h.cohesion !== null ? h.cohesion.toFixed(2) : 'n/a'}`
|
|
678
|
-
: `${h.lineCount || 0}L, ${h.symbolCount || 0} symbols`;
|
|
679
|
-
lines.push(
|
|
680
|
-
` ${String(rank++).padStart(2)}. ${h.name} <-${h.fanIn || 0} ->${h.fanOut || 0} (${extra})`,
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
return lines.join('\n');
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
export function formatModuleBoundaries(data) {
|
|
687
|
-
if (data.count === 0) return `No modules found with cohesion >= ${data.threshold}.`;
|
|
688
|
-
|
|
689
|
-
const lines = [`\nModule boundaries (cohesion >= ${data.threshold}, ${data.count} modules):\n`];
|
|
690
|
-
for (const m of data.modules) {
|
|
691
|
-
lines.push(
|
|
692
|
-
` ${m.directory}/ cohesion=${m.cohesion.toFixed(2)} (${m.fileCount} files, ${m.symbolCount} symbols)`,
|
|
693
|
-
);
|
|
694
|
-
lines.push(` Incoming: ${m.fanIn} edges Outgoing: ${m.fanOut} edges`);
|
|
695
|
-
if (m.files.length > 0) {
|
|
696
|
-
lines.push(
|
|
697
|
-
` Files: ${m.files.slice(0, 5).join(', ')}${m.files.length > 5 ? ` ... +${m.files.length - 5}` : ''}`,
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
lines.push('');
|
|
701
|
-
}
|
|
702
|
-
return lines.join('\n');
|
|
703
|
-
}
|
|
704
|
-
|
|
705
646
|
// ─── Helpers ──────────────────────────────────────────────────────────
|
|
706
647
|
|
|
707
648
|
function getSortFn(sortBy) {
|
package/src/triage.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { findNodesForTriage, openReadonlyOrFail } from './db.js';
|
|
2
|
+
import { isTestFile } from './infrastructure/test-filter.js';
|
|
2
3
|
import { warn } from './logger.js';
|
|
3
4
|
import { paginateResult } from './paginate.js';
|
|
4
|
-
import { outputResult } from './result-formatter.js';
|
|
5
|
-
import { isTestFile } from './test-filter.js';
|
|
6
5
|
|
|
7
6
|
// ─── Constants ────────────────────────────────────────────────────────
|
|
8
7
|
|
|
@@ -172,58 +171,6 @@ export function triageData(customDbPath, opts = {}) {
|
|
|
172
171
|
}
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
// ─── CLI Formatter ────────────────────────────────────────────────────
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Print triage results to console.
|
|
179
|
-
*
|
|
180
|
-
* @param {string} [customDbPath]
|
|
181
|
-
* @param {object} [opts]
|
|
182
|
-
*/
|
|
183
|
-
export function triage(customDbPath, opts = {}) {
|
|
184
|
-
const data = triageData(customDbPath, opts);
|
|
185
|
-
|
|
186
|
-
if (outputResult(data, 'items', opts)) return;
|
|
187
|
-
|
|
188
|
-
if (data.items.length === 0) {
|
|
189
|
-
if (data.summary.total === 0) {
|
|
190
|
-
console.log('\nNo symbols found. Run "codegraph build" first.\n');
|
|
191
|
-
} else {
|
|
192
|
-
console.log('\nNo symbols match the given filters.\n');
|
|
193
|
-
}
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
console.log('\n# Risk Audit Queue\n');
|
|
198
|
-
|
|
199
|
-
console.log(
|
|
200
|
-
` ${'Symbol'.padEnd(35)} ${'File'.padEnd(28)} ${'Role'.padEnd(8)} ${'Score'.padStart(6)} ${'Fan-In'.padStart(7)} ${'Cog'.padStart(4)} ${'Churn'.padStart(6)} ${'MI'.padStart(5)}`,
|
|
201
|
-
);
|
|
202
|
-
console.log(
|
|
203
|
-
` ${'─'.repeat(35)} ${'─'.repeat(28)} ${'─'.repeat(8)} ${'─'.repeat(6)} ${'─'.repeat(7)} ${'─'.repeat(4)} ${'─'.repeat(6)} ${'─'.repeat(5)}`,
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
for (const it of data.items) {
|
|
207
|
-
const name = it.name.length > 33 ? `${it.name.slice(0, 32)}…` : it.name;
|
|
208
|
-
const file = it.file.length > 26 ? `…${it.file.slice(-25)}` : it.file;
|
|
209
|
-
const role = (it.role || '-').padEnd(8);
|
|
210
|
-
const score = it.riskScore.toFixed(2).padStart(6);
|
|
211
|
-
const fanIn = String(it.fanIn).padStart(7);
|
|
212
|
-
const cog = String(it.cognitive).padStart(4);
|
|
213
|
-
const churn = String(it.churn).padStart(6);
|
|
214
|
-
const mi = it.maintainabilityIndex > 0 ? String(it.maintainabilityIndex).padStart(5) : ' -';
|
|
215
|
-
console.log(
|
|
216
|
-
` ${name.padEnd(35)} ${file.padEnd(28)} ${role} ${score} ${fanIn} ${cog} ${churn} ${mi}`,
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const s = data.summary;
|
|
221
|
-
console.log(
|
|
222
|
-
`\n ${s.analyzed} symbols scored (of ${s.total} total) | avg: ${s.avgScore.toFixed(2)} | max: ${s.maxScore.toFixed(2)} | sort: ${opts.sort || 'risk'}`,
|
|
223
|
-
);
|
|
224
|
-
console.log();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
174
|
// ─── Utilities ────────────────────────────────────────────────────────
|
|
228
175
|
|
|
229
176
|
function round4(n) {
|
package/src/viewer.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import Graph from 'graphology';
|
|
4
4
|
import louvain from 'graphology-communities-louvain';
|
|
5
|
-
import { isTestFile } from './test-filter.js';
|
|
5
|
+
import { isTestFile } from './infrastructure/test-filter.js';
|
|
6
6
|
|
|
7
7
|
const DEFAULT_MIN_CONFIDENCE = 0.5;
|
|
8
8
|
|
package/src/watcher.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { readFileSafe } from './builder.js';
|
|
4
4
|
import { appendChangeEvents, buildChangeEvent, diffSymbols } from './change-journal.js';
|
|
5
5
|
import { EXTENSIONS, IGNORE_DIRS, normalizePath } from './constants.js';
|
|
6
|
-
import { closeDb, initSchema, openDb } from './db.js';
|
|
6
|
+
import { closeDb, getNodeId as getNodeIdQuery, initSchema, openDb } from './db.js';
|
|
7
7
|
import { appendJournalEntries } from './journal.js';
|
|
8
8
|
import { info, warn } from './logger.js';
|
|
9
9
|
import { createParseTreeCache, getActiveEngine, parseFileIncremental } from './parser.js';
|
|
@@ -185,9 +185,12 @@ export async function watchProject(rootDir, opts = {}) {
|
|
|
185
185
|
insertNode: db.prepare(
|
|
186
186
|
'INSERT OR IGNORE INTO nodes (name, kind, file, line, end_line) VALUES (?, ?, ?, ?, ?)',
|
|
187
187
|
),
|
|
188
|
-
getNodeId:
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
getNodeId: {
|
|
189
|
+
get: (name, kind, file, line) => {
|
|
190
|
+
const id = getNodeIdQuery(db, name, kind, file, line);
|
|
191
|
+
return id != null ? { id } : undefined;
|
|
192
|
+
},
|
|
193
|
+
},
|
|
191
194
|
insertEdge: db.prepare(
|
|
192
195
|
'INSERT INTO edges (source_id, target_id, kind, confidence, dynamic) VALUES (?, ?, ?, ?, ?)',
|
|
193
196
|
),
|
package/src/db/repository.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { EVERY_SYMBOL_KIND, VALID_ROLES } from '../kinds.js';
|
|
2
|
-
import { NodeQuery } from './query-builder.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Find nodes matching a name pattern, with fan-in count.
|
|
6
|
-
* Used by findMatchingNodes in queries.js.
|
|
7
|
-
*
|
|
8
|
-
* @param {object} db - Database instance
|
|
9
|
-
* @param {string} namePattern - LIKE pattern (already wrapped with %)
|
|
10
|
-
* @param {object} [opts]
|
|
11
|
-
* @param {string[]} [opts.kinds] - Node kinds to match
|
|
12
|
-
* @param {string} [opts.file] - File filter (partial match)
|
|
13
|
-
* @returns {object[]}
|
|
14
|
-
*/
|
|
15
|
-
export function findNodesWithFanIn(db, namePattern, opts = {}) {
|
|
16
|
-
const q = new NodeQuery()
|
|
17
|
-
.select('n.*, COALESCE(fi.cnt, 0) AS fan_in')
|
|
18
|
-
.withFanIn()
|
|
19
|
-
.where('n.name LIKE ?', namePattern);
|
|
20
|
-
|
|
21
|
-
if (opts.kinds) {
|
|
22
|
-
q.kinds(opts.kinds);
|
|
23
|
-
}
|
|
24
|
-
if (opts.file) {
|
|
25
|
-
q.fileFilter(opts.file);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return q.all(db);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Fetch nodes for triage scoring: fan-in + complexity + churn.
|
|
33
|
-
* Used by triageData in triage.js.
|
|
34
|
-
*
|
|
35
|
-
* @param {object} db
|
|
36
|
-
* @param {object} [opts]
|
|
37
|
-
* @returns {object[]}
|
|
38
|
-
*/
|
|
39
|
-
export function findNodesForTriage(db, opts = {}) {
|
|
40
|
-
if (opts.kind && !EVERY_SYMBOL_KIND.includes(opts.kind)) {
|
|
41
|
-
throw new Error(`Invalid kind: ${opts.kind} (expected one of ${EVERY_SYMBOL_KIND.join(', ')})`);
|
|
42
|
-
}
|
|
43
|
-
if (opts.role && !VALID_ROLES.includes(opts.role)) {
|
|
44
|
-
throw new Error(`Invalid role: ${opts.role} (expected one of ${VALID_ROLES.join(', ')})`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const kindsToUse = opts.kind ? [opts.kind] : ['function', 'method', 'class'];
|
|
48
|
-
const q = new NodeQuery()
|
|
49
|
-
.select(
|
|
50
|
-
`n.id, n.name, n.kind, n.file, n.line, n.end_line, n.role,
|
|
51
|
-
COALESCE(fi.cnt, 0) AS fan_in,
|
|
52
|
-
COALESCE(fc.cognitive, 0) AS cognitive,
|
|
53
|
-
COALESCE(fc.maintainability_index, 0) AS mi,
|
|
54
|
-
COALESCE(fc.cyclomatic, 0) AS cyclomatic,
|
|
55
|
-
COALESCE(fc.max_nesting, 0) AS max_nesting,
|
|
56
|
-
COALESCE(fcc.commit_count, 0) AS churn`,
|
|
57
|
-
)
|
|
58
|
-
.kinds(kindsToUse)
|
|
59
|
-
.withFanIn()
|
|
60
|
-
.withComplexity()
|
|
61
|
-
.withChurn()
|
|
62
|
-
.excludeTests(opts.noTests)
|
|
63
|
-
.fileFilter(opts.file)
|
|
64
|
-
.roleFilter(opts.role)
|
|
65
|
-
.orderBy('n.file, n.line');
|
|
66
|
-
|
|
67
|
-
return q.all(db);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Shared query builder for function/method/class node listing.
|
|
72
|
-
* @param {object} [opts]
|
|
73
|
-
* @returns {NodeQuery}
|
|
74
|
-
*/
|
|
75
|
-
function _functionNodeQuery(opts = {}) {
|
|
76
|
-
return new NodeQuery()
|
|
77
|
-
.select('name, kind, file, line, end_line, role')
|
|
78
|
-
.kinds(['function', 'method', 'class'])
|
|
79
|
-
.fileFilter(opts.file)
|
|
80
|
-
.nameLike(opts.pattern)
|
|
81
|
-
.excludeTests(opts.noTests)
|
|
82
|
-
.orderBy('file, line');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* List function/method/class nodes with basic info.
|
|
87
|
-
* Used by listFunctionsData in queries.js.
|
|
88
|
-
*
|
|
89
|
-
* @param {object} db
|
|
90
|
-
* @param {object} [opts]
|
|
91
|
-
* @returns {object[]}
|
|
92
|
-
*/
|
|
93
|
-
export function listFunctionNodes(db, opts = {}) {
|
|
94
|
-
return _functionNodeQuery(opts).all(db);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Iterator version of listFunctionNodes for memory efficiency.
|
|
99
|
-
* Used by iterListFunctions in queries.js.
|
|
100
|
-
*
|
|
101
|
-
* @param {object} db
|
|
102
|
-
* @param {object} [opts]
|
|
103
|
-
* @returns {IterableIterator}
|
|
104
|
-
*/
|
|
105
|
-
export function iterateFunctionNodes(db, opts = {}) {
|
|
106
|
-
return _functionNodeQuery(opts).iterate(db);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Count total nodes.
|
|
111
|
-
* @param {object} db
|
|
112
|
-
* @returns {number}
|
|
113
|
-
*/
|
|
114
|
-
export function countNodes(db) {
|
|
115
|
-
return db.prepare('SELECT COUNT(*) AS cnt FROM nodes').get().cnt;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Count total edges.
|
|
120
|
-
* @param {object} db
|
|
121
|
-
* @returns {number}
|
|
122
|
-
*/
|
|
123
|
-
export function countEdges(db) {
|
|
124
|
-
return db.prepare('SELECT COUNT(*) AS cnt FROM edges').get().cnt;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Count distinct files.
|
|
129
|
-
* @param {object} db
|
|
130
|
-
* @returns {number}
|
|
131
|
-
*/
|
|
132
|
-
export function countFiles(db) {
|
|
133
|
-
return db.prepare('SELECT COUNT(DISTINCT file) AS cnt FROM nodes').get().cnt;
|
|
134
|
-
}
|
|
File without changes
|