@getfoyer/review-core 0.2.0 → 0.2.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/dist/fileReader.d.ts +17 -0
- package/dist/fileReader.d.ts.map +1 -0
- package/dist/fileReader.js +2 -0
- package/dist/fileReader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/semantic/cache.d.ts +31 -0
- package/dist/semantic/cache.d.ts.map +1 -0
- package/dist/semantic/cache.js +50 -0
- package/dist/semantic/cache.js.map +1 -0
- package/dist/semantic/contextBuilder.d.ts +17 -0
- package/dist/semantic/contextBuilder.d.ts.map +1 -0
- package/dist/semantic/contextBuilder.js +92 -0
- package/dist/semantic/contextBuilder.js.map +1 -0
- package/dist/semantic/diffParse.d.ts +30 -0
- package/dist/semantic/diffParse.d.ts.map +1 -0
- package/dist/semantic/diffParse.js +48 -0
- package/dist/semantic/diffParse.js.map +1 -0
- package/dist/semantic/imports.d.ts +16 -0
- package/dist/semantic/imports.d.ts.map +1 -0
- package/dist/semantic/imports.js +102 -0
- package/dist/semantic/imports.js.map +1 -0
- package/dist/semantic/index.d.ts +45 -0
- package/dist/semantic/index.d.ts.map +1 -0
- package/dist/semantic/index.js +78 -0
- package/dist/semantic/index.js.map +1 -0
- package/dist/semantic/languages.d.ts +14 -0
- package/dist/semantic/languages.d.ts.map +1 -0
- package/dist/semantic/languages.js +40 -0
- package/dist/semantic/languages.js.map +1 -0
- package/dist/semantic/parser.d.ts +15 -0
- package/dist/semantic/parser.d.ts.map +1 -0
- package/dist/semantic/parser.js +83 -0
- package/dist/semantic/parser.js.map +1 -0
- package/dist/semantic/symbols.d.ts +54 -0
- package/dist/semantic/symbols.d.ts.map +1 -0
- package/dist/semantic/symbols.js +287 -0
- package/dist/semantic/symbols.js.map +1 -0
- package/dist/semantic/types.d.ts +43 -0
- package/dist/semantic/types.d.ts.map +1 -0
- package/dist/semantic/types.js +2 -0
- package/dist/semantic/types.js.map +1 -0
- package/package.json +13 -5
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension → grammar mapping. Unknown extensions resolve to null and the
|
|
3
|
+
* semantic builder silently skips the file (degrades to diff-only for that
|
|
4
|
+
* path). Adding a language = one entry here + a new WASM in tree-sitter-wasms.
|
|
5
|
+
*/
|
|
6
|
+
import type { LanguageId } from './types.js';
|
|
7
|
+
export declare function languageForPath(path: string): LanguageId | null;
|
|
8
|
+
/**
|
|
9
|
+
* Filename of the WASM grammar inside the `tree-sitter-wasms` package. Both
|
|
10
|
+
* `tsx` and `typescript` ship as separate WASMs because the TypeScript grammar
|
|
11
|
+
* upstream is split that way.
|
|
12
|
+
*/
|
|
13
|
+
export declare function wasmFileForLanguage(lang: LanguageId): string;
|
|
14
|
+
//# sourceMappingURL=languages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"languages.d.ts","sourceRoot":"","sources":["../../src/semantic/languages.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAmB7C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAK/D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAU5D"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const EXTENSION_TO_LANGUAGE = {
|
|
2
|
+
ts: 'typescript',
|
|
3
|
+
cts: 'typescript',
|
|
4
|
+
mts: 'typescript',
|
|
5
|
+
tsx: 'tsx',
|
|
6
|
+
js: 'javascript',
|
|
7
|
+
cjs: 'javascript',
|
|
8
|
+
mjs: 'javascript',
|
|
9
|
+
jsx: 'javascript',
|
|
10
|
+
py: 'python',
|
|
11
|
+
pyi: 'python',
|
|
12
|
+
go: 'go',
|
|
13
|
+
rs: 'rust',
|
|
14
|
+
rb: 'ruby',
|
|
15
|
+
rake: 'ruby',
|
|
16
|
+
};
|
|
17
|
+
export function languageForPath(path) {
|
|
18
|
+
const idx = path.lastIndexOf('.');
|
|
19
|
+
if (idx < 0)
|
|
20
|
+
return null;
|
|
21
|
+
const ext = path.slice(idx + 1).toLowerCase();
|
|
22
|
+
return EXTENSION_TO_LANGUAGE[ext] ?? null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Filename of the WASM grammar inside the `tree-sitter-wasms` package. Both
|
|
26
|
+
* `tsx` and `typescript` ship as separate WASMs because the TypeScript grammar
|
|
27
|
+
* upstream is split that way.
|
|
28
|
+
*/
|
|
29
|
+
export function wasmFileForLanguage(lang) {
|
|
30
|
+
switch (lang) {
|
|
31
|
+
case 'typescript': return 'tree-sitter-typescript.wasm';
|
|
32
|
+
case 'tsx': return 'tree-sitter-tsx.wasm';
|
|
33
|
+
case 'javascript': return 'tree-sitter-javascript.wasm';
|
|
34
|
+
case 'python': return 'tree-sitter-python.wasm';
|
|
35
|
+
case 'go': return 'tree-sitter-go.wasm';
|
|
36
|
+
case 'rust': return 'tree-sitter-rust.wasm';
|
|
37
|
+
case 'ruby': return 'tree-sitter-ruby.wasm';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=languages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"languages.js","sourceRoot":"","sources":["../../src/semantic/languages.ts"],"names":[],"mappings":"AAOA,MAAM,qBAAqB,GAA+B;IACxD,EAAE,EAAE,YAAY;IAChB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,YAAY;IAChB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,QAAQ;IACb,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,MAAM;IACV,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;CACb,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,OAAO,qBAAqB,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAgB;IAClD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY,CAAC,CAAC,OAAO,6BAA6B,CAAC;QACxD,KAAK,KAAK,CAAC,CAAQ,OAAO,sBAAsB,CAAC;QACjD,KAAK,YAAY,CAAC,CAAC,OAAO,6BAA6B,CAAC;QACxD,KAAK,QAAQ,CAAC,CAAK,OAAO,yBAAyB,CAAC;QACpD,KAAK,IAAI,CAAC,CAAS,OAAO,qBAAqB,CAAC;QAChD,KAAK,MAAM,CAAC,CAAO,OAAO,uBAAuB,CAAC;QAClD,KAAK,MAAM,CAAC,CAAO,OAAO,uBAAuB,CAAC;IACpD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Tree } from 'web-tree-sitter';
|
|
2
|
+
import type { LanguageId } from './types.js';
|
|
3
|
+
export interface ParseResult {
|
|
4
|
+
tree: Tree;
|
|
5
|
+
source: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse `source` as `lang`. Returns null when the grammar can't be loaded or
|
|
9
|
+
* when parsing throws. The caller treats null as "skip this file" and the
|
|
10
|
+
* semantic block degrades silently for that path.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parse(source: string, lang: LanguageId): Promise<ParseResult | null>;
|
|
13
|
+
/** Test seam: drop caches so a unit test can simulate a cold start. */
|
|
14
|
+
export declare function _resetForTests(): void;
|
|
15
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/semantic/parser.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAoB,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA0C7C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAYzF;AAED,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy, process-singleton wrapper around web-tree-sitter.
|
|
3
|
+
*
|
|
4
|
+
* Loading WASM is async and not free (~5MB per grammar), so we initialise the
|
|
5
|
+
* runtime once and cache loaded grammars by `LanguageId`. Callers do not need
|
|
6
|
+
* to know about WASM — they ask for a `Tree` and either get one or `null` if
|
|
7
|
+
* the grammar fails to load (graceful degradation: the semantic block just
|
|
8
|
+
* skips that file).
|
|
9
|
+
*
|
|
10
|
+
* We intentionally don't expose Parser/Language to callers — every consumer
|
|
11
|
+
* just wants `parse(source, lang)`.
|
|
12
|
+
*/
|
|
13
|
+
import { readFile } from 'node:fs/promises';
|
|
14
|
+
import { dirname, resolve } from 'node:path';
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
16
|
+
import { Parser, Language } from 'web-tree-sitter';
|
|
17
|
+
import { wasmFileForLanguage } from './languages.js';
|
|
18
|
+
const require_ = createRequire(import.meta.url);
|
|
19
|
+
let initPromise = null;
|
|
20
|
+
const languageCache = new Map();
|
|
21
|
+
async function ensureInit() {
|
|
22
|
+
if (initPromise)
|
|
23
|
+
return initPromise;
|
|
24
|
+
initPromise = (async () => {
|
|
25
|
+
// web-tree-sitter looks for `tree-sitter.wasm` next to its main module by
|
|
26
|
+
// default. We override `locateFile` to point at the package directly so
|
|
27
|
+
// node ESM resolution doesn't trip over import.meta.url plumbing.
|
|
28
|
+
const wtsMain = require_.resolve('web-tree-sitter');
|
|
29
|
+
const wtsDir = dirname(wtsMain);
|
|
30
|
+
await Parser.init({
|
|
31
|
+
locateFile: (name) => resolve(wtsDir, name),
|
|
32
|
+
});
|
|
33
|
+
})().catch((err) => {
|
|
34
|
+
// Reset on failure so a transient error doesn't poison the singleton.
|
|
35
|
+
initPromise = null;
|
|
36
|
+
throw err;
|
|
37
|
+
});
|
|
38
|
+
return initPromise;
|
|
39
|
+
}
|
|
40
|
+
async function loadGrammar(lang) {
|
|
41
|
+
if (languageCache.has(lang))
|
|
42
|
+
return languageCache.get(lang) ?? null;
|
|
43
|
+
try {
|
|
44
|
+
await ensureInit();
|
|
45
|
+
const wasmsDir = dirname(require_.resolve('tree-sitter-wasms/package.json'));
|
|
46
|
+
const wasmPath = resolve(wasmsDir, 'out', wasmFileForLanguage(lang));
|
|
47
|
+
const bytes = await readFile(wasmPath);
|
|
48
|
+
const language = await Language.load(bytes);
|
|
49
|
+
languageCache.set(lang, language);
|
|
50
|
+
return language;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
languageCache.set(lang, null);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse `source` as `lang`. Returns null when the grammar can't be loaded or
|
|
59
|
+
* when parsing throws. The caller treats null as "skip this file" and the
|
|
60
|
+
* semantic block degrades silently for that path.
|
|
61
|
+
*/
|
|
62
|
+
export async function parse(source, lang) {
|
|
63
|
+
const language = await loadGrammar(lang);
|
|
64
|
+
if (!language)
|
|
65
|
+
return null;
|
|
66
|
+
try {
|
|
67
|
+
const parser = new Parser();
|
|
68
|
+
parser.setLanguage(language);
|
|
69
|
+
const tree = parser.parse(source);
|
|
70
|
+
if (!tree)
|
|
71
|
+
return null;
|
|
72
|
+
return { tree, source };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Test seam: drop caches so a unit test can simulate a cold start. */
|
|
79
|
+
export function _resetForTests() {
|
|
80
|
+
initPromise = null;
|
|
81
|
+
languageCache.clear();
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/semantic/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol extraction from a parsed AST.
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities:
|
|
5
|
+
* 1. Given a parsed file + a set of changed line ranges, return the top-level
|
|
6
|
+
* symbols (functions, classes, methods) whose body overlaps any of those
|
|
7
|
+
* ranges. Those are the "touched symbols" the reviewer should know about
|
|
8
|
+
* in full, not just by diff hunks.
|
|
9
|
+
* 2. Given a parsed file, return its import edges: which modules it imports
|
|
10
|
+
* and which named bindings it pulls in. The imports module resolves those
|
|
11
|
+
* to files; the resolver lives outside so this stays AST-only.
|
|
12
|
+
*
|
|
13
|
+
* The grammars provide the node type strings; the per-language differences
|
|
14
|
+
* are isolated in `topLevelTypes` and `importQuery` below.
|
|
15
|
+
*/
|
|
16
|
+
import type { Node } from 'web-tree-sitter';
|
|
17
|
+
import type { ParseResult } from './parser.js';
|
|
18
|
+
import type { LanguageId, TouchedSymbol } from './types.js';
|
|
19
|
+
/**
|
|
20
|
+
* Top-level definitions in order. We walk the root node's direct children
|
|
21
|
+
* (and one level into `export_statement` / `decorated_definition` wrappers).
|
|
22
|
+
*/
|
|
23
|
+
export declare function topLevelDefinitions(parsed: ParseResult, lang: LanguageId): Node[];
|
|
24
|
+
/**
|
|
25
|
+
* Heuristic name extraction. Tree-sitter exposes a `name` field for most
|
|
26
|
+
* declaration types; for lexical_declaration we walk into the first
|
|
27
|
+
* variable_declarator and read its `name` field. Returns "<anonymous>" rather
|
|
28
|
+
* than null so the rendered block always has something printable.
|
|
29
|
+
*/
|
|
30
|
+
export declare function symbolName(node: Node): string;
|
|
31
|
+
export declare function symbolKind(node: Node): TouchedSymbol['kind'];
|
|
32
|
+
/**
|
|
33
|
+
* Return the symbols whose source range overlaps any of the supplied
|
|
34
|
+
* post-image line ranges. Lines are 1-based; tree-sitter positions are
|
|
35
|
+
* 0-based, so we shift by one when comparing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function findTouchedSymbols(parsed: ParseResult, lang: LanguageId, file: string, hunks: {
|
|
38
|
+
newStart: number;
|
|
39
|
+
newLines: number;
|
|
40
|
+
}[], bodyCapBytes: number): TouchedSymbol[];
|
|
41
|
+
export interface ImportEdge {
|
|
42
|
+
/** Module specifier as it appears in source (e.g. `./foo`, `react`). */
|
|
43
|
+
source: string;
|
|
44
|
+
/** Local names imported. For `import * as X from ...` this is `['*']`. */
|
|
45
|
+
names: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Extract import edges. JS/TS use `import_statement`; Python uses
|
|
49
|
+
* `import_from_statement` / `import_statement`. We surface only the cheap
|
|
50
|
+
* cases — full semantics (re-exports, computed paths) would require a real
|
|
51
|
+
* compiler, which is out of scope per the plan.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findImports(parsed: ParseResult, lang: LanguageId): ImportEdge[];
|
|
54
|
+
//# sourceMappingURL=symbols.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../src/semantic/symbols.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA4D5D;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,CAoBjF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CA4B7C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CA8B5D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,EAC/C,YAAY,EAAE,MAAM,GACnB,aAAa,EAAE,CA4BjB;AAMD,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,CAsE/E"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
const TOP_LEVEL_DEFINITION_TYPES = {
|
|
2
|
+
typescript: new Set([
|
|
3
|
+
'function_declaration',
|
|
4
|
+
'class_declaration',
|
|
5
|
+
'method_definition',
|
|
6
|
+
'interface_declaration',
|
|
7
|
+
'type_alias_declaration',
|
|
8
|
+
'lexical_declaration', // `const foo = (...) => {...}`
|
|
9
|
+
'export_statement',
|
|
10
|
+
]),
|
|
11
|
+
tsx: new Set([
|
|
12
|
+
'function_declaration',
|
|
13
|
+
'class_declaration',
|
|
14
|
+
'method_definition',
|
|
15
|
+
'interface_declaration',
|
|
16
|
+
'type_alias_declaration',
|
|
17
|
+
'lexical_declaration',
|
|
18
|
+
'export_statement',
|
|
19
|
+
]),
|
|
20
|
+
javascript: new Set([
|
|
21
|
+
'function_declaration',
|
|
22
|
+
'class_declaration',
|
|
23
|
+
'method_definition',
|
|
24
|
+
'lexical_declaration',
|
|
25
|
+
'export_statement',
|
|
26
|
+
]),
|
|
27
|
+
python: new Set([
|
|
28
|
+
'function_definition',
|
|
29
|
+
'class_definition',
|
|
30
|
+
'decorated_definition',
|
|
31
|
+
]),
|
|
32
|
+
go: new Set([
|
|
33
|
+
'function_declaration',
|
|
34
|
+
'method_declaration',
|
|
35
|
+
'type_declaration',
|
|
36
|
+
'var_declaration',
|
|
37
|
+
'const_declaration',
|
|
38
|
+
]),
|
|
39
|
+
rust: new Set([
|
|
40
|
+
'function_item',
|
|
41
|
+
'struct_item',
|
|
42
|
+
'enum_item',
|
|
43
|
+
'trait_item',
|
|
44
|
+
'impl_item',
|
|
45
|
+
'mod_item',
|
|
46
|
+
'const_item',
|
|
47
|
+
'static_item',
|
|
48
|
+
'type_item',
|
|
49
|
+
]),
|
|
50
|
+
ruby: new Set([
|
|
51
|
+
'method',
|
|
52
|
+
'singleton_method',
|
|
53
|
+
'class',
|
|
54
|
+
'module',
|
|
55
|
+
'assignment', // constants — uppercase LHS resolved in symbolName
|
|
56
|
+
]),
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Top-level definitions in order. We walk the root node's direct children
|
|
60
|
+
* (and one level into `export_statement` / `decorated_definition` wrappers).
|
|
61
|
+
*/
|
|
62
|
+
export function topLevelDefinitions(parsed, lang) {
|
|
63
|
+
const wantedTypes = TOP_LEVEL_DEFINITION_TYPES[lang];
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const child of parsed.tree.rootNode.namedChildren) {
|
|
66
|
+
if (!child)
|
|
67
|
+
continue;
|
|
68
|
+
if (wantedTypes.has(child.type)) {
|
|
69
|
+
// Unwrap `export_statement` so we report the inner declaration. The
|
|
70
|
+
// body line range is unchanged either way.
|
|
71
|
+
if (child.type === 'export_statement') {
|
|
72
|
+
const inner = child.namedChildren.find((c) => !!c && wantedTypes.has(c.type));
|
|
73
|
+
out.push(inner ?? child);
|
|
74
|
+
}
|
|
75
|
+
else if (child.type === 'decorated_definition') {
|
|
76
|
+
const inner = child.namedChildren.find((c) => !!c && (c.type === 'function_definition' || c.type === 'class_definition'));
|
|
77
|
+
out.push(inner ?? child);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
out.push(child);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Heuristic name extraction. Tree-sitter exposes a `name` field for most
|
|
88
|
+
* declaration types; for lexical_declaration we walk into the first
|
|
89
|
+
* variable_declarator and read its `name` field. Returns "<anonymous>" rather
|
|
90
|
+
* than null so the rendered block always has something printable.
|
|
91
|
+
*/
|
|
92
|
+
export function symbolName(node) {
|
|
93
|
+
const named = node.childForFieldName('name');
|
|
94
|
+
if (named?.text)
|
|
95
|
+
return named.text;
|
|
96
|
+
if (node.type === 'lexical_declaration') {
|
|
97
|
+
const decl = node.namedChildren.find((c) => !!c && c.type === 'variable_declarator');
|
|
98
|
+
const name = decl?.childForFieldName('name');
|
|
99
|
+
if (name?.text)
|
|
100
|
+
return name.text;
|
|
101
|
+
}
|
|
102
|
+
// Go `type_declaration` / `var_declaration` / `const_declaration` wrap an
|
|
103
|
+
// inner spec node that carries the name field.
|
|
104
|
+
if (node.type === 'type_declaration' || node.type === 'var_declaration' || node.type === 'const_declaration') {
|
|
105
|
+
const spec = node.namedChildren.find((c) => !!c && (c.type === 'type_spec' || c.type === 'var_spec' || c.type === 'const_spec'));
|
|
106
|
+
const nm = spec?.childForFieldName('name');
|
|
107
|
+
if (nm?.text)
|
|
108
|
+
return nm.text;
|
|
109
|
+
}
|
|
110
|
+
// Ruby `assignment` for top-level constants: LHS is an `identifier` or
|
|
111
|
+
// `constant`. We surface only constants (uppercase start) — lowercase locals
|
|
112
|
+
// aren't useful in review context.
|
|
113
|
+
if (node.type === 'assignment') {
|
|
114
|
+
const lhs = node.childForFieldName('left');
|
|
115
|
+
if (lhs?.text && /^[A-Z]/.test(lhs.text))
|
|
116
|
+
return lhs.text;
|
|
117
|
+
}
|
|
118
|
+
// Rust `impl_item` exposes the implemented type via the `type` field.
|
|
119
|
+
if (node.type === 'impl_item') {
|
|
120
|
+
const ty = node.childForFieldName('type');
|
|
121
|
+
if (ty?.text)
|
|
122
|
+
return `impl ${ty.text}`;
|
|
123
|
+
}
|
|
124
|
+
return '<anonymous>';
|
|
125
|
+
}
|
|
126
|
+
export function symbolKind(node) {
|
|
127
|
+
switch (node.type) {
|
|
128
|
+
case 'function_declaration':
|
|
129
|
+
case 'function_definition':
|
|
130
|
+
case 'function_item':
|
|
131
|
+
case 'method':
|
|
132
|
+
case 'singleton_method':
|
|
133
|
+
case 'method_declaration':
|
|
134
|
+
return node.type.startsWith('method') ? 'method' : 'function';
|
|
135
|
+
case 'class_declaration':
|
|
136
|
+
case 'class_definition':
|
|
137
|
+
case 'class':
|
|
138
|
+
case 'struct_item':
|
|
139
|
+
case 'enum_item':
|
|
140
|
+
case 'trait_item':
|
|
141
|
+
case 'module':
|
|
142
|
+
case 'mod_item':
|
|
143
|
+
return 'class';
|
|
144
|
+
case 'method_definition':
|
|
145
|
+
return 'method';
|
|
146
|
+
case 'lexical_declaration':
|
|
147
|
+
case 'const_declaration':
|
|
148
|
+
case 'const_item':
|
|
149
|
+
case 'static_item':
|
|
150
|
+
case 'var_declaration':
|
|
151
|
+
case 'assignment':
|
|
152
|
+
return 'const';
|
|
153
|
+
default:
|
|
154
|
+
return 'unknown';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Return the symbols whose source range overlaps any of the supplied
|
|
159
|
+
* post-image line ranges. Lines are 1-based; tree-sitter positions are
|
|
160
|
+
* 0-based, so we shift by one when comparing.
|
|
161
|
+
*/
|
|
162
|
+
export function findTouchedSymbols(parsed, lang, file, hunks, bodyCapBytes) {
|
|
163
|
+
if (hunks.length === 0)
|
|
164
|
+
return [];
|
|
165
|
+
const defs = topLevelDefinitions(parsed, lang);
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const def of defs) {
|
|
169
|
+
const startLine = def.startPosition.row + 1;
|
|
170
|
+
const endLine = def.endPosition.row + 1;
|
|
171
|
+
const overlaps = hunks.some((h) => {
|
|
172
|
+
const hStart = h.newStart;
|
|
173
|
+
const hEnd = h.newStart + Math.max(0, h.newLines - 1);
|
|
174
|
+
return hEnd >= startLine && hStart <= endLine;
|
|
175
|
+
});
|
|
176
|
+
if (!overlaps)
|
|
177
|
+
continue;
|
|
178
|
+
const name = symbolName(def);
|
|
179
|
+
const key = `${name}@${startLine}`;
|
|
180
|
+
if (seen.has(key))
|
|
181
|
+
continue;
|
|
182
|
+
seen.add(key);
|
|
183
|
+
const body = def.text.length > bodyCapBytes ? def.text.slice(0, bodyCapBytes) + '\n/* …truncated… */' : def.text;
|
|
184
|
+
out.push({
|
|
185
|
+
file,
|
|
186
|
+
name,
|
|
187
|
+
kind: symbolKind(def),
|
|
188
|
+
body,
|
|
189
|
+
line: startLine,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Extract import edges. JS/TS use `import_statement`; Python uses
|
|
196
|
+
* `import_from_statement` / `import_statement`. We surface only the cheap
|
|
197
|
+
* cases — full semantics (re-exports, computed paths) would require a real
|
|
198
|
+
* compiler, which is out of scope per the plan.
|
|
199
|
+
*/
|
|
200
|
+
export function findImports(parsed, lang) {
|
|
201
|
+
const edges = [];
|
|
202
|
+
// Go imports name packages, not files; Rust `use` paths resolve through a
|
|
203
|
+
// module tree we don't model. Skip both — touched-symbol bodies still ship.
|
|
204
|
+
if (lang === 'go' || lang === 'rust')
|
|
205
|
+
return edges;
|
|
206
|
+
for (const child of parsed.tree.rootNode.namedChildren) {
|
|
207
|
+
if (!child)
|
|
208
|
+
continue;
|
|
209
|
+
if (lang === 'ruby') {
|
|
210
|
+
// Ruby `require_relative "./foo"` appears as a `call` with method
|
|
211
|
+
// `require_relative` and a string-literal argument. We surface the
|
|
212
|
+
// bare path; the resolver maps it to a `.rb` file.
|
|
213
|
+
if (child.type !== 'call')
|
|
214
|
+
continue;
|
|
215
|
+
const method = child.childForFieldName('method')?.text;
|
|
216
|
+
if (method !== 'require_relative' && method !== 'require')
|
|
217
|
+
continue;
|
|
218
|
+
const args = child.childForFieldName('arguments');
|
|
219
|
+
const strArg = args?.namedChildren.find((c) => !!c && c.type === 'string');
|
|
220
|
+
if (!strArg)
|
|
221
|
+
continue;
|
|
222
|
+
const raw = strArg.text.replace(/^['"]|['"]$/g, '');
|
|
223
|
+
// For `require_relative`, treat as relative; for plain `require`, only
|
|
224
|
+
// surface when the path looks relative (rare but harmless).
|
|
225
|
+
if (method === 'require_relative' || raw.startsWith('./') || raw.startsWith('../')) {
|
|
226
|
+
edges.push({ source: raw.startsWith('.') ? raw : `./${raw}`, names: ['*'] });
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (lang === 'python') {
|
|
231
|
+
if (child.type === 'import_from_statement') {
|
|
232
|
+
const moduleName = child.childForFieldName('module_name')?.text;
|
|
233
|
+
if (!moduleName)
|
|
234
|
+
continue;
|
|
235
|
+
const names = [];
|
|
236
|
+
for (const n of child.namedChildren) {
|
|
237
|
+
if (n && (n.type === 'dotted_name' || n.type === 'aliased_import')) {
|
|
238
|
+
const txt = n.childForFieldName('name')?.text ?? n.text;
|
|
239
|
+
if (txt)
|
|
240
|
+
names.push(txt);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (names.length > 0)
|
|
244
|
+
edges.push({ source: moduleName, names });
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (child.type !== 'import_statement')
|
|
249
|
+
continue;
|
|
250
|
+
const sourceNode = child.childForFieldName('source');
|
|
251
|
+
if (!sourceNode)
|
|
252
|
+
continue;
|
|
253
|
+
// `source` text is the raw quoted string. Strip quotes.
|
|
254
|
+
const source = sourceNode.text.replace(/^['"]|['"]$/g, '');
|
|
255
|
+
const names = [];
|
|
256
|
+
// Walk import_clause → named_imports / namespace_import / identifier.
|
|
257
|
+
for (const c of child.namedChildren) {
|
|
258
|
+
if (!c)
|
|
259
|
+
continue;
|
|
260
|
+
if (c.type === 'import_clause') {
|
|
261
|
+
for (const inner of c.namedChildren) {
|
|
262
|
+
if (!inner)
|
|
263
|
+
continue;
|
|
264
|
+
if (inner.type === 'identifier') {
|
|
265
|
+
names.push(inner.text);
|
|
266
|
+
}
|
|
267
|
+
else if (inner.type === 'namespace_import') {
|
|
268
|
+
names.push('*');
|
|
269
|
+
}
|
|
270
|
+
else if (inner.type === 'named_imports') {
|
|
271
|
+
for (const spec of inner.namedChildren) {
|
|
272
|
+
if (!spec || spec.type !== 'import_specifier')
|
|
273
|
+
continue;
|
|
274
|
+
const nm = spec.childForFieldName('name')?.text;
|
|
275
|
+
if (nm)
|
|
276
|
+
names.push(nm);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (source)
|
|
283
|
+
edges.push({ source, names });
|
|
284
|
+
}
|
|
285
|
+
return edges;
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=symbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.js","sourceRoot":"","sources":["../../src/semantic/symbols.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the semantic-context module.
|
|
3
|
+
*
|
|
4
|
+
* Purpose: enrich the diff fed to the code-review LLM with definitions of the
|
|
5
|
+
* symbols the diff touches and the symbols those touched files import. The
|
|
6
|
+
* diff says *what changed*; this block says *what those changes refer to*.
|
|
7
|
+
*/
|
|
8
|
+
export type LanguageId = 'typescript' | 'tsx' | 'javascript' | 'python' | 'go' | 'rust' | 'ruby';
|
|
9
|
+
export interface TouchedSymbol {
|
|
10
|
+
/** Repo-relative file path from the diff `+++ b/` header. */
|
|
11
|
+
file: string;
|
|
12
|
+
/** Symbol name (function, class, method, top-level const with function value). */
|
|
13
|
+
name: string;
|
|
14
|
+
kind: 'function' | 'class' | 'method' | 'const' | 'unknown';
|
|
15
|
+
/** Full source text of the symbol's definition (not just diff hunks). Capped
|
|
16
|
+
* upstream by FileReader's per-file byte cap. */
|
|
17
|
+
body: string;
|
|
18
|
+
/** 1-based line where the symbol starts in the post-image file. */
|
|
19
|
+
line: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ImportedSymbol {
|
|
22
|
+
/** Local name as used in the importing file. */
|
|
23
|
+
name: string;
|
|
24
|
+
/** Repo-relative path of the file the symbol was resolved to. */
|
|
25
|
+
fromFile: string;
|
|
26
|
+
/** Signature line(s) — the first line of the definition. Cheaper than the
|
|
27
|
+
* full body and usually enough to catch arg-shape mismatches. */
|
|
28
|
+
signature: string;
|
|
29
|
+
}
|
|
30
|
+
export interface SemanticBlock {
|
|
31
|
+
/** Rendered markdown block ready to inline in the LLM user message. Empty
|
|
32
|
+
* string when nothing useful was extracted. */
|
|
33
|
+
rendered: string;
|
|
34
|
+
/** Diagnostic counts for logging. */
|
|
35
|
+
stats: {
|
|
36
|
+
touchedFiles: number;
|
|
37
|
+
touchedSymbols: number;
|
|
38
|
+
importedSymbols: number;
|
|
39
|
+
bytes: number;
|
|
40
|
+
truncated: boolean;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/semantic/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,KAAK,GACL,YAAY,GACZ,QAAQ,GACR,IAAI,GACJ,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAC5D;sDACkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB;sEACkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;oDACgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/semantic/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getfoyer/review-core",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Frozen contract for Foyer run artifacts (RunArtifact v0.1) — the shared shape consumed by the GitHub App, the CLI, the hosted report, and PR-comment writers.",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Frozen contract for Foyer run artifacts (RunArtifact v0.1) — the shared shape consumed by the GitHub App, the CLI, the hosted report, and PR-comment writers. Also ships the cross-cutting semantic-context builder used by code review.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./plan-context": {
|
|
18
18
|
"types": "./dist/plan-context.d.ts",
|
|
19
19
|
"import": "./dist/plan-context.js"
|
|
20
|
+
},
|
|
21
|
+
"./semantic": {
|
|
22
|
+
"types": "./dist/semantic/index.d.ts",
|
|
23
|
+
"import": "./dist/semantic/index.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -30,13 +34,17 @@
|
|
|
30
34
|
},
|
|
31
35
|
"scripts": {
|
|
32
36
|
"build": "tsc",
|
|
33
|
-
"typecheck": "tsc --noEmit"
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"test": "vitest run"
|
|
34
39
|
},
|
|
35
40
|
"dependencies": {
|
|
36
|
-
"@getfoyer/client": "
|
|
41
|
+
"@getfoyer/client": "portal:../client",
|
|
42
|
+
"tree-sitter-wasms": "^0.1.12",
|
|
43
|
+
"web-tree-sitter": "^0.25.0"
|
|
37
44
|
},
|
|
38
45
|
"devDependencies": {
|
|
39
46
|
"@types/node": "^22.0.0",
|
|
40
|
-
"typescript": "^5.5.0"
|
|
47
|
+
"typescript": "^5.5.0",
|
|
48
|
+
"vitest": "^4.1.2"
|
|
41
49
|
}
|
|
42
50
|
}
|