@pyreon/cli 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # @pyreon/cli
2
+
3
+ Developer tools for Pyreon — project doctor, context generation, and React pattern detection.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ bun add -d @pyreon/cli
9
+ ```
10
+
11
+ ## Commands
12
+
13
+ ### `pyreon doctor`
14
+
15
+ Scans your project for React patterns and auto-fixes them to Pyreon equivalents.
16
+
17
+ ```bash
18
+ pyreon doctor # human-readable output
19
+ pyreon doctor --fix # auto-fix safe transforms
20
+ pyreon doctor --json # structured JSON output for AI tools
21
+ pyreon doctor --ci # exit code 1 on any error (for CI)
22
+ ```
23
+
24
+ #### What it detects and suggests
25
+
26
+ ```tsx
27
+ // BEFORE: React patterns detected by doctor
28
+ import React from "react"
29
+ import { useState, useEffect, useMemo, useCallback } from "react"
30
+
31
+ function Counter() {
32
+ const [count, setCount] = useState(0)
33
+ const doubled = useMemo(() => count * 2, [count])
34
+
35
+ useEffect(() => {
36
+ document.title = `Count: ${count}`
37
+ }, [count])
38
+
39
+ const increment = useCallback(() => setCount(c => c + 1), [])
40
+
41
+ return (
42
+ <div className="counter">
43
+ <label htmlFor="display">Count</label>
44
+ <span id="display">{doubled}</span>
45
+ <button onClick={increment}>+1</button>
46
+ </div>
47
+ )
48
+ }
49
+ ```
50
+
51
+ ```tsx
52
+ // AFTER: Pyreon equivalents
53
+ import { signal, computed, effect } from "@pyreon/reactivity"
54
+
55
+ function Counter() {
56
+ const count = signal(0)
57
+ const doubled = computed(() => count() * 2)
58
+
59
+ effect(() => {
60
+ document.title = `Count: ${count()}`
61
+ })
62
+
63
+ return (
64
+ <div class="counter">
65
+ <label for="display">Count</label>
66
+ <span id="display">{doubled()}</span>
67
+ <button onClick={() => count.update(c => c + 1)}>+1</button>
68
+ </div>
69
+ )
70
+ }
71
+ ```
72
+
73
+ #### Detection table
74
+
75
+ | React Pattern | Pyreon Equivalent | Auto-fixable |
76
+ |---|---|---|
77
+ | `import React from "react"` | `import { h } from "@pyreon/core"` | No |
78
+ | `useState(initial)` | `signal(initial)` | No |
79
+ | `useEffect(fn, deps)` | `effect(fn)` | No |
80
+ | `useMemo(fn, deps)` | `computed(fn)` | No |
81
+ | `useCallback(fn, deps)` | Use function directly | No |
82
+ | `className="..."` | `class="..."` | Yes |
83
+ | `htmlFor="..."` | `for="..."` | Yes |
84
+
85
+ #### CI integration
86
+
87
+ ```yaml
88
+ # .github/workflows/ci.yml
89
+ - name: Pyreon Doctor
90
+ run: bunx @pyreon/cli doctor --ci
91
+ ```
92
+
93
+ #### JSON output format
94
+
95
+ ```json
96
+ {
97
+ "passed": false,
98
+ "files": [
99
+ {
100
+ "file": "src/App.tsx",
101
+ "diagnostics": [
102
+ {
103
+ "code": "react-import",
104
+ "line": 1,
105
+ "message": "React import detected",
106
+ "current": "import React from \"react\"",
107
+ "suggested": "import { h } from \"@pyreon/core\"",
108
+ "fixable": false
109
+ }
110
+ ],
111
+ "fixed": false
112
+ }
113
+ ],
114
+ "summary": {
115
+ "filesScanned": 12,
116
+ "filesWithIssues": 1,
117
+ "totalErrors": 1,
118
+ "totalFixable": 0,
119
+ "totalFixed": 0
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### `pyreon context`
125
+
126
+ Generates project context for AI tools.
127
+
128
+ ```bash
129
+ pyreon context # writes to .pyreon/context.json
130
+ pyreon context --out ./ai.json # custom output path
131
+ ```
132
+
133
+ #### Output example
134
+
135
+ ```json
136
+ {
137
+ "framework": "pyreon",
138
+ "version": "0.5.0",
139
+ "generatedAt": "2026-03-19T12:00:00.000Z",
140
+ "routes": [
141
+ { "path": "/", "name": "home", "params": [], "hasLoader": false, "hasGuard": false },
142
+ { "path": "/users/:id", "name": "user", "params": ["id"], "hasLoader": true, "hasGuard": false },
143
+ { "path": "/admin", "params": [], "hasLoader": false, "hasGuard": true }
144
+ ],
145
+ "components": [
146
+ {
147
+ "name": "UserCard",
148
+ "file": "src/components/UserCard.tsx",
149
+ "props": ["user", "showAvatar"],
150
+ "hasSignals": true,
151
+ "signalNames": ["isExpanded"]
152
+ }
153
+ ],
154
+ "islands": [
155
+ { "name": "SearchBar", "file": "src/islands/SearchBar.tsx", "hydrate": "idle" }
156
+ ]
157
+ }
158
+ ```
159
+
160
+ ## Programmatic API
161
+
162
+ ```ts
163
+ import { doctor, generateContext } from "@pyreon/cli"
164
+
165
+ // Run doctor programmatically
166
+ const errorCount = await doctor({ fix: false, json: false, ci: false, cwd: process.cwd() })
167
+
168
+ // Generate context
169
+ const result = generateContext({ cwd: process.cwd(), out: ".pyreon/context.json" })
170
+ ```
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"67c993ac-1","name":"context.ts"},{"uid":"67c993ac-3","name":"doctor.ts"},{"uid":"67c993ac-5","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"67c993ac-1":{"renderedLength":6692,"gzipLength":2338,"brotliLength":0,"metaUid":"67c993ac-0"},"67c993ac-3":{"renderedLength":5128,"gzipLength":1827,"brotliLength":0,"metaUid":"67c993ac-2"},"67c993ac-5":{"renderedLength":1559,"gzipLength":677,"brotliLength":0,"metaUid":"67c993ac-4"}},"nodeMetas":{"67c993ac-0":{"id":"/src/context.ts","moduleParts":{"index.js":"67c993ac-1"},"imported":[{"uid":"67c993ac-6"},{"uid":"67c993ac-7"}],"importedBy":[{"uid":"67c993ac-4"}]},"67c993ac-2":{"id":"/src/doctor.ts","moduleParts":{"index.js":"67c993ac-3"},"imported":[{"uid":"67c993ac-6"},{"uid":"67c993ac-7"},{"uid":"67c993ac-8"}],"importedBy":[{"uid":"67c993ac-4"}]},"67c993ac-4":{"id":"/src/index.ts","moduleParts":{"index.js":"67c993ac-5"},"imported":[{"uid":"67c993ac-0"},{"uid":"67c993ac-2"}],"importedBy":[],"isEntry":true},"67c993ac-6":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"67c993ac-0"},{"uid":"67c993ac-2"}]},"67c993ac-7":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"67c993ac-0"},{"uid":"67c993ac-2"}]},"67c993ac-8":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"67c993ac-2"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"abdf3767-1","name":"context.ts"},{"uid":"abdf3767-3","name":"doctor.ts"},{"uid":"abdf3767-5","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"abdf3767-1":{"renderedLength":1323,"gzipLength":627,"brotliLength":0,"metaUid":"abdf3767-0"},"abdf3767-3":{"renderedLength":5128,"gzipLength":1827,"brotliLength":0,"metaUid":"abdf3767-2"},"abdf3767-5":{"renderedLength":1559,"gzipLength":677,"brotliLength":0,"metaUid":"abdf3767-4"}},"nodeMetas":{"abdf3767-0":{"id":"/src/context.ts","moduleParts":{"index.js":"abdf3767-1"},"imported":[{"uid":"abdf3767-6"},{"uid":"abdf3767-7"},{"uid":"abdf3767-8"}],"importedBy":[{"uid":"abdf3767-4"}]},"abdf3767-2":{"id":"/src/doctor.ts","moduleParts":{"index.js":"abdf3767-3"},"imported":[{"uid":"abdf3767-6"},{"uid":"abdf3767-7"},{"uid":"abdf3767-8"}],"importedBy":[{"uid":"abdf3767-4"}]},"abdf3767-4":{"id":"/src/index.ts","moduleParts":{"index.js":"abdf3767-5"},"imported":[{"uid":"abdf3767-0"},{"uid":"abdf3767-2"}],"importedBy":[],"isEntry":true},"abdf3767-6":{"id":"node:fs","moduleParts":{},"imported":[],"importedBy":[{"uid":"abdf3767-0"},{"uid":"abdf3767-2"}]},"abdf3767-7":{"id":"node:path","moduleParts":{},"imported":[],"importedBy":[{"uid":"abdf3767-0"},{"uid":"abdf3767-2"}]},"abdf3767-8":{"id":"@pyreon/compiler","moduleParts":{},"imported":[],"importedBy":[{"uid":"abdf3767-0"},{"uid":"abdf3767-2"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
package/lib/index.js CHANGED
@@ -1,221 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { detectReactPatterns, hasReactPatterns, migrateReactCode } from "@pyreon/compiler";
4
+ import { detectReactPatterns, generateContext as generateContext$1, hasReactPatterns, migrateReactCode } from "@pyreon/compiler";
5
5
 
6
6
  //#region src/context.ts
7
7
  /**
8
8
  * pyreon context — generates .pyreon/context.json for AI tool consumption
9
9
  *
10
- * Scans the project to extract:
11
- * - Route definitions (paths, params, loaders, guards)
12
- * - Component inventory (file, props, signals)
13
- * - Island declarations (name, hydration strategy)
14
- * - Framework version
10
+ * Delegates scanning to @pyreon/compiler's unified project scanner,
11
+ * then writes the result to disk and ensures .pyreon/ is gitignored.
15
12
  */
16
13
  async function generateContext(options) {
17
- const version = readVersion(options.cwd);
18
- const sourceFiles = collectTsxFiles(options.cwd);
19
- const routes = extractRoutes(sourceFiles, options.cwd);
20
- const components = extractComponents(sourceFiles, options.cwd);
21
- const islands = extractIslands(sourceFiles, options.cwd);
22
- const context = {
23
- framework: "pyreon",
24
- version,
25
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
26
- routes,
27
- components,
28
- islands
29
- };
14
+ const context = generateContext$1(options.cwd);
30
15
  const outDir = options.outPath ? path.dirname(options.outPath) : path.join(options.cwd, ".pyreon");
31
16
  const outFile = options.outPath ?? path.join(outDir, "context.json");
32
17
  if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
33
18
  fs.writeFileSync(outFile, JSON.stringify(context, null, 2), "utf-8");
34
19
  ensureGitignore(options.cwd);
35
20
  const relOut = path.relative(options.cwd, outFile);
36
- console.log(` ✓ Generated ${relOut} (${components.length} components, ${routes.length} routes, ${islands.length} islands)`);
21
+ console.log(` ✓ Generated ${relOut} (${context.components.length} components, ${context.routes.length} routes, ${context.islands.length} islands)`);
37
22
  return context;
38
23
  }
39
- function parseRouteFromBlock(block, routeMatch) {
40
- const routePath = routeMatch[1] ?? "";
41
- const params = extractParams(routePath);
42
- const surroundingStart = Math.max(0, routeMatch.index - 50);
43
- const surroundingEnd = Math.min(block.length, routeMatch.index + 200);
44
- const surrounding = block.slice(surroundingStart, surroundingEnd);
45
- const hasLoader = /loader\s*:/.test(surrounding);
46
- const hasGuard = /beforeEnter\s*:|beforeLeave\s*:/.test(surrounding);
47
- return {
48
- path: routePath,
49
- name: surrounding.match(/name\s*:\s*["']([^"']+)["']/)?.[1],
50
- hasLoader,
51
- hasGuard,
52
- params
53
- };
54
- }
55
- function extractRoutesFromBlock(block) {
56
- const routes = [];
57
- const routeObjRe = /path\s*:\s*["']([^"']+)["']/g;
58
- let routeMatch;
59
- while (true) {
60
- routeMatch = routeObjRe.exec(block);
61
- if (!routeMatch) break;
62
- routes.push(parseRouteFromBlock(block, routeMatch));
63
- }
64
- return routes;
65
- }
66
- function extractRoutes(files, _cwd) {
67
- const routes = [];
68
- for (const file of files) {
69
- let code;
70
- try {
71
- code = fs.readFileSync(file, "utf-8");
72
- } catch {
73
- continue;
74
- }
75
- const routeArrayRe = /(?:createRouter\s*\(\s*\[|(?:const|let)\s+routes\s*(?::\s*RouteRecord\[\])?\s*=\s*\[)([\s\S]*?)\]/g;
76
- let match;
77
- while (true) {
78
- match = routeArrayRe.exec(code);
79
- if (!match) break;
80
- const block = match[1] ?? "";
81
- for (const route of extractRoutesFromBlock(block)) routes.push(route);
82
- }
83
- }
84
- return routes;
85
- }
86
- function parseProps(propsStr) {
87
- return propsStr.split(",").map((p) => p.trim().split(":")[0]?.split("=")[0]?.trim() ?? "").filter((p) => p && p !== "props");
88
- }
89
- function collectSignalNames(body) {
90
- const signalNames = [];
91
- const signalRe = /(?:const|let)\s+(\w+)\s*=\s*signal\s*[<(]/g;
92
- let sigMatch;
93
- while (true) {
94
- sigMatch = signalRe.exec(body);
95
- if (!sigMatch) break;
96
- if (sigMatch[1]) signalNames.push(sigMatch[1]);
97
- }
98
- return signalNames;
99
- }
100
- function extractComponentsFromCode(code, relFile) {
101
- const components = [];
102
- const componentRe = /(?:export\s+)?(?:const|function)\s+([A-Z]\w*)\s*(?::\s*ComponentFn<[^>]+>\s*)?=?\s*\(?(?:\s*\{?\s*([^)]*?)\s*\}?\s*)?\)?\s*(?:=>|{)/g;
103
- let match;
104
- while (true) {
105
- match = componentRe.exec(code);
106
- if (!match) break;
107
- const name = match[1] ?? "Unknown";
108
- const props = parseProps(match[2] ?? "");
109
- const bodyStart = match.index + match[0].length;
110
- const signalNames = collectSignalNames(code.slice(bodyStart, Math.min(code.length, bodyStart + 2e3)));
111
- components.push({
112
- name,
113
- file: relFile,
114
- hasSignals: signalNames.length > 0,
115
- signalNames,
116
- props
117
- });
118
- }
119
- return components;
120
- }
121
- function extractComponents(files, _cwd) {
122
- const components = [];
123
- for (const file of files) {
124
- let code;
125
- try {
126
- code = fs.readFileSync(file, "utf-8");
127
- } catch {
128
- continue;
129
- }
130
- const relFile = path.relative(_cwd, file);
131
- for (const comp of extractComponentsFromCode(code, relFile)) components.push(comp);
132
- }
133
- return components;
134
- }
135
- function extractIslands(files, cwd) {
136
- const islands = [];
137
- for (const file of files) {
138
- let code;
139
- try {
140
- code = fs.readFileSync(file, "utf-8");
141
- } catch {
142
- continue;
143
- }
144
- const islandRe = /island\s*\(\s*\(\)\s*=>\s*import\(.+?\)\s*,\s*\{[^}]*name\s*:\s*["']([^"']+)["'][^}]*?(?:hydrate\s*:\s*["']([^"']+)["'])?[^}]*\}/g;
145
- let match;
146
- while (true) {
147
- match = islandRe.exec(code);
148
- if (!match) break;
149
- if (match[1]) islands.push({
150
- name: match[1],
151
- file: path.relative(cwd, file),
152
- hydrate: match[2] ?? "load"
153
- });
154
- }
155
- }
156
- return islands;
157
- }
158
- function extractParams(routePath) {
159
- const params = [];
160
- const paramRe = /:(\w+)\??/g;
161
- let match;
162
- while (true) {
163
- match = paramRe.exec(routePath);
164
- if (!match) break;
165
- if (match[1]) params.push(match[1]);
166
- }
167
- return params;
168
- }
169
- function readVersion(cwd) {
170
- try {
171
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
172
- const deps = {
173
- ...pkg.dependencies,
174
- ...pkg.devDependencies
175
- };
176
- for (const [name, version] of Object.entries(deps)) if (name.startsWith("@pyreon/") && typeof version === "string") return version.replace(/^[\^~]/, "");
177
- return pkg.version || "unknown";
178
- } catch {
179
- return "unknown";
180
- }
181
- }
182
- const tsxExtensions = new Set([
183
- ".tsx",
184
- ".jsx",
185
- ".ts",
186
- ".js"
187
- ]);
188
- const tsxIgnoreDirs = new Set([
189
- "node_modules",
190
- "dist",
191
- "lib",
192
- ".pyreon",
193
- ".git",
194
- "build"
195
- ]);
196
- function shouldSkipEntry(entry) {
197
- if (!entry.isDirectory()) return false;
198
- return entry.name.startsWith(".") || tsxIgnoreDirs.has(entry.name);
199
- }
200
- function walkTsxFiles(dir, results) {
201
- let entries;
202
- try {
203
- entries = fs.readdirSync(dir, { withFileTypes: true });
204
- } catch {
205
- return;
206
- }
207
- for (const entry of entries) {
208
- if (shouldSkipEntry(entry)) continue;
209
- const fullPath = path.join(dir, entry.name);
210
- if (entry.isDirectory()) walkTsxFiles(fullPath, results);
211
- else if (entry.isFile() && tsxExtensions.has(path.extname(entry.name))) results.push(fullPath);
212
- }
213
- }
214
- function collectTsxFiles(cwd) {
215
- const results = [];
216
- walkTsxFiles(cwd, results);
217
- return results;
218
- }
219
24
  function ensureGitignore(cwd) {
220
25
  const gitignorePath = path.join(cwd, ".gitignore");
221
26
  try {
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/context.ts","../src/doctor.ts","../src/index.ts"],"sourcesContent":["/**\n * pyreon context — generates .pyreon/context.json for AI tool consumption\n *\n * Scans the project to extract:\n * - Route definitions (paths, params, loaders, guards)\n * - Component inventory (file, props, signals)\n * - Island declarations (name, hydration strategy)\n * - Framework version\n */\n\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\n\nexport interface ContextOptions {\n cwd: string\n outPath?: string | undefined\n}\n\nexport interface RouteInfo {\n path: string\n name?: string | undefined\n component?: string | undefined\n hasLoader: boolean\n hasGuard: boolean\n params: string[]\n children?: RouteInfo[] | undefined\n}\n\nexport interface ComponentInfo {\n name: string\n file: string\n hasSignals: boolean\n signalNames: string[]\n props: string[]\n}\n\nexport interface IslandInfo {\n name: string\n file: string\n hydrate: string\n}\n\nexport interface ProjectContext {\n framework: \"pyreon\"\n version: string\n generatedAt: string\n routes: RouteInfo[]\n components: ComponentInfo[]\n islands: IslandInfo[]\n}\n\nexport async function generateContext(options: ContextOptions): Promise<ProjectContext> {\n const version = readVersion(options.cwd)\n const sourceFiles = collectTsxFiles(options.cwd)\n\n const routes = extractRoutes(sourceFiles, options.cwd)\n const components = extractComponents(sourceFiles, options.cwd)\n const islands = extractIslands(sourceFiles, options.cwd)\n\n const context: ProjectContext = {\n framework: \"pyreon\",\n version,\n generatedAt: new Date().toISOString(),\n routes,\n components,\n islands,\n }\n\n // Write to .pyreon/context.json\n const outDir = options.outPath ? path.dirname(options.outPath) : path.join(options.cwd, \".pyreon\")\n const outFile = options.outPath ?? path.join(outDir, \"context.json\")\n\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true })\n }\n fs.writeFileSync(outFile, JSON.stringify(context, null, 2), \"utf-8\")\n\n // Ensure .pyreon/ is in .gitignore\n ensureGitignore(options.cwd)\n\n const relOut = path.relative(options.cwd, outFile)\n console.log(\n ` ✓ Generated ${relOut} (${components.length} components, ${routes.length} routes, ${islands.length} islands)`,\n )\n\n return context\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Extractors\n// ═══════════════════════════════════════════════════════════════════════════════\n\nfunction parseRouteFromBlock(block: string, routeMatch: RegExpExecArray): RouteInfo {\n const routePath = routeMatch[1] ?? \"\"\n const params = extractParams(routePath)\n\n const surroundingStart = Math.max(0, routeMatch.index - 50)\n const surroundingEnd = Math.min(block.length, routeMatch.index + 200)\n const surrounding = block.slice(surroundingStart, surroundingEnd)\n\n const hasLoader = /loader\\s*:/.test(surrounding)\n const hasGuard = /beforeEnter\\s*:|beforeLeave\\s*:/.test(surrounding)\n const nameMatch = surrounding.match(/name\\s*:\\s*[\"']([^\"']+)[\"']/)\n\n return {\n path: routePath,\n name: nameMatch?.[1],\n hasLoader,\n hasGuard,\n params,\n }\n}\n\nfunction extractRoutesFromBlock(block: string): RouteInfo[] {\n const routes: RouteInfo[] = []\n const routeObjRe = /path\\s*:\\s*[\"']([^\"']+)[\"']/g\n let routeMatch: RegExpExecArray | null\n while (true) {\n routeMatch = routeObjRe.exec(block)\n if (!routeMatch) break\n routes.push(parseRouteFromBlock(block, routeMatch))\n }\n return routes\n}\n\nfunction extractRoutes(files: string[], _cwd: string): RouteInfo[] {\n const routes: RouteInfo[] = []\n\n for (const file of files) {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n continue\n }\n\n const routeArrayRe =\n /(?:createRouter\\s*\\(\\s*\\[|(?:const|let)\\s+routes\\s*(?::\\s*RouteRecord\\[\\])?\\s*=\\s*\\[)([\\s\\S]*?)\\]/g\n let match: RegExpExecArray | null\n while (true) {\n match = routeArrayRe.exec(code)\n if (!match) break\n const block = match[1] ?? \"\"\n for (const route of extractRoutesFromBlock(block)) {\n routes.push(route)\n }\n }\n }\n\n return routes\n}\n\nfunction parseProps(propsStr: string): string[] {\n return propsStr\n .split(\",\")\n .map((p) => p.trim().split(\":\")[0]?.split(\"=\")[0]?.trim() ?? \"\")\n .filter((p) => p && p !== \"props\")\n}\n\nfunction collectSignalNames(body: string): string[] {\n const signalNames: string[] = []\n const signalRe = /(?:const|let)\\s+(\\w+)\\s*=\\s*signal\\s*[<(]/g\n let sigMatch: RegExpExecArray | null\n while (true) {\n sigMatch = signalRe.exec(body)\n if (!sigMatch) break\n if (sigMatch[1]) signalNames.push(sigMatch[1])\n }\n return signalNames\n}\n\nfunction extractComponentsFromCode(code: string, relFile: string): ComponentInfo[] {\n const components: ComponentInfo[] = []\n const componentRe =\n /(?:export\\s+)?(?:const|function)\\s+([A-Z]\\w*)\\s*(?::\\s*ComponentFn<[^>]+>\\s*)?=?\\s*\\(?(?:\\s*\\{?\\s*([^)]*?)\\s*\\}?\\s*)?\\)?\\s*(?:=>|{)/g\n let match: RegExpExecArray | null\n\n while (true) {\n match = componentRe.exec(code)\n if (!match) break\n const name = match[1] ?? \"Unknown\"\n const props = parseProps(match[2] ?? \"\")\n\n const bodyStart = match.index + match[0].length\n const body = code.slice(bodyStart, Math.min(code.length, bodyStart + 2000))\n const signalNames = collectSignalNames(body)\n\n components.push({\n name,\n file: relFile,\n hasSignals: signalNames.length > 0,\n signalNames,\n props,\n })\n }\n\n return components\n}\n\nfunction extractComponents(files: string[], _cwd: string): ComponentInfo[] {\n const components: ComponentInfo[] = []\n\n for (const file of files) {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n continue\n }\n\n const relFile = path.relative(_cwd, file)\n for (const comp of extractComponentsFromCode(code, relFile)) {\n components.push(comp)\n }\n }\n\n return components\n}\n\nfunction extractIslands(files: string[], cwd: string): IslandInfo[] {\n const islands: IslandInfo[] = []\n\n for (const file of files) {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n continue\n }\n\n const islandRe =\n /island\\s*\\(\\s*\\(\\)\\s*=>\\s*import\\(.+?\\)\\s*,\\s*\\{[^}]*name\\s*:\\s*[\"']([^\"']+)[\"'][^}]*?(?:hydrate\\s*:\\s*[\"']([^\"']+)[\"'])?[^}]*\\}/g\n let match: RegExpExecArray | null\n while (true) {\n match = islandRe.exec(code)\n if (!match) break\n if (match[1]) {\n islands.push({\n name: match[1],\n file: path.relative(cwd, file),\n hydrate: match[2] ?? \"load\",\n })\n }\n }\n }\n\n return islands\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Helpers\n// ═══════════════════════════════════════════════════════════════════════════════\n\nfunction extractParams(routePath: string): string[] {\n const params: string[] = []\n const paramRe = /:(\\w+)\\??/g\n let match: RegExpExecArray | null\n while (true) {\n match = paramRe.exec(routePath)\n if (!match) break\n if (match[1]) params.push(match[1])\n }\n return params\n}\n\nfunction readVersion(cwd: string): string {\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(cwd, \"package.json\"), \"utf-8\"))\n const deps: Record<string, unknown> = { ...pkg.dependencies, ...pkg.devDependencies }\n for (const [name, version] of Object.entries(deps)) {\n if (name.startsWith(\"@pyreon/\") && typeof version === \"string\") {\n return version.replace(/^[\\^~]/, \"\")\n }\n }\n return (pkg.version as string) || \"unknown\"\n } catch {\n return \"unknown\"\n }\n}\n\nconst tsxExtensions = new Set([\".tsx\", \".jsx\", \".ts\", \".js\"])\nconst tsxIgnoreDirs = new Set([\"node_modules\", \"dist\", \"lib\", \".pyreon\", \".git\", \"build\"])\n\nfunction shouldSkipEntry(entry: fs.Dirent): boolean {\n if (!entry.isDirectory()) return false\n return entry.name.startsWith(\".\") || tsxIgnoreDirs.has(entry.name)\n}\n\nfunction walkTsxFiles(dir: string, results: string[]): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n\n for (const entry of entries) {\n if (shouldSkipEntry(entry)) continue\n\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkTsxFiles(fullPath, results)\n } else if (entry.isFile() && tsxExtensions.has(path.extname(entry.name))) {\n results.push(fullPath)\n }\n }\n}\n\nfunction collectTsxFiles(cwd: string): string[] {\n const results: string[] = []\n walkTsxFiles(cwd, results)\n return results\n}\n\nfunction ensureGitignore(cwd: string): void {\n const gitignorePath = path.join(cwd, \".gitignore\")\n try {\n const content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, \"utf-8\") : \"\"\n\n if (!content.includes(\".pyreon/\") && !content.includes(\".pyreon\\n\")) {\n const addition = content.endsWith(\"\\n\") ? \".pyreon/\\n\" : \"\\n.pyreon/\\n\"\n fs.appendFileSync(gitignorePath, addition)\n }\n } catch {\n // Ignore errors with .gitignore\n }\n}\n","/**\n * pyreon doctor — project-wide health check for AI-friendly development\n *\n * Runs a pipeline of checks:\n * 1. React pattern detection (imports, hooks, JSX attributes)\n * 2. Import source validation (@pyreon/* vs react/vue)\n * 3. Common Pyreon mistakes (signal without call, key vs by, etc.)\n *\n * Output modes:\n * - Human-readable (default): colored terminal output\n * - JSON (--json): structured output for AI agent consumption\n * - CI (--ci): exits with code 1 on any error\n *\n * Fix mode (--fix): auto-applies safe transforms via migrateReactCode\n */\n\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport {\n detectReactPatterns,\n hasReactPatterns,\n migrateReactCode,\n type ReactDiagnostic,\n} from \"@pyreon/compiler\"\n\nexport interface DoctorOptions {\n fix: boolean\n json: boolean\n ci: boolean\n cwd: string\n}\n\ninterface FileResult {\n file: string\n diagnostics: ReactDiagnostic[]\n fixed: boolean\n}\n\ninterface DoctorResult {\n passed: boolean\n files: FileResult[]\n summary: {\n filesScanned: number\n filesWithIssues: number\n totalErrors: number\n totalFixable: number\n totalFixed: number\n }\n}\n\nexport async function doctor(options: DoctorOptions): Promise<number> {\n const startTime = performance.now()\n const files = collectSourceFiles(options.cwd)\n const result = runChecks(files, options)\n const elapsed = Math.round(performance.now() - startTime)\n\n if (options.json) {\n printJson(result)\n } else {\n printHuman(result, elapsed)\n }\n\n return result.summary.totalErrors\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// File collection\n// ═══════════════════════════════════════════════════════════════════════════════\n\nconst sourceExtensions = new Set([\".tsx\", \".jsx\", \".ts\", \".js\"])\nconst sourceIgnoreDirs = new Set([\n \"node_modules\",\n \"dist\",\n \"lib\",\n \".pyreon\",\n \".git\",\n \".next\",\n \"build\",\n])\n\nfunction shouldSkipDirEntry(entry: fs.Dirent): boolean {\n if (!entry.isDirectory()) return false\n return entry.name.startsWith(\".\") || sourceIgnoreDirs.has(entry.name)\n}\n\nfunction walkSourceFiles(dir: string, results: string[]): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n\n for (const entry of entries) {\n if (shouldSkipDirEntry(entry)) continue\n\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkSourceFiles(fullPath, results)\n } else if (entry.isFile() && sourceExtensions.has(path.extname(entry.name))) {\n results.push(fullPath)\n }\n }\n}\n\nfunction collectSourceFiles(cwd: string): string[] {\n const results: string[] = []\n walkSourceFiles(cwd, results)\n return results\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Check pipeline\n// ═══════════════════════════════════════════════════════════════════════════════\n\nfunction checkFileWithFix(\n file: string,\n relPath: string,\n): { result: FileResult | null; fixCount: number } {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n return { result: null, fixCount: 0 }\n }\n\n if (!hasReactPatterns(code)) return { result: null, fixCount: 0 }\n\n const migrated = migrateReactCode(code, relPath)\n if (migrated.changes.length > 0) {\n fs.writeFileSync(file, migrated.code, \"utf-8\")\n }\n const remaining = detectReactPatterns(migrated.code, relPath)\n if (remaining.length > 0 || migrated.changes.length > 0) {\n return {\n result: { file: relPath, diagnostics: remaining, fixed: migrated.changes.length > 0 },\n fixCount: migrated.changes.length,\n }\n }\n return { result: null, fixCount: 0 }\n}\n\nfunction checkFileDetectOnly(file: string, relPath: string): FileResult | null {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n return null\n }\n\n if (!hasReactPatterns(code)) return null\n\n const diagnostics = detectReactPatterns(code, relPath)\n if (diagnostics.length > 0) {\n return { file: relPath, diagnostics, fixed: false }\n }\n return null\n}\n\nfunction runChecks(files: string[], options: DoctorOptions): DoctorResult {\n const fileResults: FileResult[] = []\n let totalFixed = 0\n\n for (const file of files) {\n const relPath = path.relative(options.cwd, file)\n\n if (options.fix) {\n const { result, fixCount } = checkFileWithFix(file, relPath)\n totalFixed += fixCount\n if (result) fileResults.push(result)\n } else {\n const result = checkFileDetectOnly(file, relPath)\n if (result) fileResults.push(result)\n }\n }\n\n const totalErrors = fileResults.reduce((sum, f) => sum + f.diagnostics.length, 0)\n const totalFixable = fileResults.reduce(\n (sum, f) => sum + f.diagnostics.filter((d) => d.fixable).length,\n 0,\n )\n\n return {\n passed: totalErrors === 0,\n files: fileResults,\n summary: {\n filesScanned: files.length,\n filesWithIssues: fileResults.length,\n totalErrors,\n totalFixable,\n totalFixed,\n },\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Output formatters\n// ═══════════════════════════════════════════════════════════════════════════════\n\nfunction printJson(result: DoctorResult): void {\n console.log(JSON.stringify(result, null, 2))\n}\n\nfunction printFileResult(fileResult: FileResult): void {\n if (fileResult.diagnostics.length === 0) return\n\n console.log(` ${fileResult.file}${fileResult.fixed ? \" (partially fixed)\" : \"\"}`)\n\n for (const diag of fileResult.diagnostics) {\n const fixTag = diag.fixable ? \" [fixable]\" : \"\"\n console.log(` ${diag.line}:${diag.column} — ${diag.message}${fixTag}`)\n console.log(` Current: ${diag.current}`)\n console.log(` Suggested: ${diag.suggested}`)\n console.log(\"\")\n }\n}\n\nfunction printSummary(summary: DoctorResult[\"summary\"]): void {\n console.log(\n ` ${summary.totalErrors} issue${summary.totalErrors === 1 ? \"\" : \"s\"} in ${summary.filesWithIssues} file${summary.filesWithIssues === 1 ? \"\" : \"s\"}`,\n )\n if (summary.totalFixable > 0) {\n console.log(` ${summary.totalFixable} auto-fixable — run 'pyreon doctor --fix' to apply`)\n }\n console.log(\"\")\n}\n\nfunction printHuman(result: DoctorResult, elapsed: number): void {\n const { summary } = result\n\n console.log(\"\")\n console.log(` Pyreon Doctor — scanned ${summary.filesScanned} files in ${elapsed}ms`)\n console.log(\"\")\n\n if (result.passed && summary.totalFixed === 0) {\n console.log(\" ✓ No issues found. Your code is Pyreon-native!\")\n console.log(\"\")\n return\n }\n\n if (summary.totalFixed > 0) {\n console.log(` ✓ Auto-fixed ${summary.totalFixed} issue${summary.totalFixed === 1 ? \"\" : \"s\"}`)\n console.log(\"\")\n }\n\n for (const fileResult of result.files) {\n printFileResult(fileResult)\n }\n\n printSummary(summary)\n}\n","#!/usr/bin/env node\n\n/**\n * @pyreon/cli — Developer tools for Pyreon\n *\n * Commands:\n * pyreon doctor [--fix] [--json] — Scan project for React patterns, bad imports, etc.\n * pyreon context — Generate .pyreon/context.json for AI tools\n */\n\nimport { generateContext } from \"./context\"\nimport { type DoctorOptions, doctor } from \"./doctor\"\n\nconst args = process.argv.slice(2)\nconst command = args[0]\n\nfunction printUsage(): void {\n console.log(`\n pyreon <command> [options]\n\n Commands:\n doctor [--fix] [--json] [--ci] Scan for React patterns, bad imports, and common mistakes\n context [--out <path>] Generate .pyreon/context.json for AI tools\n\n Options:\n --help Show this help message\n --version Show version\n`)\n}\n\nasync function main(): Promise<void> {\n if (!command || command === \"--help\" || command === \"-h\") {\n printUsage()\n return\n }\n\n if (command === \"--version\" || command === \"-v\") {\n console.log(\"0.4.0\")\n return\n }\n\n if (command === \"doctor\") {\n const options: DoctorOptions = {\n fix: args.includes(\"--fix\"),\n json: args.includes(\"--json\"),\n ci: args.includes(\"--ci\"),\n cwd: process.cwd(),\n }\n const exitCode = await doctor(options)\n if (options.ci && exitCode > 0) {\n process.exit(1)\n }\n return\n }\n\n if (command === \"context\") {\n const outIdx = args.indexOf(\"--out\")\n const outPath = outIdx >= 0 ? args[outIdx + 1] : undefined\n await generateContext({ cwd: process.cwd(), outPath })\n return\n }\n\n console.error(`Unknown command: ${command}`)\n printUsage()\n process.exit(1)\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n\nexport type { ContextOptions, ProjectContext } from \"./context\"\nexport type { DoctorOptions } from \"./doctor\"\nexport { doctor, generateContext }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["scanProject"],"sources":["../src/context.ts","../src/doctor.ts","../src/index.ts"],"sourcesContent":["/**\n * pyreon context — generates .pyreon/context.json for AI tool consumption\n *\n * Delegates scanning to @pyreon/compiler's unified project scanner,\n * then writes the result to disk and ensures .pyreon/ is gitignored.\n */\n\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport { type ProjectContext, generateContext as scanProject } from \"@pyreon/compiler\"\n\nexport type { ComponentInfo, IslandInfo, ProjectContext, RouteInfo } from \"@pyreon/compiler\"\n\nexport interface ContextOptions {\n cwd: string\n outPath?: string | undefined\n}\n\nexport async function generateContext(options: ContextOptions): Promise<ProjectContext> {\n const context = scanProject(options.cwd)\n\n // Write to .pyreon/context.json\n const outDir = options.outPath ? path.dirname(options.outPath) : path.join(options.cwd, \".pyreon\")\n const outFile = options.outPath ?? path.join(outDir, \"context.json\")\n\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true })\n }\n fs.writeFileSync(outFile, JSON.stringify(context, null, 2), \"utf-8\")\n\n // Ensure .pyreon/ is in .gitignore\n ensureGitignore(options.cwd)\n\n const relOut = path.relative(options.cwd, outFile)\n console.log(\n ` ✓ Generated ${relOut} (${context.components.length} components, ${context.routes.length} routes, ${context.islands.length} islands)`,\n )\n\n return context\n}\n\nfunction ensureGitignore(cwd: string): void {\n const gitignorePath = path.join(cwd, \".gitignore\")\n try {\n const content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, \"utf-8\") : \"\"\n\n if (!content.includes(\".pyreon/\") && !content.includes(\".pyreon\\n\")) {\n const addition = content.endsWith(\"\\n\") ? \".pyreon/\\n\" : \"\\n.pyreon/\\n\"\n fs.appendFileSync(gitignorePath, addition)\n }\n } catch {\n // Ignore errors with .gitignore\n }\n}\n","/**\n * pyreon doctor — project-wide health check for AI-friendly development\n *\n * Runs a pipeline of checks:\n * 1. React pattern detection (imports, hooks, JSX attributes)\n * 2. Import source validation (@pyreon/* vs react/vue)\n * 3. Common Pyreon mistakes (signal without call, key vs by, etc.)\n *\n * Output modes:\n * - Human-readable (default): colored terminal output\n * - JSON (--json): structured output for AI agent consumption\n * - CI (--ci): exits with code 1 on any error\n *\n * Fix mode (--fix): auto-applies safe transforms via migrateReactCode\n */\n\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport {\n detectReactPatterns,\n hasReactPatterns,\n migrateReactCode,\n type ReactDiagnostic,\n} from \"@pyreon/compiler\"\n\nexport interface DoctorOptions {\n fix: boolean\n json: boolean\n ci: boolean\n cwd: string\n}\n\ninterface FileResult {\n file: string\n diagnostics: ReactDiagnostic[]\n fixed: boolean\n}\n\ninterface DoctorResult {\n passed: boolean\n files: FileResult[]\n summary: {\n filesScanned: number\n filesWithIssues: number\n totalErrors: number\n totalFixable: number\n totalFixed: number\n }\n}\n\nexport async function doctor(options: DoctorOptions): Promise<number> {\n const startTime = performance.now()\n const files = collectSourceFiles(options.cwd)\n const result = runChecks(files, options)\n const elapsed = Math.round(performance.now() - startTime)\n\n if (options.json) {\n printJson(result)\n } else {\n printHuman(result, elapsed)\n }\n\n return result.summary.totalErrors\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// File collection\n// ═══════════════════════════════════════════════════════════════════════════════\n\nconst sourceExtensions = new Set([\".tsx\", \".jsx\", \".ts\", \".js\"])\nconst sourceIgnoreDirs = new Set([\n \"node_modules\",\n \"dist\",\n \"lib\",\n \".pyreon\",\n \".git\",\n \".next\",\n \"build\",\n])\n\nfunction shouldSkipDirEntry(entry: fs.Dirent): boolean {\n if (!entry.isDirectory()) return false\n return entry.name.startsWith(\".\") || sourceIgnoreDirs.has(entry.name)\n}\n\nfunction walkSourceFiles(dir: string, results: string[]): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n\n for (const entry of entries) {\n if (shouldSkipDirEntry(entry)) continue\n\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkSourceFiles(fullPath, results)\n } else if (entry.isFile() && sourceExtensions.has(path.extname(entry.name))) {\n results.push(fullPath)\n }\n }\n}\n\nfunction collectSourceFiles(cwd: string): string[] {\n const results: string[] = []\n walkSourceFiles(cwd, results)\n return results\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Check pipeline\n// ═══════════════════════════════════════════════════════════════════════════════\n\nfunction checkFileWithFix(\n file: string,\n relPath: string,\n): { result: FileResult | null; fixCount: number } {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n return { result: null, fixCount: 0 }\n }\n\n if (!hasReactPatterns(code)) return { result: null, fixCount: 0 }\n\n const migrated = migrateReactCode(code, relPath)\n if (migrated.changes.length > 0) {\n fs.writeFileSync(file, migrated.code, \"utf-8\")\n }\n const remaining = detectReactPatterns(migrated.code, relPath)\n if (remaining.length > 0 || migrated.changes.length > 0) {\n return {\n result: { file: relPath, diagnostics: remaining, fixed: migrated.changes.length > 0 },\n fixCount: migrated.changes.length,\n }\n }\n return { result: null, fixCount: 0 }\n}\n\nfunction checkFileDetectOnly(file: string, relPath: string): FileResult | null {\n let code: string\n try {\n code = fs.readFileSync(file, \"utf-8\")\n } catch {\n return null\n }\n\n if (!hasReactPatterns(code)) return null\n\n const diagnostics = detectReactPatterns(code, relPath)\n if (diagnostics.length > 0) {\n return { file: relPath, diagnostics, fixed: false }\n }\n return null\n}\n\nfunction runChecks(files: string[], options: DoctorOptions): DoctorResult {\n const fileResults: FileResult[] = []\n let totalFixed = 0\n\n for (const file of files) {\n const relPath = path.relative(options.cwd, file)\n\n if (options.fix) {\n const { result, fixCount } = checkFileWithFix(file, relPath)\n totalFixed += fixCount\n if (result) fileResults.push(result)\n } else {\n const result = checkFileDetectOnly(file, relPath)\n if (result) fileResults.push(result)\n }\n }\n\n const totalErrors = fileResults.reduce((sum, f) => sum + f.diagnostics.length, 0)\n const totalFixable = fileResults.reduce(\n (sum, f) => sum + f.diagnostics.filter((d) => d.fixable).length,\n 0,\n )\n\n return {\n passed: totalErrors === 0,\n files: fileResults,\n summary: {\n filesScanned: files.length,\n filesWithIssues: fileResults.length,\n totalErrors,\n totalFixable,\n totalFixed,\n },\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// Output formatters\n// ═══════════════════════════════════════════════════════════════════════════════\n\nfunction printJson(result: DoctorResult): void {\n console.log(JSON.stringify(result, null, 2))\n}\n\nfunction printFileResult(fileResult: FileResult): void {\n if (fileResult.diagnostics.length === 0) return\n\n console.log(` ${fileResult.file}${fileResult.fixed ? \" (partially fixed)\" : \"\"}`)\n\n for (const diag of fileResult.diagnostics) {\n const fixTag = diag.fixable ? \" [fixable]\" : \"\"\n console.log(` ${diag.line}:${diag.column} — ${diag.message}${fixTag}`)\n console.log(` Current: ${diag.current}`)\n console.log(` Suggested: ${diag.suggested}`)\n console.log(\"\")\n }\n}\n\nfunction printSummary(summary: DoctorResult[\"summary\"]): void {\n console.log(\n ` ${summary.totalErrors} issue${summary.totalErrors === 1 ? \"\" : \"s\"} in ${summary.filesWithIssues} file${summary.filesWithIssues === 1 ? \"\" : \"s\"}`,\n )\n if (summary.totalFixable > 0) {\n console.log(` ${summary.totalFixable} auto-fixable — run 'pyreon doctor --fix' to apply`)\n }\n console.log(\"\")\n}\n\nfunction printHuman(result: DoctorResult, elapsed: number): void {\n const { summary } = result\n\n console.log(\"\")\n console.log(` Pyreon Doctor — scanned ${summary.filesScanned} files in ${elapsed}ms`)\n console.log(\"\")\n\n if (result.passed && summary.totalFixed === 0) {\n console.log(\" ✓ No issues found. Your code is Pyreon-native!\")\n console.log(\"\")\n return\n }\n\n if (summary.totalFixed > 0) {\n console.log(` ✓ Auto-fixed ${summary.totalFixed} issue${summary.totalFixed === 1 ? \"\" : \"s\"}`)\n console.log(\"\")\n }\n\n for (const fileResult of result.files) {\n printFileResult(fileResult)\n }\n\n printSummary(summary)\n}\n","#!/usr/bin/env node\n\n/**\n * @pyreon/cli — Developer tools for Pyreon\n *\n * Commands:\n * pyreon doctor [--fix] [--json] — Scan project for React patterns, bad imports, etc.\n * pyreon context — Generate .pyreon/context.json for AI tools\n */\n\nimport { generateContext } from \"./context\"\nimport { type DoctorOptions, doctor } from \"./doctor\"\n\nconst args = process.argv.slice(2)\nconst command = args[0]\n\nfunction printUsage(): void {\n console.log(`\n pyreon <command> [options]\n\n Commands:\n doctor [--fix] [--json] [--ci] Scan for React patterns, bad imports, and common mistakes\n context [--out <path>] Generate .pyreon/context.json for AI tools\n\n Options:\n --help Show this help message\n --version Show version\n`)\n}\n\nasync function main(): Promise<void> {\n if (!command || command === \"--help\" || command === \"-h\") {\n printUsage()\n return\n }\n\n if (command === \"--version\" || command === \"-v\") {\n console.log(\"0.4.0\")\n return\n }\n\n if (command === \"doctor\") {\n const options: DoctorOptions = {\n fix: args.includes(\"--fix\"),\n json: args.includes(\"--json\"),\n ci: args.includes(\"--ci\"),\n cwd: process.cwd(),\n }\n const exitCode = await doctor(options)\n if (options.ci && exitCode > 0) {\n process.exit(1)\n }\n return\n }\n\n if (command === \"context\") {\n const outIdx = args.indexOf(\"--out\")\n const outPath = outIdx >= 0 ? args[outIdx + 1] : undefined\n await generateContext({ cwd: process.cwd(), outPath })\n return\n }\n\n console.error(`Unknown command: ${command}`)\n printUsage()\n process.exit(1)\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n\nexport type { ContextOptions, ProjectContext } from \"./context\"\nexport type { DoctorOptions } from \"./doctor\"\nexport { doctor, generateContext }\n"],"mappings":"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"}
@@ -1,32 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { detectReactPatterns, hasReactPatterns, migrateReactCode } from "@pyreon/compiler";
4
+ import { detectReactPatterns, generateContext as generateContext$1, hasReactPatterns, migrateReactCode } from "@pyreon/compiler";
5
5
 
6
6
  //#region src/context.ts
7
7
  /**
8
8
  * pyreon context — generates .pyreon/context.json for AI tool consumption
9
9
  *
10
- * Scans the project to extract:
11
- * - Route definitions (paths, params, loaders, guards)
12
- * - Component inventory (file, props, signals)
13
- * - Island declarations (name, hydration strategy)
14
- * - Framework version
10
+ * Delegates scanning to @pyreon/compiler's unified project scanner,
11
+ * then writes the result to disk and ensures .pyreon/ is gitignored.
15
12
  */
16
13
  async function generateContext(options) {
17
- const version = readVersion(options.cwd);
18
- const sourceFiles = collectTsxFiles(options.cwd);
19
- const routes = extractRoutes(sourceFiles, options.cwd);
20
- const components = extractComponents(sourceFiles, options.cwd);
21
- const islands = extractIslands(sourceFiles, options.cwd);
22
- const context = {
23
- framework: "pyreon",
24
- version,
25
- generatedAt: (/* @__PURE__ */new Date()).toISOString(),
26
- routes,
27
- components,
28
- islands
29
- };
14
+ const context = generateContext$1(options.cwd);
30
15
  const outDir = options.outPath ? path.dirname(options.outPath) : path.join(options.cwd, ".pyreon");
31
16
  const outFile = options.outPath ?? path.join(outDir, "context.json");
32
17
  if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, {
@@ -35,176 +20,9 @@ async function generateContext(options) {
35
20
  fs.writeFileSync(outFile, JSON.stringify(context, null, 2), "utf-8");
36
21
  ensureGitignore(options.cwd);
37
22
  const relOut = path.relative(options.cwd, outFile);
38
- console.log(` ✓ Generated ${relOut} (${components.length} components, ${routes.length} routes, ${islands.length} islands)`);
23
+ console.log(` ✓ Generated ${relOut} (${context.components.length} components, ${context.routes.length} routes, ${context.islands.length} islands)`);
39
24
  return context;
40
25
  }
41
- function parseRouteFromBlock(block, routeMatch) {
42
- const routePath = routeMatch[1] ?? "";
43
- const params = extractParams(routePath);
44
- const surroundingStart = Math.max(0, routeMatch.index - 50);
45
- const surroundingEnd = Math.min(block.length, routeMatch.index + 200);
46
- const surrounding = block.slice(surroundingStart, surroundingEnd);
47
- const hasLoader = /loader\s*:/.test(surrounding);
48
- const hasGuard = /beforeEnter\s*:|beforeLeave\s*:/.test(surrounding);
49
- return {
50
- path: routePath,
51
- name: surrounding.match(/name\s*:\s*["']([^"']+)["']/)?.[1],
52
- hasLoader,
53
- hasGuard,
54
- params
55
- };
56
- }
57
- function extractRoutesFromBlock(block) {
58
- const routes = [];
59
- const routeObjRe = /path\s*:\s*["']([^"']+)["']/g;
60
- let routeMatch;
61
- while (true) {
62
- routeMatch = routeObjRe.exec(block);
63
- if (!routeMatch) break;
64
- routes.push(parseRouteFromBlock(block, routeMatch));
65
- }
66
- return routes;
67
- }
68
- function extractRoutes(files, _cwd) {
69
- const routes = [];
70
- for (const file of files) {
71
- let code;
72
- try {
73
- code = fs.readFileSync(file, "utf-8");
74
- } catch {
75
- continue;
76
- }
77
- const routeArrayRe = /(?:createRouter\s*\(\s*\[|(?:const|let)\s+routes\s*(?::\s*RouteRecord\[\])?\s*=\s*\[)([\s\S]*?)\]/g;
78
- let match;
79
- while (true) {
80
- match = routeArrayRe.exec(code);
81
- if (!match) break;
82
- const block = match[1] ?? "";
83
- for (const route of extractRoutesFromBlock(block)) routes.push(route);
84
- }
85
- }
86
- return routes;
87
- }
88
- function parseProps(propsStr) {
89
- return propsStr.split(",").map(p => p.trim().split(":")[0]?.split("=")[0]?.trim() ?? "").filter(p => p && p !== "props");
90
- }
91
- function collectSignalNames(body) {
92
- const signalNames = [];
93
- const signalRe = /(?:const|let)\s+(\w+)\s*=\s*signal\s*[<(]/g;
94
- let sigMatch;
95
- while (true) {
96
- sigMatch = signalRe.exec(body);
97
- if (!sigMatch) break;
98
- if (sigMatch[1]) signalNames.push(sigMatch[1]);
99
- }
100
- return signalNames;
101
- }
102
- function extractComponentsFromCode(code, relFile) {
103
- const components = [];
104
- const componentRe = /(?:export\s+)?(?:const|function)\s+([A-Z]\w*)\s*(?::\s*ComponentFn<[^>]+>\s*)?=?\s*\(?(?:\s*\{?\s*([^)]*?)\s*\}?\s*)?\)?\s*(?:=>|{)/g;
105
- let match;
106
- while (true) {
107
- match = componentRe.exec(code);
108
- if (!match) break;
109
- const name = match[1] ?? "Unknown";
110
- const props = parseProps(match[2] ?? "");
111
- const bodyStart = match.index + match[0].length;
112
- const signalNames = collectSignalNames(code.slice(bodyStart, Math.min(code.length, bodyStart + 2e3)));
113
- components.push({
114
- name,
115
- file: relFile,
116
- hasSignals: signalNames.length > 0,
117
- signalNames,
118
- props
119
- });
120
- }
121
- return components;
122
- }
123
- function extractComponents(files, _cwd) {
124
- const components = [];
125
- for (const file of files) {
126
- let code;
127
- try {
128
- code = fs.readFileSync(file, "utf-8");
129
- } catch {
130
- continue;
131
- }
132
- const relFile = path.relative(_cwd, file);
133
- for (const comp of extractComponentsFromCode(code, relFile)) components.push(comp);
134
- }
135
- return components;
136
- }
137
- function extractIslands(files, cwd) {
138
- const islands = [];
139
- for (const file of files) {
140
- let code;
141
- try {
142
- code = fs.readFileSync(file, "utf-8");
143
- } catch {
144
- continue;
145
- }
146
- const islandRe = /island\s*\(\s*\(\)\s*=>\s*import\(.+?\)\s*,\s*\{[^}]*name\s*:\s*["']([^"']+)["'][^}]*?(?:hydrate\s*:\s*["']([^"']+)["'])?[^}]*\}/g;
147
- let match;
148
- while (true) {
149
- match = islandRe.exec(code);
150
- if (!match) break;
151
- if (match[1]) islands.push({
152
- name: match[1],
153
- file: path.relative(cwd, file),
154
- hydrate: match[2] ?? "load"
155
- });
156
- }
157
- }
158
- return islands;
159
- }
160
- function extractParams(routePath) {
161
- const params = [];
162
- const paramRe = /:(\w+)\??/g;
163
- let match;
164
- while (true) {
165
- match = paramRe.exec(routePath);
166
- if (!match) break;
167
- if (match[1]) params.push(match[1]);
168
- }
169
- return params;
170
- }
171
- function readVersion(cwd) {
172
- try {
173
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
174
- const deps = {
175
- ...pkg.dependencies,
176
- ...pkg.devDependencies
177
- };
178
- for (const [name, version] of Object.entries(deps)) if (name.startsWith("@pyreon/") && typeof version === "string") return version.replace(/^[\^~]/, "");
179
- return pkg.version || "unknown";
180
- } catch {
181
- return "unknown";
182
- }
183
- }
184
- function shouldSkipEntry(entry) {
185
- if (!entry.isDirectory()) return false;
186
- return entry.name.startsWith(".") || tsxIgnoreDirs.has(entry.name);
187
- }
188
- function walkTsxFiles(dir, results) {
189
- let entries;
190
- try {
191
- entries = fs.readdirSync(dir, {
192
- withFileTypes: true
193
- });
194
- } catch {
195
- return;
196
- }
197
- for (const entry of entries) {
198
- if (shouldSkipEntry(entry)) continue;
199
- const fullPath = path.join(dir, entry.name);
200
- if (entry.isDirectory()) walkTsxFiles(fullPath, results);else if (entry.isFile() && tsxExtensions.has(path.extname(entry.name))) results.push(fullPath);
201
- }
202
- }
203
- function collectTsxFiles(cwd) {
204
- const results = [];
205
- walkTsxFiles(cwd, results);
206
- return results;
207
- }
208
26
  function ensureGitignore(cwd) {
209
27
  const gitignorePath = path.join(cwd, ".gitignore");
210
28
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/context.ts","../../src/doctor.ts","../../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":["scanProject"],"sources":["../../src/context.ts","../../src/doctor.ts","../../src/index.ts"],"mappings":"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"}
@@ -1,47 +1,11 @@
1
+ import { ProjectContext, ProjectContext as ProjectContext$1 } from "@pyreon/compiler";
2
+
1
3
  //#region src/context.d.ts
2
- /**
3
- * pyreon context — generates .pyreon/context.json for AI tool consumption
4
- *
5
- * Scans the project to extract:
6
- * - Route definitions (paths, params, loaders, guards)
7
- * - Component inventory (file, props, signals)
8
- * - Island declarations (name, hydration strategy)
9
- * - Framework version
10
- */
11
4
  interface ContextOptions {
12
5
  cwd: string;
13
6
  outPath?: string | undefined;
14
7
  }
15
- interface RouteInfo {
16
- path: string;
17
- name?: string | undefined;
18
- component?: string | undefined;
19
- hasLoader: boolean;
20
- hasGuard: boolean;
21
- params: string[];
22
- children?: RouteInfo[] | undefined;
23
- }
24
- interface ComponentInfo {
25
- name: string;
26
- file: string;
27
- hasSignals: boolean;
28
- signalNames: string[];
29
- props: string[];
30
- }
31
- interface IslandInfo {
32
- name: string;
33
- file: string;
34
- hydrate: string;
35
- }
36
- interface ProjectContext {
37
- framework: "pyreon";
38
- version: string;
39
- generatedAt: string;
40
- routes: RouteInfo[];
41
- components: ComponentInfo[];
42
- islands: IslandInfo[];
43
- }
44
- declare function generateContext(options: ContextOptions): Promise<ProjectContext>;
8
+ declare function generateContext(options: ContextOptions): Promise<ProjectContext$1>;
45
9
  //#endregion
46
10
  //#region src/doctor.d.ts
47
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/context.ts","../../src/doctor.ts"],"mappings":";;AAaA;;;;;AAKA;;;UALiB,cAAA;EACf,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,SAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,SAAA;EACA,QAAA;EACA,MAAA;EACA,QAAA,GAAW,SAAA;AAAA;AAAA,UAGI,aAAA;EACf,IAAA;EACA,IAAA;EACA,UAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,UAGe,UAAA;EACf,IAAA;EACA,IAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,SAAA;EACA,OAAA;EACA,WAAA;EACA,MAAA,EAAQ,SAAA;EACR,UAAA,EAAY,aAAA;EACZ,OAAA,EAAS,UAAA;AAAA;AAAA,iBAGW,eAAA,CAAgB,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,cAAA;;;;AAtCxE;;;;;AAKA;;;;;;;;;UCOiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,EAAA;EACA,GAAA;AAAA;AAAA,iBAqBoB,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA"}
1
+ {"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/context.ts","../../src/doctor.ts"],"mappings":";;;UAaiB,cAAA;EACf,GAAA;EACA,OAAA;AAAA;AAAA,iBAGoB,eAAA,CAAgB,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,gBAAA;;;;;;AALxE;;;;;AAKA;;;;;;;UCOiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,EAAA;EACA,GAAA;AAAA;AAAA,iBAqBoB,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI tools for Pyreon — doctor, generate, context",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -42,7 +42,7 @@
42
42
  "prepublishOnly": "bun run build"
43
43
  },
44
44
  "dependencies": {
45
- "@pyreon/compiler": "workspace:*"
45
+ "@pyreon/compiler": "0.5.1"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "typescript": ">=5.0.0"
package/src/context.ts CHANGED
@@ -1,70 +1,23 @@
1
1
  /**
2
2
  * pyreon context — generates .pyreon/context.json for AI tool consumption
3
3
  *
4
- * Scans the project to extract:
5
- * - Route definitions (paths, params, loaders, guards)
6
- * - Component inventory (file, props, signals)
7
- * - Island declarations (name, hydration strategy)
8
- * - Framework version
4
+ * Delegates scanning to @pyreon/compiler's unified project scanner,
5
+ * then writes the result to disk and ensures .pyreon/ is gitignored.
9
6
  */
10
7
 
11
8
  import * as fs from "node:fs"
12
9
  import * as path from "node:path"
10
+ import { type ProjectContext, generateContext as scanProject } from "@pyreon/compiler"
11
+
12
+ export type { ComponentInfo, IslandInfo, ProjectContext, RouteInfo } from "@pyreon/compiler"
13
13
 
14
14
  export interface ContextOptions {
15
15
  cwd: string
16
16
  outPath?: string | undefined
17
17
  }
18
18
 
19
- export interface RouteInfo {
20
- path: string
21
- name?: string | undefined
22
- component?: string | undefined
23
- hasLoader: boolean
24
- hasGuard: boolean
25
- params: string[]
26
- children?: RouteInfo[] | undefined
27
- }
28
-
29
- export interface ComponentInfo {
30
- name: string
31
- file: string
32
- hasSignals: boolean
33
- signalNames: string[]
34
- props: string[]
35
- }
36
-
37
- export interface IslandInfo {
38
- name: string
39
- file: string
40
- hydrate: string
41
- }
42
-
43
- export interface ProjectContext {
44
- framework: "pyreon"
45
- version: string
46
- generatedAt: string
47
- routes: RouteInfo[]
48
- components: ComponentInfo[]
49
- islands: IslandInfo[]
50
- }
51
-
52
19
  export async function generateContext(options: ContextOptions): Promise<ProjectContext> {
53
- const version = readVersion(options.cwd)
54
- const sourceFiles = collectTsxFiles(options.cwd)
55
-
56
- const routes = extractRoutes(sourceFiles, options.cwd)
57
- const components = extractComponents(sourceFiles, options.cwd)
58
- const islands = extractIslands(sourceFiles, options.cwd)
59
-
60
- const context: ProjectContext = {
61
- framework: "pyreon",
62
- version,
63
- generatedAt: new Date().toISOString(),
64
- routes,
65
- components,
66
- islands,
67
- }
20
+ const context = scanProject(options.cwd)
68
21
 
69
22
  // Write to .pyreon/context.json
70
23
  const outDir = options.outPath ? path.dirname(options.outPath) : path.join(options.cwd, ".pyreon")
@@ -80,238 +33,12 @@ export async function generateContext(options: ContextOptions): Promise<ProjectC
80
33
 
81
34
  const relOut = path.relative(options.cwd, outFile)
82
35
  console.log(
83
- ` ✓ Generated ${relOut} (${components.length} components, ${routes.length} routes, ${islands.length} islands)`,
36
+ ` ✓ Generated ${relOut} (${context.components.length} components, ${context.routes.length} routes, ${context.islands.length} islands)`,
84
37
  )
85
38
 
86
39
  return context
87
40
  }
88
41
 
89
- // ═══════════════════════════════════════════════════════════════════════════════
90
- // Extractors
91
- // ═══════════════════════════════════════════════════════════════════════════════
92
-
93
- function parseRouteFromBlock(block: string, routeMatch: RegExpExecArray): RouteInfo {
94
- const routePath = routeMatch[1] ?? ""
95
- const params = extractParams(routePath)
96
-
97
- const surroundingStart = Math.max(0, routeMatch.index - 50)
98
- const surroundingEnd = Math.min(block.length, routeMatch.index + 200)
99
- const surrounding = block.slice(surroundingStart, surroundingEnd)
100
-
101
- const hasLoader = /loader\s*:/.test(surrounding)
102
- const hasGuard = /beforeEnter\s*:|beforeLeave\s*:/.test(surrounding)
103
- const nameMatch = surrounding.match(/name\s*:\s*["']([^"']+)["']/)
104
-
105
- return {
106
- path: routePath,
107
- name: nameMatch?.[1],
108
- hasLoader,
109
- hasGuard,
110
- params,
111
- }
112
- }
113
-
114
- function extractRoutesFromBlock(block: string): RouteInfo[] {
115
- const routes: RouteInfo[] = []
116
- const routeObjRe = /path\s*:\s*["']([^"']+)["']/g
117
- let routeMatch: RegExpExecArray | null
118
- while (true) {
119
- routeMatch = routeObjRe.exec(block)
120
- if (!routeMatch) break
121
- routes.push(parseRouteFromBlock(block, routeMatch))
122
- }
123
- return routes
124
- }
125
-
126
- function extractRoutes(files: string[], _cwd: string): RouteInfo[] {
127
- const routes: RouteInfo[] = []
128
-
129
- for (const file of files) {
130
- let code: string
131
- try {
132
- code = fs.readFileSync(file, "utf-8")
133
- } catch {
134
- continue
135
- }
136
-
137
- const routeArrayRe =
138
- /(?:createRouter\s*\(\s*\[|(?:const|let)\s+routes\s*(?::\s*RouteRecord\[\])?\s*=\s*\[)([\s\S]*?)\]/g
139
- let match: RegExpExecArray | null
140
- while (true) {
141
- match = routeArrayRe.exec(code)
142
- if (!match) break
143
- const block = match[1] ?? ""
144
- for (const route of extractRoutesFromBlock(block)) {
145
- routes.push(route)
146
- }
147
- }
148
- }
149
-
150
- return routes
151
- }
152
-
153
- function parseProps(propsStr: string): string[] {
154
- return propsStr
155
- .split(",")
156
- .map((p) => p.trim().split(":")[0]?.split("=")[0]?.trim() ?? "")
157
- .filter((p) => p && p !== "props")
158
- }
159
-
160
- function collectSignalNames(body: string): string[] {
161
- const signalNames: string[] = []
162
- const signalRe = /(?:const|let)\s+(\w+)\s*=\s*signal\s*[<(]/g
163
- let sigMatch: RegExpExecArray | null
164
- while (true) {
165
- sigMatch = signalRe.exec(body)
166
- if (!sigMatch) break
167
- if (sigMatch[1]) signalNames.push(sigMatch[1])
168
- }
169
- return signalNames
170
- }
171
-
172
- function extractComponentsFromCode(code: string, relFile: string): ComponentInfo[] {
173
- const components: ComponentInfo[] = []
174
- const componentRe =
175
- /(?:export\s+)?(?:const|function)\s+([A-Z]\w*)\s*(?::\s*ComponentFn<[^>]+>\s*)?=?\s*\(?(?:\s*\{?\s*([^)]*?)\s*\}?\s*)?\)?\s*(?:=>|{)/g
176
- let match: RegExpExecArray | null
177
-
178
- while (true) {
179
- match = componentRe.exec(code)
180
- if (!match) break
181
- const name = match[1] ?? "Unknown"
182
- const props = parseProps(match[2] ?? "")
183
-
184
- const bodyStart = match.index + match[0].length
185
- const body = code.slice(bodyStart, Math.min(code.length, bodyStart + 2000))
186
- const signalNames = collectSignalNames(body)
187
-
188
- components.push({
189
- name,
190
- file: relFile,
191
- hasSignals: signalNames.length > 0,
192
- signalNames,
193
- props,
194
- })
195
- }
196
-
197
- return components
198
- }
199
-
200
- function extractComponents(files: string[], _cwd: string): ComponentInfo[] {
201
- const components: ComponentInfo[] = []
202
-
203
- for (const file of files) {
204
- let code: string
205
- try {
206
- code = fs.readFileSync(file, "utf-8")
207
- } catch {
208
- continue
209
- }
210
-
211
- const relFile = path.relative(_cwd, file)
212
- for (const comp of extractComponentsFromCode(code, relFile)) {
213
- components.push(comp)
214
- }
215
- }
216
-
217
- return components
218
- }
219
-
220
- function extractIslands(files: string[], cwd: string): IslandInfo[] {
221
- const islands: IslandInfo[] = []
222
-
223
- for (const file of files) {
224
- let code: string
225
- try {
226
- code = fs.readFileSync(file, "utf-8")
227
- } catch {
228
- continue
229
- }
230
-
231
- const islandRe =
232
- /island\s*\(\s*\(\)\s*=>\s*import\(.+?\)\s*,\s*\{[^}]*name\s*:\s*["']([^"']+)["'][^}]*?(?:hydrate\s*:\s*["']([^"']+)["'])?[^}]*\}/g
233
- let match: RegExpExecArray | null
234
- while (true) {
235
- match = islandRe.exec(code)
236
- if (!match) break
237
- if (match[1]) {
238
- islands.push({
239
- name: match[1],
240
- file: path.relative(cwd, file),
241
- hydrate: match[2] ?? "load",
242
- })
243
- }
244
- }
245
- }
246
-
247
- return islands
248
- }
249
-
250
- // ═══════════════════════════════════════════════════════════════════════════════
251
- // Helpers
252
- // ═══════════════════════════════════════════════════════════════════════════════
253
-
254
- function extractParams(routePath: string): string[] {
255
- const params: string[] = []
256
- const paramRe = /:(\w+)\??/g
257
- let match: RegExpExecArray | null
258
- while (true) {
259
- match = paramRe.exec(routePath)
260
- if (!match) break
261
- if (match[1]) params.push(match[1])
262
- }
263
- return params
264
- }
265
-
266
- function readVersion(cwd: string): string {
267
- try {
268
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"))
269
- const deps: Record<string, unknown> = { ...pkg.dependencies, ...pkg.devDependencies }
270
- for (const [name, version] of Object.entries(deps)) {
271
- if (name.startsWith("@pyreon/") && typeof version === "string") {
272
- return version.replace(/^[\^~]/, "")
273
- }
274
- }
275
- return (pkg.version as string) || "unknown"
276
- } catch {
277
- return "unknown"
278
- }
279
- }
280
-
281
- const tsxExtensions = new Set([".tsx", ".jsx", ".ts", ".js"])
282
- const tsxIgnoreDirs = new Set(["node_modules", "dist", "lib", ".pyreon", ".git", "build"])
283
-
284
- function shouldSkipEntry(entry: fs.Dirent): boolean {
285
- if (!entry.isDirectory()) return false
286
- return entry.name.startsWith(".") || tsxIgnoreDirs.has(entry.name)
287
- }
288
-
289
- function walkTsxFiles(dir: string, results: string[]): void {
290
- let entries: fs.Dirent[]
291
- try {
292
- entries = fs.readdirSync(dir, { withFileTypes: true })
293
- } catch {
294
- return
295
- }
296
-
297
- for (const entry of entries) {
298
- if (shouldSkipEntry(entry)) continue
299
-
300
- const fullPath = path.join(dir, entry.name)
301
- if (entry.isDirectory()) {
302
- walkTsxFiles(fullPath, results)
303
- } else if (entry.isFile() && tsxExtensions.has(path.extname(entry.name))) {
304
- results.push(fullPath)
305
- }
306
- }
307
- }
308
-
309
- function collectTsxFiles(cwd: string): string[] {
310
- const results: string[] = []
311
- walkTsxFiles(cwd, results)
312
- return results
313
- }
314
-
315
42
  function ensureGitignore(cwd: string): void {
316
43
  const gitignorePath = path.join(cwd, ".gitignore")
317
44
  try {