feat: initial CodeBoard monorepo scaffold
Turborepo monorepo with npm workspaces: - apps/web: Next.js 14 frontend with Tailwind v4, SSE progress, doc viewer - apps/worker: BullMQ job processor (clone → parse → LLM generate) - packages/shared: TypeScript types - packages/parser: Babel-based AST parser (JS/TS) + regex (Python) - packages/llm: OpenAI/Anthropic provider abstraction + prompt pipeline - packages/diagrams: Mermaid architecture & dependency graph generators - packages/database: Prisma schema (PostgreSQL) - Docker multi-stage build (web + worker targets) All packages compile successfully with tsc and next build.
This commit is contained in:
24
packages/diagrams/package.json
Normal file
24
packages/diagrams/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@codeboard/diagrams",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"dev": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codeboard/shared": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7"
|
||||
}
|
||||
}
|
||||
49
packages/diagrams/src/architecture.ts
Normal file
49
packages/diagrams/src/architecture.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { ModuleNode, DependencyEdge } from "@codeboard/shared";
|
||||
|
||||
function sanitizeId(name: string): string {
|
||||
return name.replace(/[^a-zA-Z0-9]/g, "_");
|
||||
}
|
||||
|
||||
function truncateLabel(name: string, max = 20): string {
|
||||
return name.length > max ? name.slice(0, max - 1) + "\u2026" : name;
|
||||
}
|
||||
|
||||
export function generateArchitectureDiagram(
|
||||
modules: ModuleNode[],
|
||||
deps: DependencyEdge[]
|
||||
): string {
|
||||
if (modules.length === 0) {
|
||||
return "flowchart TD\n empty[No modules detected]";
|
||||
}
|
||||
|
||||
const lines: string[] = ["flowchart TD"];
|
||||
|
||||
const moduleIds = new Map<string, string>();
|
||||
for (const mod of modules) {
|
||||
const id = sanitizeId(mod.name);
|
||||
moduleIds.set(mod.path, id);
|
||||
const fileCount = mod.files.length;
|
||||
lines.push(` ${id}["${truncateLabel(mod.name)}\\n${fileCount} files"]`);
|
||||
}
|
||||
|
||||
const edgeSet = new Set<string>();
|
||||
for (const dep of deps) {
|
||||
const sourceModule = modules.find((m) => m.files.includes(dep.source));
|
||||
const targetModule = modules.find((m) => m.files.includes(dep.target));
|
||||
|
||||
if (!sourceModule || !targetModule) continue;
|
||||
if (sourceModule.path === targetModule.path) continue;
|
||||
|
||||
const sourceId = moduleIds.get(sourceModule.path);
|
||||
const targetId = moduleIds.get(targetModule.path);
|
||||
if (!sourceId || !targetId) continue;
|
||||
|
||||
const edgeKey = `${sourceId}-${targetId}`;
|
||||
if (edgeSet.has(edgeKey)) continue;
|
||||
edgeSet.add(edgeKey);
|
||||
|
||||
lines.push(` ${sourceId} --> ${targetId}`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
48
packages/diagrams/src/dependency.ts
Normal file
48
packages/diagrams/src/dependency.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { FileNode, DependencyEdge } from "@codeboard/shared";
|
||||
|
||||
function sanitizeId(path: string): string {
|
||||
return path.replace(/[^a-zA-Z0-9]/g, "_");
|
||||
}
|
||||
|
||||
function shortenPath(path: string): string {
|
||||
const parts = path.split("/");
|
||||
if (parts.length <= 2) return path;
|
||||
return parts.slice(-2).join("/");
|
||||
}
|
||||
|
||||
export function generateDependencyGraph(
|
||||
files: FileNode[],
|
||||
deps: DependencyEdge[]
|
||||
): string {
|
||||
if (files.length === 0) {
|
||||
return "graph LR\n empty[No files detected]";
|
||||
}
|
||||
|
||||
const maxFiles = 30;
|
||||
const topFiles = files.slice(0, maxFiles);
|
||||
const topPaths = new Set(topFiles.map((f) => f.path));
|
||||
|
||||
const lines: string[] = ["graph LR"];
|
||||
|
||||
for (const file of topFiles) {
|
||||
const id = sanitizeId(file.path);
|
||||
const label = shortenPath(file.path);
|
||||
lines.push(` ${id}["${label}"]`);
|
||||
}
|
||||
|
||||
const edgeSet = new Set<string>();
|
||||
for (const dep of deps) {
|
||||
if (!topPaths.has(dep.source) || !topPaths.has(dep.target)) continue;
|
||||
if (dep.source === dep.target) continue;
|
||||
|
||||
const edgeKey = `${dep.source}-${dep.target}`;
|
||||
if (edgeSet.has(edgeKey)) continue;
|
||||
edgeSet.add(edgeKey);
|
||||
|
||||
const sourceId = sanitizeId(dep.source);
|
||||
const targetId = sanitizeId(dep.target);
|
||||
lines.push(` ${sourceId} --> ${targetId}`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
2
packages/diagrams/src/index.ts
Normal file
2
packages/diagrams/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { generateArchitectureDiagram } from "./architecture.js";
|
||||
export { generateDependencyGraph } from "./dependency.js";
|
||||
8
packages/diagrams/tsconfig.json
Normal file
8
packages/diagrams/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user