gmoonc 0.0.2 → 0.0.4

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 (54) hide show
  1. package/README.md +52 -11
  2. package/dist/index.cjs +618 -0
  3. package/package.json +29 -6
  4. package/scripts/block-global-install.cjs +18 -0
  5. package/scripts/sync-templates.mjs +507 -0
  6. package/src/templates/shared/src/gmoonc/app/menu/defaultMenu.ts +91 -0
  7. package/src/templates/shared/src/gmoonc/components/GMooncMensagemForm.tsx +220 -0
  8. package/src/templates/shared/src/gmoonc/components/GMooncMensagensManager.tsx +562 -0
  9. package/src/templates/shared/src/gmoonc/components/GMooncMensagensTecnicasManager.tsx +963 -0
  10. package/src/templates/shared/src/gmoonc/components/GMooncPermissionsManager.tsx +837 -0
  11. package/src/templates/shared/src/gmoonc/components/GMooncUserProfile.tsx +294 -0
  12. package/src/templates/shared/src/gmoonc/config/defaultConfig.ts +11 -0
  13. package/src/templates/shared/src/gmoonc/core/menu.ts +5 -0
  14. package/src/templates/shared/src/gmoonc/core/types.ts +17 -0
  15. package/src/templates/shared/src/gmoonc/features/authorizations/GMooncAuthorizationsManager.tsx +540 -0
  16. package/src/templates/shared/src/gmoonc/features/notifications/GMooncNotificationsManager.tsx +1454 -0
  17. package/src/templates/shared/src/gmoonc/features/users/GMooncUserEdit.tsx +477 -0
  18. package/src/templates/shared/src/gmoonc/features/users/GMooncUserManagement.tsx +643 -0
  19. package/src/templates/shared/src/gmoonc/hooks/useGMooncAuthorizations.ts +140 -0
  20. package/src/templates/shared/src/gmoonc/hooks/useGMooncMensagens.ts +149 -0
  21. package/src/templates/shared/src/gmoonc/hooks/useGMooncMensagensTecnicas.ts +232 -0
  22. package/src/templates/shared/src/gmoonc/hooks/useGMooncNotifications.ts +284 -0
  23. package/src/templates/shared/src/gmoonc/hooks/useGMooncPermissions.ts +26 -0
  24. package/src/templates/shared/src/gmoonc/hooks/useGMooncUsers.ts +167 -0
  25. package/src/templates/shared/src/gmoonc/index.ts +45 -0
  26. package/src/templates/shared/src/gmoonc/layout/GMooncAppLayout.tsx +66 -0
  27. package/src/templates/shared/src/gmoonc/pages/app/GMooncAppHomePage.tsx +38 -0
  28. package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncAdminAuthorizationsPage.tsx +15 -0
  29. package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncAdminNotificationsPage.tsx +15 -0
  30. package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncAdminUsersPage.tsx +15 -0
  31. package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncPermissionsPage.tsx +15 -0
  32. package/src/templates/shared/src/gmoonc/pages/app/customer/GMooncCustomerMessagesPage.tsx +15 -0
  33. package/src/templates/shared/src/gmoonc/pages/app/office/GMooncOfficeAboutPage.tsx +211 -0
  34. package/src/templates/shared/src/gmoonc/pages/app/office/GMooncOfficeAccountPage.tsx +168 -0
  35. package/src/templates/shared/src/gmoonc/pages/app/technical/GMooncTechnicalMessagesPage.tsx +15 -0
  36. package/src/templates/shared/src/gmoonc/pages/auth/GMooncForgotPasswordPage.tsx +93 -0
  37. package/src/templates/shared/src/gmoonc/pages/auth/GMooncLoginPage.tsx +91 -0
  38. package/src/templates/shared/src/gmoonc/pages/auth/GMooncLogoutPage.tsx +13 -0
  39. package/src/templates/shared/src/gmoonc/pages/auth/GMooncRegisterPage.tsx +141 -0
  40. package/src/templates/shared/src/gmoonc/pages/auth/GMooncResetPasswordPage.tsx +114 -0
  41. package/src/templates/shared/src/gmoonc/routes/GmooncRoutes.tsx +115 -0
  42. package/src/templates/shared/src/gmoonc/routes/createGmooncRoutes.tsx +103 -0
  43. package/src/templates/shared/src/gmoonc/session/GMooncSessionContext.tsx +70 -0
  44. package/src/templates/shared/src/gmoonc/styles/app.css +394 -0
  45. package/src/templates/shared/src/gmoonc/styles/gmoonc.css +394 -0
  46. package/src/templates/shared/src/gmoonc/styles/theme.css +101 -0
  47. package/src/templates/shared/src/gmoonc/types/mensagens.ts +44 -0
  48. package/src/templates/shared/src/gmoonc/ui/header.tsx +30 -0
  49. package/src/templates/shared/src/gmoonc/ui/menu.tsx +537 -0
  50. package/src/templates/shared/src/gmoonc/ui/shell.tsx +128 -0
  51. package/src/templates/shared/src/gmoonc/ui/sidebar.tsx +20 -0
  52. package/src/templates/shared/src/gmoonc/ui/styles.css +510 -0
  53. package/src/templates/vite/src/gmoonc/router/createGmooncRoutes.tsx +103 -0
  54. package/index.js +0 -6
