Skip to content

Commit e9a7ecb

Browse files
committed
feat: extract const literal values into symbols.value
Captures string, number, boolean, null, negative numbers, `as const`, and simple template literals. 615 values extracted on benchmark repo.
1 parent 1ad06d1 commit e9a7ecb

7 files changed

Lines changed: 105 additions & 3 deletions

File tree

.agents/rules/codemap.mdc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ bun src/index.ts query --json "<SQL>"
9898
| Type/interface shape | `SELECT name, type, is_optional, is_readonly FROM type_members WHERE symbol_name = '...'` |
9999
| Deprecated symbols | `SELECT name, kind, file_path, doc_comment FROM symbols WHERE doc_comment LIKE '%@deprecated%'` |
100100
| Symbol docs | `SELECT name, signature, doc_comment FROM symbols WHERE name = '...' AND doc_comment IS NOT NULL` |
101+
| Const values | `SELECT name, value, file_path FROM symbols WHERE kind = 'const' AND value IS NOT NULL AND name LIKE '%...'` |
101102

102103
**Use `DISTINCT`** on dependency and import queries — a file importing multiple specifiers from the same module produces duplicate rows.
103104

.agents/skills/codemap/SKILL.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ LIMIT 10
9090
| is_default_export | INTEGER | 1 if default export |
9191
| members | TEXT | JSON enum members (NULL for non-enums) |
9292
| doc_comment | TEXT | Leading JSDoc text (cleaned), NULL when absent |
93+
| value | TEXT | Literal value for consts (`"ok"`, `42`, `true`, `null`) |
9394

9495
### `type_members` — Properties of interfaces and object-literal type aliases
9596

@@ -220,6 +221,10 @@ WHERE doc_comment LIKE '%@deprecated%';
220221
SELECT name, signature, doc_comment FROM symbols
221222
WHERE name = 'formatCurrency' AND doc_comment IS NOT NULL;
222223

224+
-- Const values (config flags, magic strings)
225+
SELECT name, value, file_path FROM symbols
226+
WHERE kind = 'const' AND value IS NOT NULL AND name LIKE '%URL%';
227+
223228
-- File overview (imports + exports)
224229
SELECT 'import' as dir, source as name, specifiers as detail
225230
FROM imports WHERE file_path LIKE '%OrderRow%'

docs/architecture.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ All tables use `STRICT` mode. Tables marked with `WITHOUT ROWID` store data dire
186186
| is_default_export | INTEGER | 1 if default export |
187187
| members | TEXT | JSON array of enum members (NULL for non-enums). Each entry: `{"name":"…","value":"…"}` (value omitted for implicit-value enums) |
188188
| doc_comment | TEXT | Leading JSDoc comment text (cleaned: `*` prefixes stripped, trimmed). NULL when absent. Preserves `@deprecated`, `@param`, etc. tags |
189+
| value | TEXT | Literal value for `const` declarations (strings, numbers, booleans, `null`). NULL for non-literal or non-const symbols. Handles `as const` and simple template literals |
189190

190191
### `type_members` — Properties and methods of interfaces and object-literal types (`STRICT`)
191192

src/db.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ export function createTables(db: CodemapDatabase) {
5151
is_exported INTEGER DEFAULT 0,
5252
is_default_export INTEGER DEFAULT 0,
5353
members TEXT,
54-
doc_comment TEXT
54+
doc_comment TEXT,
55+
value TEXT
5556
) STRICT;
5657
5758
CREATE TABLE IF NOT EXISTS imports (
@@ -272,6 +273,7 @@ export interface SymbolRow {
272273
is_default_export: number;
273274
members: string | null;
274275
doc_comment: string | null;
276+
value: string | null;
275277
}
276278

277279
const BATCH_SIZE = 100;
@@ -304,8 +306,8 @@ export function insertSymbols(db: CodemapDatabase, symbols: SymbolRow[]) {
304306
batchInsert(
305307
db,
306308
symbols,
307-
"INSERT INTO symbols (file_path, name, kind, line_start, line_end, signature, is_exported, is_default_export, members, doc_comment)",
308-
"(?,?,?,?,?,?,?,?,?,?)",
309+
"INSERT INTO symbols (file_path, name, kind, line_start, line_end, signature, is_exported, is_default_export, members, doc_comment, value)",
310+
"(?,?,?,?,?,?,?,?,?,?,?)",
309311
(s, v) =>
310312
v.push(
311313
s.file_path,
@@ -318,6 +320,7 @@ export function insertSymbols(db: CodemapDatabase, symbols: SymbolRow[]) {
318320
s.is_default_export,
319321
s.members,
320322
s.doc_comment,
323+
s.value,
321324
),
322325
);
323326
}

src/parser.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,63 @@ describe("extractFileData", () => {
265265
});
266266
});
267267

