@stainless-code/codemap 0.1.0
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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/builtin-CQ_e97VT.mjs +74 -0
- package/dist/cmd-agents-BJPx1vGG.mjs +38 -0
- package/dist/cmd-index-oyoHr0c4.mjs +33 -0
- package/dist/cmd-query-D3zXZu7K.mjs +12 -0
- package/dist/config-CsPgQWSX.mjs +885 -0
- package/dist/index.d.mts +198 -0
- package/dist/index.mjs +232 -0
- package/dist/parse-worker-core-DFFbs70b.mjs +67 -0
- package/dist/parse-worker-node.d.mts +2 -0
- package/dist/parse-worker-node.mjs +11 -0
- package/dist/parse-worker.d.mts +13 -0
- package/dist/parse-worker.mjs +9 -0
- package/dist/parsed-types-udxNyD9e.d.mts +108 -0
- package/dist/parser-VtP0EJiw.mjs +481 -0
- package/dist/run-index-DQD5afqQ.mjs +129 -0
- package/package.json +93 -0
- package/templates/agents/README.md +5 -0
- package/templates/agents/rules/agents-first-convention.mdc +37 -0
- package/templates/agents/rules/codemap.mdc +110 -0
- package/templates/agents/skills/codemap/SKILL.md +312 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { extname } from "node:path";
|
|
4
|
+
import { transform } from "lightningcss";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { Visitor, parseSync } from "oxc-parser";
|
|
7
|
+
//#region src/constants.ts
|
|
8
|
+
const LANG_MAP = {
|
|
9
|
+
".ts": "ts",
|
|
10
|
+
".tsx": "tsx",
|
|
11
|
+
".mts": "mts",
|
|
12
|
+
".cts": "cts",
|
|
13
|
+
".js": "js",
|
|
14
|
+
".jsx": "jsx",
|
|
15
|
+
".mjs": "mjs",
|
|
16
|
+
".cjs": "cjs",
|
|
17
|
+
".css": "css",
|
|
18
|
+
".md": "md",
|
|
19
|
+
".mdx": "mdx",
|
|
20
|
+
".mdc": "mdc",
|
|
21
|
+
".yml": "yaml",
|
|
22
|
+
".yaml": "yaml",
|
|
23
|
+
".txt": "txt",
|
|
24
|
+
".json": "json",
|
|
25
|
+
".sh": "sh"
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/markers.ts
|
|
29
|
+
const MARKER_RE = /\b(TODO|FIXME|HACK|NOTE)[\s:]+(.+)/g;
|
|
30
|
+
function extractMarkers(source, filePath) {
|
|
31
|
+
const markers = [];
|
|
32
|
+
MARKER_RE.lastIndex = 0;
|
|
33
|
+
let match;
|
|
34
|
+
let lineNum = 1;
|
|
35
|
+
let lastIdx = 0;
|
|
36
|
+
while ((match = MARKER_RE.exec(source)) !== null) {
|
|
37
|
+
for (let i = lastIdx; i < match.index; i++) if (source.charCodeAt(i) === 10) lineNum++;
|
|
38
|
+
lastIdx = match.index;
|
|
39
|
+
markers.push({
|
|
40
|
+
file_path: filePath,
|
|
41
|
+
line_number: lineNum,
|
|
42
|
+
kind: match[1],
|
|
43
|
+
content: match[2].trim()
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return markers;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/css-parser.ts
|
|
50
|
+
function extractCssData(filePath, source, relPath) {
|
|
51
|
+
const variables = [];
|
|
52
|
+
const classes = [];
|
|
53
|
+
const keyframes = [];
|
|
54
|
+
const markers = [];
|
|
55
|
+
const importSources = [];
|
|
56
|
+
const isModule = relPath.endsWith(".module.css");
|
|
57
|
+
const seenClasses = /* @__PURE__ */ new Set();
|
|
58
|
+
try {
|
|
59
|
+
transform({
|
|
60
|
+
filename: filePath,
|
|
61
|
+
code: Buffer.from(source),
|
|
62
|
+
errorRecovery: true,
|
|
63
|
+
analyzeDependencies: true,
|
|
64
|
+
customAtRules: { theme: { body: "declaration-list" } },
|
|
65
|
+
visitor: {
|
|
66
|
+
Declaration: { custom(property) {
|
|
67
|
+
variables.push({
|
|
68
|
+
file_path: relPath,
|
|
69
|
+
name: property.name,
|
|
70
|
+
value: stringifyCssValue(property.value),
|
|
71
|
+
scope: ":root",
|
|
72
|
+
line_number: property.loc?.line ?? 0
|
|
73
|
+
});
|
|
74
|
+
} },
|
|
75
|
+
Rule: {
|
|
76
|
+
style(rule) {
|
|
77
|
+
const line = rule.value.loc?.line ?? 0;
|
|
78
|
+
extractClassNames(rule.value.selectors, relPath, isModule, line, classes, seenClasses);
|
|
79
|
+
},
|
|
80
|
+
keyframes(rule) {
|
|
81
|
+
const name = rule.value.name;
|
|
82
|
+
if (typeof name === "string") keyframes.push({
|
|
83
|
+
file_path: relPath,
|
|
84
|
+
name,
|
|
85
|
+
line_number: rule.value.loc?.line ?? 0
|
|
86
|
+
});
|
|
87
|
+
else if (name && typeof name === "object" && "value" in name) keyframes.push({
|
|
88
|
+
file_path: relPath,
|
|
89
|
+
name: name.value,
|
|
90
|
+
line_number: rule.value.loc?.line ?? 0
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
custom: { theme(rule) {
|
|
94
|
+
const decls = rule.body?.value;
|
|
95
|
+
if (decls && typeof decls === "object") {
|
|
96
|
+
const declarations = decls.declarations ?? [];
|
|
97
|
+
for (const decl of declarations) if (decl.property === "custom" && decl.value?.name) variables.push({
|
|
98
|
+
file_path: relPath,
|
|
99
|
+
name: decl.value.name,
|
|
100
|
+
value: stringifyCssValue(decl.value.value),
|
|
101
|
+
scope: "@theme",
|
|
102
|
+
line_number: decl.value.loc?.line ?? 0
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
} }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
} catch {
|
|
110
|
+
const lines = source.split("\n");
|
|
111
|
+
extractCssVariablesRegex(lines, relPath, variables);
|
|
112
|
+
extractCssClassesRegex(lines, relPath, isModule, classes, seenClasses);
|
|
113
|
+
extractCssKeyframesRegex(lines, relPath, keyframes);
|
|
114
|
+
}
|
|
115
|
+
extractImportSources(source, importSources);
|
|
116
|
+
markers.push(...extractMarkers(source, relPath));
|
|
117
|
+
return {
|
|
118
|
+
variables,
|
|
119
|
+
classes,
|
|
120
|
+
keyframes,
|
|
121
|
+
markers,
|
|
122
|
+
importSources
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function stringifyCssValue(value) {
|
|
126
|
+
if (!value) return "";
|
|
127
|
+
if (typeof value === "string") return value;
|
|
128
|
+
if (Array.isArray(value)) return value.map((v) => stringifyToken(v)).join(" ");
|
|
129
|
+
return JSON.stringify(value);
|
|
130
|
+
}
|
|
131
|
+
function stringifyToken(token) {
|
|
132
|
+
if (!token) return "";
|
|
133
|
+
if (typeof token === "string") return token;
|
|
134
|
+
if (token.type === "length") return `${token.value?.value ?? 0}${token.value?.unit ?? ""}`;
|
|
135
|
+
if (token.type === "percentage") return `${token.value ?? 0}%`;
|
|
136
|
+
if (token.type === "color") return stringifyColor(token.value);
|
|
137
|
+
if (token.type === "token") {
|
|
138
|
+
const t = token.value;
|
|
139
|
+
if (t?.type === "ident") return t.value;
|
|
140
|
+
if (t?.type === "number") return String(t.value);
|
|
141
|
+
if (t?.type === "dimension") return `${t.value}${t.unit ?? ""}`;
|
|
142
|
+
if (t?.type === "comma") return ",";
|
|
143
|
+
if (t?.type === "string") return `"${t.value}"`;
|
|
144
|
+
return t?.value ?? "";
|
|
145
|
+
}
|
|
146
|
+
if (token.type === "var") return `var(${token.value?.name ?? ""})`;
|
|
147
|
+
if (token.type === "env") return `env(${token.value?.name ?? ""})`;
|
|
148
|
+
return "";
|
|
149
|
+
}
|
|
150
|
+
function stringifyColor(color) {
|
|
151
|
+
if (!color) return "";
|
|
152
|
+
if (color.type === "rgb") return `rgb(${color.r}, ${color.g}, ${color.b})`;
|
|
153
|
+
if (color.type === "rgba") return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.alpha})`;
|
|
154
|
+
return JSON.stringify(color);
|
|
155
|
+
}
|
|
156
|
+
function extractClassNames(selectors, filePath, isModule, line, classes, seen) {
|
|
157
|
+
if (!selectors) return;
|
|
158
|
+
for (const selector of selectors) for (const component of selector) if (component.type === "class" && !seen.has(component.name)) {
|
|
159
|
+
seen.add(component.name);
|
|
160
|
+
classes.push({
|
|
161
|
+
file_path: filePath,
|
|
162
|
+
name: component.name,
|
|
163
|
+
is_module: isModule ? 1 : 0,
|
|
164
|
+
line_number: line
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const CSS_VAR_RE = /^\s*(--[\w-]+)\s*:\s*(.+?)\s*;/gm;
|
|
169
|
+
function extractCssVariablesRegex(lines, filePath, variables) {
|
|
170
|
+
for (let i = 0; i < lines.length; i++) {
|
|
171
|
+
CSS_VAR_RE.lastIndex = 0;
|
|
172
|
+
const match = CSS_VAR_RE.exec(lines[i]);
|
|
173
|
+
if (match) variables.push({
|
|
174
|
+
file_path: filePath,
|
|
175
|
+
name: match[1],
|
|
176
|
+
value: match[2],
|
|
177
|
+
scope: "unknown",
|
|
178
|
+
line_number: i + 1
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const CSS_CLASS_RE = /\.([a-zA-Z_][\w-]*)/g;
|
|
183
|
+
function extractCssClassesRegex(lines, filePath, isModule, classes, seen) {
|
|
184
|
+
for (let i = 0; i < lines.length; i++) {
|
|
185
|
+
CSS_CLASS_RE.lastIndex = 0;
|
|
186
|
+
let match;
|
|
187
|
+
while ((match = CSS_CLASS_RE.exec(lines[i])) !== null) if (!seen.has(match[1])) {
|
|
188
|
+
seen.add(match[1]);
|
|
189
|
+
classes.push({
|
|
190
|
+
file_path: filePath,
|
|
191
|
+
name: match[1],
|
|
192
|
+
is_module: isModule ? 1 : 0,
|
|
193
|
+
line_number: i + 1
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const KEYFRAMES_RE = /@keyframes\s+([\w-]+)/g;
|
|
199
|
+
function extractCssKeyframesRegex(lines, filePath, keyframes) {
|
|
200
|
+
for (let i = 0; i < lines.length; i++) {
|
|
201
|
+
KEYFRAMES_RE.lastIndex = 0;
|
|
202
|
+
const match = KEYFRAMES_RE.exec(lines[i]);
|
|
203
|
+
if (match) keyframes.push({
|
|
204
|
+
file_path: filePath,
|
|
205
|
+
name: match[1],
|
|
206
|
+
line_number: i + 1
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const IMPORT_RE = /@import\s+(?:url\()?['"]([^'"]+)['"]\)?/g;
|
|
211
|
+
function extractImportSources(source, importSources) {
|
|
212
|
+
let match;
|
|
213
|
+
IMPORT_RE.lastIndex = 0;
|
|
214
|
+
while ((match = IMPORT_RE.exec(source)) !== null) importSources.push(match[1]);
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/hash.ts
|
|
218
|
+
/**
|
|
219
|
+
* Stable content fingerprint for incremental indexing.
|
|
220
|
+
* Same algorithm on Bun and Node (unlike Bun.hash).
|
|
221
|
+
*/
|
|
222
|
+
function hashContent(content) {
|
|
223
|
+
return createHash("sha256").update(content).digest("hex");
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/parser.ts
|
|
227
|
+
/**
|
|
228
|
+
* Compute line number from byte offset.
|
|
229
|
+
* Build a line-start-offsets array once, then binary search.
|
|
230
|
+
*/
|
|
231
|
+
function buildLineMap(source) {
|
|
232
|
+
const offsets = [0];
|
|
233
|
+
for (let i = 0; i < source.length; i++) if (source.charCodeAt(i) === 10) offsets.push(i + 1);
|
|
234
|
+
return offsets;
|
|
235
|
+
}
|
|
236
|
+
function offsetToLine(lineMap, offset) {
|
|
237
|
+
let lo = 0;
|
|
238
|
+
let hi = lineMap.length - 1;
|
|
239
|
+
while (lo < hi) {
|
|
240
|
+
const mid = lo + hi + 1 >> 1;
|
|
241
|
+
if (lineMap[mid] <= offset) lo = mid;
|
|
242
|
+
else hi = mid - 1;
|
|
243
|
+
}
|
|
244
|
+
return lo + 1;
|
|
245
|
+
}
|
|
246
|
+
function extractFileData(filePath, source, relPath) {
|
|
247
|
+
const ext = extname(filePath).toLowerCase();
|
|
248
|
+
const lang = ext === ".tsx" ? "tsx" : ext === ".jsx" ? "jsx" : ext === ".ts" || ext === ".mts" || ext === ".cts" ? "ts" : "js";
|
|
249
|
+
const isTsx = ext === ".tsx" || ext === ".jsx";
|
|
250
|
+
const result = parseSync(filePath, source, {
|
|
251
|
+
lang,
|
|
252
|
+
preserveParens: false
|
|
253
|
+
});
|
|
254
|
+
const lineMap = buildLineMap(source);
|
|
255
|
+
const mod = result.module;
|
|
256
|
+
const symbols = [];
|
|
257
|
+
const imports = [];
|
|
258
|
+
const exports = [];
|
|
259
|
+
const components = [];
|
|
260
|
+
const markers = [];
|
|
261
|
+
const exportedNames = /* @__PURE__ */ new Set();
|
|
262
|
+
const defaultExportedNames = /* @__PURE__ */ new Set();
|
|
263
|
+
for (const exp of mod.staticExports) for (const entry of exp.entries) {
|
|
264
|
+
const exportName = entry.exportName;
|
|
265
|
+
if (exportName.kind === "Default") {
|
|
266
|
+
const localName = entry.localName;
|
|
267
|
+
if (localName.name) defaultExportedNames.add(localName.name);
|
|
268
|
+
defaultExportedNames.add("default");
|
|
269
|
+
} else if (exportName.kind === "Name" && exportName.name) exportedNames.add(exportName.name);
|
|
270
|
+
exports.push(exportEntryToRow(relPath, entry));
|
|
271
|
+
}
|
|
272
|
+
for (const imp of mod.staticImports) imports.push(staticImportToRow(relPath, imp, lineMap));
|
|
273
|
+
const hookCalls = /* @__PURE__ */ new Map();
|
|
274
|
+
let currentFunctionScope = null;
|
|
275
|
+
new Visitor({
|
|
276
|
+
FunctionDeclaration(node) {
|
|
277
|
+
const name = node.id?.name;
|
|
278
|
+
if (!name) return;
|
|
279
|
+
const lineStart = offsetToLine(lineMap, node.start);
|
|
280
|
+
const lineEnd = offsetToLine(lineMap, node.end);
|
|
281
|
+
const isExported = exportedNames.has(name) || defaultExportedNames.has(name);
|
|
282
|
+
const isDefault = defaultExportedNames.has(name);
|
|
283
|
+
symbols.push({
|
|
284
|
+
file_path: relPath,
|
|
285
|
+
name,
|
|
286
|
+
kind: "function",
|
|
287
|
+
line_start: lineStart,
|
|
288
|
+
line_end: lineEnd,
|
|
289
|
+
signature: buildFunctionSignature(name, node),
|
|
290
|
+
is_exported: isExported ? 1 : 0,
|
|
291
|
+
is_default_export: isDefault ? 1 : 0
|
|
292
|
+
});
|
|
293
|
+
if (isTsx && /^[A-Z]/.test(name)) {
|
|
294
|
+
currentFunctionScope = name;
|
|
295
|
+
hookCalls.set(name, /* @__PURE__ */ new Set());
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
"FunctionDeclaration:exit"(node) {
|
|
299
|
+
const name = node.id?.name;
|
|
300
|
+
if (name && currentFunctionScope === name) {
|
|
301
|
+
maybeAddComponent(name, node, false);
|
|
302
|
+
currentFunctionScope = null;
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
VariableDeclaration(node) {
|
|
306
|
+
for (const decl of node.declarations) {
|
|
307
|
+
const name = decl.id?.name;
|
|
308
|
+
if (!name) continue;
|
|
309
|
+
const init = decl.init;
|
|
310
|
+
const lineStart = offsetToLine(lineMap, node.start);
|
|
311
|
+
const lineEnd = offsetToLine(lineMap, node.end);
|
|
312
|
+
const isExported = exportedNames.has(name) || defaultExportedNames.has(name);
|
|
313
|
+
const isDefault = defaultExportedNames.has(name);
|
|
314
|
+
const isArrowOrFn = init?.type === "ArrowFunctionExpression" || init?.type === "FunctionExpression";
|
|
315
|
+
symbols.push({
|
|
316
|
+
file_path: relPath,
|
|
317
|
+
name,
|
|
318
|
+
kind: isArrowOrFn ? "function" : "const",
|
|
319
|
+
line_start: lineStart,
|
|
320
|
+
line_end: lineEnd,
|
|
321
|
+
signature: isArrowOrFn ? buildFunctionSignature(name, init) : `const ${name}`,
|
|
322
|
+
is_exported: isExported ? 1 : 0,
|
|
323
|
+
is_default_export: isDefault ? 1 : 0
|
|
324
|
+
});
|
|
325
|
+
if (isTsx && /^[A-Z]/.test(name) && isArrowOrFn) {
|
|
326
|
+
currentFunctionScope = name;
|
|
327
|
+
hookCalls.set(name, /* @__PURE__ */ new Set());
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
"VariableDeclaration:exit"(node) {
|
|
332
|
+
for (const decl of node.declarations) {
|
|
333
|
+
const name = decl.id?.name;
|
|
334
|
+
if (name && currentFunctionScope === name) {
|
|
335
|
+
maybeAddComponent(name, decl.init, true);
|
|
336
|
+
currentFunctionScope = null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
TSTypeAliasDeclaration(node) {
|
|
341
|
+
const name = node.id?.name;
|
|
342
|
+
if (!name) return;
|
|
343
|
+
const isExported = exportedNames.has(name);
|
|
344
|
+
symbols.push({
|
|
345
|
+
file_path: relPath,
|
|
346
|
+
name,
|
|
347
|
+
kind: "type",
|
|
348
|
+
line_start: offsetToLine(lineMap, node.start),
|
|
349
|
+
line_end: offsetToLine(lineMap, node.end),
|
|
350
|
+
signature: `type ${name}`,
|
|
351
|
+
is_exported: isExported ? 1 : 0,
|
|
352
|
+
is_default_export: 0
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
TSInterfaceDeclaration(node) {
|
|
356
|
+
const name = node.id?.name;
|
|
357
|
+
if (!name) return;
|
|
358
|
+
const isExported = exportedNames.has(name);
|
|
359
|
+
symbols.push({
|
|
360
|
+
file_path: relPath,
|
|
361
|
+
name,
|
|
362
|
+
kind: "interface",
|
|
363
|
+
line_start: offsetToLine(lineMap, node.start),
|
|
364
|
+
line_end: offsetToLine(lineMap, node.end),
|
|
365
|
+
signature: `interface ${name}`,
|
|
366
|
+
is_exported: isExported ? 1 : 0,
|
|
367
|
+
is_default_export: 0
|
|
368
|
+
});
|
|
369
|
+
},
|
|
370
|
+
TSEnumDeclaration(node) {
|
|
371
|
+
const name = node.id?.name;
|
|
372
|
+
if (!name) return;
|
|
373
|
+
const isExported = exportedNames.has(name);
|
|
374
|
+
symbols.push({
|
|
375
|
+
file_path: relPath,
|
|
376
|
+
name,
|
|
377
|
+
kind: "enum",
|
|
378
|
+
line_start: offsetToLine(lineMap, node.start),
|
|
379
|
+
line_end: offsetToLine(lineMap, node.end),
|
|
380
|
+
signature: `enum ${name}`,
|
|
381
|
+
is_exported: isExported ? 1 : 0,
|
|
382
|
+
is_default_export: 0
|
|
383
|
+
});
|
|
384
|
+
},
|
|
385
|
+
ClassDeclaration(node) {
|
|
386
|
+
const name = node.id?.name;
|
|
387
|
+
if (!name) return;
|
|
388
|
+
const isExported = exportedNames.has(name) || defaultExportedNames.has(name);
|
|
389
|
+
symbols.push({
|
|
390
|
+
file_path: relPath,
|
|
391
|
+
name,
|
|
392
|
+
kind: "class",
|
|
393
|
+
line_start: offsetToLine(lineMap, node.start),
|
|
394
|
+
line_end: offsetToLine(lineMap, node.end),
|
|
395
|
+
signature: `class ${name}`,
|
|
396
|
+
is_exported: isExported ? 1 : 0,
|
|
397
|
+
is_default_export: defaultExportedNames.has(name) ? 1 : 0
|
|
398
|
+
});
|
|
399
|
+
},
|
|
400
|
+
CallExpression(node) {
|
|
401
|
+
if (!currentFunctionScope) return;
|
|
402
|
+
const callee = node.callee;
|
|
403
|
+
if (callee?.type === "Identifier" && /^use[A-Z]/.test(callee.name)) hookCalls.get(currentFunctionScope)?.add(callee.name);
|
|
404
|
+
}
|
|
405
|
+
}).visit(result.program);
|
|
406
|
+
markers.push(...extractMarkers(source, relPath));
|
|
407
|
+
function maybeAddComponent(name, node, _isArrow) {
|
|
408
|
+
if (!isTsx || !/^[A-Z]/.test(name)) return;
|
|
409
|
+
const hooks = hookCalls.get(name);
|
|
410
|
+
const isDefault = defaultExportedNames.has(name);
|
|
411
|
+
let propsType = null;
|
|
412
|
+
const params = node?.params;
|
|
413
|
+
if (params?.length > 0) {
|
|
414
|
+
const firstParam = params[0];
|
|
415
|
+
if (firstParam.typeAnnotation?.typeAnnotation) {
|
|
416
|
+
const ta = firstParam.typeAnnotation.typeAnnotation;
|
|
417
|
+
if (ta.type === "TSTypeReference" && ta.typeName?.name) propsType = ta.typeName.name;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
components.push({
|
|
421
|
+
file_path: relPath,
|
|
422
|
+
name,
|
|
423
|
+
props_type: propsType,
|
|
424
|
+
hooks_used: JSON.stringify(hooks ? [...hooks] : []),
|
|
425
|
+
is_default_export: isDefault ? 1 : 0
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
symbols,
|
|
430
|
+
imports,
|
|
431
|
+
exports,
|
|
432
|
+
components,
|
|
433
|
+
markers
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function staticImportToRow(filePath, imp, lineMap) {
|
|
437
|
+
const specifiers = [];
|
|
438
|
+
let isTypeOnly = true;
|
|
439
|
+
for (const entry of imp.entries) {
|
|
440
|
+
if (!entry.isType) isTypeOnly = false;
|
|
441
|
+
const importKind = entry.importName.kind;
|
|
442
|
+
if (importKind === "Default") specifiers.push(entry.localName.value);
|
|
443
|
+
else if (importKind === "NamespaceObject") specifiers.push(`* as ${entry.localName.value}`);
|
|
444
|
+
else if (importKind === "Name") {
|
|
445
|
+
const original = entry.importName.name;
|
|
446
|
+
const local = entry.localName.value;
|
|
447
|
+
specifiers.push(original === local ? original : `${original} as ${local}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (imp.entries.length === 0) isTypeOnly = false;
|
|
451
|
+
return {
|
|
452
|
+
file_path: filePath,
|
|
453
|
+
source: imp.moduleRequest.value,
|
|
454
|
+
resolved_path: null,
|
|
455
|
+
specifiers: JSON.stringify(specifiers),
|
|
456
|
+
is_type_only: isTypeOnly ? 1 : 0,
|
|
457
|
+
line_number: offsetToLine(lineMap, imp.start)
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function exportEntryToRow(filePath, entry) {
|
|
461
|
+
const exportName = entry.exportName;
|
|
462
|
+
const isDefault = exportName.kind === "Default";
|
|
463
|
+
const name = isDefault ? "default" : exportName.name ?? entry.localName.name ?? "unknown";
|
|
464
|
+
let kind = "value";
|
|
465
|
+
if (entry.isType) kind = "type";
|
|
466
|
+
if (entry.moduleRequest) kind = "re-export";
|
|
467
|
+
return {
|
|
468
|
+
file_path: filePath,
|
|
469
|
+
name,
|
|
470
|
+
kind,
|
|
471
|
+
is_default: isDefault ? 1 : 0,
|
|
472
|
+
re_export_source: entry.moduleRequest?.value ?? null
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function buildFunctionSignature(name, node) {
|
|
476
|
+
const params = node?.params;
|
|
477
|
+
if (!params || params.length === 0) return `${name}()`;
|
|
478
|
+
return `${name}(${params.map((p) => p.name ?? p.left?.name ?? p.argument?.name ?? "...").join(", ")})`;
|
|
479
|
+
}
|
|
480
|
+
//#endregion
|
|
481
|
+
export { LANG_MAP as a, extractMarkers as i, hashContent as n, extractCssData as r, extractFileData as t };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { _ as createSchema, b as setMeta, c as deleteFilesFromIndex, d as indexFiles, l as getChangedFiles, m as targetedReindex, o as VALID_EXTENSIONS, s as collectFiles, u as getCurrentCommit, v as getAllFileHashes } from "./config-CsPgQWSX.mjs";
|
|
4
|
+
import { extname } from "node:path";
|
|
5
|
+
//#region src/application/run-index.ts
|
|
6
|
+
function emptyStats() {
|
|
7
|
+
return {
|
|
8
|
+
files: 0,
|
|
9
|
+
symbols: 0,
|
|
10
|
+
imports: 0,
|
|
11
|
+
exports: 0,
|
|
12
|
+
components: 0,
|
|
13
|
+
dependencies: 0,
|
|
14
|
+
markers: 0,
|
|
15
|
+
css_vars: 0,
|
|
16
|
+
css_classes: 0,
|
|
17
|
+
css_keyframes: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Core indexing pipeline (CLI and `Codemap#index`).
|
|
22
|
+
*
|
|
23
|
+
* @param db - Open database; caller owns the connection lifecycle.
|
|
24
|
+
* @param options - Index mode, optional targeted paths, and logging.
|
|
25
|
+
* @returns Row counts and timing; see {@link IndexResult}.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Call `initCodemap()` and `configureResolver()` for this project before invoking (same as CLI bootstrap).
|
|
29
|
+
*/
|
|
30
|
+
async function runCodemapIndex(db, options = {}) {
|
|
31
|
+
const quiet = options.quiet ?? false;
|
|
32
|
+
const mode = options.mode ?? "incremental";
|
|
33
|
+
if (mode === "full") {
|
|
34
|
+
if (!quiet) console.log(" Full rebuild requested...");
|
|
35
|
+
const run = await indexFiles(db, collectFiles(), true, void 0, { quiet });
|
|
36
|
+
return {
|
|
37
|
+
mode: "full",
|
|
38
|
+
indexed: run.indexed,
|
|
39
|
+
skipped: run.skipped,
|
|
40
|
+
elapsedMs: run.elapsedMs,
|
|
41
|
+
stats: run.stats
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (mode === "files") {
|
|
45
|
+
const targetFiles = (options.files ?? []).filter((f) => {
|
|
46
|
+
const ext = extname(f);
|
|
47
|
+
return VALID_EXTENSIONS.has(ext);
|
|
48
|
+
});
|
|
49
|
+
if (targetFiles.length === 0) return {
|
|
50
|
+
mode: "files",
|
|
51
|
+
indexed: 0,
|
|
52
|
+
skipped: 0,
|
|
53
|
+
elapsedMs: 0,
|
|
54
|
+
stats: emptyStats(),
|
|
55
|
+
idle: true
|
|
56
|
+
};
|
|
57
|
+
const run = await targetedReindex(db, targetFiles, quiet);
|
|
58
|
+
return {
|
|
59
|
+
mode: "files",
|
|
60
|
+
indexed: run.indexed,
|
|
61
|
+
skipped: run.skipped,
|
|
62
|
+
elapsedMs: run.elapsedMs,
|
|
63
|
+
stats: run.stats
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
createSchema(db);
|
|
67
|
+
const diff = getChangedFiles(db);
|
|
68
|
+
if (diff) {
|
|
69
|
+
if (!quiet) console.log(` Incremental: ${diff.changed.length} changed, ${diff.deleted.length} deleted`);
|
|
70
|
+
deleteFilesFromIndex(db, diff.deleted, quiet);
|
|
71
|
+
if (diff.changed.length > 0) {
|
|
72
|
+
const indexedPaths = new Set(getAllFileHashes(db).keys());
|
|
73
|
+
for (const f of diff.changed) indexedPaths.add(f);
|
|
74
|
+
const run = await indexFiles(db, diff.changed, false, indexedPaths, { quiet });
|
|
75
|
+
return {
|
|
76
|
+
mode: "incremental",
|
|
77
|
+
indexed: run.indexed,
|
|
78
|
+
skipped: run.skipped,
|
|
79
|
+
elapsedMs: run.elapsedMs,
|
|
80
|
+
stats: run.stats
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (diff.deleted.length > 0) {
|
|
84
|
+
setMeta(db, "last_indexed_commit", getCurrentCommit());
|
|
85
|
+
if (!quiet) console.log(" Index updated (deletions only)");
|
|
86
|
+
return {
|
|
87
|
+
mode: "incremental",
|
|
88
|
+
indexed: 0,
|
|
89
|
+
skipped: 0,
|
|
90
|
+
elapsedMs: 0,
|
|
91
|
+
stats: fetchStats(db),
|
|
92
|
+
idle: true
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!quiet) console.log(" Index is up to date");
|
|
96
|
+
return {
|
|
97
|
+
mode: "incremental",
|
|
98
|
+
indexed: 0,
|
|
99
|
+
skipped: 0,
|
|
100
|
+
elapsedMs: 0,
|
|
101
|
+
stats: fetchStats(db),
|
|
102
|
+
idle: true
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (!quiet) console.log(" No previous index or incompatible history, doing full rebuild...");
|
|
106
|
+
const run = await indexFiles(db, collectFiles(), true, void 0, { quiet });
|
|
107
|
+
return {
|
|
108
|
+
mode: "full",
|
|
109
|
+
indexed: run.indexed,
|
|
110
|
+
skipped: run.skipped,
|
|
111
|
+
elapsedMs: run.elapsedMs,
|
|
112
|
+
stats: run.stats
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function fetchStats(db) {
|
|
116
|
+
return db.query(`SELECT
|
|
117
|
+
(SELECT COUNT(*) FROM files) as files,
|
|
118
|
+
(SELECT COUNT(*) FROM symbols) as symbols,
|
|
119
|
+
(SELECT COUNT(*) FROM imports) as imports,
|
|
120
|
+
(SELECT COUNT(*) FROM exports) as exports,
|
|
121
|
+
(SELECT COUNT(*) FROM components) as components,
|
|
122
|
+
(SELECT COUNT(*) FROM dependencies) as dependencies,
|
|
123
|
+
(SELECT COUNT(*) FROM markers) as markers,
|
|
124
|
+
(SELECT COUNT(*) FROM css_variables) as css_vars,
|
|
125
|
+
(SELECT COUNT(*) FROM css_classes) as css_classes,
|
|
126
|
+
(SELECT COUNT(*) FROM css_keyframes) as css_keyframes`).get();
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { runCodemapIndex as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stainless-code/codemap",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Query your codebase — structural SQLite index for AI agents",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agents",
|
|
7
|
+
"ai",
|
|
8
|
+
"codebase",
|
|
9
|
+
"dependencies",
|
|
10
|
+
"imports",
|
|
11
|
+
"index",
|
|
12
|
+
"sqlite",
|
|
13
|
+
"symbols"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/stainless-code/codemap#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/stainless-code/codemap/issues"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/stainless-code/codemap.git"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"codemap": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"CHANGELOG.md",
|
|
29
|
+
"dist",
|
|
30
|
+
"templates"
|
|
31
|
+
],
|
|
32
|
+
"type": "module",
|
|
33
|
+
"main": "./dist/index.mjs",
|
|
34
|
+
"types": "./dist/index.d.mts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.mts",
|
|
38
|
+
"import": "./dist/index.mjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"benchmark": "bun src/benchmark.ts",
|
|
46
|
+
"build": "tsdown",
|
|
47
|
+
"changeset": "changeset",
|
|
48
|
+
"check": "bun run build && bun run --parallel format:check lint:ci test typecheck",
|
|
49
|
+
"dev": "bun src/index.ts",
|
|
50
|
+
"fix": "bun run lint:fix && bun run format",
|
|
51
|
+
"format": "oxfmt",
|
|
52
|
+
"format:check": "oxfmt --check --no-error-on-unmatched-pattern",
|
|
53
|
+
"lint": "oxlint",
|
|
54
|
+
"lint-staged": "lint-staged",
|
|
55
|
+
"lint:ci": "oxlint --quiet",
|
|
56
|
+
"lint:fix": "oxlint --fix",
|
|
57
|
+
"pack": "bun run build && npm pack",
|
|
58
|
+
"prepare": "husky || true",
|
|
59
|
+
"prepublishOnly": "bun run build",
|
|
60
|
+
"release": "changeset publish",
|
|
61
|
+
"test": "bun test",
|
|
62
|
+
"test:ci": "bun run test:coverage",
|
|
63
|
+
"test:coverage": "bun test --coverage",
|
|
64
|
+
"typecheck": "tsgo --noEmit"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"better-sqlite3": "^12.8.0",
|
|
68
|
+
"fast-glob": "^3.3.3",
|
|
69
|
+
"lightningcss": "^1.32.0",
|
|
70
|
+
"oxc-parser": "^0.123.0",
|
|
71
|
+
"oxc-resolver": "^11.19.1",
|
|
72
|
+
"zod": "^4.3.6"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
76
|
+
"@changesets/cli": "^2.30.0",
|
|
77
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
78
|
+
"@types/bun": "^1.3.11",
|
|
79
|
+
"@types/node": "^25.5.2",
|
|
80
|
+
"@typescript/native-preview": "^7.0.0-dev.20260406.1",
|
|
81
|
+
"husky": "^9.1.7",
|
|
82
|
+
"lint-staged": "^16.4.0",
|
|
83
|
+
"oxfmt": "^0.43.0",
|
|
84
|
+
"oxlint": "^1.58.0",
|
|
85
|
+
"tsdown": "^0.21.7",
|
|
86
|
+
"typescript": "^6.0.2"
|
|
87
|
+
},
|
|
88
|
+
"engines": {
|
|
89
|
+
"bun": ">=1.0.0",
|
|
90
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
91
|
+
},
|
|
92
|
+
"contributing": ".github/CONTRIBUTING.md"
|
|
93
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Bundled agent templates
|
|
2
|
+
|
|
3
|
+
These files are **copies** of the upstream [`.agents/`](../../.agents/) rules and skills shipped with `@stainless-code/codemap` for `codemap agents init`.
|
|
4
|
+
|
|
5
|
+
After running the command, **edit** `.agents/` in your project (paths, SQL, team conventions). Treat updates here as a reference when refreshing your copy.
|