package/README.md CHANGED
@@ -1,18 +1,59 @@
1
- # Goalmoon Ctrl (gmoonc)
1
+ # gmoonc
2
2
 
3
- Goalmoon Ctrl é um kit plugável para construir dashboards com autenticação e RBAC, compatível com Next e Vite.
3
+ Goalmoon Ctrl (gmoonc): Complete dashboard installer for React projects.
4
4
 
5
- ## Pacotes reais
6
- - **@gmoonc/core**
5
+ ## Installation
7
6
 
8
- ## Instalação
9
7
  ```bash
10
- npm i @gmoonc/core
8
+ npm install gmoonc
11
9
  ```
12
10
 
13
- ## Links
14
- - Site: https://gmoonc.com
15
- - GitHub: https://github.com/gmoonc/gmoonc-packages
11
+ ## Usage
16
12
 
17
- ## Nota
18
- O pacote "gmoonc" é um meta package e aponta para os módulos reais em @gmoonc/*.
13
+ Run the installer in your React project root:
14
+
15
+ ```bash
16
+ npx gmoonc
17
+ ```
18
+
19
+ Or with options:
20
+
21
+ ```bash
22
+ npx gmoonc --yes --base /app
23
+ ```
24
+
25
+ ## What it does
26
+
27
+ 1. **Copies dashboard templates** to `src/gmoonc/` in your project
28
+ 2. **Installs required dependencies** (react-router-dom, lucide-react, etc.)
29
+ 3. **Injects CSS** into your entrypoint (`src/main.tsx` or similar)
30
+ 4. **Patches your router** (BrowserRouter) to integrate gmoonc routes
31
+
32
+ ## Options
33
+
34
+ - `--yes` / `-y`: Skip confirmations and install automatically
35
+ - `--base <path>`: Base path for dashboard routes (default: `/app`)
36
+ - `--skip-router-patch`: Skip automatic router integration
37
+ - `--dry-run`: Show what would be done without making changes
38
+
39
+ ## After installation
40
+
41
+ Your dashboard is now available at:
42
+ - Home: `/app`
43
+ - Admin: `/app/admin/*`
44
+ - Auth: `/login`, `/register`, etc.
45
+
46
+ The dashboard code is in `src/gmoonc/` and is independent. You can remove `gmoonc` from `package.json` if desired.
47
+
48
+ ## Uninstalling
49
+
50
+ To remove gmoonc:
51
+
52
+ 1. Remove the CSS import from your entrypoint
53
+ 2. Revert changes to `App.tsx` (or restore from backup)
54
+ 3. Delete `src/gmoonc/` directory
55
+ 4. Remove `gmoonc` from `package.json`
56
+
57
+ ## License
58
+
59
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,618 @@
1
+ "use strict";
2
+
3
+ // src/cli/index.ts
4
+ var import_commander = require("commander");
5
+ var import_process = require("process");
6
+ var import_path6 = require("path");
7
+
8
+ // src/cli/lib/detect.ts
9
+ var import_fs = require("fs");
10
+ var import_path = require("path");
11
+ var ENTRYPOINT_CANDIDATES = [
12
+ "src/main.tsx",
13
+ "src/main.jsx",
14
+ "src/main.ts",
15
+ "src/main.js"
16
+ ];
17
+ var ROUTER_CANDIDATES = [
18
+ "src/App.tsx",
19
+ "src/App.jsx",
20
+ "src/App.ts",
21
+ "src/App.js"
22
+ ];
23
+ function detectPackageManager(cwd2) {
24
+ if ((0, import_fs.existsSync)((0, import_path.join)(cwd2, "pnpm-lock.yaml"))) {
25
+ return "pnpm";
26
+ }
27
+ if ((0, import_fs.existsSync)((0, import_path.join)(cwd2, "yarn.lock"))) {
28
+ return "yarn";
29
+ }
30
+ if ((0, import_fs.existsSync)((0, import_path.join)(cwd2, "package-lock.json"))) {
31
+ return "npm";
32
+ }
33
+ return "npm";
34
+ }
35
+ function findEntrypoint(cwd2) {
36
+ for (const candidate of ENTRYPOINT_CANDIDATES) {
37
+ const path = (0, import_path.join)(cwd2, candidate);
38
+ if ((0, import_fs.existsSync)(path)) {
39
+ return candidate;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+ function findRouterFile(cwd2) {
45
+ for (const candidate of ROUTER_CANDIDATES) {
46
+ const path = (0, import_path.join)(cwd2, candidate);
47
+ if ((0, import_fs.existsSync)(path)) {
48
+ const content = (0, import_fs.readFileSync)(path, "utf-8");
49
+ if (content.includes("<BrowserRouter") && content.includes("<Routes") && content.includes("<Route")) {
50
+ return candidate;
51
+ }
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+ function detectProject(cwd2) {
57
+ const packageJsonPath = (0, import_path.join)(cwd2, "package.json");
58
+ if (!(0, import_fs.existsSync)(packageJsonPath)) {
59
+ throw new Error(
60
+ "package.json not found. Make sure you run this command in the root of your React project."
61
+ );
62
+ }
63
+ const packageManager = detectPackageManager(cwd2);
64
+ const entrypoint = findEntrypoint(cwd2);
65
+ const routerFile = findRouterFile(cwd2);
66
+ return {
67
+ packageManager,
68
+ entrypoint,
69
+ routerFile,
70
+ packageJsonPath
71
+ };
72
+ }
73
+
74
+ // src/cli/lib/installDeps.ts
75
+ var import_child_process = require("child_process");
76
+ var import_fs2 = require("fs");
77
+ var import_path2 = require("path");
78
+
79
+ // src/cli/lib/logger.ts
80
+ function logSuccess(message) {
81
+ console.log(`\u2713 ${message}`);
82
+ }
83
+ function logError(message) {
84
+ console.error(`\u274C ${message}`);
85
+ }
86
+ function logInfo(message) {
87
+ console.log(`\u2139\uFE0F ${message}`);
88
+ }
89
+ function logWarning(message) {
90
+ console.warn(`\u26A0\uFE0F ${message}`);
91
+ }
92
+
93
+ // src/cli/lib/installDeps.ts
94
+ var REQUIRED_DEPS = [
95
+ "react-router-dom@^6.0.0",
96
+ "lucide-react@^0.303.0"
97
+ ];
98
+ function collectDependenciesFromTemplates(templatesDir) {
99
+ return REQUIRED_DEPS;
100
+ }
101
+ function installDependencies(project, templatesDir, dryRun) {
102
+ const projectDir = (0, import_path2.join)(project.packageJsonPath, "..");
103
+ let existingDeps = {};
104
+ if ((0, import_fs2.existsSync)(project.packageJsonPath)) {
105
+ try {
106
+ const packageJson = JSON.parse((0, import_fs2.readFileSync)(project.packageJsonPath, "utf-8"));
107
+ existingDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
108
+ } catch (error) {
109
+ }
110
+ }
111
+ const requiredDeps = collectDependenciesFromTemplates(templatesDir);
112
+ const packagesToInstall = [];
113
+ for (const dep of requiredDeps) {
114
+ const depName = dep.split("@")[0];
115
+ if (!existingDeps[depName]) {
116
+ packagesToInstall.push(dep);
117
+ }
118
+ }
119
+ if (packagesToInstall.length === 0) {
120
+ logSuccess("All required dependencies already installed");
121
+ return { success: true };
122
+ }
123
+ if (dryRun) {
124
+ logInfo(`[DRY RUN] Would install: ${packagesToInstall.join(", ")}`);
125
+ return { success: true };
126
+ }
127
+ const installCmd = project.packageManager === "pnpm" ? `pnpm add ${packagesToInstall.join(" ")}` : project.packageManager === "yarn" ? `yarn add ${packagesToInstall.join(" ")}` : `npm install ${packagesToInstall.join(" ")}`;
128
+ try {
129
+ logInfo(`Installing dependencies: ${packagesToInstall.join(", ")}`);
130
+ (0, import_child_process.execSync)(installCmd, {
131
+ stdio: "inherit",
132
+ cwd: projectDir
133
+ });
134
+ logSuccess("Dependencies installed");
135
+ return { success: true };
136
+ } catch (error) {
137
+ logError("Failed to install dependencies");
138
+ logError(`Try installing manually: ${installCmd}`);
139
+ return { success: false };
140
+ }
141
+ }
142
+
143
+ // src/cli/lib/templates.ts
144
+ var import_path4 = require("path");
145
+ var import_fs4 = require("fs");
146
+
147
+ // src/cli/lib/fs.ts
148
+ var import_fs3 = require("fs");
149
+ var import_path3 = require("path");
150
+ function ensureDirectoryExists(filePath) {
151
+ const dir = (0, import_path3.dirname)(filePath);
152
+ if (!(0, import_fs3.existsSync)(dir)) {
153
+ (0, import_fs3.mkdirSync)(dir, { recursive: true });
154
+ }
155
+ }
156
+ function createBackupIfExists(filePath) {
157
+ if (!(0, import_fs3.existsSync)(filePath)) {
158
+ return null;
159
+ }
160
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "").slice(0, 15);
161
+ const ext = filePath.match(/\.[^.]+$/)?.[0] || "";
162
+ const base = filePath.replace(ext, "");
163
+ const backupPath = `${base}.gmoonc.bak-${timestamp}${ext}`;
164
+ const content = (0, import_fs3.readFileSync)(filePath, "utf-8");
165
+ (0, import_fs3.writeFileSync)(backupPath, content, "utf-8");
166
+ return backupPath;
167
+ }
168
+ function writeFileSafe(filePath, content) {
169
+ ensureDirectoryExists(filePath);
170
+ const backupPath = createBackupIfExists(filePath);
171
+ (0, import_fs3.writeFileSync)(filePath, content, "utf-8");
172
+ return backupPath;
173
+ }
174
+ function readFile(filePath) {
175
+ return (0, import_fs3.readFileSync)(filePath, "utf-8");
176
+ }
177
+ function copyDirectoryRecursive(src, dest) {
178
+ if (!(0, import_fs3.existsSync)(src)) {
179
+ return;
180
+ }
181
+ const stats = (0, import_fs3.statSync)(src);
182
+ if (!stats.isDirectory()) {
183
+ return;
184
+ }
185
+ ensureDirectoryExists(dest);
186
+ const entries = (0, import_fs3.readdirSync)(src, { withFileTypes: true });
187
+ for (const entry of entries) {
188
+ const srcPath = (0, import_path3.join)(src, entry.name);
189
+ const destPath = (0, import_path3.join)(dest, entry.name);
190
+ if (entry.isDirectory()) {
191
+ copyDirectoryRecursive(srcPath, destPath);
192
+ } else {
193
+ ensureDirectoryExists(destPath);
194
+ (0, import_fs3.copyFileSync)(srcPath, destPath);
195
+ }
196
+ }
197
+ }
198
+
199
+ // src/cli/lib/templates.ts
200
+ function getTemplatesDir() {
201
+ const possiblePaths = [
202
+ (0, import_path4.join)(process.cwd(), "node_modules/gmoonc/src/templates"),
203
+ (0, import_path4.join)(process.cwd(), "packages/gmoonc/src/templates"),
204
+ (0, import_path4.join)(__dirname, "../../../src/templates"),
205
+ // From dist/cli/lib
206
+ (0, import_path4.join)(__dirname, "../../templates")
207
+ // From src/cli/lib (dev)
208
+ ];
209
+ for (const path of possiblePaths) {
210
+ if ((0, import_fs4.existsSync)(path)) {
211
+ return path;
212
+ }
213
+ }
214
+ return (0, import_path4.join)(__dirname, "../../templates");
215
+ }
216
+ function copySharedTemplates(consumerDir, templatesDir, dryRun) {
217
+ const sharedTemplatesDir = (0, import_path4.join)(templatesDir, "shared/src/gmoonc");
218
+ const destDir = (0, import_path4.join)(consumerDir, "src/gmoonc");
219
+ if (dryRun) {
220
+ logInfo(`[DRY RUN] Would copy templates from ${sharedTemplatesDir} to ${destDir}`);
221
+ return { success: true };
222
+ }
223
+ logInfo("Copying dashboard templates...");
224
+ copyDirectoryRecursive(sharedTemplatesDir, destDir);
225
+ logSuccess("Templates copied to src/gmoonc/");
226
+ return { success: true };
227
+ }
228
+ function copyViteRouterTemplates(consumerDir, templatesDir, dryRun) {
229
+ const viteTemplatesDir = (0, import_path4.join)(templatesDir, "vite/src/gmoonc/router");
230
+ const destDir = (0, import_path4.join)(consumerDir, "src/gmoonc/router");
231
+ if (dryRun) {
232
+ logInfo(`[DRY RUN] Would copy router templates from ${viteTemplatesDir} to ${destDir}`);
233
+ return { success: true };
234
+ }
235
+ logInfo("Copying router templates...");
236
+ copyDirectoryRecursive(viteTemplatesDir, destDir);
237
+ logSuccess("Router templates copied");
238
+ return { success: true };
239
+ }
240
+
241
+ // src/cli/lib/patchEntryCss.ts
242
+ function patchEntryCss(entrypointPath, dryRun) {
243
+ if (dryRun) {
244
+ return { success: true, backupPath: null };
245
+ }
246
+ const content = readFile(entrypointPath);
247
+ const hasThemeCss = content.includes("./gmoonc/styles/theme.css") || content.includes("gmoonc/styles/theme.css");
248
+ const hasGmooncCss = content.includes("./gmoonc/styles/gmoonc.css") || content.includes("gmoonc/styles/gmoonc.css");
249
+ if (hasThemeCss && hasGmooncCss) {
250
+ logSuccess("CSS imports already exist");
251
+ return { success: true, backupPath: null };
252
+ }
253
+ const importRegex = /^import\s+.*$/gm;
254
+ const imports = content.match(importRegex) || [];
255
+ let newContent = content;
256
+ const cssImports = [];
257
+ if (!hasThemeCss) {
258
+ cssImports.push('import "./gmoonc/styles/theme.css";');
259
+ }
260
+ if (!hasGmooncCss) {
261
+ cssImports.push('import "./gmoonc/styles/gmoonc.css";');
262
+ }
263
+ if (imports.length > 0) {
264
+ const lastImport = imports[imports.length - 1];
265
+ const lastImportIndex = content.lastIndexOf(lastImport);
266
+ const insertIndex = lastImportIndex + lastImport.length;
267
+ newContent = content.slice(0, insertIndex) + "\n" + cssImports.join("\n") + content.slice(insertIndex);
268
+ } else {
269
+ newContent = cssImports.join("\n") + "\n" + content;
270
+ }
271
+ const backupPath = writeFileSafe(entrypointPath, newContent);
272
+ if (backupPath) {
273
+ logSuccess(`CSS import added (backup: ${backupPath})`);
274
+ } else {
275
+ logSuccess("CSS import added");
276
+ }
277
+ return { success: true, backupPath };
278
+ }
279
+
280
+ // src/cli/lib/patchBrowserRouter.ts
281
+ var import_fs8 = require("fs");
282
+ var import_path5 = require("path");
283
+ function extractRouteComponents(appContent) {
284
+ const indexRouteMatch = appContent.match(/<Route\s+path=["']\/["']\s+element=\{<(\w+)\s*\/?>\}\s*\/?>/);
285
+ const indexComponent = indexRouteMatch ? indexRouteMatch[1] : null;
286
+ const notFoundRouteMatch = appContent.match(/<Route\s+path=["']\*["']\s+element=\{<(\w+)\s*\/?>\}\s*\/?>/);
287
+ const notFoundComponent = notFoundRouteMatch ? notFoundRouteMatch[1] : null;
288
+ let indexImport = null;
289
+ let notFoundImport = null;
290
+ const lines = appContent.split("\n");
291
+ if (indexComponent) {
292
+ for (const line of lines) {
293
+ const trimmed = line.trim();
294
+ if (trimmed.startsWith("import")) {
295
+ const defaultImportMatch = trimmed.match(/import\s+(\w+)\s+from\s+["']([^"']+)["']/);
296
+ if (defaultImportMatch && defaultImportMatch[1] === indexComponent) {
297
+ indexImport = trimmed;
298
+ break;
299
+ }
300
+ const namedImportMatch = trimmed.match(/import\s+\{[^}]*\b(\w+)\b[^}]*\}\s+from\s+["']([^"']+)["']/);
301
+ if (namedImportMatch && namedImportMatch[1] === indexComponent) {
302
+ indexImport = trimmed;
303
+ break;
304
+ }
305
+ }
306
+ }
307
+ }
308
+ if (notFoundComponent) {
309
+ for (const line of lines) {
310
+ const trimmed = line.trim();
311
+ if (trimmed.startsWith("import")) {
312
+ const defaultImportMatch = trimmed.match(/import\s+(\w+)\s+from\s+["']([^"']+)["']/);
313
+ if (defaultImportMatch && defaultImportMatch[1] === notFoundComponent) {
314
+ notFoundImport = trimmed;
315
+ break;
316
+ }
317
+ const namedImportMatch = trimmed.match(/import\s+\{[^}]*\b(\w+)\b[^}]*\}\s+from\s+["']([^"']+)["']/);
318
+ if (namedImportMatch && namedImportMatch[1] === notFoundComponent) {
319
+ notFoundImport = trimmed;
320
+ break;
321
+ }
322
+ }
323
+ }
324
+ }
325
+ return { indexImport, notFoundImport, indexComponent, notFoundComponent };
326
+ }
327
+ function convertImportToRelative(importLine, appRoutesPath, appPath) {
328
+ const pathMatch = importLine.match(/from\s+["']([^"']+)["']/);
329
+ if (!pathMatch) {
330
+ return importLine;
331
+ }
332
+ const originalPath = pathMatch[1];
333
+ if (originalPath.startsWith("./") || originalPath.startsWith("../")) {
334
+ const appDir = appPath.substring(0, appPath.lastIndexOf("/"));
335
+ const appRoutesDir = appRoutesPath.substring(0, appRoutesPath.lastIndexOf("/"));
336
+ const resolvedPath = (0, import_path5.join)(appDir, originalPath);
337
+ const relativePath = (0, import_path5.relative)(appRoutesDir, resolvedPath);
338
+ const normalizedPath = relativePath.replace(/\\/g, "/");
339
+ const finalPath = normalizedPath.startsWith(".") ? normalizedPath : "./" + normalizedPath;
340
+ return importLine.replace(pathMatch[1], finalPath);
341
+ }
342
+ return importLine;
343
+ }
344
+ function generateAppRoutes(consumerDir, basePath, indexImport, notFoundImport, indexComponent, notFoundComponent, appPath, dryRun) {
345
+ const appRoutesPath = (0, import_path5.join)(consumerDir, "src/gmoonc/router/AppRoutes.tsx");
346
+ if (dryRun) {
347
+ logInfo(`[DRY RUN] Would generate ${appRoutesPath}`);
348
+ return { success: true };
349
+ }
350
+ ensureDirectoryExists(appRoutesPath);
351
+ const routes = [];
352
+ if (indexComponent) {
353
+ routes.push(` { path: "/", element: <${indexComponent} /> }`);
354
+ }
355
+ routes.push(` ...createGmooncRoutes({ basePath: "${basePath}" })`);
356
+ if (notFoundComponent) {
357
+ routes.push(` { path: "*", element: <${notFoundComponent} /> }`);
358
+ }
359
+ const imports = [
360
+ "import { useRoutes, type RouteObject } from 'react-router-dom';",
361
+ "import { createGmooncRoutes } from './createGmooncRoutes';"
362
+ ];
363
+ if (indexImport) {
364
+ const convertedImport = convertImportToRelative(indexImport, appRoutesPath, appPath);
365
+ imports.push(convertedImport);
366
+ }
367
+ if (notFoundImport) {
368
+ const convertedImport = convertImportToRelative(notFoundImport, appRoutesPath, appPath);
369
+ imports.push(convertedImport);
370
+ }
371
+ const content = `${imports.join("\n")}
372
+
373
+ export function GMooncAppRoutes({ basePath = "${basePath}" }: { basePath?: string }) {
374
+ const allRoutes: RouteObject[] = [
375
+ ${routes.join(",\n")}
376
+ ];
377
+
378
+ return useRoutes(allRoutes);
379
+ }
380
+ `;
381
+ writeFileSafe(appRoutesPath, content);
382
+ logSuccess("Generated src/gmoonc/router/AppRoutes.tsx");
383
+ return { success: true };
384
+ }
385
+ function patchBrowserRouter(consumerDir, basePath, dryRun) {
386
+ const appCandidates = ["src/App.tsx", "src/App.jsx", "src/App.ts", "src/App.js"];
387
+ let appPath = null;
388
+ for (const candidate of appCandidates) {
389
+ const fullPath = (0, import_path5.join)(consumerDir, candidate);
390
+ if ((0, import_fs8.existsSync)(fullPath)) {
391
+ appPath = fullPath;
392
+ break;
393
+ }
394
+ }
395
+ if (!appPath) {
396
+ return {
397
+ success: false,
398
+ backupPath: null,
399
+ message: "Could not find App.tsx, App.jsx, App.ts, or App.js"
400
+ };
401
+ }
402
+ const appContent = readFile(appPath);
403
+ if (appContent.includes("GMooncAppRoutes") || appContent.includes("gmooncAppRoutes")) {
404
+ return {
405
+ success: false,
406
+ backupPath: null,
407
+ message: "App.tsx already uses GMooncAppRoutes"
408
+ };
409
+ }
410
+ if (!appContent.includes("<BrowserRouter") || !appContent.includes("<Routes") || !appContent.includes("<Route")) {
411
+ return {
412
+ success: false,
413
+ backupPath: null,
414
+ message: "BrowserRouter pattern not found in App.tsx"
415
+ };
416
+ }
417
+ const { indexImport, notFoundImport, indexComponent, notFoundComponent } = extractRouteComponents(appContent);
418
+ if (!indexComponent && !notFoundComponent) {
419
+ return {
420
+ success: false,
421
+ backupPath: null,
422
+ message: 'Could not find Index or NotFound components in App.tsx. Please ensure you have <Route path="/" ...> and/or <Route path="*" ...>'
423
+ };
424
+ }
425
+ if (dryRun) {
426
+ logInfo(`[DRY RUN] Would patch ${appPath}`);
427
+ return { success: true, backupPath: null };
428
+ }
429
+ generateAppRoutes(consumerDir, basePath, indexImport, notFoundImport, indexComponent, notFoundComponent, appPath, false);
430
+ const backupPath = writeFileSafe(appPath, "");
431
+ const lines = appContent.split("\n");
432
+ let hasBrowserRouterImport = false;
433
+ let reactRouterImportIndex = -1;
434
+ for (let i = 0; i < lines.length; i++) {
435
+ const line = lines[i].trim();
436
+ if (line.includes("from") && line.includes("react-router-dom")) {
437
+ reactRouterImportIndex = i;
438
+ if (line.includes("BrowserRouter")) {
439
+ hasBrowserRouterImport = true;
440
+ }
441
+ }
442
+ }
443
+ if (!hasBrowserRouterImport) {
444
+ if (reactRouterImportIndex >= 0) {
445
+ const existingLine = lines[reactRouterImportIndex];
446
+ if (existingLine.includes("{") && existingLine.includes("}")) {
447
+ const updatedLine = existingLine.replace(/\{([^}]+)\}/, (match, imports) => {
448
+ const importList = imports.split(",").map((s) => s.trim());
449
+ if (!importList.includes("BrowserRouter")) {
450
+ importList.push("BrowserRouter");
451
+ }
452
+ return `{ ${importList.join(", ")} }`;
453
+ });
454
+ lines[reactRouterImportIndex] = updatedLine;
455
+ } else {
456
+ lines.splice(reactRouterImportIndex + 1, 0, `import { BrowserRouter } from "react-router-dom";`);
457
+ }
458
+ } else {
459
+ const importLine2 = `import { BrowserRouter } from "react-router-dom";`;
460
+ let lastImportIndex2 = -1;
461
+ for (let i = 0; i < lines.length; i++) {
462
+ const line = lines[i].trim();
463
+ if (line.startsWith("import ") || line.startsWith("import{") || line.startsWith("import ")) {
464
+ lastImportIndex2 = i;
465
+ } else if (line && lastImportIndex2 >= 0 && !line.startsWith("//") && !line.startsWith("/*")) {
466
+ break;
467
+ }
468
+ }
469
+ if (lastImportIndex2 >= 0) {
470
+ lines.splice(lastImportIndex2 + 1, 0, importLine2);
471
+ } else {
472
+ lines.unshift(importLine2);
473
+ }
474
+ }
475
+ }
476
+ const importLine = `import { GMooncAppRoutes } from "./gmoonc/router/AppRoutes";`;
477
+ let lastImportIndex = -1;
478
+ for (let i = 0; i < lines.length; i++) {
479
+ const line = lines[i].trim();
480
+ if (line.startsWith("import ") || line.startsWith("import{") || line.startsWith("import ")) {
481
+ lastImportIndex = i;
482
+ } else if (line && lastImportIndex >= 0 && !line.startsWith("//") && !line.startsWith("/*")) {
483
+ break;
484
+ }
485
+ }
486
+ if (lastImportIndex >= 0) {
487
+ lines.splice(lastImportIndex + 1, 0, importLine);
488
+ } else {
489
+ lines.unshift(importLine);
490
+ }
491
+ let newContent = lines.join("\n");
492
+ const routesStartMatch = newContent.match(/<Routes\s*[^>]*>/);
493
+ if (!routesStartMatch) {
494
+ return {
495
+ success: false,
496
+ backupPath,
497
+ message: "Could not find <Routes> tag"
498
+ };
499
+ }
500
+ const routesStartIndex = routesStartMatch.index;
501
+ let depth = 1;
502
+ let routesEndIndex = routesStartIndex + routesStartMatch[0].length;
503
+ for (let i = routesEndIndex; i < newContent.length; i++) {
504
+ if (newContent.substring(i).startsWith("<Routes")) {
505
+ depth++;
506
+ } else if (newContent.substring(i).startsWith("</Routes>")) {
507
+ depth--;
508
+ if (depth === 0) {
509
+ routesEndIndex = i + "</Routes>".length;
510
+ break;
511
+ }
512
+ }
513
+ }
514
+ if (depth !== 0) {
515
+ return {
516
+ success: false,
517
+ backupPath,
518
+ message: "Could not find matching </Routes> tag"
519
+ };
520
+ }
521
+ const routesLine = lines.find((line, idx) => {
522
+ const lineContent = line.trim();
523
+ return lineContent.startsWith("<Routes") && idx <= routesStartIndex;
524
+ });
525
+ const indent = routesLine ? routesLine.match(/^(\s*)/)?.[1] || " " : " ";
526
+ const before = newContent.substring(0, routesStartIndex);
527
+ const after = newContent.substring(routesEndIndex);
528
+ const replacement = `${indent}<GMooncAppRoutes basePath="${basePath}" />`;
529
+ newContent = before + replacement + after;
530
+ if (!newContent.match(/<Routes[^>]*>/g) && !newContent.match(/<Route[^>]*>/g)) {
531
+ newContent = newContent.replace(/import\s+{[^}]*Routes[^}]*}\s+from\s+["']react-router-dom["'];?\n?/g, "");
532
+ newContent = newContent.replace(/import\s+{[^}]*Route[^}]*}\s+from\s+["']react-router-dom["'];?\n?/g, "");
533
+ }
534
+ writeFileSafe(appPath, newContent);
535
+ logSuccess(`Patched ${appPath} (backup: ${backupPath})`);
536
+ return { success: true, backupPath };
537
+ }
538
+
539
+ // src/cli/index.ts
540
+ var program = new import_commander.Command();
541
+ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete dashboard into your React project").version("0.0.3").option("-y, --yes", "Skip confirmations and install automatically").option("--base <path>", "Base path for dashboard routes", "/app").option("--skip-router-patch", "Skip automatic router integration (only copy files and inject CSS)").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
542
+ try {
543
+ const projectDir = (0, import_process.cwd)();
544
+ const basePath = options.base || "/app";
545
+ const dryRun = options.dryRun || false;
546
+ const skipRouterPatch = options.skipRouterPatch || false;
547
+ const safeBasePath = basePath === "/" ? "/app" : basePath;
548
+ logInfo("\u{1F50D} Detecting React project...");
549
+ const project = detectProject(projectDir);
550
+ logSuccess(`Package manager: ${project.packageManager}`);
551
+ logSuccess("package.json found");
552
+ if (!project.entrypoint) {
553
+ logError("Entrypoint not found.");
554
+ logError("Looking for: src/main.tsx, src/main.jsx, src/main.ts, src/main.js");
555
+ process.exit(1);
556
+ }
557
+ logSuccess(`Entrypoint found: ${project.entrypoint}`);
558
+ if (!skipRouterPatch && !project.routerFile) {
559
+ logWarning("Router file not found or BrowserRouter pattern not detected.");
560
+ logWarning("Router patch will be skipped. You can integrate manually later.");
561
+ } else if (!skipRouterPatch) {
562
+ logSuccess(`Router file found: ${project.routerFile}`);
563
+ }
564
+ const templatesDir = getTemplatesDir();
565
+ logInfo(`Templates directory: ${templatesDir}`);
566
+ logInfo("\n\u{1F4E6} Installing dependencies...");
567
+ const depsResult = installDependencies(project, templatesDir, dryRun);
568
+ if (!depsResult.success && !dryRun) {
569
+ process.exit(1);
570
+ }
571
+ logInfo("\n\u{1F4CB} Copying dashboard templates...");
572
+ const sharedResult = copySharedTemplates(projectDir, templatesDir, dryRun);
573
+ if (!sharedResult.success) {
574
+ logError("Failed to copy shared templates");
575
+ process.exit(1);
576
+ }
577
+ const routerResult = copyViteRouterTemplates(projectDir, templatesDir, dryRun);
578
+ if (!routerResult.success) {
579
+ logError("Failed to copy router templates");
580
+ process.exit(1);
581
+ }
582
+ logInfo("\n\u{1F4DD} Injecting CSS imports...");
583
+ const entrypointPath = (0, import_path6.join)(projectDir, project.entrypoint);
584
+ const cssResult = patchEntryCss(entrypointPath, dryRun);
585
+ if (!cssResult.success && !dryRun) {
586
+ logError("Failed to inject CSS");
587
+ process.exit(1);
588
+ }
589
+ if (!skipRouterPatch && project.routerFile) {
590
+ logInfo("\n\u{1F527} Patching router...");
591
+ const routerPatchResult = patchBrowserRouter(projectDir, safeBasePath, dryRun);
592
+ if (!routerPatchResult.success && !dryRun) {
593
+ logWarning(`Router patch failed: ${routerPatchResult.message}`);
594
+ logWarning("You can integrate manually by importing GMooncAppRoutes in your App.tsx");
595
+ } else if (routerPatchResult.success) {
596
+ logSuccess("Router patched successfully");
597
+ }
598
+ } else if (skipRouterPatch) {
599
+ logInfo("\n\u23ED\uFE0F Skipping router patch (--skip-router-patch)");
600
+ } else {
601
+ logInfo("\n\u23ED\uFE0F Skipping router patch (router not detected)");
602
+ }
603
+ logSuccess("\n\u2705 Installation complete!");
604
+ logInfo("\nYour dashboard is now available at:");
605
+ logInfo(` - Home: ${safeBasePath}`);
606
+ logInfo(` - Admin: ${safeBasePath}/admin/*`);
607
+ logInfo(` - Auth: /login, /register, etc.`);
608
+ logInfo("\nYou can now remove gmoonc from package.json if desired.");
609
+ logInfo("The dashboard code is in src/gmoonc/ and is independent.");
610
+ } catch (error) {
611
+ logError(`Error: ${error.message}`);
612
+ if (error.stack && process.env.DEBUG) {
613
+ console.error(error.stack);
614
+ }
615
+ process.exit(1);
616
+ }
617
+ });
618
+ program.parse();