gmoonc 0.0.2 → 0.0.3
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 +52 -11
- package/dist/index.cjs +528 -0
- package/package.json +29 -6
- package/scripts/block-global-install.cjs +18 -0
- package/scripts/sync-templates.mjs +507 -0
- package/src/templates/shared/src/gmoonc/app/menu/defaultMenu.ts +91 -0
- package/src/templates/shared/src/gmoonc/components/GMooncMensagemForm.tsx +220 -0
- package/src/templates/shared/src/gmoonc/components/GMooncMensagensManager.tsx +562 -0
- package/src/templates/shared/src/gmoonc/components/GMooncMensagensTecnicasManager.tsx +963 -0
- package/src/templates/shared/src/gmoonc/components/GMooncPermissionsManager.tsx +837 -0
- package/src/templates/shared/src/gmoonc/components/GMooncUserProfile.tsx +294 -0
- package/src/templates/shared/src/gmoonc/config/defaultConfig.ts +11 -0
- package/src/templates/shared/src/gmoonc/core/menu.ts +5 -0
- package/src/templates/shared/src/gmoonc/core/types.ts +17 -0
- package/src/templates/shared/src/gmoonc/features/authorizations/GMooncAuthorizationsManager.tsx +540 -0
- package/src/templates/shared/src/gmoonc/features/notifications/GMooncNotificationsManager.tsx +1454 -0
- package/src/templates/shared/src/gmoonc/features/users/GMooncUserEdit.tsx +477 -0
- package/src/templates/shared/src/gmoonc/features/users/GMooncUserManagement.tsx +643 -0
- package/src/templates/shared/src/gmoonc/hooks/useGMooncAuthorizations.ts +140 -0
- package/src/templates/shared/src/gmoonc/hooks/useGMooncMensagens.ts +149 -0
- package/src/templates/shared/src/gmoonc/hooks/useGMooncMensagensTecnicas.ts +232 -0
- package/src/templates/shared/src/gmoonc/hooks/useGMooncNotifications.ts +284 -0
- package/src/templates/shared/src/gmoonc/hooks/useGMooncPermissions.ts +26 -0
- package/src/templates/shared/src/gmoonc/hooks/useGMooncUsers.ts +167 -0
- package/src/templates/shared/src/gmoonc/index.ts +45 -0
- package/src/templates/shared/src/gmoonc/layout/GMooncAppLayout.tsx +63 -0
- package/src/templates/shared/src/gmoonc/pages/app/GMooncAppHomePage.tsx +38 -0
- package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncAdminAuthorizationsPage.tsx +15 -0
- package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncAdminNotificationsPage.tsx +15 -0
- package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncAdminUsersPage.tsx +15 -0
- package/src/templates/shared/src/gmoonc/pages/app/admin/GMooncPermissionsPage.tsx +15 -0
- package/src/templates/shared/src/gmoonc/pages/app/customer/GMooncCustomerMessagesPage.tsx +15 -0
- package/src/templates/shared/src/gmoonc/pages/app/office/GMooncOfficeAboutPage.tsx +211 -0
- package/src/templates/shared/src/gmoonc/pages/app/office/GMooncOfficeAccountPage.tsx +168 -0
- package/src/templates/shared/src/gmoonc/pages/app/technical/GMooncTechnicalMessagesPage.tsx +15 -0
- package/src/templates/shared/src/gmoonc/pages/auth/GMooncForgotPasswordPage.tsx +93 -0
- package/src/templates/shared/src/gmoonc/pages/auth/GMooncLoginPage.tsx +91 -0
- package/src/templates/shared/src/gmoonc/pages/auth/GMooncLogoutPage.tsx +13 -0
- package/src/templates/shared/src/gmoonc/pages/auth/GMooncRegisterPage.tsx +141 -0
- package/src/templates/shared/src/gmoonc/pages/auth/GMooncResetPasswordPage.tsx +114 -0
- package/src/templates/shared/src/gmoonc/routes/GmooncRoutes.tsx +115 -0
- package/src/templates/shared/src/gmoonc/routes/createGmooncRoutes.tsx +103 -0
- package/src/templates/shared/src/gmoonc/session/GMooncSessionContext.tsx +70 -0
- package/src/templates/shared/src/gmoonc/styles/app.css +394 -0
- package/src/templates/shared/src/gmoonc/styles/gmoonc.css +394 -0
- package/src/templates/shared/src/gmoonc/types/mensagens.ts +44 -0
- package/src/templates/shared/src/gmoonc/ui/header.tsx +30 -0
- package/src/templates/shared/src/gmoonc/ui/menu.tsx +533 -0
- package/src/templates/shared/src/gmoonc/ui/shell.tsx +128 -0
- package/src/templates/shared/src/gmoonc/ui/sidebar.tsx +20 -0
- package/src/templates/shared/src/gmoonc/ui/styles.css +510 -0
- package/src/templates/vite/src/gmoonc/router/createGmooncRoutes.tsx +103 -0
- package/index.js +0 -6
package/README.md
CHANGED
|
@@ -1,18 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# gmoonc
|
|
2
2
|
|
|
3
|
-
Goalmoon Ctrl
|
|
3
|
+
Goalmoon Ctrl (gmoonc): Complete dashboard installer for React projects.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
- **@gmoonc/core**
|
|
5
|
+
## Installation
|
|
7
6
|
|
|
8
|
-
## Instalação
|
|
9
7
|
```bash
|
|
10
|
-
npm
|
|
8
|
+
npm install gmoonc
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
14
|
-
- Site: https://gmoonc.com
|
|
15
|
-
- GitHub: https://github.com/gmoonc/gmoonc-packages
|
|
11
|
+
## Usage
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
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,528 @@
|
|
|
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
|
+
if (content.includes("./gmoonc/styles/gmoonc.css") || content.includes("gmoonc/styles/gmoonc.css")) {
|
|
248
|
+
logSuccess("CSS import already exists");
|
|
249
|
+
return { success: true, backupPath: null };
|
|
250
|
+
}
|
|
251
|
+
const importRegex = /^import\s+.*$/gm;
|
|
252
|
+
const imports = content.match(importRegex) || [];
|
|
253
|
+
let newContent = content;
|
|
254
|
+
if (imports.length > 0) {
|
|
255
|
+
const lastImport = imports[imports.length - 1];
|
|
256
|
+
const lastImportIndex = content.lastIndexOf(lastImport);
|
|
257
|
+
const insertIndex = lastImportIndex + lastImport.length;
|
|
258
|
+
newContent = content.slice(0, insertIndex) + '\nimport "./gmoonc/styles/gmoonc.css";' + content.slice(insertIndex);
|
|
259
|
+
} else {
|
|
260
|
+
newContent = 'import "./gmoonc/styles/gmoonc.css";\n' + content;
|
|
261
|
+
}
|
|
262
|
+
const backupPath = writeFileSafe(entrypointPath, newContent);
|
|
263
|
+
if (backupPath) {
|
|
264
|
+
logSuccess(`CSS import added (backup: ${backupPath})`);
|
|
265
|
+
} else {
|
|
266
|
+
logSuccess("CSS import added");
|
|
267
|
+
}
|
|
268
|
+
return { success: true, backupPath };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/cli/lib/patchBrowserRouter.ts
|
|
272
|
+
var import_fs8 = require("fs");
|
|
273
|
+
var import_path5 = require("path");
|
|
274
|
+
function extractRouteComponents(appContent) {
|
|
275
|
+
const indexRouteMatch = appContent.match(/<Route\s+path=["']\/["']\s+element=\{<(\w+)\s*\/?>\}\s*\/?>/);
|
|
276
|
+
const indexComponent = indexRouteMatch ? indexRouteMatch[1] : null;
|
|
277
|
+
const notFoundRouteMatch = appContent.match(/<Route\s+path=["']\*["']\s+element=\{<(\w+)\s*\/?>\}\s*\/?>/);
|
|
278
|
+
const notFoundComponent = notFoundRouteMatch ? notFoundRouteMatch[1] : null;
|
|
279
|
+
let indexImport = null;
|
|
280
|
+
let notFoundImport = null;
|
|
281
|
+
const lines = appContent.split("\n");
|
|
282
|
+
if (indexComponent) {
|
|
283
|
+
for (const line of lines) {
|
|
284
|
+
if (line.includes("import") && line.includes(indexComponent)) {
|
|
285
|
+
indexImport = line.trim();
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (notFoundComponent) {
|
|
291
|
+
for (const line of lines) {
|
|
292
|
+
if (line.includes("import") && line.includes(notFoundComponent)) {
|
|
293
|
+
notFoundImport = line.trim();
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { indexImport, notFoundImport, indexComponent, notFoundComponent };
|
|
299
|
+
}
|
|
300
|
+
function generateAppRoutes(consumerDir, basePath, indexImport, notFoundImport, indexComponent, notFoundComponent, dryRun) {
|
|
301
|
+
const appRoutesPath = (0, import_path5.join)(consumerDir, "src/gmoonc/router/AppRoutes.tsx");
|
|
302
|
+
if (dryRun) {
|
|
303
|
+
logInfo(`[DRY RUN] Would generate ${appRoutesPath}`);
|
|
304
|
+
return { success: true };
|
|
305
|
+
}
|
|
306
|
+
ensureDirectoryExists(appRoutesPath);
|
|
307
|
+
const routes = [];
|
|
308
|
+
if (indexComponent) {
|
|
309
|
+
routes.push(` { path: "/", element: <${indexComponent} /> }`);
|
|
310
|
+
}
|
|
311
|
+
routes.push(` ...createGmooncRoutes({ basePath: "${basePath}" })`);
|
|
312
|
+
if (notFoundComponent) {
|
|
313
|
+
routes.push(` { path: "*", element: <${notFoundComponent} /> }`);
|
|
314
|
+
}
|
|
315
|
+
const imports = [
|
|
316
|
+
"import { useRoutes } from 'react-router-dom';",
|
|
317
|
+
"import { createGmooncRoutes } from './createGmooncRoutes';"
|
|
318
|
+
];
|
|
319
|
+
if (indexImport) {
|
|
320
|
+
imports.push(indexImport);
|
|
321
|
+
}
|
|
322
|
+
if (notFoundImport) {
|
|
323
|
+
imports.push(notFoundImport);
|
|
324
|
+
}
|
|
325
|
+
const content = `${imports.join("\n")}
|
|
326
|
+
|
|
327
|
+
export function GMooncAppRoutes({ basePath = "${basePath}" }: { basePath?: string }) {
|
|
328
|
+
const allRoutes = [
|
|
329
|
+
${routes.join(",\n")}
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
return useRoutes(allRoutes);
|
|
333
|
+
}
|
|
334
|
+
`;
|
|
335
|
+
writeFileSafe(appRoutesPath, content);
|
|
336
|
+
logSuccess("Generated src/gmoonc/router/AppRoutes.tsx");
|
|
337
|
+
return { success: true };
|
|
338
|
+
}
|
|
339
|
+
function patchBrowserRouter(consumerDir, basePath, dryRun) {
|
|
340
|
+
const appCandidates = ["src/App.tsx", "src/App.jsx", "src/App.ts", "src/App.js"];
|
|
341
|
+
let appPath = null;
|
|
342
|
+
for (const candidate of appCandidates) {
|
|
343
|
+
const fullPath = (0, import_path5.join)(consumerDir, candidate);
|
|
344
|
+
if ((0, import_fs8.existsSync)(fullPath)) {
|
|
345
|
+
appPath = fullPath;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (!appPath) {
|
|
350
|
+
return {
|
|
351
|
+
success: false,
|
|
352
|
+
backupPath: null,
|
|
353
|
+
message: "Could not find App.tsx, App.jsx, App.ts, or App.js"
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const appContent = readFile(appPath);
|
|
357
|
+
if (appContent.includes("GMooncAppRoutes") || appContent.includes("gmooncAppRoutes")) {
|
|
358
|
+
return {
|
|
359
|
+
success: false,
|
|
360
|
+
backupPath: null,
|
|
361
|
+
message: "App.tsx already uses GMooncAppRoutes"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (!appContent.includes("<BrowserRouter") || !appContent.includes("<Routes") || !appContent.includes("<Route")) {
|
|
365
|
+
return {
|
|
366
|
+
success: false,
|
|
367
|
+
backupPath: null,
|
|
368
|
+
message: "BrowserRouter pattern not found in App.tsx"
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const { indexImport, notFoundImport, indexComponent, notFoundComponent } = extractRouteComponents(appContent);
|
|
372
|
+
if (!indexComponent && !notFoundComponent) {
|
|
373
|
+
return {
|
|
374
|
+
success: false,
|
|
375
|
+
backupPath: null,
|
|
376
|
+
message: 'Could not find Index or NotFound components in App.tsx. Please ensure you have <Route path="/" ...> and/or <Route path="*" ...>'
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
if (dryRun) {
|
|
380
|
+
logInfo(`[DRY RUN] Would patch ${appPath}`);
|
|
381
|
+
return { success: true, backupPath: null };
|
|
382
|
+
}
|
|
383
|
+
generateAppRoutes(consumerDir, basePath, indexImport, notFoundImport, indexComponent, notFoundComponent, false);
|
|
384
|
+
const backupPath = writeFileSafe(appPath, "");
|
|
385
|
+
const importLine = `import { GMooncAppRoutes } from "./gmoonc/router/AppRoutes";`;
|
|
386
|
+
const lines = appContent.split("\n");
|
|
387
|
+
let lastImportIndex = -1;
|
|
388
|
+
for (let i = 0; i < lines.length; i++) {
|
|
389
|
+
const line = lines[i].trim();
|
|
390
|
+
if (line.startsWith("import ") || line.startsWith("import{") || line.startsWith("import ")) {
|
|
391
|
+
lastImportIndex = i;
|
|
392
|
+
} else if (line && lastImportIndex >= 0 && !line.startsWith("//") && !line.startsWith("/*")) {
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (lastImportIndex >= 0) {
|
|
397
|
+
lines.splice(lastImportIndex + 1, 0, importLine);
|
|
398
|
+
} else {
|
|
399
|
+
lines.unshift(importLine);
|
|
400
|
+
}
|
|
401
|
+
let newContent = lines.join("\n");
|
|
402
|
+
const routesStartMatch = newContent.match(/<Routes\s*[^>]*>/);
|
|
403
|
+
if (!routesStartMatch) {
|
|
404
|
+
return {
|
|
405
|
+
success: false,
|
|
406
|
+
backupPath,
|
|
407
|
+
message: "Could not find <Routes> tag"
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const routesStartIndex = routesStartMatch.index;
|
|
411
|
+
let depth = 1;
|
|
412
|
+
let routesEndIndex = routesStartIndex + routesStartMatch[0].length;
|
|
413
|
+
for (let i = routesEndIndex; i < newContent.length; i++) {
|
|
414
|
+
if (newContent.substring(i).startsWith("<Routes")) {
|
|
415
|
+
depth++;
|
|
416
|
+
} else if (newContent.substring(i).startsWith("</Routes>")) {
|
|
417
|
+
depth--;
|
|
418
|
+
if (depth === 0) {
|
|
419
|
+
routesEndIndex = i + "</Routes>".length;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (depth !== 0) {
|
|
425
|
+
return {
|
|
426
|
+
success: false,
|
|
427
|
+
backupPath,
|
|
428
|
+
message: "Could not find matching </Routes> tag"
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
const routesLine = lines.find((line, idx) => {
|
|
432
|
+
const lineContent = line.trim();
|
|
433
|
+
return lineContent.startsWith("<Routes") && idx <= routesStartIndex;
|
|
434
|
+
});
|
|
435
|
+
const indent = routesLine ? routesLine.match(/^(\s*)/)?.[1] || " " : " ";
|
|
436
|
+
const before = newContent.substring(0, routesStartIndex);
|
|
437
|
+
const after = newContent.substring(routesEndIndex);
|
|
438
|
+
const replacement = `${indent}<GMooncAppRoutes basePath="${basePath}" />`;
|
|
439
|
+
newContent = before + replacement + after;
|
|
440
|
+
if (!newContent.match(/<Routes[^>]*>/g) && !newContent.match(/<Route[^>]*>/g)) {
|
|
441
|
+
newContent = newContent.replace(/import\s+{[^}]*Routes[^}]*}\s+from\s+["']react-router-dom["'];?\n?/g, "");
|
|
442
|
+
newContent = newContent.replace(/import\s+{[^}]*Route[^}]*}\s+from\s+["']react-router-dom["'];?\n?/g, "");
|
|
443
|
+
}
|
|
444
|
+
writeFileSafe(appPath, newContent);
|
|
445
|
+
logSuccess(`Patched ${appPath} (backup: ${backupPath})`);
|
|
446
|
+
return { success: true, backupPath };
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/cli/index.ts
|
|
450
|
+
var program = new import_commander.Command();
|
|
451
|
+
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) => {
|
|
452
|
+
try {
|
|
453
|
+
const projectDir = (0, import_process.cwd)();
|
|
454
|
+
const basePath = options.base || "/app";
|
|
455
|
+
const dryRun = options.dryRun || false;
|
|
456
|
+
const skipRouterPatch = options.skipRouterPatch || false;
|
|
457
|
+
const safeBasePath = basePath === "/" ? "/app" : basePath;
|
|
458
|
+
logInfo("\u{1F50D} Detecting React project...");
|
|
459
|
+
const project = detectProject(projectDir);
|
|
460
|
+
logSuccess(`Package manager: ${project.packageManager}`);
|
|
461
|
+
logSuccess("package.json found");
|
|
462
|
+
if (!project.entrypoint) {
|
|
463
|
+
logError("Entrypoint not found.");
|
|
464
|
+
logError("Looking for: src/main.tsx, src/main.jsx, src/main.ts, src/main.js");
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
logSuccess(`Entrypoint found: ${project.entrypoint}`);
|
|
468
|
+
if (!skipRouterPatch && !project.routerFile) {
|
|
469
|
+
logWarning("Router file not found or BrowserRouter pattern not detected.");
|
|
470
|
+
logWarning("Router patch will be skipped. You can integrate manually later.");
|
|
471
|
+
} else if (!skipRouterPatch) {
|
|
472
|
+
logSuccess(`Router file found: ${project.routerFile}`);
|
|
473
|
+
}
|
|
474
|
+
const templatesDir = getTemplatesDir();
|
|
475
|
+
logInfo(`Templates directory: ${templatesDir}`);
|
|
476
|
+
logInfo("\n\u{1F4E6} Installing dependencies...");
|
|
477
|
+
const depsResult = installDependencies(project, templatesDir, dryRun);
|
|
478
|
+
if (!depsResult.success && !dryRun) {
|
|
479
|
+
process.exit(1);
|
|
480
|
+
}
|
|
481
|
+
logInfo("\n\u{1F4CB} Copying dashboard templates...");
|
|
482
|
+
const sharedResult = copySharedTemplates(projectDir, templatesDir, dryRun);
|
|
483
|
+
if (!sharedResult.success) {
|
|
484
|
+
logError("Failed to copy shared templates");
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
const routerResult = copyViteRouterTemplates(projectDir, templatesDir, dryRun);
|
|
488
|
+
if (!routerResult.success) {
|
|
489
|
+
logError("Failed to copy router templates");
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
logInfo("\n\u{1F4DD} Injecting CSS imports...");
|
|
493
|
+
const entrypointPath = (0, import_path6.join)(projectDir, project.entrypoint);
|
|
494
|
+
const cssResult = patchEntryCss(entrypointPath, dryRun);
|
|
495
|
+
if (!cssResult.success && !dryRun) {
|
|
496
|
+
logError("Failed to inject CSS");
|
|
497
|
+
process.exit(1);
|
|
498
|
+
}
|
|
499
|
+
if (!skipRouterPatch && project.routerFile) {
|
|
500
|
+
logInfo("\n\u{1F527} Patching router...");
|
|
501
|
+
const routerPatchResult = patchBrowserRouter(projectDir, safeBasePath, dryRun);
|
|
502
|
+
if (!routerPatchResult.success && !dryRun) {
|
|
503
|
+
logWarning(`Router patch failed: ${routerPatchResult.message}`);
|
|
504
|
+
logWarning("You can integrate manually by importing GMooncAppRoutes in your App.tsx");
|
|
505
|
+
} else if (routerPatchResult.success) {
|
|
506
|
+
logSuccess("Router patched successfully");
|
|
507
|
+
}
|
|
508
|
+
} else if (skipRouterPatch) {
|
|
509
|
+
logInfo("\n\u23ED\uFE0F Skipping router patch (--skip-router-patch)");
|
|
510
|
+
} else {
|
|
511
|
+
logInfo("\n\u23ED\uFE0F Skipping router patch (router not detected)");
|
|
512
|
+
}
|
|
513
|
+
logSuccess("\n\u2705 Installation complete!");
|
|
514
|
+
logInfo("\nYour dashboard is now available at:");
|
|
515
|
+
logInfo(` - Home: ${safeBasePath}`);
|
|
516
|
+
logInfo(` - Admin: ${safeBasePath}/admin/*`);
|
|
517
|
+
logInfo(` - Auth: /login, /register, etc.`);
|
|
518
|
+
logInfo("\nYou can now remove gmoonc from package.json if desired.");
|
|
519
|
+
logInfo("The dashboard code is in src/gmoonc/ and is independent.");
|
|
520
|
+
} catch (error) {
|
|
521
|
+
logError(`Error: ${error.message}`);
|
|
522
|
+
if (error.stack && process.env.DEBUG) {
|
|
523
|
+
console.error(error.stack);
|
|
524
|
+
}
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
program.parse();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gmoonc",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Goalmoon Ctrl (gmoonc):
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Goalmoon Ctrl (gmoonc): Complete dashboard installer for React projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://gmoonc.com",
|
|
7
7
|
"repository": {
|
|
@@ -12,8 +12,31 @@
|
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/gmoonc/gmoonc-packages/issues"
|
|
14
14
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"gmoonc": "./dist/cli.cjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src/templates",
|
|
22
|
+
"scripts",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"sync:templates": "node scripts/sync-templates.mjs",
|
|
28
|
+
"build": "tsup src/cli/index.ts --format cjs --no-splitting --out-dir dist && node -e \"const fs=require('fs');const f='dist/cli.js';const cjs='dist/cli.cjs';if(fs.existsSync(f)){fs.renameSync(f,cjs);fs.writeFileSync(cjs,'#!/usr/bin/env node\\n'+fs.readFileSync(cjs))}\"",
|
|
29
|
+
"clean": "rimraf dist",
|
|
30
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
31
|
+
"postinstall": "node scripts/block-global-install.cjs"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"commander": "^12.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"rimraf": "^6.0.0",
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
18
42
|
}
|
|
19
|
-
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Block global installation of gmoonc
|
|
4
|
+
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
5
|
+
process.env.npm_config_location === 'global' ||
|
|
6
|
+
process.env.npm_config_prefix &&
|
|
7
|
+
(process.env.npm_config_prefix.includes('nvm') ||
|
|
8
|
+
process.env.npm_config_prefix.includes('node_modules'));
|
|
9
|
+
|
|
10
|
+
if (isGlobal) {
|
|
11
|
+
console.error('\n❌ Do not install gmoonc globally.');
|
|
12
|
+
console.error('\n Install locally in your project:');
|
|
13
|
+
console.error(' npm install gmoonc');
|
|
14
|
+
console.error('\n Then run:');
|
|
15
|
+
console.error(' npx gmoonc');
|
|
16
|
+
console.error('');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|