react-dev-panel 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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/dist/adapters/next.cjs +95 -0
  4. package/dist/adapters/next.cjs.map +1 -0
  5. package/dist/adapters/next.d.cts +17 -0
  6. package/dist/adapters/next.d.ts +17 -0
  7. package/dist/adapters/next.js +65 -0
  8. package/dist/adapters/next.js.map +1 -0
  9. package/dist/adapters/server.cjs +72 -0
  10. package/dist/adapters/server.cjs.map +1 -0
  11. package/dist/adapters/server.d.cts +24 -0
  12. package/dist/adapters/server.d.ts +24 -0
  13. package/dist/adapters/server.js +4 -0
  14. package/dist/adapters/server.js.map +1 -0
  15. package/dist/adapters/vite.cjs +301 -0
  16. package/dist/adapters/vite.cjs.map +1 -0
  17. package/dist/adapters/vite.d.cts +35 -0
  18. package/dist/adapters/vite.d.ts +35 -0
  19. package/dist/adapters/vite.js +31 -0
  20. package/dist/adapters/vite.js.map +1 -0
  21. package/dist/chunk-2ZAPVMUL.js +25 -0
  22. package/dist/chunk-2ZAPVMUL.js.map +1 -0
  23. package/dist/chunk-MAYMGQIM.js +64 -0
  24. package/dist/chunk-MAYMGQIM.js.map +1 -0
  25. package/dist/chunk-XZ4DPO52.js +190 -0
  26. package/dist/chunk-XZ4DPO52.js.map +1 -0
  27. package/dist/cli/index.cjs +221 -0
  28. package/dist/cli/index.cjs.map +1 -0
  29. package/dist/cli/index.d.cts +1 -0
  30. package/dist/cli/index.d.ts +1 -0
  31. package/dist/cli/index.js +35 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/generate-UK65K5BU.js +3 -0
  34. package/dist/generate-UK65K5BU.js.map +1 -0
  35. package/dist/index.cjs +1745 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +53 -0
  38. package/dist/index.d.ts +53 -0
  39. package/dist/index.js +1711 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/server-open-B4FK0jQF.d.cts +92 -0
  42. package/dist/server-open-B4FK0jQF.d.ts +92 -0
  43. package/package.json +77 -0
