typegraph-mcp 0.9.38 → 0.9.39
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/check.ts +55 -16
- package/cli.ts +129 -46
- package/dist/benchmark.js +2 -2
- package/dist/check.js +48 -18
- package/dist/cli.js +417 -97
- package/dist/module-graph.js +4 -3
- package/dist/server.js +261 -43
- package/dist/smoke-test.js +2 -2
- package/export-surface-test.ts +202 -0
- package/install-oxlint-test.ts +95 -0
- package/module-graph.ts +3 -3
- package/package.json +2 -2
- package/server.ts +375 -55
package/dist/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ __export(module_graph_exports, {
|
|
|
31
31
|
createResolver: () => createResolver,
|
|
32
32
|
discoverFiles: () => discoverFiles,
|
|
33
33
|
removeFile: () => removeFile,
|
|
34
|
+
resolveProjectImport: () => resolveProjectImport,
|
|
34
35
|
startWatcher: () => startWatcher,
|
|
35
36
|
updateFile: () => updateFile
|
|
36
37
|
});
|
|
@@ -136,7 +137,7 @@ function distToSource(resolvedPath, projectRoot3) {
|
|
|
136
137
|
}
|
|
137
138
|
return resolvedPath;
|
|
138
139
|
}
|
|
139
|
-
function
|
|
140
|
+
function resolveProjectImport(resolver, fromDir, specifier, projectRoot3) {
|
|
140
141
|
try {
|
|
141
142
|
const result = resolver.sync(fromDir, specifier);
|
|
142
143
|
if (result.path && !result.path.includes("node_modules")) {
|
|
@@ -187,7 +188,7 @@ function buildForwardEdges(files, resolver, projectRoot3) {
|
|
|
187
188
|
const edges = [];
|
|
188
189
|
const fromDir = path2.dirname(filePath);
|
|
189
190
|
for (const raw of rawImports) {
|
|
190
|
-
const target =
|
|
191
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot3);
|
|
191
192
|
if (target) {
|
|
192
193
|
edges.push({
|
|
193
194
|
target,
|
|
@@ -268,7 +269,7 @@ function updateFile(graph, filePath, resolver, projectRoot3) {
|
|
|
268
269
|
const fromDir = path2.dirname(filePath);
|
|
269
270
|
const newEdges = [];
|
|
270
271
|
for (const raw of rawImports) {
|
|
271
|
-
const target =
|
|
272
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot3);
|
|
272
273
|
if (target) {
|
|
273
274
|
newEdges.push({
|
|
274
275
|
target,
|
|
@@ -513,6 +514,22 @@ function hasDeclaredDependency(packageJson, packageName) {
|
|
|
513
514
|
return typeof deps === "object" && deps !== null && packageName in deps;
|
|
514
515
|
});
|
|
515
516
|
}
|
|
517
|
+
function findLintConfigs(projectRoot3) {
|
|
518
|
+
const configs = [];
|
|
519
|
+
for (const fileName of ESLINT_CONFIG_NAMES) {
|
|
520
|
+
const fullPath = path3.resolve(projectRoot3, fileName);
|
|
521
|
+
if (fs2.existsSync(fullPath)) {
|
|
522
|
+
configs.push({ tool: "ESLint", fileName, fullPath, propertyName: "ignores" });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
for (const fileName of OXLINT_CONFIG_NAMES) {
|
|
526
|
+
const fullPath = path3.resolve(projectRoot3, fileName);
|
|
527
|
+
if (fs2.existsSync(fullPath)) {
|
|
528
|
+
configs.push({ tool: "Oxlint", fileName, fullPath, propertyName: "ignorePatterns" });
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return configs;
|
|
532
|
+
}
|
|
516
533
|
async function main(configOverride) {
|
|
517
534
|
const { projectRoot: projectRoot3, tsconfigPath: tsconfigPath3, toolDir, toolIsEmbedded, toolRelPath } = configOverride ?? resolveConfig(import.meta.dirname);
|
|
518
535
|
let passed = 0;
|
|
@@ -690,8 +707,8 @@ async function main(configOverride) {
|
|
|
690
707
|
}
|
|
691
708
|
try {
|
|
692
709
|
const oxcParserReq = createRequire(path3.join(toolDir, "package.json"));
|
|
693
|
-
const { parseSync:
|
|
694
|
-
const result =
|
|
710
|
+
const { parseSync: parseSync3 } = await import(oxcParserReq.resolve("oxc-parser"));
|
|
711
|
+
const result = parseSync3("test.ts", 'import { x } from "./y";');
|
|
695
712
|
if (result.module?.staticImports?.length === 1) {
|
|
696
713
|
pass("oxc-parser working");
|
|
697
714
|
} else {
|
|
@@ -790,28 +807,28 @@ async function main(configOverride) {
|
|
|
790
807
|
);
|
|
791
808
|
}
|
|
792
809
|
if (toolIsEmbedded) {
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
if (eslintConfigFile) {
|
|
796
|
-
const eslintConfigPath = path3.resolve(projectRoot3, eslintConfigFile);
|
|
797
|
-
const eslintContent = fs2.readFileSync(eslintConfigPath, "utf-8");
|
|
810
|
+
const lintConfigs = findLintConfigs(projectRoot3);
|
|
811
|
+
if (lintConfigs.length > 0) {
|
|
798
812
|
const parentDir = path3.basename(path3.dirname(toolDir));
|
|
799
813
|
const parentIgnorePattern = new RegExp(`["']${parentDir}\\/\\*\\*["']`);
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
814
|
+
for (const config of lintConfigs) {
|
|
815
|
+
const content = fs2.readFileSync(config.fullPath, "utf-8");
|
|
816
|
+
const hasParentIgnore = parentIgnorePattern.test(content);
|
|
817
|
+
if (hasParentIgnore) {
|
|
818
|
+
pass(`${config.tool} ignores ${parentDir}/ (${config.fileName})`);
|
|
819
|
+
} else {
|
|
820
|
+
fail(
|
|
821
|
+
`${config.tool} missing ignore: "${parentDir}/**" (${config.fileName})`,
|
|
822
|
+
`Add to ${config.propertyName} in ${config.fileName}:
|
|
807
823
|
"${parentDir}/**",`
|
|
808
|
-
|
|
824
|
+
);
|
|
825
|
+
}
|
|
809
826
|
}
|
|
810
827
|
} else {
|
|
811
|
-
skip("
|
|
828
|
+
skip("Lint config check (no ESLint or Oxlint config found)");
|
|
812
829
|
}
|
|
813
830
|
} else {
|
|
814
|
-
skip("
|
|
831
|
+
skip("Lint config check (typegraph-mcp is external to project)");
|
|
815
832
|
}
|
|
816
833
|
const gitignorePath = path3.resolve(projectRoot3, ".gitignore");
|
|
817
834
|
if (fs2.existsSync(gitignorePath)) {
|
|
@@ -850,9 +867,23 @@ async function main(configOverride) {
|
|
|
850
867
|
console.log("");
|
|
851
868
|
return { passed, failed, warned };
|
|
852
869
|
}
|
|
870
|
+
var ESLINT_CONFIG_NAMES, OXLINT_CONFIG_NAMES;
|
|
853
871
|
var init_check = __esm({
|
|
854
872
|
"check.ts"() {
|
|
855
873
|
init_config();
|
|
874
|
+
ESLINT_CONFIG_NAMES = [
|
|
875
|
+
"eslint.config.mjs",
|
|
876
|
+
"eslint.config.js",
|
|
877
|
+
"eslint.config.ts",
|
|
878
|
+
"eslint.config.cjs"
|
|
879
|
+
];
|
|
880
|
+
OXLINT_CONFIG_NAMES = [
|
|
881
|
+
".oxlintrc.json",
|
|
882
|
+
"oxlint.config.ts",
|
|
883
|
+
"oxlint.config.js",
|
|
884
|
+
"oxlint.config.mjs",
|
|
885
|
+
"oxlint.config.cjs"
|
|
886
|
+
];
|
|
856
887
|
}
|
|
857
888
|
});
|
|
858
889
|
|
|
@@ -2307,6 +2338,7 @@ var init_benchmark = __esm({
|
|
|
2307
2338
|
var server_exports = {};
|
|
2308
2339
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2309
2340
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2341
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
2310
2342
|
import { z } from "zod";
|
|
2311
2343
|
import * as fs7 from "fs";
|
|
2312
2344
|
import * as path8 from "path";
|
|
@@ -2371,8 +2403,227 @@ async function resolveParams(params) {
|
|
|
2371
2403
|
}
|
|
2372
2404
|
return { error: "Either line+column or symbol must be provided" };
|
|
2373
2405
|
}
|
|
2406
|
+
function exportPriority(source) {
|
|
2407
|
+
switch (source) {
|
|
2408
|
+
case "local":
|
|
2409
|
+
return 3;
|
|
2410
|
+
case "re-export":
|
|
2411
|
+
return 2;
|
|
2412
|
+
case "star-re-export":
|
|
2413
|
+
return 1;
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
function exportKey(item) {
|
|
2417
|
+
return `${item.symbol}:${item.exportKind}`;
|
|
2418
|
+
}
|
|
2419
|
+
function sameExportOrigin(a, b) {
|
|
2420
|
+
return a.symbol === b.symbol && a.exportKind === b.exportKind && a.from === b.from && a.definedIn === b.definedIn && a.definedLine === b.definedLine;
|
|
2421
|
+
}
|
|
2422
|
+
function kindImpliesTypeOnly(kind) {
|
|
2423
|
+
return kind === "type" || kind === "interface";
|
|
2424
|
+
}
|
|
2425
|
+
function normalizeExportKindLabel(kind, exportKind) {
|
|
2426
|
+
if (exportKind === "type" && !kindImpliesTypeOnly(kind)) {
|
|
2427
|
+
return "type";
|
|
2428
|
+
}
|
|
2429
|
+
return kind;
|
|
2430
|
+
}
|
|
2431
|
+
function upsertExport(map, conflicts, nextExport) {
|
|
2432
|
+
const key = exportKey(nextExport);
|
|
2433
|
+
if (conflicts.has(key)) {
|
|
2434
|
+
if (nextExport.source === "star-re-export") return;
|
|
2435
|
+
conflicts.delete(key);
|
|
2436
|
+
map.set(key, nextExport);
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
const existing = map.get(key);
|
|
2440
|
+
if (existing && existing.source === "star-re-export" && nextExport.source === "star-re-export" && !sameExportOrigin(existing, nextExport)) {
|
|
2441
|
+
map.delete(key);
|
|
2442
|
+
conflicts.add(key);
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
if (!existing || exportPriority(nextExport.source) > exportPriority(existing.source)) {
|
|
2446
|
+
map.set(key, nextExport);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
function offsetToLineColumn(source, offset) {
|
|
2450
|
+
const safeOffset = Math.max(0, Math.min(offset ?? 0, source.length));
|
|
2451
|
+
const prefix = source.slice(0, safeOffset);
|
|
2452
|
+
const lines = prefix.split("\n");
|
|
2453
|
+
return {
|
|
2454
|
+
line: lines.length,
|
|
2455
|
+
column: (lines.at(-1)?.length ?? 0) + 1
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
function normalizeExistingPath(filePath) {
|
|
2459
|
+
const resolved = path8.resolve(filePath);
|
|
2460
|
+
try {
|
|
2461
|
+
return fs7.realpathSync.native(resolved);
|
|
2462
|
+
} catch {
|
|
2463
|
+
return resolved;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
function projectPath(file) {
|
|
2467
|
+
return path8.isAbsolute(file) ? relPath2(file) : file;
|
|
2468
|
+
}
|
|
2469
|
+
function exportSymbol(entry) {
|
|
2470
|
+
if (entry.exportName.kind === "Default") return "default";
|
|
2471
|
+
return entry.exportName.name ?? entry.localName.name ?? entry.importName.name;
|
|
2472
|
+
}
|
|
2473
|
+
function exportLookupOffset(entry) {
|
|
2474
|
+
if (entry.moduleRequest) {
|
|
2475
|
+
return entry.importName.start ?? entry.exportName.start ?? entry.start;
|
|
2476
|
+
}
|
|
2477
|
+
if (entry.exportName.kind === "Default") {
|
|
2478
|
+
return entry.localName.start ?? entry.exportName.start ?? entry.start;
|
|
2479
|
+
}
|
|
2480
|
+
return entry.exportName.start ?? entry.localName.start ?? entry.start;
|
|
2481
|
+
}
|
|
2482
|
+
async function resolveExportMetadata(file, line, column, fallbackKind) {
|
|
2483
|
+
const defs = await client.definition(file, line, column);
|
|
2484
|
+
const def = defs[0] ?? null;
|
|
2485
|
+
let info = await client.quickinfo(file, line, column);
|
|
2486
|
+
if ((!info || info.kind === "alias") && def) {
|
|
2487
|
+
info = await client.quickinfo(def.file, def.start.line, def.start.offset) ?? info;
|
|
2488
|
+
}
|
|
2489
|
+
return {
|
|
2490
|
+
kind: info?.kind ?? fallbackKind,
|
|
2491
|
+
type: info?.displayString ?? null,
|
|
2492
|
+
definedIn: projectPath(def?.file ?? file),
|
|
2493
|
+
definedLine: def?.start.line ?? null
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
async function getModuleExports(file, visited = /* @__PURE__ */ new Set()) {
|
|
2497
|
+
const relFile = path8.isAbsolute(file) ? relPath2(file) : file;
|
|
2498
|
+
const absFile = normalizeExistingPath(client.resolvePath(relFile));
|
|
2499
|
+
if (visited.has(absFile)) return [];
|
|
2500
|
+
const nextVisited = new Set(visited);
|
|
2501
|
+
nextVisited.add(absFile);
|
|
2502
|
+
const exportMap = /* @__PURE__ */ new Map();
|
|
2503
|
+
const conflictingStarExports = /* @__PURE__ */ new Set();
|
|
2504
|
+
let source;
|
|
2505
|
+
try {
|
|
2506
|
+
source = fs7.readFileSync(absFile, "utf-8");
|
|
2507
|
+
} catch {
|
|
2508
|
+
return [...exportMap.values()];
|
|
2509
|
+
}
|
|
2510
|
+
let parsed;
|
|
2511
|
+
try {
|
|
2512
|
+
parsed = parseSync2(absFile, source);
|
|
2513
|
+
} catch {
|
|
2514
|
+
return [...exportMap.values()];
|
|
2515
|
+
}
|
|
2516
|
+
for (const exp of parsed.module.staticExports) {
|
|
2517
|
+
for (const entry of exp.entries) {
|
|
2518
|
+
const moduleRequest = entry.moduleRequest;
|
|
2519
|
+
if (!moduleRequest) continue;
|
|
2520
|
+
const targetFile = resolveProjectImport(
|
|
2521
|
+
moduleResolver,
|
|
2522
|
+
path8.dirname(absFile),
|
|
2523
|
+
moduleRequest.value,
|
|
2524
|
+
projectRoot2
|
|
2525
|
+
);
|
|
2526
|
+
const exportLoc = offsetToLineColumn(
|
|
2527
|
+
source,
|
|
2528
|
+
entry.exportName.start ?? entry.localName.start ?? entry.importName.start ?? entry.start
|
|
2529
|
+
);
|
|
2530
|
+
const importKind = entry.importName.kind;
|
|
2531
|
+
const exportKind = entry.exportName.kind;
|
|
2532
|
+
if (importKind === "AllButDefault" && exportKind === "None") {
|
|
2533
|
+
if (!targetFile) continue;
|
|
2534
|
+
const nestedExports = await getModuleExports(targetFile, nextVisited);
|
|
2535
|
+
for (const nested of nestedExports) {
|
|
2536
|
+
if (nested.symbol === "default") continue;
|
|
2537
|
+
const starExportKind = entry.isType ? "type" : nested.exportKind;
|
|
2538
|
+
upsertExport(exportMap, conflictingStarExports, {
|
|
2539
|
+
...nested,
|
|
2540
|
+
line: exportLoc.line,
|
|
2541
|
+
exportKind: starExportKind,
|
|
2542
|
+
isTypeOnly: starExportKind === "type",
|
|
2543
|
+
source: "star-re-export",
|
|
2544
|
+
from: relPath2(targetFile)
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
const symbol = exportSymbol(entry);
|
|
2550
|
+
if (!symbol) continue;
|
|
2551
|
+
const importedSymbol = importKind === "Default" ? "default" : importKind === "Name" ? entry.importName.name : null;
|
|
2552
|
+
const nestedMatch = targetFile && importedSymbol ? (await getModuleExports(targetFile, nextVisited)).find(
|
|
2553
|
+
(item) => item.symbol === importedSymbol
|
|
2554
|
+
) ?? null : null;
|
|
2555
|
+
const lookupLoc = offsetToLineColumn(
|
|
2556
|
+
source,
|
|
2557
|
+
exportLookupOffset(entry)
|
|
2558
|
+
);
|
|
2559
|
+
const metadata = await resolveExportMetadata(
|
|
2560
|
+
relFile,
|
|
2561
|
+
lookupLoc.line,
|
|
2562
|
+
lookupLoc.column,
|
|
2563
|
+
importKind === "All" ? "namespace" : "alias"
|
|
2564
|
+
);
|
|
2565
|
+
const resolvedExportKind = entry.isType || nestedMatch?.exportKind === "type" || kindImpliesTypeOnly(nestedMatch?.kind ?? metadata.kind) ? "type" : "value";
|
|
2566
|
+
const resolvedKind = normalizeExportKindLabel(
|
|
2567
|
+
nestedMatch?.kind ?? metadata.kind,
|
|
2568
|
+
resolvedExportKind
|
|
2569
|
+
);
|
|
2570
|
+
upsertExport(exportMap, conflictingStarExports, {
|
|
2571
|
+
symbol,
|
|
2572
|
+
kind: resolvedKind,
|
|
2573
|
+
line: exportLoc.line,
|
|
2574
|
+
type: nestedMatch?.type ?? metadata.type,
|
|
2575
|
+
exportKind: resolvedExportKind,
|
|
2576
|
+
isTypeOnly: resolvedExportKind === "type",
|
|
2577
|
+
isNamespace: importKind === "All",
|
|
2578
|
+
source: "re-export",
|
|
2579
|
+
from: targetFile ? relPath2(targetFile) : moduleRequest.value,
|
|
2580
|
+
definedIn: nestedMatch?.definedIn ?? metadata.definedIn,
|
|
2581
|
+
definedLine: nestedMatch?.definedLine ?? metadata.definedLine
|
|
2582
|
+
});
|
|
2583
|
+
continue;
|
|
2584
|
+
}
|
|
2585
|
+
for (const entry of exp.entries) {
|
|
2586
|
+
const moduleRequest = entry.moduleRequest;
|
|
2587
|
+
if (moduleRequest) continue;
|
|
2588
|
+
const symbol = exportSymbol(entry);
|
|
2589
|
+
if (!symbol) continue;
|
|
2590
|
+
const exportLoc = offsetToLineColumn(
|
|
2591
|
+
source,
|
|
2592
|
+
entry.exportName.start ?? entry.localName.start ?? entry.start
|
|
2593
|
+
);
|
|
2594
|
+
const lookupLoc = offsetToLineColumn(source, exportLookupOffset(entry));
|
|
2595
|
+
const metadata = await resolveExportMetadata(
|
|
2596
|
+
relFile,
|
|
2597
|
+
lookupLoc.line,
|
|
2598
|
+
lookupLoc.column,
|
|
2599
|
+
entry.isType ? "type" : "value"
|
|
2600
|
+
);
|
|
2601
|
+
const resolvedExportKind = entry.isType || kindImpliesTypeOnly(metadata.kind) ? "type" : "value";
|
|
2602
|
+
const resolvedKind = normalizeExportKindLabel(metadata.kind, resolvedExportKind);
|
|
2603
|
+
if (resolvedExportKind === "value" && symbol !== "default" && !exportKinds.has(resolvedKind) && resolvedKind !== "namespace" && resolvedKind !== "class") {
|
|
2604
|
+
continue;
|
|
2605
|
+
}
|
|
2606
|
+
upsertExport(exportMap, conflictingStarExports, {
|
|
2607
|
+
symbol,
|
|
2608
|
+
kind: resolvedKind,
|
|
2609
|
+
line: exportLoc.line,
|
|
2610
|
+
type: metadata.type,
|
|
2611
|
+
exportKind: resolvedExportKind,
|
|
2612
|
+
isTypeOnly: resolvedExportKind === "type",
|
|
2613
|
+
isNamespace: false,
|
|
2614
|
+
source: "local",
|
|
2615
|
+
from: null,
|
|
2616
|
+
definedIn: relFile,
|
|
2617
|
+
definedLine: resolvedExportKind === "type" ? exportLoc.line : metadata.definedLine
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
return [...exportMap.values()].sort(
|
|
2622
|
+
(a, b) => a.line - b.line || a.symbol.localeCompare(b.symbol)
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2374
2625
|
function relPath2(absPath2) {
|
|
2375
|
-
return path8.relative(
|
|
2626
|
+
return path8.relative(normalizedProjectRoot, normalizeExistingPath(absPath2));
|
|
2376
2627
|
}
|
|
2377
2628
|
function absPath(file) {
|
|
2378
2629
|
return path8.isAbsolute(file) ? file : path8.resolve(projectRoot2, file);
|
|
@@ -2386,12 +2637,13 @@ async function main4() {
|
|
|
2386
2637
|
buildGraph(projectRoot2, tsconfigPath2)
|
|
2387
2638
|
]);
|
|
2388
2639
|
moduleGraph = graphResult.graph;
|
|
2640
|
+
moduleResolver = graphResult.resolver;
|
|
2389
2641
|
startWatcher(projectRoot2, moduleGraph, graphResult.resolver);
|
|
2390
2642
|
const transport = new StdioServerTransport();
|
|
2391
2643
|
await mcpServer.connect(transport);
|
|
2392
2644
|
log3("MCP server connected and ready");
|
|
2393
2645
|
}
|
|
2394
|
-
var projectRoot2, tsconfigPath2, log3, client, moduleGraph, mcpServer, locationOrSymbol;
|
|
2646
|
+
var projectRoot2, tsconfigPath2, log3, client, moduleGraph, moduleResolver, mcpServer, locationOrSymbol, exportKinds, normalizedProjectRoot;
|
|
2395
2647
|
var init_server = __esm({
|
|
2396
2648
|
"server.ts"() {
|
|
2397
2649
|
init_tsserver_client();
|
|
@@ -2411,6 +2663,18 @@ var init_server = __esm({
|
|
|
2411
2663
|
column: z.number().int().positive().optional().describe("Column/offset (1-based). Required if symbol is not provided."),
|
|
2412
2664
|
symbol: z.string().optional().describe("Symbol name to find. Alternative to line+column.")
|
|
2413
2665
|
};
|
|
2666
|
+
exportKinds = /* @__PURE__ */ new Set([
|
|
2667
|
+
"function",
|
|
2668
|
+
"const",
|
|
2669
|
+
"class",
|
|
2670
|
+
"interface",
|
|
2671
|
+
"type",
|
|
2672
|
+
"enum",
|
|
2673
|
+
"var",
|
|
2674
|
+
"let",
|
|
2675
|
+
"method"
|
|
2676
|
+
]);
|
|
2677
|
+
normalizedProjectRoot = normalizeExistingPath(projectRoot2);
|
|
2414
2678
|
mcpServer.tool(
|
|
2415
2679
|
"ts_find_symbol",
|
|
2416
2680
|
"Find a symbol's location in a file by name. Entry point for navigating without exact coordinates.",
|
|
@@ -2676,53 +2940,37 @@ var init_server = __esm({
|
|
|
2676
2940
|
);
|
|
2677
2941
|
mcpServer.tool(
|
|
2678
2942
|
"ts_module_exports",
|
|
2679
|
-
"List all exported symbols from a module with their resolved types. Gives an at-a-glance understanding of what a file provides.",
|
|
2943
|
+
"List all exported symbols from a module with their resolved types, including re-exports when possible. Gives an at-a-glance understanding of what a file provides.",
|
|
2680
2944
|
{
|
|
2681
2945
|
file: z.string().describe("File to inspect")
|
|
2682
2946
|
},
|
|
2683
2947
|
async ({ file }) => {
|
|
2684
|
-
const
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
}
|
|
2695
|
-
const moduleItem = bar.find((item) => item.kind === "module");
|
|
2696
|
-
const topItems = moduleItem?.childItems ?? bar;
|
|
2697
|
-
const exportKinds = /* @__PURE__ */ new Set([
|
|
2698
|
-
"function",
|
|
2699
|
-
"const",
|
|
2700
|
-
"class",
|
|
2701
|
-
"interface",
|
|
2702
|
-
"type",
|
|
2703
|
-
"enum",
|
|
2704
|
-
"var",
|
|
2705
|
-
"let",
|
|
2706
|
-
"method"
|
|
2707
|
-
]);
|
|
2708
|
-
const candidates = topItems.filter((item) => exportKinds.has(item.kind));
|
|
2709
|
-
const exports = [];
|
|
2710
|
-
for (const item of candidates) {
|
|
2711
|
-
if (!item.spans[0]) continue;
|
|
2712
|
-
const span = item.spans[0];
|
|
2713
|
-
const info = await client.quickinfo(file, span.start.line, span.start.offset);
|
|
2714
|
-
exports.push({
|
|
2715
|
-
symbol: item.text,
|
|
2716
|
-
kind: item.kind,
|
|
2717
|
-
line: span.start.line,
|
|
2718
|
-
type: info?.displayString ?? null
|
|
2719
|
-
});
|
|
2720
|
-
}
|
|
2948
|
+
const exports = await getModuleExports(file);
|
|
2949
|
+
const localCount = exports.filter((item) => item.source === "local").length;
|
|
2950
|
+
const reExportCount = exports.length - localCount;
|
|
2951
|
+
const typeOnlyCount = exports.filter((item) => item.isTypeOnly).length;
|
|
2952
|
+
const valueCount = exports.length - typeOnlyCount;
|
|
2953
|
+
const namespaceExportCount = exports.filter((item) => item.isNamespace).length;
|
|
2954
|
+
const hasLocalRuntimeExports = exports.some(
|
|
2955
|
+
(item) => item.source === "local" && !item.isTypeOnly
|
|
2956
|
+
);
|
|
2957
|
+
const isPrimarilyBarrel = exports.length > 0 && localCount < reExportCount;
|
|
2721
2958
|
return {
|
|
2722
2959
|
content: [
|
|
2723
2960
|
{
|
|
2724
2961
|
type: "text",
|
|
2725
|
-
text: JSON.stringify({
|
|
2962
|
+
text: JSON.stringify({
|
|
2963
|
+
file,
|
|
2964
|
+
exports,
|
|
2965
|
+
count: exports.length,
|
|
2966
|
+
localCount,
|
|
2967
|
+
reExportCount,
|
|
2968
|
+
typeOnlyCount,
|
|
2969
|
+
valueCount,
|
|
2970
|
+
namespaceExportCount,
|
|
2971
|
+
hasLocalRuntimeExports,
|
|
2972
|
+
isPrimarilyBarrel
|
|
2973
|
+
})
|
|
2726
2974
|
}
|
|
2727
2975
|
]
|
|
2728
2976
|
};
|
|
@@ -3342,13 +3590,11 @@ function ensureTsconfigExclude(projectRoot3) {
|
|
|
3342
3590
|
if (!fs8.existsSync(tsconfigPath3)) return;
|
|
3343
3591
|
try {
|
|
3344
3592
|
const raw = fs8.readFileSync(tsconfigPath3, "utf-8");
|
|
3345
|
-
const
|
|
3346
|
-
|
|
3347
|
-
const exclude = tsconfig.exclude || [];
|
|
3348
|
-
if (exclude.some((e) => e === "plugins" || e === "plugins/**" || e === "plugins/*")) {
|
|
3593
|
+
const excludeArrayMatch = raw.match(/("exclude"\s*:\s*\[)([\s\S]*?)(\])/);
|
|
3594
|
+
if (excludeArrayMatch && /["']plugins(?:\/\*\*|\/\*|)["']/.test(excludeArrayMatch[2])) {
|
|
3349
3595
|
return;
|
|
3350
3596
|
}
|
|
3351
|
-
if (
|
|
3597
|
+
if (excludeArrayMatch) {
|
|
3352
3598
|
const updated = raw.replace(
|
|
3353
3599
|
/("exclude"\s*:\s*\[)([\s\S]*?)(\])/,
|
|
3354
3600
|
(_match, open, items, close) => {
|
|
@@ -3376,40 +3622,114 @@ function ensureTsconfigExclude(projectRoot3) {
|
|
|
3376
3622
|
p.log.warn('Could not update tsconfig.json \u2014 manually add "plugins/**" to the exclude array to prevent build errors');
|
|
3377
3623
|
}
|
|
3378
3624
|
}
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3625
|
+
var ESLINT_CONFIG_NAMES2 = [
|
|
3626
|
+
"eslint.config.mjs",
|
|
3627
|
+
"eslint.config.js",
|
|
3628
|
+
"eslint.config.ts",
|
|
3629
|
+
"eslint.config.cjs"
|
|
3630
|
+
];
|
|
3631
|
+
var OXLINT_CONFIG_NAMES2 = [
|
|
3632
|
+
".oxlintrc.json",
|
|
3633
|
+
"oxlint.config.ts",
|
|
3634
|
+
"oxlint.config.js",
|
|
3635
|
+
"oxlint.config.mjs",
|
|
3636
|
+
"oxlint.config.cjs"
|
|
3637
|
+
];
|
|
3638
|
+
function findLintConfigs2(projectRoot3) {
|
|
3639
|
+
const configs = [];
|
|
3640
|
+
for (const fileName of ESLINT_CONFIG_NAMES2) {
|
|
3641
|
+
const fullPath = path9.resolve(projectRoot3, fileName);
|
|
3642
|
+
if (fs8.existsSync(fullPath)) {
|
|
3643
|
+
configs.push({ tool: "ESLint", fileName, fullPath, format: "flat" });
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
for (const fileName of OXLINT_CONFIG_NAMES2) {
|
|
3647
|
+
const fullPath = path9.resolve(projectRoot3, fileName);
|
|
3648
|
+
if (fs8.existsSync(fullPath)) {
|
|
3649
|
+
configs.push({
|
|
3650
|
+
tool: "Oxlint",
|
|
3651
|
+
fileName,
|
|
3652
|
+
fullPath,
|
|
3653
|
+
format: fileName.endsWith(".json") ? "json" : "module"
|
|
3395
3654
|
});
|
|
3396
|
-
fs8.writeFileSync(eslintConfigPath, updated);
|
|
3397
|
-
p.log.success(`Added "plugins/**" to ${eslintConfigFile} ignores`);
|
|
3398
|
-
return;
|
|
3399
3655
|
}
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3656
|
+
}
|
|
3657
|
+
return configs;
|
|
3658
|
+
}
|
|
3659
|
+
function appendToArrayLiteral(raw, propertyPattern, valueLiteral) {
|
|
3660
|
+
if (!propertyPattern.test(raw)) return null;
|
|
3661
|
+
return raw.replace(propertyPattern, (_match, open, items, close) => {
|
|
3662
|
+
const trimmed = items.trimEnd();
|
|
3663
|
+
const needsComma = trimmed.length > 0 && !trimmed.endsWith(",");
|
|
3664
|
+
return `${open}${items.trimEnd()}${needsComma ? "," : ""} ${valueLiteral}${close}`;
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
function insertTopLevelJsonArrayProperty(raw, propertyName, valueLiteral) {
|
|
3668
|
+
const lastBrace = raw.lastIndexOf("}");
|
|
3669
|
+
if (lastBrace === -1) return null;
|
|
3670
|
+
const before = raw.slice(0, lastBrace).trimEnd();
|
|
3671
|
+
const needsComma = !before.endsWith(",") && !before.endsWith("{");
|
|
3672
|
+
return `${before}${needsComma ? "," : ""}
|
|
3673
|
+
"${propertyName}": [${valueLiteral}]
|
|
3674
|
+
}
|
|
3404
3675
|
`;
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3676
|
+
}
|
|
3677
|
+
function patchEslintConfig(raw) {
|
|
3678
|
+
const updatedIgnores = appendToArrayLiteral(raw, /(ignores\s*:\s*\[)([\s\S]*?)(\])/, '"plugins/**"');
|
|
3679
|
+
if (updatedIgnores) return updatedIgnores;
|
|
3680
|
+
const exportArrayRe = /(export\s+default\s+(?:\w+\.config\(|\[))\s*\n?/;
|
|
3681
|
+
if (exportArrayRe.test(raw)) {
|
|
3682
|
+
return raw.replace(exportArrayRe, (match) => `${match} { ignores: ["plugins/**"] },
|
|
3683
|
+
`);
|
|
3684
|
+
}
|
|
3685
|
+
return null;
|
|
3686
|
+
}
|
|
3687
|
+
function patchOxlintJsonConfig(raw) {
|
|
3688
|
+
const updatedIgnores = appendToArrayLiteral(
|
|
3689
|
+
raw,
|
|
3690
|
+
/("ignorePatterns"\s*:\s*\[)([\s\S]*?)(\])/,
|
|
3691
|
+
'"plugins/**"'
|
|
3692
|
+
);
|
|
3693
|
+
if (updatedIgnores) return updatedIgnores;
|
|
3694
|
+
return insertTopLevelJsonArrayProperty(raw, "ignorePatterns", '"plugins/**"');
|
|
3695
|
+
}
|
|
3696
|
+
function patchOxlintModuleConfig(raw) {
|
|
3697
|
+
const updatedIgnores = appendToArrayLiteral(
|
|
3698
|
+
raw,
|
|
3699
|
+
/(ignorePatterns\s*:\s*\[)([\s\S]*?)(\])/,
|
|
3700
|
+
'"plugins/**"'
|
|
3701
|
+
);
|
|
3702
|
+
if (updatedIgnores) return updatedIgnores;
|
|
3703
|
+
const exportObjectRe = /(export\s+default\s*\{)\s*\n?/;
|
|
3704
|
+
if (exportObjectRe.test(raw)) {
|
|
3705
|
+
return raw.replace(exportObjectRe, (match) => `${match}
|
|
3706
|
+
ignorePatterns: ["plugins/**"],`);
|
|
3707
|
+
}
|
|
3708
|
+
return null;
|
|
3709
|
+
}
|
|
3710
|
+
function ensureLintIgnores(projectRoot3) {
|
|
3711
|
+
const configs = findLintConfigs2(projectRoot3);
|
|
3712
|
+
for (const config of configs) {
|
|
3713
|
+
try {
|
|
3714
|
+
const raw = fs8.readFileSync(config.fullPath, "utf-8");
|
|
3715
|
+
if (/["']plugins\/\*\*["']/.test(raw)) continue;
|
|
3716
|
+
const updated = config.tool === "ESLint" ? patchEslintConfig(raw) : config.format === "json" ? patchOxlintJsonConfig(raw) : patchOxlintModuleConfig(raw);
|
|
3717
|
+
if (updated) {
|
|
3718
|
+
fs8.writeFileSync(config.fullPath, updated);
|
|
3719
|
+
const propertyName = config.tool === "ESLint" ? "ignores" : "ignorePatterns";
|
|
3720
|
+
p.log.success(`Added "plugins/**" to ${config.fileName} ${propertyName}`);
|
|
3721
|
+
} else {
|
|
3722
|
+
const propertyName = config.tool === "ESLint" ? "ignores" : "ignorePatterns";
|
|
3723
|
+
p.log.warn(
|
|
3724
|
+
`Could not patch ${config.fileName} \u2014 manually add "plugins/**" to ${propertyName}`
|
|
3725
|
+
);
|
|
3726
|
+
}
|
|
3727
|
+
} catch {
|
|
3728
|
+
const propertyName = config.tool === "ESLint" ? "ignores" : "ignorePatterns";
|
|
3729
|
+
p.log.warn(
|
|
3730
|
+
`Could not update ${config.fileName} \u2014 manually add "plugins/**" to ${propertyName}`
|
|
3731
|
+
);
|
|
3409
3732
|
}
|
|
3410
|
-
p.log.warn(`Could not patch ${eslintConfigFile} \u2014 manually add "plugins/**" to the ignores array`);
|
|
3411
|
-
} catch {
|
|
3412
|
-
p.log.warn(`Could not update ${eslintConfigFile} \u2014 manually add "plugins/**" to the ignores array`);
|
|
3413
3733
|
}
|
|
3414
3734
|
}
|
|
3415
3735
|
function detectAgents(projectRoot3) {
|
|
@@ -3584,7 +3904,7 @@ async function setup(yes2) {
|
|
|
3584
3904
|
await setupAgentInstructions(projectRoot3, selectedAgents);
|
|
3585
3905
|
registerMcpServers(projectRoot3, selectedAgents);
|
|
3586
3906
|
ensureTsconfigExclude(projectRoot3);
|
|
3587
|
-
|
|
3907
|
+
ensureLintIgnores(projectRoot3);
|
|
3588
3908
|
await runVerification(targetDir, selectedAgents);
|
|
3589
3909
|
}
|
|
3590
3910
|
async function removePlugin(projectRoot3, pluginDir, options) {
|
package/dist/module-graph.js
CHANGED
|
@@ -117,7 +117,7 @@ function distToSource(resolvedPath, projectRoot) {
|
|
|
117
117
|
}
|
|
118
118
|
return resolvedPath;
|
|
119
119
|
}
|
|
120
|
-
function
|
|
120
|
+
function resolveProjectImport(resolver, fromDir, specifier, projectRoot) {
|
|
121
121
|
try {
|
|
122
122
|
const result = resolver.sync(fromDir, specifier);
|
|
123
123
|
if (result.path && !result.path.includes("node_modules")) {
|
|
@@ -168,7 +168,7 @@ function buildForwardEdges(files, resolver, projectRoot) {
|
|
|
168
168
|
const edges = [];
|
|
169
169
|
const fromDir = path.dirname(filePath);
|
|
170
170
|
for (const raw of rawImports) {
|
|
171
|
-
const target =
|
|
171
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot);
|
|
172
172
|
if (target) {
|
|
173
173
|
edges.push({
|
|
174
174
|
target,
|
|
@@ -249,7 +249,7 @@ function updateFile(graph, filePath, resolver, projectRoot) {
|
|
|
249
249
|
const fromDir = path.dirname(filePath);
|
|
250
250
|
const newEdges = [];
|
|
251
251
|
for (const raw of rawImports) {
|
|
252
|
-
const target =
|
|
252
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot);
|
|
253
253
|
if (target) {
|
|
254
254
|
newEdges.push({
|
|
255
255
|
target,
|
|
@@ -331,6 +331,7 @@ export {
|
|
|
331
331
|
createResolver,
|
|
332
332
|
discoverFiles,
|
|
333
333
|
removeFile,
|
|
334
|
+
resolveProjectImport,
|
|
334
335
|
startWatcher,
|
|
335
336
|
updateFile
|
|
336
337
|
};
|