@optave/codegraph 3.0.4 → 3.1.1
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 +59 -52
- package/grammars/tree-sitter-go.wasm +0 -0
- package/package.json +9 -10
- package/src/ast-analysis/rules/csharp.js +201 -0
- package/src/ast-analysis/rules/go.js +182 -0
- package/src/ast-analysis/rules/index.js +82 -0
- package/src/ast-analysis/rules/java.js +175 -0
- package/src/ast-analysis/rules/javascript.js +246 -0
- package/src/ast-analysis/rules/php.js +219 -0
- package/src/ast-analysis/rules/python.js +196 -0
- package/src/ast-analysis/rules/ruby.js +204 -0
- package/src/ast-analysis/rules/rust.js +173 -0
- package/src/ast-analysis/shared.js +223 -0
- package/src/ast.js +15 -28
- package/src/audit.js +4 -5
- package/src/boundaries.js +1 -1
- package/src/branch-compare.js +84 -79
- package/src/builder.js +274 -159
- package/src/cfg.js +111 -341
- package/src/check.js +3 -3
- package/src/cli.js +122 -167
- package/src/cochange.js +1 -1
- package/src/communities.js +13 -16
- package/src/complexity.js +196 -1239
- package/src/cycles.js +1 -1
- package/src/dataflow.js +274 -697
- package/src/db/connection.js +88 -0
- package/src/db/migrations.js +312 -0
- package/src/db/query-builder.js +280 -0
- package/src/db/repository.js +134 -0
- package/src/db.js +19 -392
- package/src/embedder.js +145 -141
- package/src/export.js +1 -1
- package/src/flow.js +160 -228
- package/src/index.js +36 -2
- package/src/kinds.js +49 -0
- package/src/manifesto.js +3 -8
- package/src/mcp.js +97 -20
- package/src/owners.js +132 -132
- package/src/parser.js +58 -131
- package/src/queries-cli.js +866 -0
- package/src/queries.js +1356 -2261
- package/src/resolve.js +11 -2
- package/src/result-formatter.js +21 -0
- package/src/sequence.js +364 -0
- package/src/structure.js +200 -199
- package/src/test-filter.js +7 -0
- package/src/triage.js +120 -162
- package/src/viewer.js +1 -1
package/src/mcp.js
CHANGED
|
@@ -113,6 +113,11 @@ const BASE_TOOLS = [
|
|
|
113
113
|
properties: {
|
|
114
114
|
file: { type: 'string', description: 'File path (partial match supported)' },
|
|
115
115
|
no_tests: { type: 'boolean', description: 'Exclude test files', default: false },
|
|
116
|
+
unused: {
|
|
117
|
+
type: 'boolean',
|
|
118
|
+
description: 'Show only exports with zero consumers',
|
|
119
|
+
default: false,
|
|
120
|
+
},
|
|
116
121
|
...PAGINATION_PROPS,
|
|
117
122
|
},
|
|
118
123
|
required: ['file'],
|
|
@@ -418,6 +423,43 @@ const BASE_TOOLS = [
|
|
|
418
423
|
},
|
|
419
424
|
},
|
|
420
425
|
},
|
|
426
|
+
{
|
|
427
|
+
name: 'sequence',
|
|
428
|
+
description:
|
|
429
|
+
'Generate a Mermaid sequence diagram from call graph edges. Participants are files, messages are function calls between them.',
|
|
430
|
+
inputSchema: {
|
|
431
|
+
type: 'object',
|
|
432
|
+
properties: {
|
|
433
|
+
name: {
|
|
434
|
+
type: 'string',
|
|
435
|
+
description: 'Entry point or function name to trace from (partial match)',
|
|
436
|
+
},
|
|
437
|
+
depth: { type: 'number', description: 'Max forward traversal depth', default: 10 },
|
|
438
|
+
format: {
|
|
439
|
+
type: 'string',
|
|
440
|
+
enum: ['mermaid', 'json'],
|
|
441
|
+
description: 'Output format (default: mermaid)',
|
|
442
|
+
},
|
|
443
|
+
dataflow: {
|
|
444
|
+
type: 'boolean',
|
|
445
|
+
description: 'Annotate with parameter names and return arrows',
|
|
446
|
+
default: false,
|
|
447
|
+
},
|
|
448
|
+
file: {
|
|
449
|
+
type: 'string',
|
|
450
|
+
description: 'Scope search to functions in this file (partial match)',
|
|
451
|
+
},
|
|
452
|
+
kind: {
|
|
453
|
+
type: 'string',
|
|
454
|
+
enum: EVERY_SYMBOL_KIND,
|
|
455
|
+
description: 'Filter to a specific symbol kind',
|
|
456
|
+
},
|
|
457
|
+
no_tests: { type: 'boolean', description: 'Exclude test files', default: false },
|
|
458
|
+
...PAGINATION_PROPS,
|
|
459
|
+
},
|
|
460
|
+
required: ['name'],
|
|
461
|
+
},
|
|
462
|
+
},
|
|
421
463
|
{
|
|
422
464
|
name: 'complexity',
|
|
423
465
|
description:
|
|
@@ -794,26 +836,27 @@ export async function startMCPServer(customDbPath, options = {}) {
|
|
|
794
836
|
process.exit(1);
|
|
795
837
|
}
|
|
796
838
|
|
|
797
|
-
//
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
fnDepsData,
|
|
804
|
-
fnImpactData,
|
|
805
|
-
pathData,
|
|
806
|
-
contextData,
|
|
807
|
-
childrenData,
|
|
808
|
-
explainData,
|
|
809
|
-
whereData,
|
|
810
|
-
diffImpactData,
|
|
811
|
-
listFunctionsData,
|
|
812
|
-
rolesData,
|
|
813
|
-
} = await import('./queries.js');
|
|
839
|
+
// Connect transport FIRST so the server can receive the client's
|
|
840
|
+
// `initialize` request while heavy modules (queries, better-sqlite3)
|
|
841
|
+
// are still loading. These are lazy-loaded on the first tool call
|
|
842
|
+
// and cached for subsequent calls.
|
|
843
|
+
let _queries;
|
|
844
|
+
let _Database;
|
|
814
845
|
|
|
815
|
-
|
|
816
|
-
|
|
846
|
+
async function getQueries() {
|
|
847
|
+
if (!_queries) {
|
|
848
|
+
_queries = await import('./queries.js');
|
|
849
|
+
}
|
|
850
|
+
return _queries;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function getDatabase() {
|
|
854
|
+
if (!_Database) {
|
|
855
|
+
const require = createRequire(import.meta.url);
|
|
856
|
+
_Database = require('better-sqlite3');
|
|
857
|
+
}
|
|
858
|
+
return _Database;
|
|
859
|
+
}
|
|
817
860
|
|
|
818
861
|
const server = new Server(
|
|
819
862
|
{ name: 'codegraph', version: '1.0.0' },
|
|
@@ -826,8 +869,24 @@ export async function startMCPServer(customDbPath, options = {}) {
|
|
|
826
869
|
|
|
827
870
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
828
871
|
const { name, arguments: args } = request.params;
|
|
829
|
-
|
|
830
872
|
try {
|
|
873
|
+
const {
|
|
874
|
+
impactAnalysisData,
|
|
875
|
+
moduleMapData,
|
|
876
|
+
fileDepsData,
|
|
877
|
+
exportsData,
|
|
878
|
+
fnDepsData,
|
|
879
|
+
fnImpactData,
|
|
880
|
+
pathData,
|
|
881
|
+
contextData,
|
|
882
|
+
childrenData,
|
|
883
|
+
explainData,
|
|
884
|
+
whereData,
|
|
885
|
+
diffImpactData,
|
|
886
|
+
listFunctionsData,
|
|
887
|
+
rolesData,
|
|
888
|
+
} = await getQueries();
|
|
889
|
+
const Database = getDatabase();
|
|
831
890
|
if (!multiRepo && args.repo) {
|
|
832
891
|
throw new Error(
|
|
833
892
|
'Multi-repo access is disabled. Restart with `codegraph mcp --multi-repo` to access other repositories.',
|
|
@@ -902,6 +961,7 @@ export async function startMCPServer(customDbPath, options = {}) {
|
|
|
902
961
|
case 'file_exports':
|
|
903
962
|
result = exportsData(args.file, dbPath, {
|
|
904
963
|
noTests: args.no_tests,
|
|
964
|
+
unused: args.unused,
|
|
905
965
|
limit: Math.min(args.limit ?? MCP_DEFAULTS.file_exports, MCP_MAX_LIMIT),
|
|
906
966
|
offset: args.offset ?? 0,
|
|
907
967
|
});
|
|
@@ -1165,6 +1225,23 @@ export async function startMCPServer(customDbPath, options = {}) {
|
|
|
1165
1225
|
}
|
|
1166
1226
|
break;
|
|
1167
1227
|
}
|
|
1228
|
+
case 'sequence': {
|
|
1229
|
+
const { sequenceData, sequenceToMermaid } = await import('./sequence.js');
|
|
1230
|
+
const seqResult = sequenceData(args.name, dbPath, {
|
|
1231
|
+
depth: args.depth,
|
|
1232
|
+
file: args.file,
|
|
1233
|
+
kind: args.kind,
|
|
1234
|
+
dataflow: args.dataflow,
|
|
1235
|
+
noTests: args.no_tests,
|
|
1236
|
+
limit: Math.min(args.limit ?? MCP_DEFAULTS.execution_flow, MCP_MAX_LIMIT),
|
|
1237
|
+
offset: args.offset ?? 0,
|
|
1238
|
+
});
|
|
1239
|
+
result =
|
|
1240
|
+
args.format === 'json'
|
|
1241
|
+
? seqResult
|
|
1242
|
+
: { text: sequenceToMermaid(seqResult), ...seqResult };
|
|
1243
|
+
break;
|
|
1244
|
+
}
|
|
1168
1245
|
case 'complexity': {
|
|
1169
1246
|
const { complexityData } = await import('./complexity.js');
|
|
1170
1247
|
result = complexityData(dbPath, {
|
package/src/owners.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { findDbPath, openReadonlyOrFail } from './db.js';
|
|
4
|
-
import {
|
|
4
|
+
import { outputResult } from './result-formatter.js';
|
|
5
|
+
import { isTestFile } from './test-filter.js';
|
|
5
6
|
|
|
6
7
|
// ─── CODEOWNERS Parsing ──────────────────────────────────────────────
|
|
7
8
|
|
|
@@ -163,142 +164,144 @@ export function ownersForFiles(filePaths, repoRoot) {
|
|
|
163
164
|
*/
|
|
164
165
|
export function ownersData(customDbPath, opts = {}) {
|
|
165
166
|
const db = openReadonlyOrFail(customDbPath);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
167
|
+
try {
|
|
168
|
+
const dbPath = findDbPath(customDbPath);
|
|
169
|
+
const repoRoot = path.resolve(path.dirname(dbPath), '..');
|
|
170
|
+
|
|
171
|
+
const parsed = parseCodeowners(repoRoot);
|
|
172
|
+
if (!parsed) {
|
|
173
|
+
return {
|
|
174
|
+
codeownersFile: null,
|
|
175
|
+
files: [],
|
|
176
|
+
symbols: [],
|
|
177
|
+
boundaries: [],
|
|
178
|
+
summary: {
|
|
179
|
+
totalFiles: 0,
|
|
180
|
+
ownedFiles: 0,
|
|
181
|
+
unownedFiles: 0,
|
|
182
|
+
coveragePercent: 0,
|
|
183
|
+
ownerCount: 0,
|
|
184
|
+
byOwner: [],
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
// Get all distinct files from nodes
|
|
190
|
+
let allFiles = db
|
|
191
|
+
.prepare('SELECT DISTINCT file FROM nodes')
|
|
192
|
+
.all()
|
|
193
|
+
.map((r) => r.file);
|
|
193
194
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
if (opts.noTests) allFiles = allFiles.filter((f) => !isTestFile(f));
|
|
196
|
+
if (opts.file) {
|
|
197
|
+
const filter = opts.file;
|
|
198
|
+
allFiles = allFiles.filter((f) => f.includes(filter));
|
|
199
|
+
}
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
201
|
+
// Map files to owners
|
|
202
|
+
const fileOwners = allFiles.map((file) => ({
|
|
203
|
+
file,
|
|
204
|
+
owners: matchOwners(file, parsed.rules),
|
|
205
|
+
}));
|
|
206
|
+
|
|
207
|
+
// Build owner-to-files index
|
|
208
|
+
const ownerIndex = new Map();
|
|
209
|
+
let ownedCount = 0;
|
|
210
|
+
for (const fo of fileOwners) {
|
|
211
|
+
if (fo.owners.length > 0) ownedCount++;
|
|
212
|
+
for (const o of fo.owners) {
|
|
213
|
+
if (!ownerIndex.has(o)) ownerIndex.set(o, []);
|
|
214
|
+
ownerIndex.get(o).push(fo.file);
|
|
215
|
+
}
|
|
214
216
|
}
|
|
215
|
-
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
// Filter files if --owner specified
|
|
219
|
+
let filteredFiles = fileOwners;
|
|
220
|
+
if (opts.owner) {
|
|
221
|
+
filteredFiles = fileOwners.filter((fo) => fo.owners.includes(opts.owner));
|
|
222
|
+
}
|
|
222
223
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
224
|
+
// Get symbols for filtered files
|
|
225
|
+
const fileSet = new Set(filteredFiles.map((fo) => fo.file));
|
|
226
|
+
let symbols = db
|
|
227
|
+
.prepare('SELECT name, kind, file, line FROM nodes')
|
|
228
|
+
.all()
|
|
229
|
+
.filter((n) => fileSet.has(n.file));
|
|
230
|
+
|
|
231
|
+
if (opts.noTests) symbols = symbols.filter((s) => !isTestFile(s.file));
|
|
232
|
+
if (opts.kind) symbols = symbols.filter((s) => s.kind === opts.kind);
|
|
233
|
+
|
|
234
|
+
const symbolsWithOwners = symbols.map((s) => ({
|
|
235
|
+
...s,
|
|
236
|
+
owners: matchOwners(s.file, parsed.rules),
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
// Boundary analysis — cross-owner call edges
|
|
240
|
+
const boundaries = [];
|
|
241
|
+
if (opts.boundary) {
|
|
242
|
+
const edges = db
|
|
243
|
+
.prepare(
|
|
244
|
+
`SELECT e.id, e.kind AS edgeKind,
|
|
245
|
+
s.name AS srcName, s.kind AS srcKind, s.file AS srcFile, s.line AS srcLine,
|
|
246
|
+
t.name AS tgtName, t.kind AS tgtKind, t.file AS tgtFile, t.line AS tgtLine
|
|
247
|
+
FROM edges e
|
|
248
|
+
JOIN nodes s ON e.source_id = s.id
|
|
249
|
+
JOIN nodes t ON e.target_id = t.id
|
|
250
|
+
WHERE e.kind = 'calls'`,
|
|
251
|
+
)
|
|
252
|
+
.all();
|
|
253
|
+
|
|
254
|
+
for (const e of edges) {
|
|
255
|
+
if (opts.noTests && (isTestFile(e.srcFile) || isTestFile(e.tgtFile))) continue;
|
|
256
|
+
const srcOwners = matchOwners(e.srcFile, parsed.rules);
|
|
257
|
+
const tgtOwners = matchOwners(e.tgtFile, parsed.rules);
|
|
258
|
+
// Cross-boundary: different owner sets
|
|
259
|
+
const srcKey = srcOwners.sort().join(',');
|
|
260
|
+
const tgtKey = tgtOwners.sort().join(',');
|
|
261
|
+
if (srcKey !== tgtKey) {
|
|
262
|
+
boundaries.push({
|
|
263
|
+
from: {
|
|
264
|
+
name: e.srcName,
|
|
265
|
+
kind: e.srcKind,
|
|
266
|
+
file: e.srcFile,
|
|
267
|
+
line: e.srcLine,
|
|
268
|
+
owners: srcOwners,
|
|
269
|
+
},
|
|
270
|
+
to: {
|
|
271
|
+
name: e.tgtName,
|
|
272
|
+
kind: e.tgtKind,
|
|
273
|
+
file: e.tgtFile,
|
|
274
|
+
line: e.tgtLine,
|
|
275
|
+
owners: tgtOwners,
|
|
276
|
+
},
|
|
277
|
+
edgeKind: e.edgeKind,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
278
280
|
}
|
|
279
281
|
}
|
|
280
|
-
}
|
|
281
282
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
}
|
|
283
|
+
// Summary
|
|
284
|
+
const byOwner = [...ownerIndex.entries()]
|
|
285
|
+
.map(([owner, files]) => ({ owner, fileCount: files.length }))
|
|
286
|
+
.sort((a, b) => b.fileCount - a.fileCount);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
codeownersFile: parsed.path,
|
|
290
|
+
files: filteredFiles,
|
|
291
|
+
symbols: symbolsWithOwners,
|
|
292
|
+
boundaries,
|
|
293
|
+
summary: {
|
|
294
|
+
totalFiles: allFiles.length,
|
|
295
|
+
ownedFiles: ownedCount,
|
|
296
|
+
unownedFiles: allFiles.length - ownedCount,
|
|
297
|
+
coveragePercent: allFiles.length > 0 ? Math.round((ownedCount / allFiles.length) * 100) : 0,
|
|
298
|
+
ownerCount: ownerIndex.size,
|
|
299
|
+
byOwner,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
} finally {
|
|
303
|
+
db.close();
|
|
304
|
+
}
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
// ─── CLI Display ─────────────────────────────────────────────────────
|
|
@@ -310,10 +313,7 @@ export function ownersData(customDbPath, opts = {}) {
|
|
|
310
313
|
*/
|
|
311
314
|
export function owners(customDbPath, opts = {}) {
|
|
312
315
|
const data = ownersData(customDbPath, opts);
|
|
313
|
-
if (opts
|
|
314
|
-
console.log(JSON.stringify(data, null, 2));
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
316
|
+
if (outputResult(data, null, opts)) return;
|
|
317
317
|
|
|
318
318
|
if (!data.codeownersFile) {
|
|
319
319
|
console.log('No CODEOWNERS file found.');
|
package/src/parser.js
CHANGED
|
@@ -183,133 +183,55 @@ function resolveEngine(opts = {}) {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
186
|
+
* Patch native engine output in-place for the few remaining semantic transforms.
|
|
187
|
+
* With #[napi(js_name)] on Rust types, most fields already arrive as camelCase.
|
|
188
|
+
* This only handles:
|
|
189
|
+
* - _lineCount compat for builder.js
|
|
190
|
+
* - Backward compat for older native binaries missing js_name annotations
|
|
191
|
+
* - dataflow argFlows/mutations bindingType → binding wrapper
|
|
188
192
|
*/
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
?
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
: undefined,
|
|
232
|
-
})),
|
|
233
|
-
calls: (result.calls || []).map((c) => ({
|
|
234
|
-
name: c.name,
|
|
235
|
-
line: c.line,
|
|
236
|
-
dynamic: c.dynamic,
|
|
237
|
-
receiver: c.receiver,
|
|
238
|
-
})),
|
|
239
|
-
imports: (result.imports || []).map((i) => ({
|
|
240
|
-
source: i.source,
|
|
241
|
-
names: i.names || [],
|
|
242
|
-
line: i.line,
|
|
243
|
-
typeOnly: i.typeOnly ?? i.type_only,
|
|
244
|
-
reexport: i.reexport,
|
|
245
|
-
wildcardReexport: i.wildcardReexport ?? i.wildcard_reexport,
|
|
246
|
-
pythonImport: i.pythonImport ?? i.python_import,
|
|
247
|
-
goImport: i.goImport ?? i.go_import,
|
|
248
|
-
rustUse: i.rustUse ?? i.rust_use,
|
|
249
|
-
javaImport: i.javaImport ?? i.java_import,
|
|
250
|
-
csharpUsing: i.csharpUsing ?? i.csharp_using,
|
|
251
|
-
rubyRequire: i.rubyRequire ?? i.ruby_require,
|
|
252
|
-
phpUse: i.phpUse ?? i.php_use,
|
|
253
|
-
})),
|
|
254
|
-
classes: (result.classes || []).map((c) => ({
|
|
255
|
-
name: c.name,
|
|
256
|
-
extends: c.extends,
|
|
257
|
-
implements: c.implements,
|
|
258
|
-
line: c.line,
|
|
259
|
-
})),
|
|
260
|
-
exports: (result.exports || []).map((e) => ({
|
|
261
|
-
name: e.name,
|
|
262
|
-
kind: e.kind,
|
|
263
|
-
line: e.line,
|
|
264
|
-
})),
|
|
265
|
-
astNodes: (result.astNodes ?? result.ast_nodes ?? []).map((n) => ({
|
|
266
|
-
kind: n.kind,
|
|
267
|
-
name: n.name,
|
|
268
|
-
line: n.line,
|
|
269
|
-
text: n.text ?? null,
|
|
270
|
-
receiver: n.receiver ?? null,
|
|
271
|
-
})),
|
|
272
|
-
dataflow: result.dataflow
|
|
273
|
-
? {
|
|
274
|
-
parameters: (result.dataflow.parameters || []).map((p) => ({
|
|
275
|
-
funcName: p.funcName,
|
|
276
|
-
paramName: p.paramName,
|
|
277
|
-
paramIndex: p.paramIndex,
|
|
278
|
-
line: p.line,
|
|
279
|
-
})),
|
|
280
|
-
returns: (result.dataflow.returns || []).map((r) => ({
|
|
281
|
-
funcName: r.funcName,
|
|
282
|
-
expression: r.expression ?? '',
|
|
283
|
-
referencedNames: r.referencedNames ?? [],
|
|
284
|
-
line: r.line,
|
|
285
|
-
})),
|
|
286
|
-
assignments: (result.dataflow.assignments || []).map((a) => ({
|
|
287
|
-
varName: a.varName,
|
|
288
|
-
callerFunc: a.callerFunc ?? null,
|
|
289
|
-
sourceCallName: a.sourceCallName,
|
|
290
|
-
expression: a.expression ?? '',
|
|
291
|
-
line: a.line,
|
|
292
|
-
})),
|
|
293
|
-
argFlows: (result.dataflow.argFlows ?? []).map((f) => ({
|
|
294
|
-
callerFunc: f.callerFunc ?? null,
|
|
295
|
-
calleeName: f.calleeName,
|
|
296
|
-
argIndex: f.argIndex,
|
|
297
|
-
argName: f.argName ?? null,
|
|
298
|
-
binding: f.bindingType ? { type: f.bindingType } : null,
|
|
299
|
-
confidence: f.confidence,
|
|
300
|
-
expression: f.expression ?? '',
|
|
301
|
-
line: f.line,
|
|
302
|
-
})),
|
|
303
|
-
mutations: (result.dataflow.mutations || []).map((m) => ({
|
|
304
|
-
funcName: m.funcName ?? null,
|
|
305
|
-
receiverName: m.receiverName,
|
|
306
|
-
binding: m.bindingType ? { type: m.bindingType } : null,
|
|
307
|
-
mutatingExpr: m.mutatingExpr,
|
|
308
|
-
line: m.line,
|
|
309
|
-
})),
|
|
310
|
-
}
|
|
311
|
-
: null,
|
|
312
|
-
};
|
|
193
|
+
function patchNativeResult(r) {
|
|
194
|
+
// lineCount: napi(js_name) emits "lineCount"; older binaries may emit "line_count"
|
|
195
|
+
r.lineCount = r.lineCount ?? r.line_count ?? null;
|
|
196
|
+
r._lineCount = r.lineCount;
|
|
197
|
+
|
|
198
|
+
// Backward compat for older binaries missing js_name annotations
|
|
199
|
+
if (r.definitions) {
|
|
200
|
+
for (const d of r.definitions) {
|
|
201
|
+
if (d.endLine === undefined && d.end_line !== undefined) {
|
|
202
|
+
d.endLine = d.end_line;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (r.imports) {
|
|
207
|
+
for (const i of r.imports) {
|
|
208
|
+
if (i.typeOnly === undefined) i.typeOnly = i.type_only;
|
|
209
|
+
if (i.wildcardReexport === undefined) i.wildcardReexport = i.wildcard_reexport;
|
|
210
|
+
if (i.pythonImport === undefined) i.pythonImport = i.python_import;
|
|
211
|
+
if (i.goImport === undefined) i.goImport = i.go_import;
|
|
212
|
+
if (i.rustUse === undefined) i.rustUse = i.rust_use;
|
|
213
|
+
if (i.javaImport === undefined) i.javaImport = i.java_import;
|
|
214
|
+
if (i.csharpUsing === undefined) i.csharpUsing = i.csharp_using;
|
|
215
|
+
if (i.rubyRequire === undefined) i.rubyRequire = i.ruby_require;
|
|
216
|
+
if (i.phpUse === undefined) i.phpUse = i.php_use;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// dataflow: wrap bindingType into binding object for argFlows and mutations
|
|
221
|
+
if (r.dataflow) {
|
|
222
|
+
if (r.dataflow.argFlows) {
|
|
223
|
+
for (const f of r.dataflow.argFlows) {
|
|
224
|
+
f.binding = f.bindingType ? { type: f.bindingType } : null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (r.dataflow.mutations) {
|
|
228
|
+
for (const m of r.dataflow.mutations) {
|
|
229
|
+
m.binding = m.bindingType ? { type: m.bindingType } : null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return r;
|
|
313
235
|
}
|
|
314
236
|
|
|
315
237
|
/**
|
|
@@ -440,8 +362,8 @@ export async function parseFileAuto(filePath, source, opts = {}) {
|
|
|
440
362
|
const { native } = resolveEngine(opts);
|
|
441
363
|
|
|
442
364
|
if (native) {
|
|
443
|
-
const result = native.parseFile(filePath, source, !!opts.dataflow);
|
|
444
|
-
return result ?
|
|
365
|
+
const result = native.parseFile(filePath, source, !!opts.dataflow, opts.ast !== false);
|
|
366
|
+
return result ? patchNativeResult(result) : null;
|
|
445
367
|
}
|
|
446
368
|
|
|
447
369
|
// WASM path
|
|
@@ -463,11 +385,16 @@ export async function parseFilesAuto(filePaths, rootDir, opts = {}) {
|
|
|
463
385
|
const result = new Map();
|
|
464
386
|
|
|
465
387
|
if (native) {
|
|
466
|
-
const nativeResults = native.parseFiles(
|
|
388
|
+
const nativeResults = native.parseFiles(
|
|
389
|
+
filePaths,
|
|
390
|
+
rootDir,
|
|
391
|
+
!!opts.dataflow,
|
|
392
|
+
opts.ast !== false,
|
|
393
|
+
);
|
|
467
394
|
for (const r of nativeResults) {
|
|
468
395
|
if (!r) continue;
|
|
469
396
|
const relPath = path.relative(rootDir, r.file).split(path.sep).join('/');
|
|
470
|
-
result.set(relPath,
|
|
397
|
+
result.set(relPath, patchNativeResult(r));
|
|
471
398
|
}
|
|
472
399
|
return result;
|
|
473
400
|
}
|
|
@@ -532,7 +459,7 @@ export function createParseTreeCache() {
|
|
|
532
459
|
export async function parseFileIncremental(cache, filePath, source, opts = {}) {
|
|
533
460
|
if (cache) {
|
|
534
461
|
const result = cache.parseFile(filePath, source);
|
|
535
|
-
return result ?
|
|
462
|
+
return result ? patchNativeResult(result) : null;
|
|
536
463
|
}
|
|
537
464
|
return parseFileAuto(filePath, source, opts);
|
|
538
465
|
}
|