@@ -0,0 +1,301 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var path = require('path');
6
+ var fs = require('fs');
7
+
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __esm = (fn, res) => function __init() {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+
18
+ // src/cli/generate.ts
19
+ var generate_exports = {};
20
+ __export(generate_exports, {
21
+ generateComponentGraph: () => generateComponentGraph
22
+ });
23
+ function collectFiles(dir, out) {
24
+ let entries;
25
+ try {
26
+ entries = fs.readdirSync(dir);
27
+ } catch {
28
+ return out;
29
+ }
30
+ for (const entry of entries) {
31
+ if (SKIP_DIR.has(entry)) continue;
32
+ const full = path.join(dir, entry);
33
+ let st;
34
+ try {
35
+ st = fs.statSync(full);
36
+ } catch {
37
+ continue;
38
+ }
39
+ if (st.isDirectory()) collectFiles(full, out);
40
+ else if (isComponentFile(entry)) out.push(full);
41
+ }
42
+ return out;
43
+ }
44
+ async function generateComponentGraph(options = {}) {
45
+ let ts;
46
+ try {
47
+ ts = (await import('typescript')).default ?? await import('typescript');
48
+ } catch {
49
+ throw new Error(
50
+ "[dev-panel-graph] 'typescript' is required to generate the component graph. Install it: npm i -D typescript"
51
+ );
52
+ }
53
+ const root = path.resolve(options.root ?? process.cwd());
54
+ const scanDirs = (options.scan ?? ["src"]).map((d) => path.resolve(root, d));
55
+ const outFile = path.resolve(root, options.out ?? ".dev-panel/component-graph.json");
56
+ const toRel = (abs) => path.relative(root, abs).split(path.sep).join("/");
57
+ const files = scanDirs.flatMap((d) => collectFiles(d, []));
58
+ const containsJsx = (node) => {
59
+ let found = false;
60
+ const visit = (n) => {
61
+ if (found) return;
62
+ if (ts.isJsxElement(n) || ts.isJsxSelfClosingElement(n) || ts.isJsxFragment(n)) {
63
+ found = true;
64
+ return;
65
+ }
66
+ ts.forEachChild(n, visit);
67
+ };
68
+ visit(node);
69
+ return found;
70
+ };
71
+ const jsxRoot = (tag) => {
72
+ if (ts.isIdentifier(tag)) return tag.text;
73
+ if (ts.isPropertyAccessExpression(tag)) {
74
+ let expr = tag.expression;
75
+ while (ts.isPropertyAccessExpression(expr)) expr = expr.expression;
76
+ if (ts.isIdentifier(expr)) return expr.text;
77
+ }
78
+ return null;
79
+ };
80
+ const renderedIn = (node) => {
81
+ const names = /* @__PURE__ */ new Set();
82
+ const visit = (n) => {
83
+ if (ts.isJsxOpeningElement(n) || ts.isJsxSelfClosingElement(n)) {
84
+ const r = jsxRoot(n.tagName);
85
+ if (r && isPascal(r)) names.add(r);
86
+ }
87
+ ts.forEachChild(n, visit);
88
+ };
89
+ visit(node);
90
+ return names;
91
+ };
92
+ const importedIn = (sf) => {
93
+ const names = /* @__PURE__ */ new Set();
94
+ for (const stmt of sf.statements) {
95
+ if (!ts.isImportDeclaration(stmt) || !stmt.importClause) continue;
96
+ const clause = stmt.importClause;
97
+ if (clause.isTypeOnly) continue;
98
+ if (clause.name && isPascal(clause.name.text)) names.add(clause.name.text);
99
+ const b = clause.namedBindings;
100
+ if (b && ts.isNamedImports(b)) {
101
+ for (const spec of b.elements) {
102
+ if (spec.isTypeOnly) continue;
103
+ if (isPascal(spec.name.text)) names.add(spec.name.text);
104
+ }
105
+ }
106
+ }
107
+ return names;
108
+ };
109
+ const hasExport = (node) => !!(ts.canHaveModifiers(node) ? ts.getModifiers(node) : void 0)?.some(
110
+ (m) => m.kind === ts.SyntaxKind.ExportKeyword
111
+ );
112
+ const routeFor = (absPath) => {
113
+ const rel = toRel(absPath);
114
+ const marker = "/app/";
115
+ const idx = rel.indexOf(marker);
116
+ if (idx === -1 || !rel.endsWith("/page.tsx")) return null;
117
+ const inner = rel.slice(idx + marker.length, -"/page.tsx".length);
118
+ const segments = inner.split("/").filter(Boolean).filter((s) => !(s.startsWith("(") && s.endsWith(")"))).map((s) => s.replace(/^\[\.\.\.(.+)\]$/, "*$1").replace(/^\[(.+)\]$/, ":$1"));
119
+ return "/" + segments.join("/");
120
+ };
121
+ const nodesByName = /* @__PURE__ */ new Map();
122
+ const edgeSet = /* @__PURE__ */ new Set();
123
+ const edges = [];
124
+ let duplicateNames = 0;
125
+ const addEdge = (from, to, type) => {
126
+ if (!from || !to || from === to) return;
127
+ const key = `${from}|${to}|${type}`;
128
+ if (edgeSet.has(key)) return;
129
+ edgeSet.add(key);
130
+ edges.push({ from, to, type });
131
+ };
132
+ const addNode = (name, sf, decl, exported) => {
133
+ if (nodesByName.has(name)) {
134
+ duplicateNames += 1;
135
+ return;
136
+ }
137
+ const pos = sf.getLineAndCharacterOfPosition(decl.getStart(sf));
138
+ nodesByName.set(name, {
139
+ id: name,
140
+ name,
141
+ filePath: toRel(sf.fileName),
142
+ line: pos.line + 1,
143
+ column: pos.character + 1,
144
+ type: "component",
145
+ exported
146
+ });
147
+ };
148
+ for (const file of files) {
149
+ const text = fs.readFileSync(file, "utf8");
150
+ const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
151
+ const localComponents = [];
152
+ for (const stmt of sf.statements) {
153
+ if (ts.isFunctionDeclaration(stmt) && stmt.name && isPascal(stmt.name.text) && stmt.body) {
154
+ if (containsJsx(stmt.body)) {
155
+ addNode(stmt.name.text, sf, stmt, hasExport(stmt));
156
+ localComponents.push({ name: stmt.name.text, body: stmt.body });
157
+ }
158
+ } else if (ts.isVariableStatement(stmt)) {
159
+ const exported = hasExport(stmt);
160
+ for (const decl of stmt.declarationList.declarations) {
161
+ if (!ts.isIdentifier(decl.name) || !isPascal(decl.name.text) || !decl.initializer) continue;
162
+ const init = decl.initializer;
163
+ if ((ts.isArrowFunction(init) || ts.isFunctionExpression(init)) && containsJsx(init)) {
164
+ addNode(decl.name.text, sf, decl, exported);
165
+ localComponents.push({ name: decl.name.text, body: init });
166
+ }
167
+ }
168
+ }
169
+ }
170
+ if (localComponents.length === 0) continue;
171
+ const base = file.split(path.sep).pop().replace(/\.tsx$/, "");
172
+ const primary = localComponents.find(
173
+ (c) => c.name.toLowerCase() === base.replace(/[-_]/g, "").toLowerCase()
174
+ ) ?? localComponents[0];
175
+ for (const comp of localComponents) {
176
+ for (const rendered of renderedIn(comp.body)) addEdge(comp.name, rendered, "renders");
177
+ }
178
+ for (const imported of importedIn(sf)) addEdge(primary.name, imported, "imports");
179
+ const route = routeFor(file);
180
+ if (route) {
181
+ const routeId = `route:${route}`;
182
+ if (!nodesByName.has(routeId)) {
183
+ nodesByName.set(routeId, { id: routeId, name: route, filePath: toRel(file), type: "route", route });
184
+ }
185
+ const primaryNode = nodesByName.get(primary.name);
186
+ if (primaryNode && !primaryNode.route) primaryNode.route = route;
187
+ addEdge(routeId, primary.name, "route");
188
+ }
189
+ }
190
+ const known = new Set(nodesByName.keys());
191
+ const prunedEdges = edges.filter((e) => known.has(e.from) && known.has(e.to));
192
+ const graph = {
193
+ root,
194
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
195
+ nodes: [...nodesByName.values()],
196
+ edges: prunedEdges
197
+ };
198
+ fs.mkdirSync(path.resolve(outFile, ".."), { recursive: true });
199
+ fs.writeFileSync(outFile, JSON.stringify(graph, null, 2), "utf8");
200
+ return { graph, outFile, fileCount: files.length, duplicateNames };
201
+ }
202
+ var SKIP_DIR, isComponentFile, isPascal;
203
+ var init_generate = __esm({
204
+ "src/cli/generate.ts"() {
205
+ SKIP_DIR = /* @__PURE__ */ new Set(["node_modules", "dist", ".next", ".turbo", "__tests__", "generated", ".git"]);
206
+ isComponentFile = (f) => f.endsWith(".tsx") && !f.endsWith(".test.tsx") && !f.endsWith(".stories.tsx");
207
+ isPascal = (name) => typeof name === "string" && /^[A-Z]/.test(name);
208
+ }
209
+ });
210
+
211
+ // src/core/server-open.ts
212
+ var DEFAULT_GRAPH_ENDPOINT = "/dev-panel/graph";
213
+ var DEFAULT_OPEN_ENDPOINT = "/dev-panel/open-file";
214
+
215
+ // src/adapters/server.ts
216
+ var EDITOR_BIN = {
217
+ auto: void 0,
218
+ vscode: "code",
219
+ cursor: "cursor",
220
+ webstorm: "webstorm",
221
+ zed: "zed"
222
+ };
223
+ function send(res, status, body) {
224
+ res.statusCode = status;
225
+ res.setHeader("Content-Type", "application/json");
226
+ res.setHeader("Cache-Control", "no-store");
227
+ res.end(typeof body === "string" ? body : JSON.stringify(body));
228
+ }
229
+ async function readBody(req) {
230
+ const chunks = [];
231
+ for await (const c of req) chunks.push(c);
232
+ const raw = Buffer.concat(chunks).toString("utf8");
233
+ return raw ? JSON.parse(raw) : {};
234
+ }
235
+ function createDevPanelMiddleware(options = {}) {
236
+ const root = path.resolve(options.root ?? process.cwd());
237
+ const graphFile = path.resolve(root, options.graphFile ?? ".dev-panel/component-graph.json");
238
+ const graphEndpoint = options.graphEndpoint ?? DEFAULT_GRAPH_ENDPOINT;
239
+ const openEndpoint = options.openEndpoint ?? DEFAULT_OPEN_ENDPOINT;
240
+ return function devPanelMiddleware(req, res, next) {
241
+ const url = (req.url ?? "").split("?")[0];
242
+ if (req.method === "GET" && url === graphEndpoint) {
243
+ try {
244
+ send(res, 200, fs.readFileSync(graphFile, "utf8"));
245
+ } catch {
246
+ send(res, 404, { error: "graph-not-generated", hint: "Run: npx dev-panel-graph" });
247
+ }
248
+ return;
249
+ }
250
+ if (req.method === "POST" && url === openEndpoint) {
251
+ void (async () => {
252
+ try {
253
+ const body = await readBody(req);
254
+ if (!body.file) return send(res, 400, { error: "file required" });
255
+ const abs = path.isAbsolute(body.file) ? path.resolve(body.file) : path.resolve(root, body.file);
256
+ const rel = path.relative(root, abs);
257
+ if (rel.startsWith("..") || path.isAbsolute(rel)) return send(res, 403, { error: "path escapes root" });
258
+ if (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) return send(res, 404, { error: "not found" });
259
+ const launch = (await import('launch-editor')).default;
260
+ const target = `${abs}:${body.line ?? 1}:${body.column ?? 1}`;
261
+ launch(target, EDITOR_BIN[body.editor ?? "auto"]);
262
+ return send(res, 200, { ok: true });
263
+ } catch (err) {
264
+ return send(res, 500, { error: err instanceof Error ? err.message : "open failed" });
265
+ }
266
+ })();
267
+ return;
268
+ }
269
+ next();
270
+ };
271
+ }
272
+
273
+ // src/adapters/vite.ts
274
+ function devPanel(options = {}) {
275
+ const root = options.root ?? process.cwd();
276
+ return {
277
+ name: "react-dev-panel",
278
+ apply: "serve",
279
+ async configureServer(server) {
280
+ if (options.generateOnStart !== false) {
281
+ try {
282
+ const { generateComponentGraph: generateComponentGraph2 } = await Promise.resolve().then(() => (init_generate(), generate_exports));
283
+ const res = await generateComponentGraph2({ root, scan: options.scan, out: options.graphFile });
284
+ console.log(`[react-dev-panel] graph: ${res.graph.nodes.length} nodes \u2192 ${res.outFile}`);
285
+ } catch (err) {
286
+ console.warn("[react-dev-panel] graph generation skipped:", err instanceof Error ? err.message : err);
287
+ }
288
+ }
289
+ const mw = createDevPanelMiddleware(options);
290
+ server.middlewares.use(
291
+ (req, res, next) => mw(req, res, next)
292
+ );
293
+ }
294
+ };
295
+ }
296
+ var vite_default = devPanel;
297
+
298
+ exports.default = vite_default;
299
+ exports.devPanel = devPanel;
300
+ //# sourceMappingURL=vite.cjs.map
301
+ //# sourceMappingURL=vite.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/generate.ts","../../src/core/server-open.ts","../../src/adapters/server.ts","../../src/adapters/vite.ts"],"names":["readdirSync","join","statSync","resolve","relative","sep","readFileSync","mkdirSync","writeFileSync","isAbsolute","existsSync","generateComponentGraph"],"mappings":"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","file":"vite.cjs","sourcesContent":["/**\n * Static component-graph generator using the TypeScript Compiler API (no ts-morph). Scans .tsx\n * files for React components and emits nodes + edges (renders / imports / route). Shared by the\n * `dev-panel-graph` CLI and the Vite adapter. `typescript` is an optional peer dep — imported\n * dynamically with a friendly error if absent.\n */\nimport { join, sep, resolve, relative } from 'node:path';\nimport { readdirSync, statSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\n\nimport type { ComponentGraph, ComponentGraphNode, ComponentGraphEdge } from '../core/graph-types';\n\nexport interface GenerateOptions {\n /** Repo root (absolute). Default: process.cwd(). */\n root?: string;\n /** Directories to scan, relative to root. Default: ['src']. */\n scan?: string[];\n /** Output file, relative to root. Default: '.dev-panel/component-graph.json'. */\n out?: string;\n}\n\nexport interface GenerateResult {\n graph: ComponentGraph;\n outFile: string;\n fileCount: number;\n duplicateNames: number;\n}\n\nconst SKIP_DIR = new Set(['node_modules', 'dist', '.next', '.turbo', '__tests__', 'generated', '.git']);\nconst isComponentFile = (f: string) =>\n f.endsWith('.tsx') && !f.endsWith('.test.tsx') && !f.endsWith('.stories.tsx');\nconst isPascal = (name: unknown): name is string => typeof name === 'string' && /^[A-Z]/.test(name);\n\nfunction collectFiles(dir: string, out: string[]): string[] {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return out;\n }\n for (const entry of entries) {\n if (SKIP_DIR.has(entry)) continue;\n const full = join(dir, entry);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) collectFiles(full, out);\n else if (isComponentFile(entry)) out.push(full);\n }\n return out;\n}\n\nexport async function generateComponentGraph(options: GenerateOptions = {}): Promise<GenerateResult> {\n let ts: typeof import('typescript');\n try {\n ts = (await import('typescript')).default ?? (await import('typescript'));\n } catch {\n throw new Error(\n \"[dev-panel-graph] 'typescript' is required to generate the component graph. Install it: npm i -D typescript\",\n );\n }\n\n const root = resolve(options.root ?? process.cwd());\n const scanDirs = (options.scan ?? ['src']).map((d) => resolve(root, d));\n const outFile = resolve(root, options.out ?? '.dev-panel/component-graph.json');\n const toRel = (abs: string) => relative(root, abs).split(sep).join('/');\n\n const files = scanDirs.flatMap((d) => collectFiles(d, []));\n\n const containsJsx = (node: import('typescript').Node): boolean => {\n let found = false;\n const visit = (n: import('typescript').Node) => {\n if (found) return;\n if (ts.isJsxElement(n) || ts.isJsxSelfClosingElement(n) || ts.isJsxFragment(n)) {\n found = true;\n return;\n }\n ts.forEachChild(n, visit);\n };\n visit(node);\n return found;\n };\n\n const jsxRoot = (tag: import('typescript').JsxTagNameExpression): string | null => {\n if (ts.isIdentifier(tag)) return tag.text;\n if (ts.isPropertyAccessExpression(tag)) {\n let expr: import('typescript').Expression = tag.expression;\n while (ts.isPropertyAccessExpression(expr)) expr = expr.expression;\n if (ts.isIdentifier(expr)) return expr.text;\n }\n return null;\n };\n\n const renderedIn = (node: import('typescript').Node): Set<string> => {\n const names = new Set<string>();\n const visit = (n: import('typescript').Node) => {\n if (ts.isJsxOpeningElement(n) || ts.isJsxSelfClosingElement(n)) {\n const r = jsxRoot(n.tagName);\n if (r && isPascal(r)) names.add(r);\n }\n ts.forEachChild(n, visit);\n };\n visit(node);\n return names;\n };\n\n const importedIn = (sf: import('typescript').SourceFile): Set<string> => {\n const names = new Set<string>();\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt) || !stmt.importClause) continue;\n const clause = stmt.importClause;\n if (clause.isTypeOnly) continue;\n if (clause.name && isPascal(clause.name.text)) names.add(clause.name.text);\n const b = clause.namedBindings;\n if (b && ts.isNamedImports(b)) {\n for (const spec of b.elements) {\n if (spec.isTypeOnly) continue;\n if (isPascal(spec.name.text)) names.add(spec.name.text);\n }\n }\n }\n return names;\n };\n\n const hasExport = (node: import('typescript').Node): boolean =>\n !!(ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined)?.some(\n (m) => m.kind === ts.SyntaxKind.ExportKeyword,\n );\n\n const routeFor = (absPath: string): string | null => {\n const rel = toRel(absPath);\n const marker = '/app/';\n const idx = rel.indexOf(marker);\n if (idx === -1 || !rel.endsWith('/page.tsx')) return null;\n const inner = rel.slice(idx + marker.length, -'/page.tsx'.length);\n const segments = inner\n .split('/')\n .filter(Boolean)\n .filter((s) => !(s.startsWith('(') && s.endsWith(')')))\n .map((s) => s.replace(/^\\[\\.\\.\\.(.+)\\]$/, '*$1').replace(/^\\[(.+)\\]$/, ':$1'));\n return '/' + segments.join('/');\n };\n\n const nodesByName = new Map<string, ComponentGraphNode>();\n const edgeSet = new Set<string>();\n const edges: ComponentGraphEdge[] = [];\n let duplicateNames = 0;\n\n const addEdge = (from: string, to: string, type: ComponentGraphEdge['type']) => {\n if (!from || !to || from === to) return;\n const key = `${from}|${to}|${type}`;\n if (edgeSet.has(key)) return;\n edgeSet.add(key);\n edges.push({ from, to, type });\n };\n\n const addNode = (\n name: string,\n sf: import('typescript').SourceFile,\n decl: import('typescript').Node,\n exported: boolean,\n ) => {\n if (nodesByName.has(name)) {\n duplicateNames += 1;\n return;\n }\n const pos = sf.getLineAndCharacterOfPosition(decl.getStart(sf));\n nodesByName.set(name, {\n id: name,\n name,\n filePath: toRel(sf.fileName),\n line: pos.line + 1,\n column: pos.character + 1,\n type: 'component',\n exported,\n });\n };\n\n for (const file of files) {\n const text = readFileSync(file, 'utf8');\n const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);\n const localComponents: Array<{ name: string; body: import('typescript').Node }> = [];\n\n for (const stmt of sf.statements) {\n if (ts.isFunctionDeclaration(stmt) && stmt.name && isPascal(stmt.name.text) && stmt.body) {\n if (containsJsx(stmt.body)) {\n addNode(stmt.name.text, sf, stmt, hasExport(stmt));\n localComponents.push({ name: stmt.name.text, body: stmt.body });\n }\n } else if (ts.isVariableStatement(stmt)) {\n const exported = hasExport(stmt);\n for (const decl of stmt.declarationList.declarations) {\n if (!ts.isIdentifier(decl.name) || !isPascal(decl.name.text) || !decl.initializer) continue;\n const init = decl.initializer;\n if ((ts.isArrowFunction(init) || ts.isFunctionExpression(init)) && containsJsx(init)) {\n addNode(decl.name.text, sf, decl, exported);\n localComponents.push({ name: decl.name.text, body: init });\n }\n }\n }\n }\n\n if (localComponents.length === 0) continue;\n\n const base = file.split(sep).pop()!.replace(/\\.tsx$/, '');\n const primary =\n localComponents.find(\n (c) => c.name.toLowerCase() === base.replace(/[-_]/g, '').toLowerCase(),\n ) ?? localComponents[0];\n\n for (const comp of localComponents) {\n for (const rendered of renderedIn(comp.body)) addEdge(comp.name, rendered, 'renders');\n }\n for (const imported of importedIn(sf)) addEdge(primary.name, imported, 'imports');\n\n const route = routeFor(file);\n if (route) {\n const routeId = `route:${route}`;\n if (!nodesByName.has(routeId)) {\n nodesByName.set(routeId, { id: routeId, name: route, filePath: toRel(file), type: 'route', route });\n }\n const primaryNode = nodesByName.get(primary.name);\n if (primaryNode && !primaryNode.route) primaryNode.route = route;\n addEdge(routeId, primary.name, 'route');\n }\n }\n\n const known = new Set(nodesByName.keys());\n const prunedEdges = edges.filter((e) => known.has(e.from) && known.has(e.to));\n\n const graph: ComponentGraph = {\n root,\n generatedAt: new Date().toISOString(),\n nodes: [...nodesByName.values()],\n edges: prunedEdges,\n };\n\n mkdirSync(resolve(outFile, '..'), { recursive: true });\n writeFileSync(outFile, JSON.stringify(graph, null, 2), 'utf8');\n\n return { graph, outFile, fileCount: files.length, duplicateNames };\n}\n","import type { OpenInEditor } from './types';\n\n/**\n * Default endpoint paths the adapters mount and the client talks to. No leading underscore —\n * Next.js App Router treats `_`-prefixed segments as private (non-routable), so this path works\n * uniformly across the Next, Vite, and generic-server adapters.\n */\nexport const DEFAULT_GRAPH_ENDPOINT = '/dev-panel/graph';\nexport const DEFAULT_OPEN_ENDPOINT = '/dev-panel/open-file';\n\n/**\n * A client `openInEditor` that POSTs to a server endpoint (mounted by an adapter), which opens\n * the file in the editor running the project via `launch-editor`. Falls back to copying the\n * path when the endpoint is unreachable. Pass to `<DevPanel openInEditor={serverOpenInEditor} />`.\n */\nexport function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT): OpenInEditor {\n return async (loc, editor = 'auto') => {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor }),\n });\n if (res.ok) return true;\n } catch {\n /* fall through to copy */\n }\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n await navigator.clipboard\n .writeText(`${loc.file}${loc.line ? `:${loc.line}` : ''}`)\n .catch(() => undefined);\n }\n return false;\n };\n}\n\n/** Ready-to-use server opener pointed at the default endpoint. */\nexport const serverOpenInEditor: OpenInEditor = createServerOpenInEditor();\n","/**\n * Framework-agnostic dev middleware (connect/express/http style) that powers the Component\n * Graph Inspector's server features:\n * GET /__dev-panel/graph → serves the generated component-graph JSON\n * POST /__dev-panel/open-file → opens a file in the editor running the project (launch-editor)\n *\n * Dev-only by design — mount it only in your dev server. Opens are path-traversal-guarded to\n * the repo root. Used directly for CRA/Express/custom servers, and internally by the Vite plugin.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { resolve, relative, isAbsolute } from 'node:path';\nimport { existsSync, statSync, readFileSync } from 'node:fs';\n\nimport { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT } from '../core/server-open';\n\nexport interface DevPanelServerOptions {\n /** Repo root (absolute). Default: process.cwd(). */\n root?: string;\n /** Path to the generated graph JSON, relative to root. Default: .dev-panel/component-graph.json */\n graphFile?: string;\n graphEndpoint?: string;\n openEndpoint?: string;\n}\n\nconst EDITOR_BIN: Record<string, string | undefined> = {\n auto: undefined,\n vscode: 'code',\n cursor: 'cursor',\n webstorm: 'webstorm',\n zed: 'zed',\n};\n\ntype Next = (err?: unknown) => void;\n\nfunction send(res: ServerResponse, status: number, body: unknown) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Cache-Control', 'no-store');\n res.end(typeof body === 'string' ? body : JSON.stringify(body));\n}\n\nasync function readBody(req: IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n const raw = Buffer.concat(chunks).toString('utf8');\n return raw ? JSON.parse(raw) : {};\n}\n\nexport function createDevPanelMiddleware(options: DevPanelServerOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n const graphFile = resolve(root, options.graphFile ?? '.dev-panel/component-graph.json');\n const graphEndpoint = options.graphEndpoint ?? DEFAULT_GRAPH_ENDPOINT;\n const openEndpoint = options.openEndpoint ?? DEFAULT_OPEN_ENDPOINT;\n\n return function devPanelMiddleware(req: IncomingMessage, res: ServerResponse, next: Next): void {\n const url = (req.url ?? '').split('?')[0];\n\n if (req.method === 'GET' && url === graphEndpoint) {\n try {\n send(res, 200, readFileSync(graphFile, 'utf8'));\n } catch {\n send(res, 404, { error: 'graph-not-generated', hint: 'Run: npx dev-panel-graph' });\n }\n return;\n }\n\n if (req.method === 'POST' && url === openEndpoint) {\n void (async () => {\n try {\n const body = (await readBody(req)) as {\n file?: string;\n line?: number;\n column?: number;\n editor?: string;\n };\n if (!body.file) return send(res, 400, { error: 'file required' });\n const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);\n const rel = relative(root, abs);\n if (rel.startsWith('..') || isAbsolute(rel)) return send(res, 403, { error: 'path escapes root' });\n if (!existsSync(abs) || !statSync(abs).isFile()) return send(res, 404, { error: 'not found' });\n\n const launch = (await import('launch-editor')).default;\n const target = `${abs}:${body.line ?? 1}:${body.column ?? 1}`;\n launch(target, EDITOR_BIN[body.editor ?? 'auto']);\n return send(res, 200, { ok: true });\n } catch (err) {\n return send(res, 500, { error: err instanceof Error ? err.message : 'open failed' });\n }\n })();\n return;\n }\n\n next();\n };\n}\n","/**\n * Vite plugin for react-dev-panel. Mounts the dev middleware (graph + open-file endpoints) and\n * regenerates the component graph on dev-server start (and optionally on change).\n *\n * // vite.config.ts\n * import { devPanel } from 'react-dev-panel/vite';\n * export default defineConfig({ plugins: [react(), devPanel()] });\n *\n * Then on the client:\n * import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';\n * <DevPanel graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />\n */\nimport { createDevPanelMiddleware, type DevPanelServerOptions } from './server';\n\nexport interface DevPanelViteOptions extends DevPanelServerOptions {\n /** Dirs to scan for the graph (relative to root). Default ['src']. */\n scan?: string[];\n /** Generate the graph when the dev server starts. Default true. */\n generateOnStart?: boolean;\n}\n\n// Loosely typed to avoid a hard `vite` dependency.\ninterface ViteDevServerLike {\n middlewares: { use: (fn: (req: unknown, res: unknown, next: unknown) => void) => void };\n}\ninterface VitePluginLike {\n name: string;\n apply?: 'serve' | 'build';\n configureServer?: (server: ViteDevServerLike) => void | Promise<void>;\n}\n\nexport function devPanel(options: DevPanelViteOptions = {}): VitePluginLike {\n const root = options.root ?? process.cwd();\n return {\n name: 'react-dev-panel',\n apply: 'serve',\n async configureServer(server) {\n if (options.generateOnStart !== false) {\n try {\n const { generateComponentGraph } = await import('../cli/generate');\n const res = await generateComponentGraph({ root, scan: options.scan, out: options.graphFile });\n // eslint-disable-next-line no-console\n console.log(`[react-dev-panel] graph: ${res.graph.nodes.length} nodes → ${res.outFile}`);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn('[react-dev-panel] graph generation skipped:', err instanceof Error ? err.message : err);\n }\n }\n const mw = createDevPanelMiddleware(options);\n server.middlewares.use((req, res, next) =>\n mw(req as never, res as never, next as never),\n );\n },\n };\n}\n\nexport default devPanel;\n"]}
@@ -0,0 +1,35 @@
1
+ import { DevPanelServerOptions } from './server.cjs';
2
+ import 'node:http';
3
+
4
+ /**
5
+ * Vite plugin for react-dev-panel. Mounts the dev middleware (graph + open-file endpoints) and
6
+ * regenerates the component graph on dev-server start (and optionally on change).
7
+ *
8
+ * // vite.config.ts
9
+ * import { devPanel } from 'react-dev-panel/vite';
10
+ * export default defineConfig({ plugins: [react(), devPanel()] });
11
+ *
12
+ * Then on the client:
13
+ * import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';
14
+ * <DevPanel graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />
15
+ */
16
+
17
+ interface DevPanelViteOptions extends DevPanelServerOptions {
18
+ /** Dirs to scan for the graph (relative to root). Default ['src']. */
19
+ scan?: string[];
20
+ /** Generate the graph when the dev server starts. Default true. */
21
+ generateOnStart?: boolean;
22
+ }
23
+ interface ViteDevServerLike {
24
+ middlewares: {
25
+ use: (fn: (req: unknown, res: unknown, next: unknown) => void) => void;
26
+ };
27
+ }
28
+ interface VitePluginLike {
29
+ name: string;
30
+ apply?: 'serve' | 'build';
31
+ configureServer?: (server: ViteDevServerLike) => void | Promise<void>;
32
+ }
33
+ declare function devPanel(options?: DevPanelViteOptions): VitePluginLike;
34
+
35
+ export { type DevPanelViteOptions, devPanel as default, devPanel };
@@ -0,0 +1,35 @@
1
+ import { DevPanelServerOptions } from './server.js';
2
+ import 'node:http';
3
+
4
+ /**
5
+ * Vite plugin for react-dev-panel. Mounts the dev middleware (graph + open-file endpoints) and
6
+ * regenerates the component graph on dev-server start (and optionally on change).
7
+ *
8
+ * // vite.config.ts
9
+ * import { devPanel } from 'react-dev-panel/vite';
10
+ * export default defineConfig({ plugins: [react(), devPanel()] });
11
+ *
12
+ * Then on the client:
13
+ * import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';
14
+ * <DevPanel graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />
15
+ */
16
+
17
+ interface DevPanelViteOptions extends DevPanelServerOptions {
18
+ /** Dirs to scan for the graph (relative to root). Default ['src']. */
19
+ scan?: string[];
20
+ /** Generate the graph when the dev server starts. Default true. */
21
+ generateOnStart?: boolean;
22
+ }
23
+ interface ViteDevServerLike {
24
+ middlewares: {
25
+ use: (fn: (req: unknown, res: unknown, next: unknown) => void) => void;
26
+ };
27
+ }
28
+ interface VitePluginLike {
29
+ name: string;
30
+ apply?: 'serve' | 'build';
31
+ configureServer?: (server: ViteDevServerLike) => void | Promise<void>;
32
+ }
33
+ declare function devPanel(options?: DevPanelViteOptions): VitePluginLike;
34
+
35
+ export { type DevPanelViteOptions, devPanel as default, devPanel };
@@ -0,0 +1,31 @@
1
+ import { createDevPanelMiddleware } from '../chunk-MAYMGQIM.js';
2
+ import '../chunk-2ZAPVMUL.js';
3
+
4
+ // src/adapters/vite.ts
5
+ function devPanel(options = {}) {
6
+ const root = options.root ?? process.cwd();
7
+ return {
8
+ name: "react-dev-panel",
9
+ apply: "serve",
10
+ async configureServer(server) {
11
+ if (options.generateOnStart !== false) {
12
+ try {
13
+ const { generateComponentGraph } = await import('../generate-UK65K5BU.js');
14
+ const res = await generateComponentGraph({ root, scan: options.scan, out: options.graphFile });
15
+ console.log(`[react-dev-panel] graph: ${res.graph.nodes.length} nodes \u2192 ${res.outFile}`);
16
+ } catch (err) {
17
+ console.warn("[react-dev-panel] graph generation skipped:", err instanceof Error ? err.message : err);
18
+ }
19
+ }
20
+ const mw = createDevPanelMiddleware(options);
21
+ server.middlewares.use(
22
+ (req, res, next) => mw(req, res, next)
23
+ );
24
+ }
25
+ };
26
+ }
27
+ var vite_default = devPanel;
28
+
29
+ export { vite_default as default, devPanel };
30
+ //# sourceMappingURL=vite.js.map
31
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/vite.ts"],"names":[],"mappings":";;;;AA+BO,SAAS,QAAA,CAAS,OAAA,GAA+B,EAAC,EAAmB;AAC1E,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,IAAQ,OAAA,CAAQ,GAAA,EAAI;AACzC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,OAAA;AAAA,IACP,MAAM,gBAAgB,MAAA,EAAQ;AAC5B,MAAA,IAAI,OAAA,CAAQ,oBAAoB,KAAA,EAAO;AACrC,QAAA,IAAI;AACF,UAAA,MAAM,EAAE,sBAAA,EAAuB,GAAI,MAAM,OAAO,yBAAiB,CAAA;AACjE,UAAA,MAAM,GAAA,GAAM,MAAM,sBAAA,CAAuB,EAAE,IAAA,EAAM,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAM,GAAA,EAAK,OAAA,CAAQ,SAAA,EAAW,CAAA;AAE7F,UAAA,OAAA,CAAQ,GAAA,CAAI,4BAA4B,GAAA,CAAI,KAAA,CAAM,MAAM,MAAM,CAAA,cAAA,EAAY,GAAA,CAAI,OAAO,CAAA,CAAE,CAAA;AAAA,QACzF,SAAS,GAAA,EAAK;AAEZ,UAAA,OAAA,CAAQ,KAAK,6CAAA,EAA+C,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,UAAU,GAAG,CAAA;AAAA,QACtG;AAAA,MACF;AACA,MAAA,MAAM,EAAA,GAAK,yBAAyB,OAAO,CAAA;AAC3C,MAAA,MAAA,CAAO,WAAA,CAAY,GAAA;AAAA,QAAI,CAAC,GAAA,EAAK,GAAA,EAAK,SAChC,EAAA,CAAG,GAAA,EAAc,KAAc,IAAa;AAAA,OAC9C;AAAA,IACF;AAAA,GACF;AACF;AAEA,IAAO,YAAA,GAAQ","file":"vite.js","sourcesContent":["/**\n * Vite plugin for react-dev-panel. Mounts the dev middleware (graph + open-file endpoints) and\n * regenerates the component graph on dev-server start (and optionally on change).\n *\n * // vite.config.ts\n * import { devPanel } from 'react-dev-panel/vite';\n * export default defineConfig({ plugins: [react(), devPanel()] });\n *\n * Then on the client:\n * import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';\n * <DevPanel graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />\n */\nimport { createDevPanelMiddleware, type DevPanelServerOptions } from './server';\n\nexport interface DevPanelViteOptions extends DevPanelServerOptions {\n /** Dirs to scan for the graph (relative to root). Default ['src']. */\n scan?: string[];\n /** Generate the graph when the dev server starts. Default true. */\n generateOnStart?: boolean;\n}\n\n// Loosely typed to avoid a hard `vite` dependency.\ninterface ViteDevServerLike {\n middlewares: { use: (fn: (req: unknown, res: unknown, next: unknown) => void) => void };\n}\ninterface VitePluginLike {\n name: string;\n apply?: 'serve' | 'build';\n configureServer?: (server: ViteDevServerLike) => void | Promise<void>;\n}\n\nexport function devPanel(options: DevPanelViteOptions = {}): VitePluginLike {\n const root = options.root ?? process.cwd();\n return {\n name: 'react-dev-panel',\n apply: 'serve',\n async configureServer(server) {\n if (options.generateOnStart !== false) {\n try {\n const { generateComponentGraph } = await import('../cli/generate');\n const res = await generateComponentGraph({ root, scan: options.scan, out: options.graphFile });\n // eslint-disable-next-line no-console\n console.log(`[react-dev-panel] graph: ${res.graph.nodes.length} nodes → ${res.outFile}`);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn('[react-dev-panel] graph generation skipped:', err instanceof Error ? err.message : err);\n }\n }\n const mw = createDevPanelMiddleware(options);\n server.middlewares.use((req, res, next) =>\n mw(req as never, res as never, next as never),\n );\n },\n };\n}\n\nexport default devPanel;\n"]}
@@ -0,0 +1,25 @@
1
+ // src/core/server-open.ts
2
+ var DEFAULT_GRAPH_ENDPOINT = "/dev-panel/graph";
3
+ var DEFAULT_OPEN_ENDPOINT = "/dev-panel/open-file";
4
+ function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT) {
5
+ return async (loc, editor = "auto") => {
6
+ try {
7
+ const res = await fetch(endpoint, {
8
+ method: "POST",
9
+ headers: { "Content-Type": "application/json" },
10
+ body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor })
11
+ });
12
+ if (res.ok) return true;
13
+ } catch {
14
+ }
15
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
16
+ await navigator.clipboard.writeText(`${loc.file}${loc.line ? `:${loc.line}` : ""}`).catch(() => void 0);
17
+ }
18
+ return false;
19
+ };
20
+ }
21
+ var serverOpenInEditor = createServerOpenInEditor();
22
+
23
+ export { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT, createServerOpenInEditor, serverOpenInEditor };
24
+ //# sourceMappingURL=chunk-2ZAPVMUL.js.map
25
+ //# sourceMappingURL=chunk-2ZAPVMUL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/server-open.ts"],"names":[],"mappings":";AAOO,IAAM,sBAAA,GAAyB;AAC/B,IAAM,qBAAA,GAAwB;AAO9B,SAAS,wBAAA,CAAyB,WAAW,qBAAA,EAAqC;AACvF,EAAA,OAAO,OAAO,GAAA,EAAK,MAAA,GAAS,MAAA,KAAW;AACrC,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,QAAA,EAAU;AAAA,QAChC,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,QAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,MAAM,GAAA,CAAI,IAAA,EAAM,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,QAAQ;AAAA,OACpF,CAAA;AACD,MAAA,IAAI,GAAA,CAAI,IAAI,OAAO,IAAA;AAAA,IACrB,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,SAAA,CAAU,SAAA,EAAW;AAC3D,MAAA,MAAM,UAAU,SAAA,CACb,SAAA,CAAU,GAAG,GAAA,CAAI,IAAI,GAAG,GAAA,CAAI,IAAA,GAAO,CAAA,CAAA,EAAI,GAAA,CAAI,IAAI,CAAA,CAAA,GAAK,EAAE,EAAE,CAAA,CACxD,KAAA,CAAM,MAAM,MAAS,CAAA;AAAA,IAC1B;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF;AAGO,IAAM,qBAAmC,wBAAA","file":"chunk-2ZAPVMUL.js","sourcesContent":["import type { OpenInEditor } from './types';\n\n/**\n * Default endpoint paths the adapters mount and the client talks to. No leading underscore —\n * Next.js App Router treats `_`-prefixed segments as private (non-routable), so this path works\n * uniformly across the Next, Vite, and generic-server adapters.\n */\nexport const DEFAULT_GRAPH_ENDPOINT = '/dev-panel/graph';\nexport const DEFAULT_OPEN_ENDPOINT = '/dev-panel/open-file';\n\n/**\n * A client `openInEditor` that POSTs to a server endpoint (mounted by an adapter), which opens\n * the file in the editor running the project via `launch-editor`. Falls back to copying the\n * path when the endpoint is unreachable. Pass to `<DevPanel openInEditor={serverOpenInEditor} />`.\n */\nexport function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT): OpenInEditor {\n return async (loc, editor = 'auto') => {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor }),\n });\n if (res.ok) return true;\n } catch {\n /* fall through to copy */\n }\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n await navigator.clipboard\n .writeText(`${loc.file}${loc.line ? `:${loc.line}` : ''}`)\n .catch(() => undefined);\n }\n return false;\n };\n}\n\n/** Ready-to-use server opener pointed at the default endpoint. */\nexport const serverOpenInEditor: OpenInEditor = createServerOpenInEditor();\n"]}
@@ -0,0 +1,64 @@
1
+ import { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT } from './chunk-2ZAPVMUL.js';
2
+ import { resolve, isAbsolute, relative } from 'path';
3
+ import { readFileSync, existsSync, statSync } from 'fs';
4
+
5
+ var EDITOR_BIN = {
6
+ auto: void 0,
7
+ vscode: "code",
8
+ cursor: "cursor",
9
+ webstorm: "webstorm",
10
+ zed: "zed"
11
+ };
12
+ function send(res, status, body) {
13
+ res.statusCode = status;
14
+ res.setHeader("Content-Type", "application/json");
15
+ res.setHeader("Cache-Control", "no-store");
16
+ res.end(typeof body === "string" ? body : JSON.stringify(body));
17
+ }
18
+ async function readBody(req) {
19
+ const chunks = [];
20
+ for await (const c of req) chunks.push(c);
21
+ const raw = Buffer.concat(chunks).toString("utf8");
22
+ return raw ? JSON.parse(raw) : {};
23
+ }
24
+ function createDevPanelMiddleware(options = {}) {
25
+ const root = resolve(options.root ?? process.cwd());
26
+ const graphFile = resolve(root, options.graphFile ?? ".dev-panel/component-graph.json");
27
+ const graphEndpoint = options.graphEndpoint ?? DEFAULT_GRAPH_ENDPOINT;
28
+ const openEndpoint = options.openEndpoint ?? DEFAULT_OPEN_ENDPOINT;
29
+ return function devPanelMiddleware(req, res, next) {
30
+ const url = (req.url ?? "").split("?")[0];
31
+ if (req.method === "GET" && url === graphEndpoint) {
32
+ try {
33
+ send(res, 200, readFileSync(graphFile, "utf8"));
34
+ } catch {
35
+ send(res, 404, { error: "graph-not-generated", hint: "Run: npx dev-panel-graph" });
36
+ }
37
+ return;
38
+ }
39
+ if (req.method === "POST" && url === openEndpoint) {
40
+ void (async () => {
41
+ try {
42
+ const body = await readBody(req);
43
+ if (!body.file) return send(res, 400, { error: "file required" });
44
+ const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);
45
+ const rel = relative(root, abs);
46
+ if (rel.startsWith("..") || isAbsolute(rel)) return send(res, 403, { error: "path escapes root" });
47
+ if (!existsSync(abs) || !statSync(abs).isFile()) return send(res, 404, { error: "not found" });
48
+ const launch = (await import('launch-editor')).default;
49
+ const target = `${abs}:${body.line ?? 1}:${body.column ?? 1}`;
50
+ launch(target, EDITOR_BIN[body.editor ?? "auto"]);
51
+ return send(res, 200, { ok: true });
52
+ } catch (err) {
53
+ return send(res, 500, { error: err instanceof Error ? err.message : "open failed" });
54
+ }
55
+ })();
56
+ return;
57
+ }
58
+ next();
59
+ };
60
+ }
61
+
62
+ export { createDevPanelMiddleware };
63
+ //# sourceMappingURL=chunk-MAYMGQIM.js.map
64
+ //# sourceMappingURL=chunk-MAYMGQIM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/server.ts"],"names":[],"mappings":"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","file":"chunk-MAYMGQIM.js","sourcesContent":["/**\n * Framework-agnostic dev middleware (connect/express/http style) that powers the Component\n * Graph Inspector's server features:\n * GET /__dev-panel/graph → serves the generated component-graph JSON\n * POST /__dev-panel/open-file → opens a file in the editor running the project (launch-editor)\n *\n * Dev-only by design — mount it only in your dev server. Opens are path-traversal-guarded to\n * the repo root. Used directly for CRA/Express/custom servers, and internally by the Vite plugin.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { resolve, relative, isAbsolute } from 'node:path';\nimport { existsSync, statSync, readFileSync } from 'node:fs';\n\nimport { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT } from '../core/server-open';\n\nexport interface DevPanelServerOptions {\n /** Repo root (absolute). Default: process.cwd(). */\n root?: string;\n /** Path to the generated graph JSON, relative to root. Default: .dev-panel/component-graph.json */\n graphFile?: string;\n graphEndpoint?: string;\n openEndpoint?: string;\n}\n\nconst EDITOR_BIN: Record<string, string | undefined> = {\n auto: undefined,\n vscode: 'code',\n cursor: 'cursor',\n webstorm: 'webstorm',\n zed: 'zed',\n};\n\ntype Next = (err?: unknown) => void;\n\nfunction send(res: ServerResponse, status: number, body: unknown) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Cache-Control', 'no-store');\n res.end(typeof body === 'string' ? body : JSON.stringify(body));\n}\n\nasync function readBody(req: IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n const raw = Buffer.concat(chunks).toString('utf8');\n return raw ? JSON.parse(raw) : {};\n}\n\nexport function createDevPanelMiddleware(options: DevPanelServerOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n const graphFile = resolve(root, options.graphFile ?? '.dev-panel/component-graph.json');\n const graphEndpoint = options.graphEndpoint ?? DEFAULT_GRAPH_ENDPOINT;\n const openEndpoint = options.openEndpoint ?? DEFAULT_OPEN_ENDPOINT;\n\n return function devPanelMiddleware(req: IncomingMessage, res: ServerResponse, next: Next): void {\n const url = (req.url ?? '').split('?')[0];\n\n if (req.method === 'GET' && url === graphEndpoint) {\n try {\n send(res, 200, readFileSync(graphFile, 'utf8'));\n } catch {\n send(res, 404, { error: 'graph-not-generated', hint: 'Run: npx dev-panel-graph' });\n }\n return;\n }\n\n if (req.method === 'POST' && url === openEndpoint) {\n void (async () => {\n try {\n const body = (await readBody(req)) as {\n file?: string;\n line?: number;\n column?: number;\n editor?: string;\n };\n if (!body.file) return send(res, 400, { error: 'file required' });\n const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);\n const rel = relative(root, abs);\n if (rel.startsWith('..') || isAbsolute(rel)) return send(res, 403, { error: 'path escapes root' });\n if (!existsSync(abs) || !statSync(abs).isFile()) return send(res, 404, { error: 'not found' });\n\n const launch = (await import('launch-editor')).default;\n const target = `${abs}:${body.line ?? 1}:${body.column ?? 1}`;\n launch(target, EDITOR_BIN[body.editor ?? 'auto']);\n return send(res, 200, { ok: true });\n } catch (err) {\n return send(res, 500, { error: err instanceof Error ? err.message : 'open failed' });\n }\n })();\n return;\n }\n\n next();\n };\n}\n"]}