268+
describe("const literal value extraction", () => {
269+
it("extracts string literal", () => {
270+
const src = `export const URL = "https://api.example.com";\n`;
271+
const d = extractFileData("/proj/x.ts", src, "x.ts");
272+
expect(d.symbols.find((s) => s.name === "URL")?.value).toBe(
273+
"https://api.example.com",
274+
);
275+
});
276+
277+
it("extracts number literal", () => {
278+
const src = `export const MAX = 42;\n`;
279+
const d = extractFileData("/proj/x.ts", src, "x.ts");
280+
expect(d.symbols.find((s) => s.name === "MAX")?.value).toBe("42");
281+
});
282+
283+
it("extracts boolean literal", () => {
284+
const src = `export const DEBUG = true;\n`;
285+
const d = extractFileData("/proj/x.ts", src, "x.ts");
286+
expect(d.symbols.find((s) => s.name === "DEBUG")?.value).toBe("true");
287+
});
288+
289+
it("extracts negative number", () => {
290+
const src = `export const OFFSET = -1;\n`;
291+
const d = extractFileData("/proj/x.ts", src, "x.ts");
292+
expect(d.symbols.find((s) => s.name === "OFFSET")?.value).toBe("-1");
293+
});
294+
295+
it("extracts null literal", () => {
296+
const src = `export const EMPTY = null;\n`;
297+
const d = extractFileData("/proj/x.ts", src, "x.ts");
298+
expect(d.symbols.find((s) => s.name === "EMPTY")?.value).toBe("null");
299+
});
300+
301+
it("extracts value through as const", () => {
302+
const src = `export const MODE = "production" as const;\n`;
303+
const d = extractFileData("/proj/x.ts", src, "x.ts");
304+
expect(d.symbols.find((s) => s.name === "MODE")?.value).toBe(
305+
"production",
306+
);
307+
});
308+
309+
it("extracts simple template literal without expressions", () => {
310+
const src = `export const GREETING = \`hello world\`;\n`;
311+
const d = extractFileData("/proj/x.ts", src, "x.ts");
312+
expect(d.symbols.find((s) => s.name === "GREETING")?.value).toBe(
313+
"hello world",
314+
);
315+
});
316+
317+
it("returns null for non-literal values", () => {
318+
const src = `export const arr = [1, 2];\nexport const fn = () => {};\n`;
319+
const d = extractFileData("/proj/x.ts", src, "x.ts");
320+
expect(d.symbols.find((s) => s.name === "arr")?.value).toBeNull();
321+
expect(d.symbols.find((s) => s.name === "fn")?.value).toBeNull();
322+
});
323+
});
324+
268325
describe("component detection heuristic", () => {
269326
it("detects components that return JSX", () => {
270327
const src = `export function Card() { return <div>card</div>; }\n`;

src/parser.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export function extractFileData(
132132
is_default_export: isDefault ? 1 : 0,
133133
members: null,
134134
doc_comment: findJsDoc(jsDocComments, node.start, source),
135+
value: null,
135136
});
136137

137138
if (isTsx && /^[A-Z]/.test(name)) {
@@ -175,6 +176,7 @@ export function extractFileData(
175176
is_default_export: isDefault ? 1 : 0,
176177
members: null,
177178
doc_comment: findJsDoc(jsDocComments, node.start, source),
179+
value: isArrowOrFn ? null : extractLiteralValue(init),
178180
});
179181

180182
if (isTsx && /^[A-Z]/.test(name) && isArrowOrFn) {
@@ -209,6 +211,7 @@ export function extractFileData(
209211
is_default_export: 0,
210212
members: null,
211213
doc_comment: findJsDoc(jsDocComments, node.start, source),
214+
value: null,
212215
});
213216
if (node.typeAnnotation?.type === "TSTypeLiteral") {
214217
extractObjectMembers(
@@ -252,6 +255,7 @@ export function extractFileData(
252255
is_default_export: 0,
253256
members: null,
254257
doc_comment: findJsDoc(jsDocComments, node.start, source),
258+
value: null,
255259
});
256260
extractObjectMembers(node.body?.body, relPath, name, typeMembers);
257261
},
@@ -288,6 +292,7 @@ export function extractFileData(
288292
is_default_export: 0,
289293
members,
290294
doc_comment: findJsDoc(jsDocComments, node.start, source),
295+
value: null,
291296
});
292297
},
293298

@@ -332,6 +337,7 @@ export function extractFileData(
332337
is_default_export: defaultExportedNames.has(name) ? 1 : 0,
333338
members: null,
334339
doc_comment: findJsDoc(jsDocComments, node.start, source),
340+
value: null,
335341
});
336342
},
337343

@@ -614,6 +620,34 @@ function findJsDoc(
614620
return doc.text || null;
615621
}
616622

623+
function extractLiteralValue(init: any): string | null {
624+
if (!init) return null;
625+
let node = init;
626+
if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression") {
627+
node = node.expression;
628+
}
629+
if (node.type === "Literal") {
630+
return node.value === null ? "null" : String(node.value);
631+
}
632+
if (
633+
node.type === "UnaryExpression" &&
634+
node.prefix &&
635+
node.operator === "-" &&
636+
node.argument?.type === "Literal" &&
637+
typeof node.argument.value === "number"
638+
) {
639+
return String(-node.argument.value);
640+
}
641+
if (
642+
node.type === "TemplateLiteral" &&
643+
node.expressions?.length === 0 &&
644+
node.quasis?.length === 1
645+
) {
646+
return node.quasis[0].value?.cooked ?? null;
647+
}
648+
return null;
649+
}
650+
617651
function extractObjectMembers(
618652
members: any[] | undefined,
619653
filePath: string,

templates/agents/skills/codemap/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ LIMIT 10
9090
| is_default_export | INTEGER | 1 if default export |
9191
| members | TEXT | JSON enum members (NULL for non-enums) |
9292
| doc_comment | TEXT | Leading JSDoc text (cleaned), NULL when absent |
93+
| value | TEXT | Literal value for consts (`"ok"`, `42`, `true`, `null`) |
9394

9495
### `type_members` — Properties of interfaces and object-literal type aliases
9596

0 commit comments

Comments
 (0)