@snevins/repo-mapper 1.0.3
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/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/cache.d.ts +21 -0
- package/dist/cache.js +123 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +65 -0
- package/dist/files.d.ts +20 -0
- package/dist/files.js +206 -0
- package/dist/graph.d.ts +6 -0
- package/dist/graph.js +77 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/dist/languages.d.ts +40 -0
- package/dist/languages.js +609 -0
- package/dist/output.d.ts +19 -0
- package/dist/output.js +135 -0
- package/dist/pagerank.d.ts +6 -0
- package/dist/pagerank.js +155 -0
- package/dist/parser.d.ts +31 -0
- package/dist/parser.js +344 -0
- package/dist/ranking.d.ts +13 -0
- package/dist/ranking.js +55 -0
- package/dist/tokens.d.ts +10 -0
- package/dist/tokens.js +23 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.js +1 -0
- package/package.json +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFile, stat } from "node:fs/promises";
|
|
3
|
+
import { resolve, relative } from "node:path";
|
|
4
|
+
import { parseCliArgs } from "./cli.js";
|
|
5
|
+
import { loadCache, getCachedTags, setCacheEntry, saveCache } from "./cache.js";
|
|
6
|
+
import { discoverFiles, DEFAULT_IGNORED_PATTERNS } from "./files.js";
|
|
7
|
+
import { initParser, parseFile, shutdownParser } from "./parser.js";
|
|
8
|
+
import { buildFileGraph } from "./graph.js";
|
|
9
|
+
import { computePageRank } from "./pagerank.js";
|
|
10
|
+
import { buildPersonalization, rankDefinitions } from "./ranking.js";
|
|
11
|
+
import { fitToTokenBudget } from "./output.js";
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = parseCliArgs(process.argv.slice(2));
|
|
14
|
+
const { options } = args;
|
|
15
|
+
// Determine root directory (first path or cwd)
|
|
16
|
+
const firstPath = args.paths[0];
|
|
17
|
+
const rootDir = firstPath !== undefined ? resolve(firstPath) : process.cwd();
|
|
18
|
+
// Resolve focus files to relative paths
|
|
19
|
+
const focusFiles = options.focus.map((f) => relative(rootDir, resolve(f)));
|
|
20
|
+
const focusSet = new Set(focusFiles);
|
|
21
|
+
if (options.verbose) {
|
|
22
|
+
console.error(`Root: ${rootDir}`);
|
|
23
|
+
console.error(`Focus files: ${String(focusFiles.length)}`);
|
|
24
|
+
}
|
|
25
|
+
// 1. Discover files (apply ignore patterns unless --no-ignore)
|
|
26
|
+
const ignoredPatterns = options.noIgnore
|
|
27
|
+
? [...options.ignore] // custom patterns only
|
|
28
|
+
: [...DEFAULT_IGNORED_PATTERNS, ...options.ignore]; // defaults + custom
|
|
29
|
+
const discoveryResult = await discoverFiles({
|
|
30
|
+
rootDir,
|
|
31
|
+
ignoredPatterns: ignoredPatterns.length > 0 ? ignoredPatterns : undefined,
|
|
32
|
+
maxFiles: options.maxFiles,
|
|
33
|
+
});
|
|
34
|
+
const files = discoveryResult.files;
|
|
35
|
+
if (discoveryResult.wasLimited) {
|
|
36
|
+
console.error(`Warning: Found ${String(discoveryResult.totalDiscovered)} files, limited to ${String(options.maxFiles)}. ` +
|
|
37
|
+
`Use --max-files to increase limit or --max-files 0 for unlimited.`);
|
|
38
|
+
}
|
|
39
|
+
if (options.verbose) {
|
|
40
|
+
console.error(`Discovered ${String(files.length)} files`);
|
|
41
|
+
if (options.noIgnore) {
|
|
42
|
+
console.error("Default ignore patterns disabled (--no-ignore)");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 2. Parse files (with cache)
|
|
46
|
+
const cache = await loadCache(rootDir, options.refresh);
|
|
47
|
+
await initParser();
|
|
48
|
+
const allTags = [];
|
|
49
|
+
let cacheHits = 0;
|
|
50
|
+
let cacheMisses = 0;
|
|
51
|
+
for (const absPath of files) {
|
|
52
|
+
const relPath = relative(rootDir, absPath);
|
|
53
|
+
let fileStat;
|
|
54
|
+
try {
|
|
55
|
+
fileStat = await stat(absPath);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
if (options.verbose) {
|
|
59
|
+
console.error(`Skipping ${relPath}: file not accessible`);
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const cachedTags = getCachedTags(cache, absPath, fileStat.mtimeMs);
|
|
64
|
+
if (cachedTags !== undefined) {
|
|
65
|
+
allTags.push(...cachedTags);
|
|
66
|
+
cacheHits++;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const tags = await parseFile(absPath, relPath);
|
|
70
|
+
setCacheEntry(cache, absPath, fileStat.mtimeMs, tags);
|
|
71
|
+
allTags.push(...tags);
|
|
72
|
+
cacheMisses++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
shutdownParser();
|
|
76
|
+
try {
|
|
77
|
+
await saveCache(cache, rootDir);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (options.verbose) {
|
|
81
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
82
|
+
console.error(`Warning: failed to save cache: ${msg}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (options.verbose) {
|
|
86
|
+
const defs = allTags.filter((t) => t.kind === "def");
|
|
87
|
+
const refs = allTags.filter((t) => t.kind === "ref");
|
|
88
|
+
console.error(`Cache: ${String(cacheHits)} hits, ${String(cacheMisses)} misses`);
|
|
89
|
+
console.error(`Parsed ${String(defs.length)} definitions, ${String(refs.length)} references`);
|
|
90
|
+
}
|
|
91
|
+
// 3. Build graph
|
|
92
|
+
const graph = buildFileGraph(allTags);
|
|
93
|
+
// 4. Compute PageRank
|
|
94
|
+
const personalization = focusFiles.length > 0
|
|
95
|
+
? buildPersonalization(focusFiles)
|
|
96
|
+
: undefined;
|
|
97
|
+
const fileRanks = computePageRank(graph, { personalization });
|
|
98
|
+
// 5. Rank definitions (exclude focus files)
|
|
99
|
+
const rankedDefs = rankDefinitions(graph, fileRanks, focusSet);
|
|
100
|
+
if (options.verbose) {
|
|
101
|
+
console.error(`Ranked ${String(rankedDefs.length)} definitions`);
|
|
102
|
+
}
|
|
103
|
+
// 6. Fit to token budget and format output
|
|
104
|
+
const output = fitToTokenBudget(rankedDefs, graph, fileRanks, options.tokens, focusFiles.length > 0 ? focusFiles : undefined);
|
|
105
|
+
// 7. Write output
|
|
106
|
+
if (options.output) {
|
|
107
|
+
await writeFile(options.output, output);
|
|
108
|
+
if (options.verbose) {
|
|
109
|
+
console.error(`Output written to ${options.output}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(output);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
main().catch((err) => {
|
|
117
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
+
console.error(`Error: ${message}`);
|
|
119
|
+
console.error("\nUsage: repo-mapper [paths...] [options]");
|
|
120
|
+
console.error("Options:");
|
|
121
|
+
console.error(" -t, --tokens <n> Max tokens (default: 1024)");
|
|
122
|
+
console.error(" -f, --focus <file> Focus file (repeatable)");
|
|
123
|
+
console.error(" -o, --output <file> Output file");
|
|
124
|
+
console.error(" -r, --refresh Ignore cache");
|
|
125
|
+
console.error(" -v, --verbose Verbose output");
|
|
126
|
+
console.error(" --ignore <pattern> Add ignore pattern (repeatable)");
|
|
127
|
+
console.error(" --no-ignore Disable default ignore patterns");
|
|
128
|
+
console.error(" --max-files <n> Max files to process (default: 10000, 0=unlimited)");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language configuration for tree-sitter parsing.
|
|
3
|
+
* Registry maps language id to config with extensions, grammar, definition types, builtins.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for a supported language.
|
|
7
|
+
*/
|
|
8
|
+
export interface LanguageConfig {
|
|
9
|
+
/** File extensions that map to this language (e.g., [".py", ".pyw"]) */
|
|
10
|
+
readonly extensions: readonly string[];
|
|
11
|
+
/** Grammar name for tree-sitter */
|
|
12
|
+
readonly grammarName: string;
|
|
13
|
+
/** Resolve WASM path given node_modules directory */
|
|
14
|
+
readonly wasmPath: (nodeModules: string) => string;
|
|
15
|
+
/** AST node types that represent definitions */
|
|
16
|
+
readonly definitionTypes: ReadonlySet<string>;
|
|
17
|
+
/** Language-specific builtins/keywords to filter from references */
|
|
18
|
+
readonly builtins: ReadonlySet<string>;
|
|
19
|
+
/** Fallback regex patterns for when tree-sitter fails. Captures name in group 1. */
|
|
20
|
+
readonly fallbackPatterns: readonly RegExp[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Language registry - keyed by language id.
|
|
24
|
+
*/
|
|
25
|
+
export declare const LANGUAGE_REGISTRY: Readonly<Record<string, LanguageConfig>>;
|
|
26
|
+
/**
|
|
27
|
+
* Get language config for a file extension.
|
|
28
|
+
* Returns undefined for unsupported extensions.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getLanguageForExtension(ext: string): LanguageConfig | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Check if extension is supported for tree-sitter parsing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isTreeSitterSupported(ext: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Filter out builtin/keyword identifiers for a language.
|
|
37
|
+
* Also filters single-character identifiers as they're typically loop vars.
|
|
38
|
+
* If no config provided, uses TS/JS builtins as default.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isBuiltin(name: string, config?: LanguageConfig): boolean;
|