create-fullstack-scaffold 0.4.20 → 0.4.22
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/package.json +1 -1
- package/template/admin.html +4 -0
- package/template/merchant.html +4 -0
- package/template/package.json +2 -0
- package/template/src/admin/components/index.ts +4 -1
- package/template/src/tenant/pages/ContentPage.tsx +2 -2
- package/template/tenant.html +4 -0
- package/dist/cli/index.js +0 -3950
- package/dist/cli/index.js.map +0 -1
package/dist/cli/index.js
DELETED
|
@@ -1,3950 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
3
|
-
import path, { join } from 'path';
|
|
4
|
-
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
|
-
import { Command } from 'commander';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import { select } from '@inquirer/prompts';
|
|
8
|
-
import fs from 'fs-extra';
|
|
9
|
-
import ora from 'ora';
|
|
10
|
-
|
|
11
|
-
var tsImportFn;
|
|
12
|
-
async function getTsImport() {
|
|
13
|
-
if (tsImportFn) return tsImportFn;
|
|
14
|
-
const { tsImport } = await import('tsx/esm/api');
|
|
15
|
-
tsImportFn = tsImport;
|
|
16
|
-
return tsImportFn;
|
|
17
|
-
}
|
|
18
|
-
async function loadManifests(templateDir) {
|
|
19
|
-
const tsImport = await getTsImport();
|
|
20
|
-
const serverDir = join(templateDir, "src", "server");
|
|
21
|
-
const modules = /* @__PURE__ */ new Map();
|
|
22
|
-
const entries = readdirSync(serverDir);
|
|
23
|
-
const moduleDirs = entries.filter(
|
|
24
|
-
(e) => e.startsWith("module-") && statSync(join(serverDir, e)).isDirectory()
|
|
25
|
-
);
|
|
26
|
-
const parentURL = pathToFileURL(join(serverDir, "dummy.ts")).href;
|
|
27
|
-
for (const dir of moduleDirs) {
|
|
28
|
-
const manifestPath = join(serverDir, dir, "module.ts");
|
|
29
|
-
if (!existsSync(manifestPath)) continue;
|
|
30
|
-
try {
|
|
31
|
-
const mod = await tsImport(manifestPath, { parentURL });
|
|
32
|
-
const manifest = mod.default;
|
|
33
|
-
if (!manifest || !manifest.name) {
|
|
34
|
-
console.error(`\u274C Invalid manifest in ${dir}: missing name`);
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
modules.set(manifest.name, manifest);
|
|
38
|
-
} catch (err) {
|
|
39
|
-
console.error(`\u274C Failed to load manifest from ${dir}:`, err);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return modules;
|
|
43
|
-
}
|
|
44
|
-
async function loadPresets(templateDir) {
|
|
45
|
-
const configPath = join(templateDir, "modules.config.ts");
|
|
46
|
-
if (!existsSync(configPath)) {
|
|
47
|
-
return [getDefaultPreset()];
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
const tsImport = await getTsImport();
|
|
51
|
-
const parentURL = pathToFileURL(configPath).href;
|
|
52
|
-
const mod = await tsImport(configPath, { parentURL });
|
|
53
|
-
if (mod.TEMPLATE_PRESETS) {
|
|
54
|
-
return mod.TEMPLATE_PRESETS;
|
|
55
|
-
}
|
|
56
|
-
} catch (err) {
|
|
57
|
-
console.error("\u274C Failed to load presets:", err);
|
|
58
|
-
}
|
|
59
|
-
return [getDefaultPreset()];
|
|
60
|
-
}
|
|
61
|
-
function getDefaultPreset() {
|
|
62
|
-
return {
|
|
63
|
-
id: "fullstack-admin",
|
|
64
|
-
name: "Full Admin",
|
|
65
|
-
description: "All modules included",
|
|
66
|
-
modules: [
|
|
67
|
-
"todos",
|
|
68
|
-
"chat",
|
|
69
|
-
"notifications",
|
|
70
|
-
"file",
|
|
71
|
-
"captcha",
|
|
72
|
-
"permission",
|
|
73
|
-
"admin",
|
|
74
|
-
"order",
|
|
75
|
-
"ticket",
|
|
76
|
-
"dispute",
|
|
77
|
-
"content"
|
|
78
|
-
]
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function resolvePreset(preset, allManifests) {
|
|
82
|
-
const modules = /* @__PURE__ */ new Map();
|
|
83
|
-
const toProcess = [...preset.modules];
|
|
84
|
-
const processed = /* @__PURE__ */ new Set();
|
|
85
|
-
while (toProcess.length > 0) {
|
|
86
|
-
const name = toProcess.shift();
|
|
87
|
-
if (processed.has(name)) continue;
|
|
88
|
-
processed.add(name);
|
|
89
|
-
const manifest = allManifests.get(name);
|
|
90
|
-
if (manifest) {
|
|
91
|
-
modules.set(name, manifest);
|
|
92
|
-
for (const dep of manifest.dependsOn) {
|
|
93
|
-
if (!processed.has(dep)) {
|
|
94
|
-
toProcess.push(dep);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
let hasSSE = false;
|
|
100
|
-
let hasWebSocket = false;
|
|
101
|
-
let hasAdmin = false;
|
|
102
|
-
for (const manifest of modules.values()) {
|
|
103
|
-
if (manifest.hasSSE) hasSSE = true;
|
|
104
|
-
if (manifest.hasWebSocket) hasWebSocket = true;
|
|
105
|
-
if (manifest.adminPages && manifest.adminPages.length > 0) hasAdmin = true;
|
|
106
|
-
if (manifest.routes.admin && manifest.routes.admin.length > 0) hasAdmin = true;
|
|
107
|
-
}
|
|
108
|
-
const isCliOnly = preset.id === "cli-only";
|
|
109
|
-
return {
|
|
110
|
-
preset,
|
|
111
|
-
modules,
|
|
112
|
-
hasAdmin,
|
|
113
|
-
hasClient: !isCliOnly,
|
|
114
|
-
hasCli: true,
|
|
115
|
-
hasSSE,
|
|
116
|
-
hasWebSocket,
|
|
117
|
-
hasPermission: modules.has("permission"),
|
|
118
|
-
hasCaptcha: modules.has("captcha")
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function getDbSchemaFiles(resolved) {
|
|
122
|
-
const files = [];
|
|
123
|
-
for (const [, manifest] of resolved.modules) {
|
|
124
|
-
if (manifest.dbSchemas) {
|
|
125
|
-
files.push(...manifest.dbSchemas.files);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return files;
|
|
129
|
-
}
|
|
130
|
-
function getClientPages(resolved) {
|
|
131
|
-
const pages = [];
|
|
132
|
-
for (const [, manifest] of resolved.modules) {
|
|
133
|
-
if (manifest.clientPages) {
|
|
134
|
-
pages.push(...manifest.clientPages);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return pages;
|
|
138
|
-
}
|
|
139
|
-
function getAdminPages(resolved) {
|
|
140
|
-
const pages = [];
|
|
141
|
-
for (const [, manifest] of resolved.modules) {
|
|
142
|
-
if (manifest.adminPages) {
|
|
143
|
-
pages.push(...manifest.adminPages);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return pages;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// src/generators/file-filter.ts
|
|
150
|
-
function getExcludePatterns(resolved, allManifests) {
|
|
151
|
-
const excludes = [];
|
|
152
|
-
for (const [name, manifest] of allManifests) {
|
|
153
|
-
if (resolved.modules.has(name)) continue;
|
|
154
|
-
excludes.push(`src/server/module-${name}`);
|
|
155
|
-
if (manifest.sharedSchemas) {
|
|
156
|
-
excludes.push(`src/shared/modules/${manifest.sharedSchemas.path}`);
|
|
157
|
-
if (manifest.sharedSchemas.additionalPaths) {
|
|
158
|
-
for (const extra of manifest.sharedSchemas.additionalPaths) {
|
|
159
|
-
excludes.push(`src/shared/modules/${extra}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (manifest.dbSchemas) {
|
|
164
|
-
for (const file of manifest.dbSchemas.files) {
|
|
165
|
-
excludes.push(`src/server/db/schema/${file}.ts`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (manifest.clientPages) {
|
|
169
|
-
for (const page of manifest.clientPages) {
|
|
170
|
-
excludes.push(`src/client/pages/${page.name}.tsx`);
|
|
171
|
-
excludes.push(`src/client/pages/__tests__/${page.name}.test.tsx`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (manifest.clientStores) {
|
|
175
|
-
for (const store of manifest.clientStores) {
|
|
176
|
-
excludes.push(`src/client/stores/${store}.ts`);
|
|
177
|
-
excludes.push(`src/client/stores/__tests__/${store}.test.ts`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (manifest.adminPages) {
|
|
181
|
-
for (const page of manifest.adminPages) {
|
|
182
|
-
excludes.push(`src/admin/pages/${page.name}.tsx`);
|
|
183
|
-
excludes.push(`src/admin/pages/__tests__/${page.name}.test.tsx`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (manifest.providesMiddleware) {
|
|
187
|
-
for (const mw of manifest.providesMiddleware) {
|
|
188
|
-
excludes.push(`src/server/middleware/${mw.name}.ts`);
|
|
189
|
-
excludes.push(`src/server/middleware/__tests__/${mw.name}.test.ts`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (name === "todos") {
|
|
193
|
-
excludes.push("src/server/__tests__/integration/todos-api.test.ts");
|
|
194
|
-
}
|
|
195
|
-
if (manifest.cliModule) {
|
|
196
|
-
excludes.push(`src/cli/modules/${manifest.cliModule.dir}`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
if (!resolved.modules.has("admin")) {
|
|
200
|
-
excludes.push("src/admin");
|
|
201
|
-
excludes.push("admin.html");
|
|
202
|
-
excludes.push("auth-inject.html");
|
|
203
|
-
}
|
|
204
|
-
if (!resolved.modules.has("tenant")) {
|
|
205
|
-
excludes.push("src/tenant");
|
|
206
|
-
excludes.push("tenant.html");
|
|
207
|
-
excludes.push("src/merchant");
|
|
208
|
-
excludes.push("merchant.html");
|
|
209
|
-
excludes.push("src/server/middleware/tenant-isolation.ts");
|
|
210
|
-
excludes.push("src/server/middleware/__tests__/tenant-isolation.test.ts");
|
|
211
|
-
}
|
|
212
|
-
if (!resolved.hasClient) {
|
|
213
|
-
excludes.push("src/client");
|
|
214
|
-
excludes.push("index.html");
|
|
215
|
-
excludes.push("admin.html");
|
|
216
|
-
excludes.push("auth-inject.html");
|
|
217
|
-
excludes.push("src/admin");
|
|
218
|
-
excludes.push("vite.config.ts");
|
|
219
|
-
excludes.push("postcss.config.js");
|
|
220
|
-
excludes.push("tailwind.config.js");
|
|
221
|
-
}
|
|
222
|
-
if (!resolved.hasClient) {
|
|
223
|
-
excludes.push("src/client/components/AuthButton.tsx");
|
|
224
|
-
excludes.push("src/client/components/__tests__/AuthButton.test.tsx");
|
|
225
|
-
} else if (!resolved.modules.has("admin") && !resolved.modules.has("auth")) {
|
|
226
|
-
excludes.push("src/client/components/AuthButton.tsx");
|
|
227
|
-
excludes.push("src/client/components/__tests__/AuthButton.test.tsx");
|
|
228
|
-
excludes.push("src/server/utils/auth.ts");
|
|
229
|
-
}
|
|
230
|
-
excludes.push("src/client/preset-ui-config.ts");
|
|
231
|
-
const standaloneSharedModules = {
|
|
232
|
-
cart: ["CartPage"],
|
|
233
|
-
community: ["TopicsPage"],
|
|
234
|
-
dashboard: ["DashboardPage"]
|
|
235
|
-
};
|
|
236
|
-
for (const [moduleName, requiredPages] of Object.entries(standaloneSharedModules)) {
|
|
237
|
-
const hasRelevantPage = [...resolved.modules.values()].some(
|
|
238
|
-
(m) => m.clientPages?.some((p) => requiredPages.includes(p.name)) ?? false
|
|
239
|
-
);
|
|
240
|
-
if (!hasRelevantPage) {
|
|
241
|
-
excludes.push(`src/shared/modules/${moduleName}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (!resolved.hasPermission && !resolved.modules.has("auth")) {
|
|
245
|
-
excludes.push("src/server/utils/permission-utils.ts");
|
|
246
|
-
excludes.push("src/server/utils/__tests__/permission-utils.test.ts");
|
|
247
|
-
excludes.push("src/server/middleware/__tests__/auth-simple.test.ts");
|
|
248
|
-
excludes.push("src/server/middleware/__tests__/auth.test.ts");
|
|
249
|
-
excludes.push("src/server/middleware/__tests__/error-response-format.test.ts");
|
|
250
|
-
excludes.push("src/server/utils/__tests__/auth.test.ts");
|
|
251
|
-
} else if (!resolved.hasPermission && resolved.modules.has("auth")) {
|
|
252
|
-
excludes.push("src/server/utils/permission-utils.ts");
|
|
253
|
-
excludes.push("src/server/utils/__tests__/permission-utils.test.ts");
|
|
254
|
-
excludes.push("src/server/middleware/__tests__/auth-simple.test.ts");
|
|
255
|
-
excludes.push("src/server/middleware/__tests__/auth.test.ts");
|
|
256
|
-
excludes.push("src/server/middleware/__tests__/error-response-format.test.ts");
|
|
257
|
-
excludes.push("src/server/utils/__tests__/auth.test.ts");
|
|
258
|
-
excludes.push("src/server/module-auth/__tests__/auth-service.test.ts");
|
|
259
|
-
} else if (!resolved.modules.has("admin")) {
|
|
260
|
-
excludes.push("src/server/middleware/__tests__/error-response-format.test.ts");
|
|
261
|
-
}
|
|
262
|
-
if (!resolved.modules.has("captcha")) {
|
|
263
|
-
excludes.push("src/server/utils/__tests__/captcha.test.ts");
|
|
264
|
-
}
|
|
265
|
-
return excludes;
|
|
266
|
-
}
|
|
267
|
-
function getGeneratedFiles(resolved) {
|
|
268
|
-
const files = [
|
|
269
|
-
"src/server/route-registry.ts",
|
|
270
|
-
"src/server/db/schema/index.ts",
|
|
271
|
-
"src/shared/modules/index.ts",
|
|
272
|
-
"src/shared/schemas/index.ts",
|
|
273
|
-
"src/server/middleware/index.ts",
|
|
274
|
-
"src/server/app.ts",
|
|
275
|
-
"src/cli/modules/index.ts"
|
|
276
|
-
];
|
|
277
|
-
if (resolved.hasClient) {
|
|
278
|
-
files.push(
|
|
279
|
-
"src/client/App.tsx",
|
|
280
|
-
"src/client/components/Navigation.tsx",
|
|
281
|
-
"src/client/components/index.ts",
|
|
282
|
-
"src/client/Layout.tsx",
|
|
283
|
-
"src/client/components/__tests__/App.test.tsx",
|
|
284
|
-
"src/client/components/__tests__/Navigation.test.tsx",
|
|
285
|
-
"src/client/main.tsx",
|
|
286
|
-
"src/client/preset-ui-config.ts"
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
if (resolved.hasClient && resolved.modules.has("admin")) {
|
|
290
|
-
files.push("src/admin/App.tsx");
|
|
291
|
-
}
|
|
292
|
-
if (resolved.hasClient && !resolved.modules.has("admin")) {
|
|
293
|
-
files.push("vite.config.ts");
|
|
294
|
-
}
|
|
295
|
-
if (resolved.hasClient && resolved.modules.has("admin")) {
|
|
296
|
-
files.push("vite.config.ts");
|
|
297
|
-
}
|
|
298
|
-
if (!resolved.hasPermission) {
|
|
299
|
-
files.push("src/server/middleware/auth.ts");
|
|
300
|
-
files.push("src/server/utils/auth.ts");
|
|
301
|
-
}
|
|
302
|
-
if (!resolved.modules.has("admin") && resolved.hasPermission) {
|
|
303
|
-
files.push("src/server/utils/auth.ts");
|
|
304
|
-
}
|
|
305
|
-
files.push("src/server/db/init.ts");
|
|
306
|
-
return files;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// src/generators/route-registry.ts
|
|
310
|
-
function generateRouteRegistry(resolved) {
|
|
311
|
-
const imports = [
|
|
312
|
-
`import { OpenAPIHono } from '@hono/zod-openapi'`,
|
|
313
|
-
`import { rateLimitMiddleware } from './middleware/rate-limit'`
|
|
314
|
-
];
|
|
315
|
-
const clientRoutes = [];
|
|
316
|
-
const adminRoutes = [];
|
|
317
|
-
const moduleEntries = [...resolved.modules.entries()];
|
|
318
|
-
const usedNames = /* @__PURE__ */ new Set();
|
|
319
|
-
for (const [name, manifest] of moduleEntries) {
|
|
320
|
-
const moduleDir = `module-${name}`;
|
|
321
|
-
if (manifest.routes.client) {
|
|
322
|
-
const clientRouteList = Array.isArray(manifest.routes.client) ? manifest.routes.client : [manifest.routes.client];
|
|
323
|
-
for (const route of clientRouteList) {
|
|
324
|
-
const { importPath, exportName } = route;
|
|
325
|
-
const localName = usedNames.has(exportName) ? `${name}${exportName.charAt(0).toUpperCase()}${exportName.slice(1)}` : exportName;
|
|
326
|
-
usedNames.add(localName);
|
|
327
|
-
const importStmt = localName === exportName ? `import { ${exportName} } from './${moduleDir}/${importPath.replace(/^\.\//, "")}'` : `import { ${exportName} as ${localName} } from './${moduleDir}/${importPath.replace(
|
|
328
|
-
/^\.\//,
|
|
329
|
-
""
|
|
330
|
-
)}'`;
|
|
331
|
-
imports.push(importStmt);
|
|
332
|
-
clientRoutes.push(` .route('/api', ${localName})`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (manifest.routes.admin) {
|
|
336
|
-
for (const route of manifest.routes.admin) {
|
|
337
|
-
const { importPath, exportName } = route;
|
|
338
|
-
const localName = usedNames.has(exportName) ? `${name}${exportName.charAt(0).toUpperCase()}${exportName.slice(1)}` : exportName;
|
|
339
|
-
usedNames.add(localName);
|
|
340
|
-
const importStmt = localName === exportName ? `import { ${exportName} } from './${moduleDir}/${importPath.replace(/^\.\//, "")}'` : `import { ${exportName} as ${localName} } from './${moduleDir}/${importPath.replace(
|
|
341
|
-
/^\.\//,
|
|
342
|
-
""
|
|
343
|
-
)}'`;
|
|
344
|
-
imports.push(importStmt);
|
|
345
|
-
adminRoutes.push(` .route('/api', ${localName})`);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
let content = imports.join("\n") + "\n\n";
|
|
350
|
-
content += `const apiRateLimit = rateLimitMiddleware({
|
|
351
|
-
`;
|
|
352
|
-
content += ` windowMs: 60 * 1000,
|
|
353
|
-
`;
|
|
354
|
-
content += ` max: 100,
|
|
355
|
-
`;
|
|
356
|
-
content += `})
|
|
357
|
-
|
|
358
|
-
`;
|
|
359
|
-
if (clientRoutes.length > 0) {
|
|
360
|
-
content += `// client API routes
|
|
361
|
-
`;
|
|
362
|
-
content += `export const clientApiRoutes = new OpenAPIHono()
|
|
363
|
-
`;
|
|
364
|
-
content += ` .use('*', apiRateLimit)
|
|
365
|
-
`;
|
|
366
|
-
content += clientRoutes.join("\n") + "\n\n";
|
|
367
|
-
} else {
|
|
368
|
-
content += `// No client modules selected
|
|
369
|
-
`;
|
|
370
|
-
content += `export const clientApiRoutes = new OpenAPIHono()
|
|
371
|
-
|
|
372
|
-
`;
|
|
373
|
-
}
|
|
374
|
-
if (adminRoutes.length > 0) {
|
|
375
|
-
content += `// admin API routes
|
|
376
|
-
`;
|
|
377
|
-
content += `export const adminApiRoutes = new OpenAPIHono()
|
|
378
|
-
`;
|
|
379
|
-
content += adminRoutes.join("\n") + "\n\n";
|
|
380
|
-
} else {
|
|
381
|
-
content += `// No admin modules selected
|
|
382
|
-
`;
|
|
383
|
-
content += `export const adminApiRoutes = new OpenAPIHono()
|
|
384
|
-
|
|
385
|
-
`;
|
|
386
|
-
}
|
|
387
|
-
content += `export type ClientApiRoutes = typeof clientApiRoutes
|
|
388
|
-
`;
|
|
389
|
-
content += `export type AdminApiRoutes = typeof adminApiRoutes
|
|
390
|
-
`;
|
|
391
|
-
return content;
|
|
392
|
-
}
|
|
393
|
-
function getStandaloneRoutes(resolved) {
|
|
394
|
-
const routes = [];
|
|
395
|
-
const usedNames = /* @__PURE__ */ new Set();
|
|
396
|
-
for (const [name, manifest] of resolved.modules) {
|
|
397
|
-
if (manifest.routes.standalone) {
|
|
398
|
-
const { exportName, mountPath } = manifest.routes.standalone;
|
|
399
|
-
const localName = usedNames.has(exportName) ? `${name}${exportName.charAt(0).toUpperCase()}${exportName.slice(1)}` : exportName;
|
|
400
|
-
usedNames.add(localName);
|
|
401
|
-
routes.push({ localName, mountPath });
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return routes;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// src/generators/client-navigation.ts
|
|
408
|
-
var DEFAULT_ICON = "Circle";
|
|
409
|
-
var ICON_MAP = {
|
|
410
|
-
TodoPage: "CheckCircle",
|
|
411
|
-
NotificationPage: "Bell",
|
|
412
|
-
WebSocketPage: "Plug",
|
|
413
|
-
ContentListPage: "FileText",
|
|
414
|
-
PluginsPage: "Package",
|
|
415
|
-
CategoriesPage: "Grid",
|
|
416
|
-
SearchPage: "Search",
|
|
417
|
-
PublishPage: "Upload",
|
|
418
|
-
DeveloperDashboardPage: "Code",
|
|
419
|
-
PluginDetailPage: "Package",
|
|
420
|
-
TopicsPage: "Hash",
|
|
421
|
-
ProfilePage: "User",
|
|
422
|
-
DashboardPage: "LayoutDashboard",
|
|
423
|
-
SettingsPage: "Settings",
|
|
424
|
-
CartPage: "ShoppingCart",
|
|
425
|
-
OrdersPage: "Package"
|
|
426
|
-
};
|
|
427
|
-
function generateClientNavigation(resolved) {
|
|
428
|
-
const pages = getClientPages(resolved).filter((p) => !p.route.includes(":"));
|
|
429
|
-
const hasAuth = resolved.modules.has("auth");
|
|
430
|
-
const navItems = [];
|
|
431
|
-
for (const page of pages) {
|
|
432
|
-
const icon = ICON_MAP[page.name] || DEFAULT_ICON;
|
|
433
|
-
const label = page.name === "TodoPage" ? "Todos" : page.name === "NotificationPage" ? "Notifications" : page.name === "WebSocketPage" ? "WebSocket" : page.name === "PluginsPage" ? "Plugins" : page.name === "CategoriesPage" ? "Categories" : page.name === "SearchPage" ? "Search" : page.name === "PublishPage" ? "Publish" : page.name === "DeveloperDashboardPage" ? "Developer" : page.name === "TopicsPage" ? "Topics" : page.name === "ProfilePage" ? "Profile" : page.name === "DashboardPage" ? "Dashboard" : page.name === "SettingsPage" ? "Settings" : page.name === "CartPage" ? "Cart" : page.name === "OrdersPage" ? "Orders" : page.route.replace(/^\//, "").charAt(0).toUpperCase() + page.route.replace(/^\//, "").slice(1);
|
|
434
|
-
navItems.push(` { label: '${label}', icon: '${icon}', path: '${page.route}' },`);
|
|
435
|
-
}
|
|
436
|
-
const authImport = hasAuth ? `
|
|
437
|
-
import { useAuthStore } from '../stores/authStore'` : "";
|
|
438
|
-
const authSection = hasAuth ? `
|
|
439
|
-
function AuthSection({ style }: { style: string }) {
|
|
440
|
-
const isAuthenticated = useAuthStore((state: any) => state.isAuthenticated)
|
|
441
|
-
const user = useAuthStore((state: any) => state.user)
|
|
442
|
-
const logout = useAuthStore((state: any) => state.logout)
|
|
443
|
-
|
|
444
|
-
if (style === 'none') return null
|
|
445
|
-
|
|
446
|
-
if (isAuthenticated) {
|
|
447
|
-
return (
|
|
448
|
-
<div className="flex items-center gap-2">
|
|
449
|
-
<span className="text-sm text-gray-600">{user?.username}</span>
|
|
450
|
-
<button onClick={logout} className="text-xs text-gray-400 hover:text-red-500">Sign Out</button>
|
|
451
|
-
</div>
|
|
452
|
-
)
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (style === 'buttons') {
|
|
456
|
-
return (
|
|
457
|
-
<div className="flex items-center gap-2">
|
|
458
|
-
<Link
|
|
459
|
-
to="/login"
|
|
460
|
-
className="px-3 py-1 text-sm bg-blue-500 text-white rounded hover:bg-blue-600"
|
|
461
|
-
>
|
|
462
|
-
Sign In
|
|
463
|
-
</Link>
|
|
464
|
-
<Link
|
|
465
|
-
to="/register"
|
|
466
|
-
className="px-3 py-1 text-sm border border-gray-300 text-gray-700 rounded hover:bg-gray-50"
|
|
467
|
-
>
|
|
468
|
-
Sign Up
|
|
469
|
-
</Link>
|
|
470
|
-
</div>
|
|
471
|
-
)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return (
|
|
475
|
-
<Link to="/login" className="text-sm text-gray-500 hover:text-gray-900">
|
|
476
|
-
Login
|
|
477
|
-
</Link>
|
|
478
|
-
)
|
|
479
|
-
}
|
|
480
|
-
` : `
|
|
481
|
-
function AuthSection(_style: { style: string }) {
|
|
482
|
-
return null
|
|
483
|
-
}
|
|
484
|
-
`;
|
|
485
|
-
return `import { NavLink${hasAuth ? ", Link" : ""} } from 'react-router-dom'
|
|
486
|
-
import { Rocket, Sparkles } from 'lucide-react'${authImport}
|
|
487
|
-
import type { PresetTheme, NavigationConfig, ClientNavItem } from '../preset-ui-config'
|
|
488
|
-
|
|
489
|
-
interface NavigationProps {
|
|
490
|
-
preset?: string
|
|
491
|
-
items?: ClientNavItem[]
|
|
492
|
-
theme?: PresetTheme
|
|
493
|
-
navigation?: NavigationConfig
|
|
494
|
-
}
|
|
495
|
-
${authSection}
|
|
496
|
-
export const Navigation: React.FC<NavigationProps> = ({
|
|
497
|
-
items,
|
|
498
|
-
theme,
|
|
499
|
-
navigation,
|
|
500
|
-
}) => {
|
|
501
|
-
const navItems = navigation?.navItems === 'none' ? [] : (items ?? [])
|
|
502
|
-
const primaryColor = theme?.primaryColor ?? '#6366f1'
|
|
503
|
-
const logoText = theme?.logoText ?? 'Biomimic'
|
|
504
|
-
const showLogo = navigation?.showLogo !== false
|
|
505
|
-
const authStyle = navigation?.authStyle ?? 'none'
|
|
506
|
-
|
|
507
|
-
return (
|
|
508
|
-
<nav className="hidden md:block bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50" data-testid="app-nav">
|
|
509
|
-
<div className="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between gap-3">
|
|
510
|
-
{showLogo && (
|
|
511
|
-
<NavLink to="/" className="flex items-center gap-2 group shrink-0" data-testid="app-title">
|
|
512
|
-
<div
|
|
513
|
-
className="w-8 h-8 rounded-lg flex items-center justify-center shadow-sm group-hover:shadow-md transition-shadow"
|
|
514
|
-
style={{ backgroundColor: primaryColor }}
|
|
515
|
-
>
|
|
516
|
-
<Rocket className="w-4 h-4 text-white" />
|
|
517
|
-
</div>
|
|
518
|
-
<span className="text-lg font-semibold text-gray-900 tracking-tight whitespace-nowrap">
|
|
519
|
-
{logoText}
|
|
520
|
-
</span>
|
|
521
|
-
<Sparkles className="w-3.5 h-3.5 shrink-0" style={{ color: primaryColor }} />
|
|
522
|
-
</NavLink>
|
|
523
|
-
)}
|
|
524
|
-
|
|
525
|
-
<div className="flex items-center gap-0.5 overflow-x-auto flex-1 min-w-0 scrollbar-hide">
|
|
526
|
-
{navItems.map(item => (
|
|
527
|
-
<NavLink
|
|
528
|
-
key={item.path}
|
|
529
|
-
to={item.path}
|
|
530
|
-
data-testid={\`nav-\${item.label.toLowerCase().replace(/\\s+/g, '-')}-button\`}
|
|
531
|
-
className={({ isActive }: { isActive: boolean }) =>
|
|
532
|
-
\`px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 shrink-0 whitespace-nowrap \${
|
|
533
|
-
isActive ? 'text-white' : 'text-gray-500 hover:text-gray-900 hover:bg-gray-50'
|
|
534
|
-
}\`
|
|
535
|
-
}
|
|
536
|
-
style={
|
|
537
|
-
(({ isActive }: { isActive: boolean }) =>
|
|
538
|
-
isActive
|
|
539
|
-
? { backgroundColor: \`\${primaryColor}15\`, color: primaryColor }
|
|
540
|
-
: undefined) as never
|
|
541
|
-
}
|
|
542
|
-
>
|
|
543
|
-
{item.label}
|
|
544
|
-
</NavLink>
|
|
545
|
-
))}
|
|
546
|
-
</div>
|
|
547
|
-
|
|
548
|
-
<AuthSection style={authStyle} />
|
|
549
|
-
</div>
|
|
550
|
-
</nav>
|
|
551
|
-
)
|
|
552
|
-
}
|
|
553
|
-
`;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// src/generators/client-app-test.ts
|
|
557
|
-
function generateClientAppTest(resolved) {
|
|
558
|
-
const pages = getClientPages(resolved);
|
|
559
|
-
const mocks = pages.map(
|
|
560
|
-
(p) => `vi.mock('@client/pages/${p.name}', () => ({
|
|
561
|
-
${p.name}: () => <div data-testid="${p.name.toLowerCase()}-page">${p.name}</div>,
|
|
562
|
-
}))`
|
|
563
|
-
).join("\n\n ");
|
|
564
|
-
return `import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
565
|
-
import { render, screen, cleanup } from '@testing-library/react'
|
|
566
|
-
import '@testing-library/jest-dom'
|
|
567
|
-
import { App } from '@client/App'
|
|
568
|
-
|
|
569
|
-
${mocks}
|
|
570
|
-
|
|
571
|
-
describe('App Component', () => {
|
|
572
|
-
beforeEach(() => {
|
|
573
|
-
vi.clearAllMocks()
|
|
574
|
-
})
|
|
575
|
-
|
|
576
|
-
afterEach(() => {
|
|
577
|
-
cleanup()
|
|
578
|
-
})
|
|
579
|
-
|
|
580
|
-
describe('Initial Render', () => {
|
|
581
|
-
it('should render navigation', () => {
|
|
582
|
-
render(<App />)
|
|
583
|
-
expect(screen.getByTestId('app-nav')).toBeInTheDocument()
|
|
584
|
-
})
|
|
585
|
-
|
|
586
|
-
it('should render main content area', () => {
|
|
587
|
-
render(<App />)
|
|
588
|
-
expect(screen.getByTestId('app-main')).toBeInTheDocument()
|
|
589
|
-
})
|
|
590
|
-
|
|
591
|
-
it('should render container', () => {
|
|
592
|
-
render(<App />)
|
|
593
|
-
expect(screen.getByTestId('app-container')).toBeInTheDocument()
|
|
594
|
-
})
|
|
595
|
-
})
|
|
596
|
-
|
|
597
|
-
describe('Navigation Links', () => {
|
|
598
|
-
it('should render footer', () => {
|
|
599
|
-
render(<App />)
|
|
600
|
-
expect(screen.getByTestId('app-footer')).toBeInTheDocument()
|
|
601
|
-
})
|
|
602
|
-
})
|
|
603
|
-
})
|
|
604
|
-
`;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// src/generators/client-navigation-test.ts
|
|
608
|
-
var LABEL_MAP = {
|
|
609
|
-
TodoPage: "Todos",
|
|
610
|
-
NotificationPage: "Notifications",
|
|
611
|
-
WebSocketPage: "WebSocket",
|
|
612
|
-
PluginsPage: "Plugins",
|
|
613
|
-
CategoriesPage: "Categories",
|
|
614
|
-
SearchPage: "Search",
|
|
615
|
-
PublishPage: "Publish",
|
|
616
|
-
DeveloperDashboardPage: "Developer",
|
|
617
|
-
TopicsPage: "Topics",
|
|
618
|
-
ProfilePage: "Profile",
|
|
619
|
-
DashboardPage: "Dashboard",
|
|
620
|
-
SettingsPage: "Settings",
|
|
621
|
-
CartPage: "Cart",
|
|
622
|
-
OrdersPage: "Orders"
|
|
623
|
-
};
|
|
624
|
-
function generateClientNavigationTest(resolved) {
|
|
625
|
-
const pages = getClientPages(resolved).filter((p) => !p.route.includes(":"));
|
|
626
|
-
const firstPage = pages[0];
|
|
627
|
-
if (!firstPage) {
|
|
628
|
-
return `import { describe, it, expect } from 'vitest'
|
|
629
|
-
import { render, screen } from '@testing-library/react'
|
|
630
|
-
import { BrowserRouter } from 'react-router-dom'
|
|
631
|
-
import { Navigation } from '../Navigation'
|
|
632
|
-
|
|
633
|
-
const renderWithRouter = (component: React.ReactNode) => {
|
|
634
|
-
return render(<BrowserRouter>{component}</BrowserRouter>)
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
describe('Navigation', () => {
|
|
638
|
-
it('should render navigation', () => {
|
|
639
|
-
renderWithRouter(<Navigation />)
|
|
640
|
-
expect(screen.getByTestId('app-nav')).toBeInTheDocument()
|
|
641
|
-
})
|
|
642
|
-
})
|
|
643
|
-
`;
|
|
644
|
-
}
|
|
645
|
-
const firstLabel = LABEL_MAP[firstPage.name] || firstPage.name.replace("Page", "");
|
|
646
|
-
return `import { describe, it, expect } from 'vitest'
|
|
647
|
-
import { render, screen } from '@testing-library/react'
|
|
648
|
-
import { BrowserRouter } from 'react-router-dom'
|
|
649
|
-
import { Navigation } from '../Navigation'
|
|
650
|
-
|
|
651
|
-
const renderWithRouter = (component: React.ReactNode) => {
|
|
652
|
-
return render(<BrowserRouter>{component}</BrowserRouter>)
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
describe('Navigation', () => {
|
|
656
|
-
it('should render navigation', () => {
|
|
657
|
-
renderWithRouter(<Navigation />)
|
|
658
|
-
expect(screen.getByTestId('app-nav')).toBeInTheDocument()
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
it('should render nav items', () => {
|
|
662
|
-
renderWithRouter(<Navigation />)
|
|
663
|
-
expect(screen.getByText('${firstLabel}')).toBeInTheDocument()
|
|
664
|
-
})
|
|
665
|
-
})
|
|
666
|
-
`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
// src/generators/admin-app.ts
|
|
670
|
-
function generateAdminApp(resolved) {
|
|
671
|
-
if (!resolved.hasAdmin) return null;
|
|
672
|
-
const pages = getAdminPages(resolved) ?? [];
|
|
673
|
-
if (pages.length === 0) return null;
|
|
674
|
-
const publicPages = pages.filter((p) => p.isPublic);
|
|
675
|
-
const protectedPages = pages.filter((p) => !p.isPublic);
|
|
676
|
-
const imports = [
|
|
677
|
-
`import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'`,
|
|
678
|
-
`import { ConfigProvider } from 'antd'`,
|
|
679
|
-
`import { Layout } from './layouts/Layout'`
|
|
680
|
-
];
|
|
681
|
-
for (const page of pages) {
|
|
682
|
-
imports.push(`import { ${page.name} } from './pages/${page.name}'`);
|
|
683
|
-
}
|
|
684
|
-
const captchaImport = resolved.hasCaptcha ? ", CaptchaModal" : "";
|
|
685
|
-
imports.push(`import { ProtectedRoute${captchaImport} } from './components'`);
|
|
686
|
-
const protectedRouteElements = protectedPages.map(
|
|
687
|
-
(p) => ` <Route path="${p.route}" element={<${p.name} />} />`
|
|
688
|
-
);
|
|
689
|
-
const defaultProtectedRoute = protectedPages.length > 0 ? protectedPages[0].route : "/";
|
|
690
|
-
const captchaElement = resolved.hasCaptcha ? `
|
|
691
|
-
<CaptchaModal />` : "";
|
|
692
|
-
const publicRouteLines = publicPages.map(
|
|
693
|
-
(p) => ` <Route path="${p.route}" element={<${p.name} />} />`
|
|
694
|
-
);
|
|
695
|
-
return `${imports.join("\n")}
|
|
696
|
-
|
|
697
|
-
export const App: React.FC<{ basePath?: string }> = ({ basePath = '/admin' }) => {
|
|
698
|
-
return (
|
|
699
|
-
<ConfigProvider
|
|
700
|
-
theme={{
|
|
701
|
-
token: {
|
|
702
|
-
colorPrimary: '#1890ff',
|
|
703
|
-
},
|
|
704
|
-
}}
|
|
705
|
-
>
|
|
706
|
-
<BrowserRouter basename={basePath}>
|
|
707
|
-
<Routes>
|
|
708
|
-
${publicRouteLines.join("\n")}
|
|
709
|
-
<Route
|
|
710
|
-
path="/*"
|
|
711
|
-
element={
|
|
712
|
-
<ProtectedRoute>
|
|
713
|
-
<Layout>
|
|
714
|
-
<Routes>
|
|
715
|
-
<Route path="/" element={<Navigate to="${defaultProtectedRoute}" replace />} />
|
|
716
|
-
${protectedRouteElements.join("\n")}
|
|
717
|
-
<Route path="/system/monitor" element={<div className="p-6"><h2 className="text-xl font-semibold">System Monitor</h2><p className="text-gray-500 mt-2">Coming soon...</p></div>} />
|
|
718
|
-
</Routes>
|
|
719
|
-
</Layout>
|
|
720
|
-
</ProtectedRoute>
|
|
721
|
-
}
|
|
722
|
-
/>
|
|
723
|
-
</Routes>${captchaElement}
|
|
724
|
-
</BrowserRouter>
|
|
725
|
-
</ConfigProvider>
|
|
726
|
-
)
|
|
727
|
-
}
|
|
728
|
-
`;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// src/generators/db-schema-barrel.ts
|
|
732
|
-
function generateDbSchemaBarrel(resolved) {
|
|
733
|
-
const files = getDbSchemaFiles(resolved);
|
|
734
|
-
const exports = files.map((f) => `export * from './${f}'`);
|
|
735
|
-
return exports.join("\n") + "\n";
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// src/generators/db-init.ts
|
|
739
|
-
var INITIAL_PERMISSIONS = `const initialPermissions = [
|
|
740
|
-
{
|
|
741
|
-
id: 'perm_user_view',
|
|
742
|
-
code: 'user:view',
|
|
743
|
-
name: '\u67E5\u770B\u7528\u6237',
|
|
744
|
-
label: '\u67E5\u770B\u7528\u6237',
|
|
745
|
-
category: 'user',
|
|
746
|
-
sortOrder: 1,
|
|
747
|
-
},
|
|
748
|
-
{
|
|
749
|
-
id: 'perm_user_create',
|
|
750
|
-
code: 'user:create',
|
|
751
|
-
name: '\u521B\u5EFA\u7528\u6237',
|
|
752
|
-
label: '\u521B\u5EFA\u7528\u6237',
|
|
753
|
-
category: 'user',
|
|
754
|
-
sortOrder: 2,
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
id: 'perm_user_edit',
|
|
758
|
-
code: 'user:edit',
|
|
759
|
-
name: '\u7F16\u8F91\u7528\u6237',
|
|
760
|
-
label: '\u7F16\u8F91\u7528\u6237',
|
|
761
|
-
category: 'user',
|
|
762
|
-
sortOrder: 3,
|
|
763
|
-
},
|
|
764
|
-
{
|
|
765
|
-
id: 'perm_user_delete',
|
|
766
|
-
code: 'user:delete',
|
|
767
|
-
name: '\u5220\u9664\u7528\u6237',
|
|
768
|
-
label: '\u5220\u9664\u7528\u6237',
|
|
769
|
-
category: 'user',
|
|
770
|
-
sortOrder: 4,
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
id: 'perm_content_view',
|
|
774
|
-
code: 'content:view',
|
|
775
|
-
name: '\u67E5\u770B\u5185\u5BB9',
|
|
776
|
-
label: '\u67E5\u770B\u5185\u5BB9',
|
|
777
|
-
category: 'content',
|
|
778
|
-
sortOrder: 1,
|
|
779
|
-
},
|
|
780
|
-
{
|
|
781
|
-
id: 'perm_content_create',
|
|
782
|
-
code: 'content:create',
|
|
783
|
-
name: '\u521B\u5EFA\u5185\u5BB9',
|
|
784
|
-
label: '\u521B\u5EFA\u5185\u5BB9',
|
|
785
|
-
category: 'content',
|
|
786
|
-
sortOrder: 2,
|
|
787
|
-
},
|
|
788
|
-
{
|
|
789
|
-
id: 'perm_content_edit',
|
|
790
|
-
code: 'content:edit',
|
|
791
|
-
name: '\u7F16\u8F91\u5185\u5BB9',
|
|
792
|
-
label: '\u7F16\u8F91\u5185\u5BB9',
|
|
793
|
-
category: 'content',
|
|
794
|
-
sortOrder: 3,
|
|
795
|
-
},
|
|
796
|
-
{
|
|
797
|
-
id: 'perm_content_delete',
|
|
798
|
-
code: 'content:delete',
|
|
799
|
-
name: '\u5220\u9664\u5185\u5BB9',
|
|
800
|
-
label: '\u5220\u9664\u5185\u5BB9',
|
|
801
|
-
category: 'content',
|
|
802
|
-
sortOrder: 4,
|
|
803
|
-
},
|
|
804
|
-
{
|
|
805
|
-
id: 'perm_system_settings',
|
|
806
|
-
code: 'system:settings',
|
|
807
|
-
name: '\u7CFB\u7EDF\u8BBE\u7F6E',
|
|
808
|
-
label: '\u7CFB\u7EDF\u8BBE\u7F6E',
|
|
809
|
-
category: 'system',
|
|
810
|
-
sortOrder: 1,
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
id: 'perm_system_logs',
|
|
814
|
-
code: 'system:logs',
|
|
815
|
-
name: '\u7CFB\u7EDF\u65E5\u5FD7',
|
|
816
|
-
label: '\u7CFB\u7EDF\u65E5\u5FD7',
|
|
817
|
-
category: 'system',
|
|
818
|
-
sortOrder: 2,
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
id: 'perm_system_monitor',
|
|
822
|
-
code: 'system:monitor',
|
|
823
|
-
name: '\u7CFB\u7EDF\u76D1\u63A7',
|
|
824
|
-
label: '\u7CFB\u7EDF\u76D1\u63A7',
|
|
825
|
-
category: 'system',
|
|
826
|
-
sortOrder: 3,
|
|
827
|
-
},
|
|
828
|
-
{
|
|
829
|
-
id: 'perm_data_export',
|
|
830
|
-
code: 'data:export',
|
|
831
|
-
name: '\u6570\u636E\u5BFC\u51FA',
|
|
832
|
-
label: '\u6570\u636E\u5BFC\u51FA',
|
|
833
|
-
category: 'data',
|
|
834
|
-
sortOrder: 1,
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
id: 'perm_data_import',
|
|
838
|
-
code: 'data:import',
|
|
839
|
-
name: '\u6570\u636E\u5BFC\u5165',
|
|
840
|
-
label: '\u6570\u636E\u5BFC\u5165',
|
|
841
|
-
category: 'data',
|
|
842
|
-
sortOrder: 2,
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
id: 'perm_order_view',
|
|
846
|
-
code: 'order:view',
|
|
847
|
-
name: '\u67E5\u770B\u8BA2\u5355',
|
|
848
|
-
label: '\u67E5\u770B\u8BA2\u5355',
|
|
849
|
-
category: 'order',
|
|
850
|
-
sortOrder: 1,
|
|
851
|
-
},
|
|
852
|
-
{
|
|
853
|
-
id: 'perm_order_create',
|
|
854
|
-
code: 'order:create',
|
|
855
|
-
name: '\u521B\u5EFA\u8BA2\u5355',
|
|
856
|
-
label: '\u521B\u5EFA\u8BA2\u5355',
|
|
857
|
-
category: 'order',
|
|
858
|
-
sortOrder: 2,
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
id: 'perm_order_edit',
|
|
862
|
-
code: 'order:edit',
|
|
863
|
-
name: '\u7F16\u8F91\u8BA2\u5355',
|
|
864
|
-
label: '\u7F16\u8F91\u8BA2\u5355',
|
|
865
|
-
category: 'order',
|
|
866
|
-
sortOrder: 3,
|
|
867
|
-
},
|
|
868
|
-
{
|
|
869
|
-
id: 'perm_order_delete',
|
|
870
|
-
code: 'order:delete',
|
|
871
|
-
name: '\u5220\u9664\u8BA2\u5355',
|
|
872
|
-
label: '\u5220\u9664\u8BA2\u5355',
|
|
873
|
-
category: 'order',
|
|
874
|
-
sortOrder: 4,
|
|
875
|
-
},
|
|
876
|
-
{
|
|
877
|
-
id: 'perm_order_process',
|
|
878
|
-
code: 'order:process',
|
|
879
|
-
name: '\u5904\u7406\u8BA2\u5355',
|
|
880
|
-
label: '\u5904\u7406\u8BA2\u5355',
|
|
881
|
-
category: 'order',
|
|
882
|
-
sortOrder: 5,
|
|
883
|
-
},
|
|
884
|
-
{
|
|
885
|
-
id: 'perm_ticket_view',
|
|
886
|
-
code: 'ticket:view',
|
|
887
|
-
name: '\u67E5\u770B\u5DE5\u5355',
|
|
888
|
-
label: '\u67E5\u770B\u5DE5\u5355',
|
|
889
|
-
category: 'ticket',
|
|
890
|
-
sortOrder: 1,
|
|
891
|
-
},
|
|
892
|
-
{
|
|
893
|
-
id: 'perm_ticket_create',
|
|
894
|
-
code: 'ticket:create',
|
|
895
|
-
name: '\u521B\u5EFA\u5DE5\u5355',
|
|
896
|
-
label: '\u521B\u5EFA\u5DE5\u5355',
|
|
897
|
-
category: 'ticket',
|
|
898
|
-
sortOrder: 2,
|
|
899
|
-
},
|
|
900
|
-
{
|
|
901
|
-
id: 'perm_ticket_edit',
|
|
902
|
-
code: 'ticket:edit',
|
|
903
|
-
name: '\u7F16\u8F91\u5DE5\u5355',
|
|
904
|
-
label: '\u7F16\u8F91\u5DE5\u5355',
|
|
905
|
-
category: 'ticket',
|
|
906
|
-
sortOrder: 3,
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
id: 'perm_ticket_delete',
|
|
910
|
-
code: 'ticket:delete',
|
|
911
|
-
name: '\u5220\u9664\u5DE5\u5355',
|
|
912
|
-
label: '\u5220\u9664\u5DE5\u5355',
|
|
913
|
-
category: 'ticket',
|
|
914
|
-
sortOrder: 4,
|
|
915
|
-
},
|
|
916
|
-
{
|
|
917
|
-
id: 'perm_ticket_reply',
|
|
918
|
-
code: 'ticket:reply',
|
|
919
|
-
name: '\u56DE\u590D\u5DE5\u5355',
|
|
920
|
-
label: '\u56DE\u590D\u5DE5\u5355',
|
|
921
|
-
category: 'ticket',
|
|
922
|
-
sortOrder: 5,
|
|
923
|
-
},
|
|
924
|
-
{
|
|
925
|
-
id: 'perm_ticket_close',
|
|
926
|
-
code: 'ticket:close',
|
|
927
|
-
name: '\u5173\u95ED\u5DE5\u5355',
|
|
928
|
-
label: '\u5173\u95ED\u5DE5\u5355',
|
|
929
|
-
category: 'ticket',
|
|
930
|
-
sortOrder: 6,
|
|
931
|
-
},
|
|
932
|
-
{
|
|
933
|
-
id: 'perm_dispute_view',
|
|
934
|
-
code: 'dispute:view',
|
|
935
|
-
name: '\u67E5\u770B\u4E89\u8BAE',
|
|
936
|
-
label: '\u67E5\u770B\u4E89\u8BAE',
|
|
937
|
-
category: 'dispute',
|
|
938
|
-
sortOrder: 1,
|
|
939
|
-
},
|
|
940
|
-
{
|
|
941
|
-
id: 'perm_dispute_create',
|
|
942
|
-
code: 'dispute:create',
|
|
943
|
-
name: '\u521B\u5EFA\u4E89\u8BAE',
|
|
944
|
-
label: '\u521B\u5EFA\u4E89\u8BAE',
|
|
945
|
-
category: 'dispute',
|
|
946
|
-
sortOrder: 2,
|
|
947
|
-
},
|
|
948
|
-
{
|
|
949
|
-
id: 'perm_dispute_edit',
|
|
950
|
-
code: 'dispute:edit',
|
|
951
|
-
name: '\u7F16\u8F91\u4E89\u8BAE',
|
|
952
|
-
label: '\u7F16\u8F91\u4E89\u8BAE',
|
|
953
|
-
category: 'dispute',
|
|
954
|
-
sortOrder: 3,
|
|
955
|
-
},
|
|
956
|
-
{
|
|
957
|
-
id: 'perm_dispute_delete',
|
|
958
|
-
code: 'dispute:delete',
|
|
959
|
-
name: '\u5220\u9664\u4E89\u8BAE',
|
|
960
|
-
label: '\u5220\u9664\u4E89\u8BAE',
|
|
961
|
-
category: 'dispute',
|
|
962
|
-
sortOrder: 4,
|
|
963
|
-
},
|
|
964
|
-
{
|
|
965
|
-
id: 'perm_dispute_resolve',
|
|
966
|
-
code: 'dispute:resolve',
|
|
967
|
-
name: '\u89E3\u51B3\u4E89\u8BAE',
|
|
968
|
-
label: '\u89E3\u51B3\u4E89\u8BAE',
|
|
969
|
-
category: 'dispute',
|
|
970
|
-
sortOrder: 5,
|
|
971
|
-
},
|
|
972
|
-
{
|
|
973
|
-
id: 'perm_role_view',
|
|
974
|
-
code: 'role:view',
|
|
975
|
-
name: '\u67E5\u770B\u89D2\u8272',
|
|
976
|
-
label: '\u67E5\u770B\u89D2\u8272',
|
|
977
|
-
category: 'role',
|
|
978
|
-
sortOrder: 1,
|
|
979
|
-
},
|
|
980
|
-
{
|
|
981
|
-
id: 'perm_role_create',
|
|
982
|
-
code: 'role:create',
|
|
983
|
-
name: '\u521B\u5EFA\u89D2\u8272',
|
|
984
|
-
label: '\u521B\u5EFA\u89D2\u8272',
|
|
985
|
-
category: 'role',
|
|
986
|
-
sortOrder: 2,
|
|
987
|
-
},
|
|
988
|
-
{
|
|
989
|
-
id: 'perm_role_edit',
|
|
990
|
-
code: 'role:edit',
|
|
991
|
-
name: '\u7F16\u8F91\u89D2\u8272',
|
|
992
|
-
label: '\u7F16\u8F91\u89D2\u8272',
|
|
993
|
-
category: 'role',
|
|
994
|
-
sortOrder: 3,
|
|
995
|
-
},
|
|
996
|
-
{
|
|
997
|
-
id: 'perm_role_delete',
|
|
998
|
-
code: 'role:delete',
|
|
999
|
-
name: '\u5220\u9664\u89D2\u8272',
|
|
1000
|
-
label: '\u5220\u9664\u89D2\u8272',
|
|
1001
|
-
category: 'role',
|
|
1002
|
-
sortOrder: 4,
|
|
1003
|
-
},
|
|
1004
|
-
]`;
|
|
1005
|
-
var INITIAL_ROLES = `const initialRoles = [
|
|
1006
|
-
{
|
|
1007
|
-
id: 'role_super_admin',
|
|
1008
|
-
code: 'super_admin',
|
|
1009
|
-
name: '\u8D85\u7EA7\u7BA1\u7406\u5458',
|
|
1010
|
-
label: '\u8D85\u7EA7\u7BA1\u7406\u5458',
|
|
1011
|
-
isSystem: true,
|
|
1012
|
-
sortOrder: 1,
|
|
1013
|
-
},
|
|
1014
|
-
{
|
|
1015
|
-
id: 'role_customer_service',
|
|
1016
|
-
code: 'customer_service',
|
|
1017
|
-
name: '\u5BA2\u670D\u4EBA\u5458',
|
|
1018
|
-
label: '\u5BA2\u670D\u4EBA\u5458',
|
|
1019
|
-
isSystem: true,
|
|
1020
|
-
sortOrder: 2,
|
|
1021
|
-
},
|
|
1022
|
-
{
|
|
1023
|
-
id: 'role_user',
|
|
1024
|
-
code: 'user',
|
|
1025
|
-
name: '\u666E\u901A\u7528\u6237',
|
|
1026
|
-
label: '\u666E\u901A\u7528\u6237',
|
|
1027
|
-
isSystem: true,
|
|
1028
|
-
sortOrder: 3,
|
|
1029
|
-
},
|
|
1030
|
-
]`;
|
|
1031
|
-
var INITIAL_ROLE_PERMISSIONS = `const initialRolePermissions = [
|
|
1032
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_user_view' },
|
|
1033
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_user_create' },
|
|
1034
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_user_edit' },
|
|
1035
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_user_delete' },
|
|
1036
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_content_view' },
|
|
1037
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_content_create' },
|
|
1038
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_content_edit' },
|
|
1039
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_content_delete' },
|
|
1040
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_system_settings' },
|
|
1041
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_system_logs' },
|
|
1042
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_system_monitor' },
|
|
1043
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_data_export' },
|
|
1044
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_data_import' },
|
|
1045
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_order_view' },
|
|
1046
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_order_create' },
|
|
1047
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_order_edit' },
|
|
1048
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_order_delete' },
|
|
1049
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_order_process' },
|
|
1050
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_ticket_view' },
|
|
1051
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_ticket_create' },
|
|
1052
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_ticket_edit' },
|
|
1053
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_ticket_delete' },
|
|
1054
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_ticket_reply' },
|
|
1055
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_ticket_close' },
|
|
1056
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_dispute_view' },
|
|
1057
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_dispute_create' },
|
|
1058
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_dispute_edit' },
|
|
1059
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_dispute_delete' },
|
|
1060
|
-
{ roleId: 'role_super_admin', permissionId: 'perm_dispute_resolve' },
|
|
1061
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_content_view' },
|
|
1062
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_order_view' },
|
|
1063
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_order_create' },
|
|
1064
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_order_edit' },
|
|
1065
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_order_delete' },
|
|
1066
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_order_process' },
|
|
1067
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_ticket_view' },
|
|
1068
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_ticket_create' },
|
|
1069
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_ticket_edit' },
|
|
1070
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_ticket_delete' },
|
|
1071
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_ticket_reply' },
|
|
1072
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_ticket_close' },
|
|
1073
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_dispute_view' },
|
|
1074
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_dispute_create' },
|
|
1075
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_dispute_edit' },
|
|
1076
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_dispute_delete' },
|
|
1077
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_dispute_resolve' },
|
|
1078
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_data_export' },
|
|
1079
|
-
{ roleId: 'role_customer_service', permissionId: 'perm_system_logs' },
|
|
1080
|
-
{ roleId: 'role_user', permissionId: 'perm_content_view' },
|
|
1081
|
-
{ roleId: 'role_user', permissionId: 'perm_order_view' },
|
|
1082
|
-
]`;
|
|
1083
|
-
function generateDbInit(resolved) {
|
|
1084
|
-
const activeSeeds = [];
|
|
1085
|
-
for (const [name, manifest] of resolved.modules) {
|
|
1086
|
-
if (manifest.dbSchemas?.hasSeed && manifest.dbSchemas.seed) {
|
|
1087
|
-
activeSeeds.push({
|
|
1088
|
-
moduleDir: `module-${name}`,
|
|
1089
|
-
serviceFile: manifest.dbSchemas.seed.serviceFile,
|
|
1090
|
-
functionName: manifest.dbSchemas.seed.functionName
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
if (!resolved.hasPermission) {
|
|
1095
|
-
return generateMinimalDbInit(activeSeeds);
|
|
1096
|
-
}
|
|
1097
|
-
return generateFullDbInit(activeSeeds);
|
|
1098
|
-
}
|
|
1099
|
-
function generateMinimalDbInit(activeSeeds) {
|
|
1100
|
-
const seedCalls = activeSeeds.map((s) => ` import('../${s.moduleDir}/services/${s.serviceFile}').then(m => m.${s.functionName}()),`).join("\n");
|
|
1101
|
-
const seedBlock = activeSeeds.length > 0 ? `
|
|
1102
|
-
log.info({}, 'Seeding module data...')
|
|
1103
|
-
await Promise.all([
|
|
1104
|
-
${seedCalls}
|
|
1105
|
-
])
|
|
1106
|
-
log.info({}, 'Module data seeding complete!')` : "";
|
|
1107
|
-
return `import { getDb } from './driver'
|
|
1108
|
-
import { logger } from '../utils/logger'
|
|
1109
|
-
|
|
1110
|
-
const log = logger.db()
|
|
1111
|
-
|
|
1112
|
-
export async function initializeDatabase() {
|
|
1113
|
-
await getDb()
|
|
1114
|
-
|
|
1115
|
-
log.info({}, 'Initializing database...')
|
|
1116
|
-
|
|
1117
|
-
log.info({}, 'Database initialization complete!')${seedBlock}
|
|
1118
|
-
}
|
|
1119
|
-
`;
|
|
1120
|
-
}
|
|
1121
|
-
function generateFullDbInit(activeSeeds) {
|
|
1122
|
-
const seedCalls = activeSeeds.map((s) => ` import('../${s.moduleDir}/services/${s.serviceFile}').then(m => m.${s.functionName}()),`).join("\n");
|
|
1123
|
-
const seedBlock = activeSeeds.length > 0 ? `
|
|
1124
|
-
log.info({}, 'Seeding module data...')
|
|
1125
|
-
await Promise.all([
|
|
1126
|
-
${seedCalls}
|
|
1127
|
-
])
|
|
1128
|
-
log.info({}, 'Module data seeding complete!')` : "";
|
|
1129
|
-
return `import { getDb } from './driver'
|
|
1130
|
-
import { permissions, roles, rolePermissions } from './schema'
|
|
1131
|
-
import { logger } from '../utils/logger'
|
|
1132
|
-
|
|
1133
|
-
const log = logger.db()
|
|
1134
|
-
|
|
1135
|
-
${INITIAL_PERMISSIONS}
|
|
1136
|
-
|
|
1137
|
-
${INITIAL_ROLES}
|
|
1138
|
-
|
|
1139
|
-
${INITIAL_ROLE_PERMISSIONS}
|
|
1140
|
-
|
|
1141
|
-
export async function initializeDatabase() {
|
|
1142
|
-
const db = await getDb()
|
|
1143
|
-
|
|
1144
|
-
log.info({}, 'Initializing database...')
|
|
1145
|
-
|
|
1146
|
-
const existingPermissions = await db.select().from(permissions)
|
|
1147
|
-
if (existingPermissions.length === 0) {
|
|
1148
|
-
log.info({}, 'Inserting initial permissions...')
|
|
1149
|
-
await db.insert(permissions).values(
|
|
1150
|
-
initialPermissions.map(p => ({
|
|
1151
|
-
...p,
|
|
1152
|
-
description: null,
|
|
1153
|
-
isActive: true,
|
|
1154
|
-
createdAt: new Date(),
|
|
1155
|
-
updatedAt: new Date(),
|
|
1156
|
-
}))
|
|
1157
|
-
)
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
const existingRoles = await db.select().from(roles)
|
|
1161
|
-
if (existingRoles.length === 0) {
|
|
1162
|
-
log.info({}, 'Inserting initial roles...')
|
|
1163
|
-
await db.insert(roles).values(
|
|
1164
|
-
initialRoles.map(r => ({
|
|
1165
|
-
...r,
|
|
1166
|
-
description: null,
|
|
1167
|
-
isActive: true,
|
|
1168
|
-
createdAt: new Date(),
|
|
1169
|
-
updatedAt: new Date(),
|
|
1170
|
-
}))
|
|
1171
|
-
)
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
const existingRolePermissions = await db.select().from(rolePermissions)
|
|
1175
|
-
if (existingRolePermissions.length === 0) {
|
|
1176
|
-
log.info({}, 'Inserting initial role permissions...')
|
|
1177
|
-
await db.insert(rolePermissions).values(
|
|
1178
|
-
initialRolePermissions.map(rp => ({
|
|
1179
|
-
...rp,
|
|
1180
|
-
createdAt: new Date(),
|
|
1181
|
-
}))
|
|
1182
|
-
)
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
log.info({}, 'Database initialization complete!')${seedBlock}
|
|
1186
|
-
}
|
|
1187
|
-
`;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// src/generators/server-app.ts
|
|
1191
|
-
function generateServerApp(resolved) {
|
|
1192
|
-
const useRealtime = resolved.hasSSE || resolved.hasWebSocket;
|
|
1193
|
-
const useAuditLog = resolved.hasPermission;
|
|
1194
|
-
const useCaptcha = resolved.hasCaptcha;
|
|
1195
|
-
const standaloneRoutes = getStandaloneRoutes(resolved);
|
|
1196
|
-
const imports = [
|
|
1197
|
-
`import { OpenAPIHono } from '@hono/zod-openapi'`,
|
|
1198
|
-
``,
|
|
1199
|
-
`import { HTTPException } from 'hono/http-exception'`,
|
|
1200
|
-
`import type { ContentfulStatusCode } from 'hono/utils/http-status'`,
|
|
1201
|
-
`import { ZodError } from 'zod'`,
|
|
1202
|
-
`import type { AppBindings, CreateAppOptions } from './types/bindings'`,
|
|
1203
|
-
`import { AppError } from './utils/app-error'`,
|
|
1204
|
-
`import { autoRegisterRealtime } from './core/realtime-scanner'`,
|
|
1205
|
-
`import { corsMiddleware, loggerMiddleware, errorHandlerMiddleware } from './middleware'`
|
|
1206
|
-
];
|
|
1207
|
-
if (useRealtime) {
|
|
1208
|
-
imports.push(`import { realtimeEnvMiddleware } from './middleware/realtime-env'`);
|
|
1209
|
-
}
|
|
1210
|
-
if (useAuditLog) {
|
|
1211
|
-
imports.push(`import { auditLogMiddleware } from './middleware/audit-log'`);
|
|
1212
|
-
}
|
|
1213
|
-
if (useCaptcha) {
|
|
1214
|
-
imports.push(`import { captchaMiddleware } from './middleware/captcha'`);
|
|
1215
|
-
}
|
|
1216
|
-
imports.push(
|
|
1217
|
-
`import { createModuleLoggerSync } from './utils/logger'`,
|
|
1218
|
-
`import { adminApiRoutes, clientApiRoutes } from './route-registry'`
|
|
1219
|
-
);
|
|
1220
|
-
const standaloneImportSet = /* @__PURE__ */ new Set();
|
|
1221
|
-
for (const [name, manifest] of resolved.modules) {
|
|
1222
|
-
if (manifest.routes.standalone) {
|
|
1223
|
-
const { importPath, exportName } = manifest.routes.standalone;
|
|
1224
|
-
const moduleDir = `module-${name}`;
|
|
1225
|
-
const relPath = importPath.replace(/^\.\//, "");
|
|
1226
|
-
const stmt = `import { ${exportName} } from './${moduleDir}/${relPath}'`;
|
|
1227
|
-
if (!standaloneImportSet.has(stmt)) {
|
|
1228
|
-
standaloneImportSet.add(stmt);
|
|
1229
|
-
imports.push(stmt);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
const middlewareChain = [
|
|
1234
|
-
`.use('*', errorHandlerMiddleware())`,
|
|
1235
|
-
`.use('*', loggerMiddleware())`,
|
|
1236
|
-
`.use('*', corsMiddleware())`
|
|
1237
|
-
];
|
|
1238
|
-
if (useRealtime) {
|
|
1239
|
-
middlewareChain.push(`.use('*', realtimeEnvMiddleware())`);
|
|
1240
|
-
}
|
|
1241
|
-
if (useAuditLog) {
|
|
1242
|
-
middlewareChain.push(`.use('/api/*', auditLogMiddleware())`);
|
|
1243
|
-
}
|
|
1244
|
-
if (useCaptcha) {
|
|
1245
|
-
middlewareChain.push(
|
|
1246
|
-
`.use(
|
|
1247
|
-
'/api/admin/*',
|
|
1248
|
-
captchaMiddleware({
|
|
1249
|
-
maxRequests: 20,
|
|
1250
|
-
windowMs: 60000,
|
|
1251
|
-
})
|
|
1252
|
-
)`
|
|
1253
|
-
);
|
|
1254
|
-
}
|
|
1255
|
-
const routes = [`.route('/', clientApiRoutes)`, `.route('/', adminApiRoutes)`];
|
|
1256
|
-
for (const sr of standaloneRoutes) {
|
|
1257
|
-
routes.push(`.route('${sr.mountPath}', ${sr.localName})`);
|
|
1258
|
-
}
|
|
1259
|
-
const indent = " ";
|
|
1260
|
-
const chain = [...middlewareChain, ...routes].join(`
|
|
1261
|
-
${indent}`);
|
|
1262
|
-
return `${imports.join("\n")}
|
|
1263
|
-
|
|
1264
|
-
export { type AppBindings, type CreateAppOptions } from './types/bindings'
|
|
1265
|
-
|
|
1266
|
-
export function createApp<T extends AppBindings = AppBindings>(_options: CreateAppOptions = {}) {
|
|
1267
|
-
const app = new OpenAPIHono<{ Bindings: T }>()
|
|
1268
|
-
${chain}
|
|
1269
|
-
.get('/health', async c => {
|
|
1270
|
-
try {
|
|
1271
|
-
const { getDb } = await import('./db')
|
|
1272
|
-
await getDb()
|
|
1273
|
-
return c.json({ status: 'ok', timestamp: new Date().toISOString(), db: 'connected' })
|
|
1274
|
-
} catch {
|
|
1275
|
-
return c.json({ status: 'ok', timestamp: new Date().toISOString(), db: 'not configured' })
|
|
1276
|
-
}
|
|
1277
|
-
})
|
|
1278
|
-
.post('/api/__test__/cleanup', async c => {
|
|
1279
|
-
try {
|
|
1280
|
-
const { cleanupTestDatabase } = await import('./db/test-setup')
|
|
1281
|
-
await cleanupTestDatabase()
|
|
1282
|
-
return c.json({ success: true as const, message: 'Database cleaned up' })
|
|
1283
|
-
} catch (error) {
|
|
1284
|
-
console.error('Error during database cleanup:', error)
|
|
1285
|
-
return c.json({ success: false as const, message: 'Failed to cleanup database' }, 500)
|
|
1286
|
-
}
|
|
1287
|
-
})
|
|
1288
|
-
|
|
1289
|
-
autoRegisterRealtime(app as unknown as Parameters<typeof autoRegisterRealtime>[0])
|
|
1290
|
-
|
|
1291
|
-
app.onError((err, c) => {
|
|
1292
|
-
const log = createModuleLoggerSync('api')
|
|
1293
|
-
c.res.headers.set('Content-Type', 'application/json')
|
|
1294
|
-
|
|
1295
|
-
if (AppError.isAppError(err)) {
|
|
1296
|
-
return c.json(
|
|
1297
|
-
{
|
|
1298
|
-
success: false as const,
|
|
1299
|
-
error: err.message,
|
|
1300
|
-
status: err.statusCode,
|
|
1301
|
-
details: err.details,
|
|
1302
|
-
},
|
|
1303
|
-
err.statusCode as ContentfulStatusCode
|
|
1304
|
-
)
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
if (err instanceof HTTPException) {
|
|
1308
|
-
return c.json(
|
|
1309
|
-
{ success: false as const, error: err.message, status: err.status },
|
|
1310
|
-
err.status as ContentfulStatusCode
|
|
1311
|
-
)
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
if (err instanceof ZodError) {
|
|
1315
|
-
const details = err.issues.map(issue => ({
|
|
1316
|
-
field: issue.path.join('.'),
|
|
1317
|
-
message: issue.message,
|
|
1318
|
-
}))
|
|
1319
|
-
return c.json(
|
|
1320
|
-
{ success: false as const, error: 'Validation failed', status: 400, details },
|
|
1321
|
-
400
|
|
1322
|
-
)
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
log.error({ err, path: c.req.path }, 'Unhandled error')
|
|
1326
|
-
return c.json(
|
|
1327
|
-
{ success: false as const, error: err.message || 'Internal server error', status: 500 },
|
|
1328
|
-
500
|
|
1329
|
-
)
|
|
1330
|
-
})
|
|
1331
|
-
|
|
1332
|
-
return app
|
|
1333
|
-
}
|
|
1334
|
-
export type AdminApiType = typeof adminApiRoutes
|
|
1335
|
-
export type ClientApiType = typeof clientApiRoutes
|
|
1336
|
-
export type AppType = ReturnType<typeof createApp>
|
|
1337
|
-
`;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
// src/generators/shared-modules-index.ts
|
|
1341
|
-
var MODULE_EXPORTS = {
|
|
1342
|
-
chat: {
|
|
1343
|
-
namedExports: ["ChatProtocolSchema", "type ChatProtocol"]
|
|
1344
|
-
},
|
|
1345
|
-
todos: {
|
|
1346
|
-
namedExports: [
|
|
1347
|
-
"TodoSchema",
|
|
1348
|
-
"TodoStatusSchema",
|
|
1349
|
-
"CreateTodoSchema",
|
|
1350
|
-
"UpdateTodoSchema",
|
|
1351
|
-
"TodoIdSchema",
|
|
1352
|
-
"type Todo",
|
|
1353
|
-
"type TodoStatus",
|
|
1354
|
-
"type CreateTodoInput",
|
|
1355
|
-
"type UpdateTodoInput"
|
|
1356
|
-
]
|
|
1357
|
-
},
|
|
1358
|
-
files: {
|
|
1359
|
-
namedExports: [
|
|
1360
|
-
"FileDownloadSchema",
|
|
1361
|
-
"PrivateFileQuerySchema",
|
|
1362
|
-
"PublicFileUrlSchema",
|
|
1363
|
-
"PrivateFileUrlSchema",
|
|
1364
|
-
"GenerateUrlRequestSchema",
|
|
1365
|
-
"FileUrlResponseSchema",
|
|
1366
|
-
"EmptySchema"
|
|
1367
|
-
]
|
|
1368
|
-
},
|
|
1369
|
-
notifications: {
|
|
1370
|
-
namedExports: [
|
|
1371
|
-
"NotificationSchema",
|
|
1372
|
-
"NotificationTypeSchema",
|
|
1373
|
-
"CreateNotificationSchema",
|
|
1374
|
-
"NotificationListQuerySchema",
|
|
1375
|
-
"SSEEventSchema",
|
|
1376
|
-
"AppSSEProtocolSchema",
|
|
1377
|
-
"type AppNotification",
|
|
1378
|
-
"type NotificationType",
|
|
1379
|
-
"type CreateNotificationInput",
|
|
1380
|
-
"type NotificationListQuery",
|
|
1381
|
-
"type SSEEvent",
|
|
1382
|
-
"type AppSSEProtocol"
|
|
1383
|
-
]
|
|
1384
|
-
},
|
|
1385
|
-
admin: {
|
|
1386
|
-
namedExports: [
|
|
1387
|
-
"SystemStatsSchema",
|
|
1388
|
-
"HealthCheckSchema",
|
|
1389
|
-
"RecentActivityItemSchema",
|
|
1390
|
-
"RecentActivitySchema",
|
|
1391
|
-
"AuthUserSchema",
|
|
1392
|
-
"ClearTodosResultSchema",
|
|
1393
|
-
"type SystemStats",
|
|
1394
|
-
"type HealthCheck",
|
|
1395
|
-
"type RecentActivityItem",
|
|
1396
|
-
"type AuthUserResponse",
|
|
1397
|
-
"type ClearTodosResult"
|
|
1398
|
-
]
|
|
1399
|
-
},
|
|
1400
|
-
permission: {
|
|
1401
|
-
namedExports: [
|
|
1402
|
-
"RoleEnum",
|
|
1403
|
-
"RoleInfoSchema",
|
|
1404
|
-
"PermissionInfoSchema",
|
|
1405
|
-
"UserPermissionsSchema",
|
|
1406
|
-
"RoleListSchema",
|
|
1407
|
-
"PermissionListSchema",
|
|
1408
|
-
"Role",
|
|
1409
|
-
"Permission",
|
|
1410
|
-
"ROLE_PERMISSIONS",
|
|
1411
|
-
"ROLE_LABELS",
|
|
1412
|
-
"PERMISSION_LABELS",
|
|
1413
|
-
"PERMISSION_CATEGORIES",
|
|
1414
|
-
"getPermissionsByRole",
|
|
1415
|
-
"hasPermission",
|
|
1416
|
-
"hasAnyPermission",
|
|
1417
|
-
"hasAllPermissions",
|
|
1418
|
-
"type PermissionRoleType",
|
|
1419
|
-
"type RoleInfo",
|
|
1420
|
-
"type PermissionInfo",
|
|
1421
|
-
"type UserPermissions"
|
|
1422
|
-
]
|
|
1423
|
-
},
|
|
1424
|
-
auth: {
|
|
1425
|
-
namedExports: [
|
|
1426
|
-
"DeveloperProfileSchema",
|
|
1427
|
-
"LoginSchema",
|
|
1428
|
-
"RegisterSchema",
|
|
1429
|
-
"TokenResponseSchema",
|
|
1430
|
-
"type DeveloperProfile",
|
|
1431
|
-
"type LoginInput",
|
|
1432
|
-
"type RegisterInput",
|
|
1433
|
-
"type TokenResponse"
|
|
1434
|
-
]
|
|
1435
|
-
},
|
|
1436
|
-
plugin: {
|
|
1437
|
-
namedExports: [
|
|
1438
|
-
"PluginSchema",
|
|
1439
|
-
"PluginStatusSchema",
|
|
1440
|
-
"CreatePluginSchema",
|
|
1441
|
-
"UpdatePluginSchema",
|
|
1442
|
-
"PluginVersionStatusSchema",
|
|
1443
|
-
"VersionSchema",
|
|
1444
|
-
"CategorySchema",
|
|
1445
|
-
"ReviewSchema",
|
|
1446
|
-
"CreateReviewSchema",
|
|
1447
|
-
"MarketplaceStatsSchema",
|
|
1448
|
-
"PluginListResponseSchema",
|
|
1449
|
-
"AdminPluginSchema",
|
|
1450
|
-
"AdminDashboardStatsSchema",
|
|
1451
|
-
"PluginListQuerySchema",
|
|
1452
|
-
"PluginSlugSchema",
|
|
1453
|
-
"type Plugin",
|
|
1454
|
-
"type PluginStatus",
|
|
1455
|
-
"type CreatePluginInput",
|
|
1456
|
-
"type UpdatePluginInput",
|
|
1457
|
-
"type PluginVersionStatus",
|
|
1458
|
-
"type Version",
|
|
1459
|
-
"type Category",
|
|
1460
|
-
"type Review",
|
|
1461
|
-
"type CreateReviewInput",
|
|
1462
|
-
"type MarketplaceStats",
|
|
1463
|
-
"type PluginListResponse",
|
|
1464
|
-
"type AdminPlugin",
|
|
1465
|
-
"type AdminDashboardStats",
|
|
1466
|
-
"type PluginListQuery"
|
|
1467
|
-
]
|
|
1468
|
-
},
|
|
1469
|
-
merchant: {
|
|
1470
|
-
namedExports: [
|
|
1471
|
-
"ProductSchema",
|
|
1472
|
-
"CreateProductSchema",
|
|
1473
|
-
"UpdateProductSchema",
|
|
1474
|
-
"ProductListSchema",
|
|
1475
|
-
"type Product",
|
|
1476
|
-
"type CreateProductInput",
|
|
1477
|
-
"type UpdateProductInput"
|
|
1478
|
-
]
|
|
1479
|
-
},
|
|
1480
|
-
tenant: {
|
|
1481
|
-
namedExports: [
|
|
1482
|
-
"TenantSchema",
|
|
1483
|
-
"TenantStatusSchema",
|
|
1484
|
-
"TenantPlanSchema",
|
|
1485
|
-
"TenantSettingsSchema",
|
|
1486
|
-
"CreateTenantSchema",
|
|
1487
|
-
"UpdateTenantSchema",
|
|
1488
|
-
"TenantIdSchema",
|
|
1489
|
-
"TenantSlugSchema",
|
|
1490
|
-
"TenantListResponseSchema",
|
|
1491
|
-
"TenantQuerySchema",
|
|
1492
|
-
"TenantIdResponseSchema",
|
|
1493
|
-
"type Tenant",
|
|
1494
|
-
"type TenantStatus",
|
|
1495
|
-
"type TenantPlan",
|
|
1496
|
-
"type TenantSettings",
|
|
1497
|
-
"type CreateTenantInput",
|
|
1498
|
-
"type UpdateTenantInput",
|
|
1499
|
-
"type TenantId",
|
|
1500
|
-
"type TenantSlug",
|
|
1501
|
-
"type TenantListResponse",
|
|
1502
|
-
"type TenantQuery",
|
|
1503
|
-
"type TenantIdResponse"
|
|
1504
|
-
]
|
|
1505
|
-
},
|
|
1506
|
-
order: {
|
|
1507
|
-
namedExports: [
|
|
1508
|
-
"OrderStatusSchema",
|
|
1509
|
-
"OrderSchema",
|
|
1510
|
-
"CreateOrderSchema",
|
|
1511
|
-
"UpdateOrderSchema",
|
|
1512
|
-
"OrderListSchema",
|
|
1513
|
-
"OrderQuerySchema",
|
|
1514
|
-
"OrderDeleteResultSchema",
|
|
1515
|
-
"ProcessOrderSchema",
|
|
1516
|
-
"CancelOrderSchema",
|
|
1517
|
-
"RemoveCartItemResponseSchema",
|
|
1518
|
-
"ECommerceProductSchema",
|
|
1519
|
-
"ECommerceOrderStatusSchema",
|
|
1520
|
-
"ECommerceOrderSchema",
|
|
1521
|
-
"ECommerceOrderListSchema",
|
|
1522
|
-
"type OrderStatus",
|
|
1523
|
-
"type Order",
|
|
1524
|
-
"type CreateOrderInput",
|
|
1525
|
-
"type UpdateOrderInput",
|
|
1526
|
-
"type OrderDeleteResult",
|
|
1527
|
-
"type ProcessOrderInput",
|
|
1528
|
-
"type CancelOrderInput",
|
|
1529
|
-
"type OrderQueryInput",
|
|
1530
|
-
"type RemoveCartItemResponse",
|
|
1531
|
-
"type ECommerceProduct",
|
|
1532
|
-
"type ECommerceOrderStatus",
|
|
1533
|
-
"type ECommerceOrder"
|
|
1534
|
-
]
|
|
1535
|
-
},
|
|
1536
|
-
ticket: {
|
|
1537
|
-
namedExports: [
|
|
1538
|
-
"TicketStatusSchema",
|
|
1539
|
-
"TicketPrioritySchema",
|
|
1540
|
-
"TicketCategorySchema",
|
|
1541
|
-
"TicketReplySchema",
|
|
1542
|
-
"TicketSchema",
|
|
1543
|
-
"CreateTicketSchema",
|
|
1544
|
-
"UpdateTicketSchema",
|
|
1545
|
-
"ReplyTicketSchema",
|
|
1546
|
-
"TicketListSchema",
|
|
1547
|
-
"TicketDeleteResultSchema",
|
|
1548
|
-
"type TicketStatus",
|
|
1549
|
-
"type TicketPriority",
|
|
1550
|
-
"type TicketCategory",
|
|
1551
|
-
"type TicketReply",
|
|
1552
|
-
"type Ticket",
|
|
1553
|
-
"type CreateTicketInput",
|
|
1554
|
-
"type UpdateTicketInput",
|
|
1555
|
-
"type ReplyTicketInput",
|
|
1556
|
-
"type TicketDeleteResult"
|
|
1557
|
-
]
|
|
1558
|
-
},
|
|
1559
|
-
dispute: {
|
|
1560
|
-
namedExports: [
|
|
1561
|
-
"DisputeTypeSchema",
|
|
1562
|
-
"DisputeStatusSchema",
|
|
1563
|
-
"DisputeSchema",
|
|
1564
|
-
"CreateDisputeSchema",
|
|
1565
|
-
"UpdateDisputeSchema",
|
|
1566
|
-
"ResolveDisputeSchema",
|
|
1567
|
-
"DisputeListSchema",
|
|
1568
|
-
"DisputeDeleteResultSchema",
|
|
1569
|
-
"type DisputeType",
|
|
1570
|
-
"type DisputeStatus",
|
|
1571
|
-
"type Dispute",
|
|
1572
|
-
"type CreateDisputeInput",
|
|
1573
|
-
"type UpdateDisputeInput",
|
|
1574
|
-
"type ResolveDisputeInput",
|
|
1575
|
-
"type DisputeDeleteResult"
|
|
1576
|
-
]
|
|
1577
|
-
},
|
|
1578
|
-
content: {
|
|
1579
|
-
namedExports: [
|
|
1580
|
-
"ContentCategorySchema",
|
|
1581
|
-
"ContentStatusSchema",
|
|
1582
|
-
"ContentSchema",
|
|
1583
|
-
"CreateContentSchema",
|
|
1584
|
-
"UpdateContentSchema",
|
|
1585
|
-
"ContentListSchema",
|
|
1586
|
-
"ContentDeleteResultSchema",
|
|
1587
|
-
"type ContentCategory",
|
|
1588
|
-
"type ContentStatus",
|
|
1589
|
-
"type Content",
|
|
1590
|
-
"type CreateContentInput",
|
|
1591
|
-
"type UpdateContentInput",
|
|
1592
|
-
"type ContentDeleteResult"
|
|
1593
|
-
]
|
|
1594
|
-
},
|
|
1595
|
-
captcha: {
|
|
1596
|
-
namedExports: [
|
|
1597
|
-
"CaptchaResponseSchema",
|
|
1598
|
-
"VerifyCaptchaRequestSchema",
|
|
1599
|
-
"CaptchaVerifyResponseSchema",
|
|
1600
|
-
"type CaptchaResponse",
|
|
1601
|
-
"type VerifyCaptchaRequest",
|
|
1602
|
-
"type CaptchaVerifyResponse"
|
|
1603
|
-
]
|
|
1604
|
-
},
|
|
1605
|
-
dashboard: {
|
|
1606
|
-
namedExports: [
|
|
1607
|
-
"DashboardStatSchema",
|
|
1608
|
-
"RevenueDataSchema",
|
|
1609
|
-
"ActivityStatusSchema",
|
|
1610
|
-
"ActivitySchema",
|
|
1611
|
-
"DashboardResponseSchema",
|
|
1612
|
-
"type DashboardStat",
|
|
1613
|
-
"type RevenueData",
|
|
1614
|
-
"type ActivityStatus",
|
|
1615
|
-
"type Activity",
|
|
1616
|
-
"type DashboardResponse"
|
|
1617
|
-
]
|
|
1618
|
-
},
|
|
1619
|
-
cart: {
|
|
1620
|
-
namedExports: [
|
|
1621
|
-
"CartItemSchema",
|
|
1622
|
-
"CartSummarySchema",
|
|
1623
|
-
"CartResponseSchema",
|
|
1624
|
-
"AddCartItemSchema",
|
|
1625
|
-
"CartItemIdSchema",
|
|
1626
|
-
"type CartItem",
|
|
1627
|
-
"type CartSummary",
|
|
1628
|
-
"type CartResponse",
|
|
1629
|
-
"type AddCartItemInput"
|
|
1630
|
-
]
|
|
1631
|
-
},
|
|
1632
|
-
community: {
|
|
1633
|
-
namedExports: [
|
|
1634
|
-
"TopicStatusSchema",
|
|
1635
|
-
"TopicTagSchema",
|
|
1636
|
-
"TopicAuthorSchema",
|
|
1637
|
-
"TopicSchema",
|
|
1638
|
-
"TopicsResponseSchema",
|
|
1639
|
-
"ProfileStatsSchema",
|
|
1640
|
-
"ActivityTypeSchema",
|
|
1641
|
-
"ProfileActivitySchema",
|
|
1642
|
-
"ProfileResponseSchema",
|
|
1643
|
-
"type TopicStatus",
|
|
1644
|
-
"type TopicTag",
|
|
1645
|
-
"type TopicAuthor",
|
|
1646
|
-
"type Topic",
|
|
1647
|
-
"type ProfileStats",
|
|
1648
|
-
"type ActivityType",
|
|
1649
|
-
"type ProfileActivity",
|
|
1650
|
-
"type ProfileResponse"
|
|
1651
|
-
]
|
|
1652
|
-
},
|
|
1653
|
-
audit: {
|
|
1654
|
-
namedExports: [
|
|
1655
|
-
"ResourceTypeSchema",
|
|
1656
|
-
"ActionTypeSchema",
|
|
1657
|
-
"AuditLogSchema",
|
|
1658
|
-
"type AuditLogType"
|
|
1659
|
-
]
|
|
1660
|
-
},
|
|
1661
|
-
role: {
|
|
1662
|
-
namedExports: [
|
|
1663
|
-
"RoleSchema",
|
|
1664
|
-
"CreateRoleSchema",
|
|
1665
|
-
"UpdateRoleSchema",
|
|
1666
|
-
"UpdateRolePermissionsSchema",
|
|
1667
|
-
"RoleSuccessSchema",
|
|
1668
|
-
"type RoleDataType",
|
|
1669
|
-
"type CreateRoleType",
|
|
1670
|
-
"type UpdateRoleType"
|
|
1671
|
-
]
|
|
1672
|
-
}
|
|
1673
|
-
};
|
|
1674
|
-
function generateSharedModulesIndex(resolved) {
|
|
1675
|
-
const lines = [];
|
|
1676
|
-
const moduleOrder2 = [
|
|
1677
|
-
"chat",
|
|
1678
|
-
"todos",
|
|
1679
|
-
"file",
|
|
1680
|
-
"notifications",
|
|
1681
|
-
"admin",
|
|
1682
|
-
"permission",
|
|
1683
|
-
"auth",
|
|
1684
|
-
"plugin",
|
|
1685
|
-
"merchant",
|
|
1686
|
-
"tenant",
|
|
1687
|
-
"order",
|
|
1688
|
-
"ticket",
|
|
1689
|
-
"dispute",
|
|
1690
|
-
"content",
|
|
1691
|
-
"captcha"
|
|
1692
|
-
];
|
|
1693
|
-
const STANDALONE_SHARED_MODULES2 = {
|
|
1694
|
-
cart: ["CartPage.tsx"],
|
|
1695
|
-
community: ["TopicsPage.tsx"],
|
|
1696
|
-
dashboard: ["DashboardPage.tsx"]
|
|
1697
|
-
};
|
|
1698
|
-
for (const moduleName of moduleOrder2) {
|
|
1699
|
-
if (!resolved.modules.has(moduleName)) continue;
|
|
1700
|
-
const manifest = resolved.modules.get(moduleName);
|
|
1701
|
-
const exportKey = manifest.sharedSchemas?.path ?? moduleName;
|
|
1702
|
-
const exports = MODULE_EXPORTS[exportKey];
|
|
1703
|
-
if (!exports) continue;
|
|
1704
|
-
lines.push(`export {
|
|
1705
|
-
${exports.namedExports.join(",\n ")},
|
|
1706
|
-
} from './${exportKey}'`);
|
|
1707
|
-
}
|
|
1708
|
-
const additionalExported = /* @__PURE__ */ new Set();
|
|
1709
|
-
for (const moduleName of moduleOrder2) {
|
|
1710
|
-
if (!resolved.modules.has(moduleName)) continue;
|
|
1711
|
-
const manifest = resolved.modules.get(moduleName);
|
|
1712
|
-
if (manifest.sharedSchemas?.additionalPaths) {
|
|
1713
|
-
for (const extra of manifest.sharedSchemas.additionalPaths) {
|
|
1714
|
-
if (additionalExported.has(extra)) continue;
|
|
1715
|
-
additionalExported.add(extra);
|
|
1716
|
-
const exports = MODULE_EXPORTS[extra];
|
|
1717
|
-
if (!exports) continue;
|
|
1718
|
-
lines.push(`export {
|
|
1719
|
-
${exports.namedExports.join(",\n ")},
|
|
1720
|
-
} from './${extra}'`);
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
for (const [moduleName, requiredPages] of Object.entries(STANDALONE_SHARED_MODULES2)) {
|
|
1725
|
-
const hasRelevantPage = [...resolved.modules.values()].some(
|
|
1726
|
-
(m) => m.clientPages?.some((p) => requiredPages.includes(p.name + ".tsx")) ?? false
|
|
1727
|
-
);
|
|
1728
|
-
if (!hasRelevantPage) continue;
|
|
1729
|
-
const exports = MODULE_EXPORTS[moduleName];
|
|
1730
|
-
if (!exports) continue;
|
|
1731
|
-
lines.push(`export {
|
|
1732
|
-
${exports.namedExports.join(",\n ")},
|
|
1733
|
-
} from './${moduleName}'`);
|
|
1734
|
-
}
|
|
1735
|
-
return lines.join("\n") + "\n";
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// src/generators/shared-schemas-index.ts
|
|
1739
|
-
var MODULE_EXPORTS2 = {
|
|
1740
|
-
chat: {
|
|
1741
|
-
namedExports: [
|
|
1742
|
-
"ChatProtocolSchema",
|
|
1743
|
-
"WebSocketStatusSchema",
|
|
1744
|
-
"type ChatProtocol",
|
|
1745
|
-
"type WebSocketStatus"
|
|
1746
|
-
]
|
|
1747
|
-
},
|
|
1748
|
-
file: {
|
|
1749
|
-
namedExports: [
|
|
1750
|
-
"FileDownloadSchema",
|
|
1751
|
-
"PrivateFileQuerySchema",
|
|
1752
|
-
"PublicFileUrlSchema",
|
|
1753
|
-
"PrivateFileUrlSchema",
|
|
1754
|
-
"GenerateUrlRequestSchema",
|
|
1755
|
-
"FileUrlResponseSchema",
|
|
1756
|
-
"EmptySchema",
|
|
1757
|
-
"UploadResultSchema",
|
|
1758
|
-
"UploadFileBodySchema"
|
|
1759
|
-
]
|
|
1760
|
-
},
|
|
1761
|
-
todos: {
|
|
1762
|
-
namedExports: [
|
|
1763
|
-
"TodoSchema",
|
|
1764
|
-
"TodoStatusSchema",
|
|
1765
|
-
"CreateTodoSchema",
|
|
1766
|
-
"UpdateTodoSchema",
|
|
1767
|
-
"TodoIdSchema",
|
|
1768
|
-
"TodoIdResponseSchema",
|
|
1769
|
-
"TodoAttachmentSchema",
|
|
1770
|
-
"TodoAttachmentListSchema",
|
|
1771
|
-
"TodoWithAttachmentsSchema",
|
|
1772
|
-
"UploadFileSchema",
|
|
1773
|
-
"AttachmentIdResponseSchema",
|
|
1774
|
-
"type Todo",
|
|
1775
|
-
"type TodoStatus",
|
|
1776
|
-
"type CreateTodoInput",
|
|
1777
|
-
"type UpdateTodoInput",
|
|
1778
|
-
"type TodoIdResponse",
|
|
1779
|
-
"type TodoAttachment",
|
|
1780
|
-
"type TodoWithAttachments"
|
|
1781
|
-
]
|
|
1782
|
-
},
|
|
1783
|
-
notifications: {
|
|
1784
|
-
namedExports: [
|
|
1785
|
-
"NotificationSchema",
|
|
1786
|
-
"NotificationTypeSchema",
|
|
1787
|
-
"CreateNotificationSchema",
|
|
1788
|
-
"NotificationListQuerySchema",
|
|
1789
|
-
"SSEEventSchema",
|
|
1790
|
-
"AppSSEProtocolSchema",
|
|
1791
|
-
"UnreadCountSchema",
|
|
1792
|
-
"NotificationIdSchema",
|
|
1793
|
-
"UnreadCountEventSchema",
|
|
1794
|
-
"type AppNotification",
|
|
1795
|
-
"type NotificationType",
|
|
1796
|
-
"type CreateNotificationInput",
|
|
1797
|
-
"type NotificationListQuery",
|
|
1798
|
-
"type SSEEvent",
|
|
1799
|
-
"type AppSSEProtocol",
|
|
1800
|
-
"type UnreadCount",
|
|
1801
|
-
"type NotificationId",
|
|
1802
|
-
"type UnreadCountEvent"
|
|
1803
|
-
]
|
|
1804
|
-
},
|
|
1805
|
-
auth: {
|
|
1806
|
-
namedExports: [
|
|
1807
|
-
"DeveloperProfileSchema",
|
|
1808
|
-
"LoginSchema",
|
|
1809
|
-
"RegisterSchema",
|
|
1810
|
-
"TokenResponseSchema",
|
|
1811
|
-
"ProfileSchema",
|
|
1812
|
-
"type DeveloperProfile",
|
|
1813
|
-
"type LoginInput",
|
|
1814
|
-
"type RegisterInput",
|
|
1815
|
-
"type TokenResponse",
|
|
1816
|
-
"type Profile"
|
|
1817
|
-
]
|
|
1818
|
-
},
|
|
1819
|
-
plugin: {
|
|
1820
|
-
namedExports: [
|
|
1821
|
-
"PluginSchema",
|
|
1822
|
-
"PluginStatusSchema",
|
|
1823
|
-
"CreatePluginSchema",
|
|
1824
|
-
"UpdatePluginSchema",
|
|
1825
|
-
"PluginVersionStatusSchema",
|
|
1826
|
-
"VersionSchema",
|
|
1827
|
-
"CategorySchema",
|
|
1828
|
-
"ReviewSchema",
|
|
1829
|
-
"CreateReviewSchema",
|
|
1830
|
-
"MarketplaceStatsSchema",
|
|
1831
|
-
"PluginListResponseSchema",
|
|
1832
|
-
"AdminPluginSchema",
|
|
1833
|
-
"AdminDashboardStatsSchema",
|
|
1834
|
-
"PluginListQuerySchema",
|
|
1835
|
-
"PluginSlugSchema",
|
|
1836
|
-
"PluginSearchQuerySchema",
|
|
1837
|
-
"PluginDeleteResponseSchema",
|
|
1838
|
-
"ReviewIdParamsSchema",
|
|
1839
|
-
"ReviewDeleteResponseSchema",
|
|
1840
|
-
"CategorySlugParamsSchema",
|
|
1841
|
-
"CategoryPluginsQuerySchema",
|
|
1842
|
-
"PluginListAdminSchema",
|
|
1843
|
-
"AdminListQuerySchema",
|
|
1844
|
-
"AdminListAllQuerySchema",
|
|
1845
|
-
"RejectPluginBodySchema",
|
|
1846
|
-
"BulkApproveBodySchema",
|
|
1847
|
-
"BulkRejectBodySchema",
|
|
1848
|
-
"BulkResponseSchema",
|
|
1849
|
-
"CreateCategoryBodySchema",
|
|
1850
|
-
"UpdateCategoryBodySchema",
|
|
1851
|
-
"CategoryIdParamsSchema",
|
|
1852
|
-
"CategoryIdResponseSchema",
|
|
1853
|
-
"type Plugin",
|
|
1854
|
-
"type PluginStatus",
|
|
1855
|
-
"type CreatePluginInput",
|
|
1856
|
-
"type UpdatePluginInput",
|
|
1857
|
-
"type PluginVersionStatus",
|
|
1858
|
-
"type Version",
|
|
1859
|
-
"type Category",
|
|
1860
|
-
"type Review",
|
|
1861
|
-
"type CreateReviewInput",
|
|
1862
|
-
"type MarketplaceStats",
|
|
1863
|
-
"type PluginListResponse",
|
|
1864
|
-
"type AdminPlugin",
|
|
1865
|
-
"type AdminDashboardStats",
|
|
1866
|
-
"type PluginListQuery"
|
|
1867
|
-
]
|
|
1868
|
-
},
|
|
1869
|
-
admin: {
|
|
1870
|
-
namedExports: [
|
|
1871
|
-
"SystemStatsSchema",
|
|
1872
|
-
"HealthCheckSchema",
|
|
1873
|
-
"RecentActivityItemSchema",
|
|
1874
|
-
"RecentActivitySchema",
|
|
1875
|
-
"AuthUserSchema",
|
|
1876
|
-
"LoginRequestSchema",
|
|
1877
|
-
"LoginResponseSchema",
|
|
1878
|
-
"RegisterRequestSchema",
|
|
1879
|
-
"UserSchema",
|
|
1880
|
-
"UserListSchema",
|
|
1881
|
-
"UpdateUserRequestSchema",
|
|
1882
|
-
"CreateUserRequestSchema",
|
|
1883
|
-
"ClearTodosResultSchema",
|
|
1884
|
-
"AdminSuccessSchema",
|
|
1885
|
-
"DownloadTokenSchema",
|
|
1886
|
-
"type SystemStats",
|
|
1887
|
-
"type HealthCheck",
|
|
1888
|
-
"type RecentActivityItem",
|
|
1889
|
-
"type AuthUserResponse",
|
|
1890
|
-
"type CreateUserRequest",
|
|
1891
|
-
"type LoginRequest",
|
|
1892
|
-
"type LoginResponse",
|
|
1893
|
-
"type RegisterRequest",
|
|
1894
|
-
"type User",
|
|
1895
|
-
"type UpdateUserRequest",
|
|
1896
|
-
"type ClearTodosResult"
|
|
1897
|
-
]
|
|
1898
|
-
},
|
|
1899
|
-
audit: {
|
|
1900
|
-
namedExports: ["ResourceTypeSchema", "ActionTypeSchema", "AuditLogSchema", "type AuditLogType"]
|
|
1901
|
-
},
|
|
1902
|
-
captcha: {
|
|
1903
|
-
namedExports: [
|
|
1904
|
-
"CaptchaResponseSchema",
|
|
1905
|
-
"VerifyCaptchaRequestSchema",
|
|
1906
|
-
"CaptchaVerifyResponseSchema",
|
|
1907
|
-
"type CaptchaResponse",
|
|
1908
|
-
"type VerifyCaptchaRequest",
|
|
1909
|
-
"type CaptchaVerifyResponse"
|
|
1910
|
-
]
|
|
1911
|
-
},
|
|
1912
|
-
cart: {
|
|
1913
|
-
namedExports: [
|
|
1914
|
-
"CartItemSchema",
|
|
1915
|
-
"CartSummarySchema",
|
|
1916
|
-
"CartResponseSchema",
|
|
1917
|
-
"AddCartItemSchema",
|
|
1918
|
-
"CartItemIdSchema",
|
|
1919
|
-
"type CartItem",
|
|
1920
|
-
"type CartSummary",
|
|
1921
|
-
"type CartResponse",
|
|
1922
|
-
"type AddCartItemInput"
|
|
1923
|
-
]
|
|
1924
|
-
},
|
|
1925
|
-
community: {
|
|
1926
|
-
namedExports: [
|
|
1927
|
-
"TopicStatusSchema",
|
|
1928
|
-
"TopicTagSchema",
|
|
1929
|
-
"TopicAuthorSchema",
|
|
1930
|
-
"TopicSchema",
|
|
1931
|
-
"TopicsResponseSchema",
|
|
1932
|
-
"ProfileStatsSchema",
|
|
1933
|
-
"ActivityTypeSchema",
|
|
1934
|
-
"ProfileActivitySchema",
|
|
1935
|
-
"ProfileResponseSchema",
|
|
1936
|
-
"type TopicStatus",
|
|
1937
|
-
"type TopicTag",
|
|
1938
|
-
"type TopicAuthor",
|
|
1939
|
-
"type Topic",
|
|
1940
|
-
"type ProfileStats",
|
|
1941
|
-
"type ActivityType",
|
|
1942
|
-
"type ProfileActivity",
|
|
1943
|
-
"type ProfileResponse"
|
|
1944
|
-
]
|
|
1945
|
-
},
|
|
1946
|
-
content: {
|
|
1947
|
-
namedExports: [
|
|
1948
|
-
"ContentCategorySchema",
|
|
1949
|
-
"ContentStatusSchema",
|
|
1950
|
-
"ContentSchema",
|
|
1951
|
-
"CreateContentSchema",
|
|
1952
|
-
"UpdateContentSchema",
|
|
1953
|
-
"ContentListSchema",
|
|
1954
|
-
"ContentDeleteResultSchema",
|
|
1955
|
-
"type ContentCategory",
|
|
1956
|
-
"type ContentStatus",
|
|
1957
|
-
"type Content",
|
|
1958
|
-
"type CreateContentInput",
|
|
1959
|
-
"type UpdateContentInput",
|
|
1960
|
-
"type ContentDeleteResult"
|
|
1961
|
-
]
|
|
1962
|
-
},
|
|
1963
|
-
dashboard: {
|
|
1964
|
-
namedExports: [
|
|
1965
|
-
"DashboardStatSchema",
|
|
1966
|
-
"RevenueDataSchema",
|
|
1967
|
-
"ActivityStatusSchema",
|
|
1968
|
-
"ActivitySchema",
|
|
1969
|
-
"DashboardResponseSchema",
|
|
1970
|
-
"type DashboardStat",
|
|
1971
|
-
"type RevenueData",
|
|
1972
|
-
"type ActivityStatus",
|
|
1973
|
-
"type Activity",
|
|
1974
|
-
"type DashboardResponse"
|
|
1975
|
-
]
|
|
1976
|
-
},
|
|
1977
|
-
merchant: {
|
|
1978
|
-
namedExports: [
|
|
1979
|
-
"MerchantSchema",
|
|
1980
|
-
"MerchantLoginSchema",
|
|
1981
|
-
"MerchantLoginResponseSchema",
|
|
1982
|
-
"MerchantStatsSchema",
|
|
1983
|
-
"ProductSchema",
|
|
1984
|
-
"CreateProductSchema",
|
|
1985
|
-
"UpdateProductSchema",
|
|
1986
|
-
"ProductListSchema",
|
|
1987
|
-
"ProductListResponseSchema",
|
|
1988
|
-
"ProductQuerySchema",
|
|
1989
|
-
"type Merchant",
|
|
1990
|
-
"type MerchantLoginInput",
|
|
1991
|
-
"type MerchantLoginResponse",
|
|
1992
|
-
"type MerchantStats",
|
|
1993
|
-
"type Product",
|
|
1994
|
-
"type CreateProductInput",
|
|
1995
|
-
"type UpdateProductInput",
|
|
1996
|
-
"type ProductListResponse",
|
|
1997
|
-
"type ProductQuery"
|
|
1998
|
-
]
|
|
1999
|
-
},
|
|
2000
|
-
tenant: {
|
|
2001
|
-
namedExports: [
|
|
2002
|
-
"TenantSchema",
|
|
2003
|
-
"TenantStatusSchema",
|
|
2004
|
-
"TenantPlanSchema",
|
|
2005
|
-
"TenantSettingsSchema",
|
|
2006
|
-
"CreateTenantSchema",
|
|
2007
|
-
"UpdateTenantSchema",
|
|
2008
|
-
"TenantIdSchema",
|
|
2009
|
-
"TenantSlugSchema",
|
|
2010
|
-
"TenantListResponseSchema",
|
|
2011
|
-
"TenantQuerySchema",
|
|
2012
|
-
"TenantIdResponseSchema",
|
|
2013
|
-
"type Tenant",
|
|
2014
|
-
"type TenantStatus",
|
|
2015
|
-
"type TenantPlan",
|
|
2016
|
-
"type TenantSettings",
|
|
2017
|
-
"type CreateTenantInput",
|
|
2018
|
-
"type UpdateTenantInput",
|
|
2019
|
-
"type TenantId",
|
|
2020
|
-
"type TenantSlug",
|
|
2021
|
-
"type TenantListResponse",
|
|
2022
|
-
"type TenantQuery",
|
|
2023
|
-
"type TenantIdResponse"
|
|
2024
|
-
]
|
|
2025
|
-
},
|
|
2026
|
-
dispute: {
|
|
2027
|
-
namedExports: [
|
|
2028
|
-
"DisputeTypeSchema",
|
|
2029
|
-
"DisputeStatusSchema",
|
|
2030
|
-
"DisputeSchema",
|
|
2031
|
-
"CreateDisputeSchema",
|
|
2032
|
-
"UpdateDisputeSchema",
|
|
2033
|
-
"ResolveDisputeSchema",
|
|
2034
|
-
"DisputeListSchema",
|
|
2035
|
-
"DisputeDeleteResultSchema",
|
|
2036
|
-
"type DisputeType",
|
|
2037
|
-
"type DisputeStatus",
|
|
2038
|
-
"type Dispute",
|
|
2039
|
-
"type CreateDisputeInput",
|
|
2040
|
-
"type UpdateDisputeInput",
|
|
2041
|
-
"type ResolveDisputeInput",
|
|
2042
|
-
"type DisputeDeleteResult"
|
|
2043
|
-
]
|
|
2044
|
-
},
|
|
2045
|
-
order: {
|
|
2046
|
-
namedExports: [
|
|
2047
|
-
"OrderStatusSchema",
|
|
2048
|
-
"OrderSchema",
|
|
2049
|
-
"CreateOrderSchema",
|
|
2050
|
-
"UpdateOrderSchema",
|
|
2051
|
-
"OrderListSchema",
|
|
2052
|
-
"OrderQuerySchema",
|
|
2053
|
-
"OrderDeleteResultSchema",
|
|
2054
|
-
"ProcessOrderSchema",
|
|
2055
|
-
"CancelOrderSchema",
|
|
2056
|
-
"RemoveCartItemResponseSchema",
|
|
2057
|
-
"ECommerceProductSchema",
|
|
2058
|
-
"ECommerceOrderStatusSchema",
|
|
2059
|
-
"ECommerceOrderSchema",
|
|
2060
|
-
"ECommerceOrderListSchema",
|
|
2061
|
-
"type OrderStatus",
|
|
2062
|
-
"type Order",
|
|
2063
|
-
"type CreateOrderInput",
|
|
2064
|
-
"type UpdateOrderInput",
|
|
2065
|
-
"type OrderDeleteResult",
|
|
2066
|
-
"type ProcessOrderInput",
|
|
2067
|
-
"type CancelOrderInput",
|
|
2068
|
-
"type OrderQueryInput",
|
|
2069
|
-
"type RemoveCartItemResponse",
|
|
2070
|
-
"type ECommerceProduct",
|
|
2071
|
-
"type ECommerceOrderStatus",
|
|
2072
|
-
"type ECommerceOrder"
|
|
2073
|
-
]
|
|
2074
|
-
},
|
|
2075
|
-
permission: {
|
|
2076
|
-
namedExports: [
|
|
2077
|
-
"RoleEnum",
|
|
2078
|
-
"PermissionEnum",
|
|
2079
|
-
"RoleInfoSchema",
|
|
2080
|
-
"PermissionInfoSchema",
|
|
2081
|
-
"UserPermissionsSchema",
|
|
2082
|
-
"MenuItemSchema",
|
|
2083
|
-
"PageActionSchema",
|
|
2084
|
-
"PagePermissionConfigSchema",
|
|
2085
|
-
"PermissionCategorySchema",
|
|
2086
|
-
"RoleListSchema",
|
|
2087
|
-
"PermissionListSchema",
|
|
2088
|
-
"MenuConfigSchema",
|
|
2089
|
-
"PagePermissionsSchema",
|
|
2090
|
-
"PermissionCategoriesSchema",
|
|
2091
|
-
"RoleLabelsSchema",
|
|
2092
|
-
"PermissionLabelsSchema",
|
|
2093
|
-
"PermissionInitSchema",
|
|
2094
|
-
"type PermissionRoleType",
|
|
2095
|
-
"type PermissionType",
|
|
2096
|
-
"type RoleInfo",
|
|
2097
|
-
"type PermissionInfo",
|
|
2098
|
-
"type UserPermissions",
|
|
2099
|
-
"type MenuItem",
|
|
2100
|
-
"type PageAction",
|
|
2101
|
-
"type PagePermissionConfig",
|
|
2102
|
-
"type PermissionCategory",
|
|
2103
|
-
"type PermissionInit",
|
|
2104
|
-
"Role",
|
|
2105
|
-
"Permission",
|
|
2106
|
-
"ROLE_PERMISSIONS",
|
|
2107
|
-
"ROLE_LABELS",
|
|
2108
|
-
"PERMISSION_LABELS",
|
|
2109
|
-
"PERMISSION_CATEGORIES",
|
|
2110
|
-
"getPermissionsByRole",
|
|
2111
|
-
"hasPermission",
|
|
2112
|
-
"hasAnyPermission",
|
|
2113
|
-
"hasAllPermissions"
|
|
2114
|
-
]
|
|
2115
|
-
},
|
|
2116
|
-
role: {
|
|
2117
|
-
namedExports: [
|
|
2118
|
-
"RoleSchema",
|
|
2119
|
-
"CreateRoleSchema",
|
|
2120
|
-
"UpdateRoleSchema",
|
|
2121
|
-
"UpdateRolePermissionsSchema",
|
|
2122
|
-
"RoleSuccessSchema",
|
|
2123
|
-
"type RoleDataType",
|
|
2124
|
-
"type CreateRoleType",
|
|
2125
|
-
"type UpdateRoleType"
|
|
2126
|
-
]
|
|
2127
|
-
},
|
|
2128
|
-
ticket: {
|
|
2129
|
-
namedExports: [
|
|
2130
|
-
"TicketStatusSchema",
|
|
2131
|
-
"TicketPrioritySchema",
|
|
2132
|
-
"TicketCategorySchema",
|
|
2133
|
-
"TicketReplySchema",
|
|
2134
|
-
"TicketSchema",
|
|
2135
|
-
"CreateTicketSchema",
|
|
2136
|
-
"UpdateTicketSchema",
|
|
2137
|
-
"ReplyTicketSchema",
|
|
2138
|
-
"TicketListSchema",
|
|
2139
|
-
"TicketDeleteResultSchema",
|
|
2140
|
-
"type TicketStatus",
|
|
2141
|
-
"type TicketPriority",
|
|
2142
|
-
"type TicketCategory",
|
|
2143
|
-
"type TicketReply",
|
|
2144
|
-
"type Ticket",
|
|
2145
|
-
"type CreateTicketInput",
|
|
2146
|
-
"type UpdateTicketInput",
|
|
2147
|
-
"type ReplyTicketInput",
|
|
2148
|
-
"type TicketDeleteResult"
|
|
2149
|
-
]
|
|
2150
|
-
}
|
|
2151
|
-
};
|
|
2152
|
-
var ADDITIONAL_PATHS_MAP = {
|
|
2153
|
-
permission: ["role", "audit"]
|
|
2154
|
-
};
|
|
2155
|
-
var STANDALONE_SHARED_MODULES = {
|
|
2156
|
-
cart: ["CartPage.tsx"],
|
|
2157
|
-
community: ["TopicsPage.tsx"],
|
|
2158
|
-
dashboard: ["DashboardPage.tsx"]
|
|
2159
|
-
};
|
|
2160
|
-
var moduleOrder = [
|
|
2161
|
-
"chat",
|
|
2162
|
-
"file",
|
|
2163
|
-
"todos",
|
|
2164
|
-
"notifications",
|
|
2165
|
-
"auth",
|
|
2166
|
-
"plugin",
|
|
2167
|
-
"admin",
|
|
2168
|
-
"audit",
|
|
2169
|
-
"captcha",
|
|
2170
|
-
"cart",
|
|
2171
|
-
"community",
|
|
2172
|
-
"content",
|
|
2173
|
-
"dashboard",
|
|
2174
|
-
"dispute",
|
|
2175
|
-
"merchant",
|
|
2176
|
-
"order",
|
|
2177
|
-
"permission",
|
|
2178
|
-
"role",
|
|
2179
|
-
"tenant",
|
|
2180
|
-
"ticket"
|
|
2181
|
-
];
|
|
2182
|
-
function generateSharedSchemasIndex(resolved) {
|
|
2183
|
-
const header = `// Re-export interfaces from implementation files
|
|
2184
|
-
export type { WSClient, WSProtocol, WSStatus } from '../core/ws-client'
|
|
2185
|
-
export type { SSEClient, SSEProtocol } from '../core/sse-client'
|
|
2186
|
-
|
|
2187
|
-
// Re-export core
|
|
2188
|
-
export {
|
|
2189
|
-
ApiSuccessSchema,
|
|
2190
|
-
ApiErrorSchema,
|
|
2191
|
-
ApiResponseSchema,
|
|
2192
|
-
type ApiSuccess,
|
|
2193
|
-
type ApiError,
|
|
2194
|
-
type ApiResponse,
|
|
2195
|
-
type RpcMethod,
|
|
2196
|
-
type EventName,
|
|
2197
|
-
type RpcInput,
|
|
2198
|
-
type RpcOutput,
|
|
2199
|
-
type EventPayload,
|
|
2200
|
-
createWSClient,
|
|
2201
|
-
createSSEClient,
|
|
2202
|
-
} from '../core'
|
|
2203
|
-
|
|
2204
|
-
// Re-export modules
|
|
2205
|
-
`;
|
|
2206
|
-
const moduleLines = [];
|
|
2207
|
-
const exportedNames = /* @__PURE__ */ new Set();
|
|
2208
|
-
const firstSeenModule = /* @__PURE__ */ new Map();
|
|
2209
|
-
const droppedExports = [];
|
|
2210
|
-
for (const moduleName of moduleOrder) {
|
|
2211
|
-
const exports = MODULE_EXPORTS2[moduleName];
|
|
2212
|
-
if (!exports) continue;
|
|
2213
|
-
const shouldInclude = shouldIncludeModule(moduleName, resolved);
|
|
2214
|
-
if (!shouldInclude) continue;
|
|
2215
|
-
const uniqueExports = exports.namedExports.filter((name) => {
|
|
2216
|
-
if (exportedNames.has(name)) {
|
|
2217
|
-
const originalModule = firstSeenModule.get(name) ?? "unknown";
|
|
2218
|
-
droppedExports.push({ name, moduleName, originalModule });
|
|
2219
|
-
return false;
|
|
2220
|
-
}
|
|
2221
|
-
exportedNames.add(name);
|
|
2222
|
-
firstSeenModule.set(name, moduleName);
|
|
2223
|
-
return true;
|
|
2224
|
-
});
|
|
2225
|
-
if (uniqueExports.length === 0) continue;
|
|
2226
|
-
const importPath = getImportPath(moduleName, resolved);
|
|
2227
|
-
moduleLines.push(
|
|
2228
|
-
`export {
|
|
2229
|
-
${uniqueExports.join(",\n ")},
|
|
2230
|
-
} from '../modules/${importPath}'`
|
|
2231
|
-
);
|
|
2232
|
-
}
|
|
2233
|
-
for (const { name, moduleName, originalModule } of droppedExports) {
|
|
2234
|
-
console.warn(
|
|
2235
|
-
`\u26A0\uFE0F Schema collision: "${name}" defined in both ${originalModule} and ${moduleName}. Using ${originalModule}'s version. Rename to avoid ambiguity.`
|
|
2236
|
-
);
|
|
2237
|
-
}
|
|
2238
|
-
return header + moduleLines.join("\n") + "\n";
|
|
2239
|
-
}
|
|
2240
|
-
function shouldIncludeModule(moduleName, resolved) {
|
|
2241
|
-
if (resolved.modules.has(moduleName)) return true;
|
|
2242
|
-
const standalonePages = STANDALONE_SHARED_MODULES[moduleName];
|
|
2243
|
-
if (standalonePages) {
|
|
2244
|
-
const hasRelevantPage = [...resolved.modules.values()].some(
|
|
2245
|
-
(m) => m.clientPages?.some((p) => standalonePages.includes(p.name + ".tsx")) ?? false
|
|
2246
|
-
);
|
|
2247
|
-
return hasRelevantPage;
|
|
2248
|
-
}
|
|
2249
|
-
for (const [parentModule, additionalPaths] of Object.entries(ADDITIONAL_PATHS_MAP)) {
|
|
2250
|
-
if (additionalPaths.includes(moduleName) && resolved.modules.has(parentModule)) {
|
|
2251
|
-
return true;
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
return false;
|
|
2255
|
-
}
|
|
2256
|
-
function getImportPath(moduleName, resolved) {
|
|
2257
|
-
const manifest = resolved.modules.get(moduleName);
|
|
2258
|
-
if (manifest?.sharedSchemas?.path) {
|
|
2259
|
-
return manifest.sharedSchemas.path;
|
|
2260
|
-
}
|
|
2261
|
-
return moduleName;
|
|
2262
|
-
}
|
|
2263
|
-
|
|
2264
|
-
// src/generators/middleware-index.ts
|
|
2265
|
-
function generateMiddlewareIndex(resolved) {
|
|
2266
|
-
const lines = [];
|
|
2267
|
-
const hasAuthOrPermission = resolved.modules.has("auth") || resolved.hasPermission;
|
|
2268
|
-
lines.push(`export { corsMiddleware, createCorsMiddleware, type CorsOptions } from './cors'`);
|
|
2269
|
-
lines.push(
|
|
2270
|
-
`export { loggerMiddleware, createLoggerMiddleware, type LoggerOptions } from './logger'`
|
|
2271
|
-
);
|
|
2272
|
-
lines.push(
|
|
2273
|
-
`export {
|
|
2274
|
-
errorHandlerMiddleware,
|
|
2275
|
-
createErrorHandlerMiddleware,
|
|
2276
|
-
type ErrorHandlerOptions,
|
|
2277
|
-
} from './error-handler'`
|
|
2278
|
-
);
|
|
2279
|
-
if (hasAuthOrPermission) {
|
|
2280
|
-
lines.push(
|
|
2281
|
-
`export {
|
|
2282
|
-
authMiddleware,
|
|
2283
|
-
requireSuperAdminMiddleware,
|
|
2284
|
-
requireCustomerServiceMiddleware,
|
|
2285
|
-
requirePermissionsMiddleware,
|
|
2286
|
-
type AuthUser,
|
|
2287
|
-
type AuthMiddlewareOptions,
|
|
2288
|
-
} from './auth'`
|
|
2289
|
-
);
|
|
2290
|
-
}
|
|
2291
|
-
if (resolved.hasCaptcha) {
|
|
2292
|
-
lines.push(
|
|
2293
|
-
`export {
|
|
2294
|
-
captchaMiddleware,
|
|
2295
|
-
markCaptchaVerifiedMiddleware,
|
|
2296
|
-
clearCaptchaSessionMiddleware,
|
|
2297
|
-
type CaptchaConfig,
|
|
2298
|
-
} from './captcha'`
|
|
2299
|
-
);
|
|
2300
|
-
}
|
|
2301
|
-
if (resolved.hasPermission) {
|
|
2302
|
-
lines.push(`export { permissionMiddleware } from './permission'`);
|
|
2303
|
-
}
|
|
2304
|
-
lines.push(`export { rateLimitMiddleware, type RateLimitOptions } from './rate-limit'`);
|
|
2305
|
-
if (hasAuthOrPermission) {
|
|
2306
|
-
lines.push(`export { getAuthUser } from '../utils/auth'`);
|
|
2307
|
-
}
|
|
2308
|
-
return lines.join("\n") + "\n";
|
|
2309
|
-
}
|
|
2310
|
-
|
|
2311
|
-
// src/generators/auth-middleware.ts
|
|
2312
|
-
function generateAuthMiddleware(resolved) {
|
|
2313
|
-
if (resolved.modules.has("auth") && !resolved.hasPermission) {
|
|
2314
|
-
return generateSimplifiedAuthMiddleware();
|
|
2315
|
-
}
|
|
2316
|
-
return generateNoopAuthMiddleware();
|
|
2317
|
-
}
|
|
2318
|
-
function generateSimplifiedAuthMiddleware() {
|
|
2319
|
-
return `import type { MiddlewareHandler } from 'hono'
|
|
2320
|
-
import { createModuleLoggerSync } from '../utils/logger'
|
|
2321
|
-
|
|
2322
|
-
export type UserRole = 'user' | 'admin'
|
|
2323
|
-
|
|
2324
|
-
export interface AuthUser {
|
|
2325
|
-
id: string
|
|
2326
|
-
username: string
|
|
2327
|
-
email: string
|
|
2328
|
-
role: UserRole
|
|
2329
|
-
avatar?: string
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
export interface AuthMiddlewareOptions {
|
|
2333
|
-
requiredRole?: UserRole
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
declare module 'hono' {
|
|
2337
|
-
interface ContextVariableMap {
|
|
2338
|
-
authUser: AuthUser
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
function extractToken(authHeader: string | undefined): string | null {
|
|
2343
|
-
if (!authHeader) return null
|
|
2344
|
-
if (!authHeader.startsWith('Bearer ')) return null
|
|
2345
|
-
return authHeader.slice(7)
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
export function authMiddleware(_options: AuthMiddlewareOptions = {}): MiddlewareHandler {
|
|
2349
|
-
const log = createModuleLoggerSync('auth')
|
|
2350
|
-
|
|
2351
|
-
return async (c, next) => {
|
|
2352
|
-
const token = extractToken(c.req.header('Authorization'))
|
|
2353
|
-
|
|
2354
|
-
if (!token) {
|
|
2355
|
-
log.warn({ path: c.req.path, method: c.req.method }, 'Missing auth token')
|
|
2356
|
-
return c.json({ success: false, error: 'Authentication required', status: 401 }, 401)
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
|
-
try {
|
|
2360
|
-
const jwt = await import('jsonwebtoken')
|
|
2361
|
-
const secretKey = process.env.AUTH_SECRET_KEY || 'dev-secret-key-change-in-production'
|
|
2362
|
-
const decoded = jwt.verify(token, secretKey) as {
|
|
2363
|
-
userId: string
|
|
2364
|
-
username: string
|
|
2365
|
-
email: string
|
|
2366
|
-
role: string
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
const user: AuthUser = {
|
|
2370
|
-
id: decoded.userId,
|
|
2371
|
-
username: decoded.username,
|
|
2372
|
-
email: decoded.email,
|
|
2373
|
-
role: decoded.role as UserRole,
|
|
2374
|
-
}
|
|
2375
|
-
|
|
2376
|
-
c.set('authUser', user)
|
|
2377
|
-
log.info({ userId: user.id, path: c.req.path }, 'User authenticated')
|
|
2378
|
-
await next()
|
|
2379
|
-
} catch {
|
|
2380
|
-
log.warn({ path: c.req.path, method: c.req.method }, 'Invalid auth token')
|
|
2381
|
-
return c.json({ success: false, error: 'Invalid or expired token', status: 401 }, 401)
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
|
|
2386
|
-
export function requireSuperAdminMiddleware(): MiddlewareHandler {
|
|
2387
|
-
return authMiddleware()
|
|
2388
|
-
}
|
|
2389
|
-
|
|
2390
|
-
export function requireCustomerServiceMiddleware(): MiddlewareHandler {
|
|
2391
|
-
return authMiddleware()
|
|
2392
|
-
}
|
|
2393
|
-
|
|
2394
|
-
export function requirePermissionsMiddleware(): MiddlewareHandler {
|
|
2395
|
-
return authMiddleware()
|
|
2396
|
-
}
|
|
2397
|
-
`;
|
|
2398
|
-
}
|
|
2399
|
-
function generateNoopAuthMiddleware() {
|
|
2400
|
-
return `import type { MiddlewareHandler } from 'hono'
|
|
2401
|
-
import { createModuleLoggerSync } from '../utils/logger'
|
|
2402
|
-
|
|
2403
|
-
export type UserRole = 'user' | 'admin'
|
|
2404
|
-
|
|
2405
|
-
export interface AuthUser {
|
|
2406
|
-
id: string
|
|
2407
|
-
username: string
|
|
2408
|
-
email: string
|
|
2409
|
-
role: UserRole
|
|
2410
|
-
avatar?: string
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
export interface AuthMiddlewareOptions {
|
|
2414
|
-
requiredRole?: UserRole
|
|
2415
|
-
}
|
|
2416
|
-
|
|
2417
|
-
declare module 'hono' {
|
|
2418
|
-
interface ContextVariableMap {
|
|
2419
|
-
authUser: AuthUser
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
const DEV_USER: AuthUser = {
|
|
2424
|
-
id: 'dev-user-1',
|
|
2425
|
-
username: 'devuser',
|
|
2426
|
-
email: 'dev@example.com',
|
|
2427
|
-
role: 'admin',
|
|
2428
|
-
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=dev',
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
function extractToken(authHeader: string | undefined): string | null {
|
|
2432
|
-
if (!authHeader) return null
|
|
2433
|
-
if (!authHeader.startsWith('Bearer ')) return null
|
|
2434
|
-
return authHeader.slice(7)
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
|
-
export function authMiddleware(_options: AuthMiddlewareOptions = {}): MiddlewareHandler {
|
|
2438
|
-
const log = createModuleLoggerSync('auth')
|
|
2439
|
-
|
|
2440
|
-
return async (c, next) => {
|
|
2441
|
-
const token = extractToken(c.req.header('Authorization'))
|
|
2442
|
-
|
|
2443
|
-
if (!token) {
|
|
2444
|
-
c.set('authUser', { ...DEV_USER, id: 'anonymous' })
|
|
2445
|
-
log.info({ path: c.req.path }, 'Anonymous access')
|
|
2446
|
-
await next()
|
|
2447
|
-
return
|
|
2448
|
-
}
|
|
2449
|
-
|
|
2450
|
-
c.set('authUser', DEV_USER)
|
|
2451
|
-
log.info({ userId: DEV_USER.id, path: c.req.path }, 'Dev user authenticated')
|
|
2452
|
-
await next()
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
export function requireSuperAdminMiddleware(): MiddlewareHandler {
|
|
2457
|
-
return authMiddleware()
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
export function requireCustomerServiceMiddleware(): MiddlewareHandler {
|
|
2461
|
-
return authMiddleware()
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
export function requirePermissionsMiddleware(): MiddlewareHandler {
|
|
2465
|
-
return authMiddleware()
|
|
2466
|
-
}
|
|
2467
|
-
`;
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2470
|
-
// src/generators/auth-utils.ts
|
|
2471
|
-
function generateAuthUtils(resolved) {
|
|
2472
|
-
const hasAuthOrPermission = resolved.modules.has("auth") || resolved.hasPermission;
|
|
2473
|
-
if (!hasAuthOrPermission) {
|
|
2474
|
-
return `import type { Context } from 'hono'
|
|
2475
|
-
import type { AuthUser } from '../middleware/auth'
|
|
2476
|
-
|
|
2477
|
-
export function getAuthUser(c: Context): AuthUser {
|
|
2478
|
-
return c.get('authUser')
|
|
2479
|
-
}
|
|
2480
|
-
`;
|
|
2481
|
-
}
|
|
2482
|
-
if (resolved.modules.has("auth") && !resolved.hasPermission) {
|
|
2483
|
-
return `import type { Context } from 'hono'
|
|
2484
|
-
import type { AuthUser } from '../middleware/auth'
|
|
2485
|
-
|
|
2486
|
-
export function getAuthUser(c: Context): AuthUser {
|
|
2487
|
-
return c.get('authUser')
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
export function getOptionalAuthUser(c: Context): AuthUser | null {
|
|
2491
|
-
try {
|
|
2492
|
-
return c.get('authUser')
|
|
2493
|
-
} catch {
|
|
2494
|
-
return null
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
`;
|
|
2498
|
-
}
|
|
2499
|
-
return `import type { Context } from 'hono'
|
|
2500
|
-
import type { AuthUser } from '../middleware/auth'
|
|
2501
|
-
import { Role } from '@shared/modules/permission'
|
|
2502
|
-
|
|
2503
|
-
interface MockUser {
|
|
2504
|
-
id: string
|
|
2505
|
-
username: string
|
|
2506
|
-
email: string
|
|
2507
|
-
role: string
|
|
2508
|
-
status: string
|
|
2509
|
-
avatar: string
|
|
2510
|
-
createdAt: string
|
|
2511
|
-
updatedAt: string
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
const mockUsers: MockUser[] = [
|
|
2515
|
-
{
|
|
2516
|
-
id: '1',
|
|
2517
|
-
username: 'superadmin',
|
|
2518
|
-
email: 'superadmin@example.com',
|
|
2519
|
-
role: Role.SUPER_ADMIN,
|
|
2520
|
-
status: 'active',
|
|
2521
|
-
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=superadmin',
|
|
2522
|
-
createdAt: '2024-01-01T00:00:00Z',
|
|
2523
|
-
updatedAt: '2024-01-01T00:00:00Z',
|
|
2524
|
-
},
|
|
2525
|
-
{
|
|
2526
|
-
id: '2',
|
|
2527
|
-
username: 'customerservice',
|
|
2528
|
-
email: 'customerservice@example.com',
|
|
2529
|
-
role: Role.CUSTOMER_SERVICE,
|
|
2530
|
-
status: 'active',
|
|
2531
|
-
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=customerservice',
|
|
2532
|
-
createdAt: '2024-01-02T00:00:00Z',
|
|
2533
|
-
updatedAt: '2024-01-02T00:00:00Z',
|
|
2534
|
-
},
|
|
2535
|
-
{
|
|
2536
|
-
id: '3',
|
|
2537
|
-
username: 'user1',
|
|
2538
|
-
email: 'user1@example.com',
|
|
2539
|
-
role: Role.USER,
|
|
2540
|
-
status: 'active',
|
|
2541
|
-
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=user1',
|
|
2542
|
-
createdAt: '2024-01-03T00:00:00Z',
|
|
2543
|
-
updatedAt: '2024-01-03T00:00:00Z',
|
|
2544
|
-
},
|
|
2545
|
-
]
|
|
2546
|
-
|
|
2547
|
-
const mockTokens: Map<string, string> = new Map([
|
|
2548
|
-
['super-admin-token', '1'],
|
|
2549
|
-
['customer-service-token', '2'],
|
|
2550
|
-
['user-token', '3'],
|
|
2551
|
-
])
|
|
2552
|
-
|
|
2553
|
-
export function getAuthUser(c: Context): AuthUser {
|
|
2554
|
-
return c.get('authUser')
|
|
2555
|
-
}
|
|
2556
|
-
|
|
2557
|
-
export function verifyToken(token: string): MockUser | null {
|
|
2558
|
-
const userId = mockTokens.get(token)
|
|
2559
|
-
|
|
2560
|
-
if (!userId) {
|
|
2561
|
-
return null
|
|
2562
|
-
}
|
|
2563
|
-
|
|
2564
|
-
return mockUsers.find(u => u.id === userId) || null
|
|
2565
|
-
}
|
|
2566
|
-
|
|
2567
|
-
export function getMockUsers(): MockUser[] {
|
|
2568
|
-
return mockUsers
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
export function getMockTokens(): Map<string, string> {
|
|
2572
|
-
return mockTokens
|
|
2573
|
-
}
|
|
2574
|
-
`;
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
// src/generators/client-components-index.ts
|
|
2578
|
-
function generateClientComponentsIndex(resolved) {
|
|
2579
|
-
const lines = [];
|
|
2580
|
-
lines.push(`export { StatusBadge, type ColorScheme } from './StatusBadge'`);
|
|
2581
|
-
lines.push(`export { LoadingSpinner } from './LoadingSpinner'`);
|
|
2582
|
-
lines.push(`export { EmptyState } from './EmptyState'`);
|
|
2583
|
-
if (resolved.modules.has("chat") || resolved.modules.has("notifications")) {
|
|
2584
|
-
lines.push(`export { ConnectionStatus } from './ConnectionStatus'`);
|
|
2585
|
-
}
|
|
2586
|
-
if (resolved.modules.has("chat")) {
|
|
2587
|
-
lines.push(`export { MessageCard } from './MessageCard'`);
|
|
2588
|
-
}
|
|
2589
|
-
if (resolved.modules.has("admin") || resolved.modules.has("auth")) {
|
|
2590
|
-
lines.push(`export { AuthButton } from './AuthButton'`);
|
|
2591
|
-
}
|
|
2592
|
-
return lines.join("\n") + "\n";
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
// src/generators/cli-modules-index.ts
|
|
2596
|
-
var ALWAYS_INCLUDED = {
|
|
2597
|
-
dir: "config",
|
|
2598
|
-
registerFunction: "registerConfigCommands"
|
|
2599
|
-
};
|
|
2600
|
-
function generateCliModulesIndex(resolved) {
|
|
2601
|
-
const modules = [];
|
|
2602
|
-
const registrations = [];
|
|
2603
|
-
for (const [, manifest] of resolved.modules) {
|
|
2604
|
-
if (manifest.cliModule) {
|
|
2605
|
-
const { dir, registerFunction } = manifest.cliModule;
|
|
2606
|
-
modules.push(`import { ${registerFunction} } from './${dir}'`);
|
|
2607
|
-
registrations.push(`${registerFunction}(site)`);
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
modules.push(`import { ${ALWAYS_INCLUDED.registerFunction} } from './${ALWAYS_INCLUDED.dir}'`);
|
|
2611
|
-
registrations.push(`${ALWAYS_INCLUDED.registerFunction}(site)`);
|
|
2612
|
-
const exports = modules.map((m) => {
|
|
2613
|
-
const match = m.match(/\{ (\w+) \}/);
|
|
2614
|
-
return match ? match[1] : "";
|
|
2615
|
-
}).filter(Boolean);
|
|
2616
|
-
return `import type { Core } from '@dyyz1993/xcli-core'
|
|
2617
|
-
${modules.join("\n")}
|
|
2618
|
-
|
|
2619
|
-
/**
|
|
2620
|
-
* Register all builtin CLI commands to xcli-core.
|
|
2621
|
-
* Each register function receives a SiteInstance for command registration.
|
|
2622
|
-
*/
|
|
2623
|
-
export function registerBuiltinCommands(app: Core) {
|
|
2624
|
-
const api = app.loader.getAPI()
|
|
2625
|
-
|
|
2626
|
-
const site = api.createSite({
|
|
2627
|
-
name: 'local-server',
|
|
2628
|
-
url: 'http://localhost:3010',
|
|
2629
|
-
})
|
|
2630
|
-
|
|
2631
|
-
${registrations.map((r) => ` ${r}`).join("\n")}
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
|
-
export { ${exports.join(", ")} }
|
|
2635
|
-
`;
|
|
2636
|
-
}
|
|
2637
|
-
|
|
2638
|
-
// src/generators/package-json.ts
|
|
2639
|
-
var MODULE_PACKAGES = {
|
|
2640
|
-
admin: ["bcryptjs"],
|
|
2641
|
-
auth: ["bcryptjs"]
|
|
2642
|
-
};
|
|
2643
|
-
var ADMIN_PANEL_PACKAGES = ["antd"];
|
|
2644
|
-
var CLI_PACKAGES = ["commander"];
|
|
2645
|
-
var UNUSED_PACKAGES = ["lodash-es", "chalk", "mysql2"];
|
|
2646
|
-
var CLIENT_PACKAGES = [
|
|
2647
|
-
"react",
|
|
2648
|
-
"react-dom",
|
|
2649
|
-
"react-helmet-async",
|
|
2650
|
-
"react-router-dom",
|
|
2651
|
-
"lucide-react",
|
|
2652
|
-
"zustand"
|
|
2653
|
-
];
|
|
2654
|
-
var CLIENT_DEV_PACKAGES = [
|
|
2655
|
-
"@vitejs/plugin-react",
|
|
2656
|
-
"@testing-library/react",
|
|
2657
|
-
"@testing-library/jest-dom",
|
|
2658
|
-
"@testing-library/dom",
|
|
2659
|
-
"vite",
|
|
2660
|
-
"jsdom",
|
|
2661
|
-
"tailwindcss",
|
|
2662
|
-
"@tailwindcss/postcss",
|
|
2663
|
-
"postcss",
|
|
2664
|
-
"autoprefixer",
|
|
2665
|
-
"@playwright/test",
|
|
2666
|
-
"playwright",
|
|
2667
|
-
"@prerenderer/renderer-jsdom",
|
|
2668
|
-
"@prerenderer/renderer-puppeteer",
|
|
2669
|
-
"@prerenderer/rollup-plugin",
|
|
2670
|
-
"eventsource"
|
|
2671
|
-
];
|
|
2672
|
-
var CLIENT_TYPE_PACKAGES = ["@types/react", "@types/react-dom"];
|
|
2673
|
-
function filterPackageJson(pkg, resolved) {
|
|
2674
|
-
const result = { ...pkg };
|
|
2675
|
-
const packagesToRemove = new Set(UNUSED_PACKAGES);
|
|
2676
|
-
const packageToModules = {};
|
|
2677
|
-
for (const [module, packages] of Object.entries(MODULE_PACKAGES)) {
|
|
2678
|
-
for (const pkg2 of packages) {
|
|
2679
|
-
if (!packageToModules[pkg2]) packageToModules[pkg2] = [];
|
|
2680
|
-
packageToModules[pkg2].push(module);
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2683
|
-
for (const [pkg2, modules] of Object.entries(packageToModules)) {
|
|
2684
|
-
if (!modules.some((m) => resolved.modules.has(m))) {
|
|
2685
|
-
packagesToRemove.add(pkg2);
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
if (!resolved.modules.has("admin")) {
|
|
2689
|
-
for (const pkg2 of ADMIN_PANEL_PACKAGES) {
|
|
2690
|
-
packagesToRemove.add(pkg2);
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
|
-
for (const pkg2 of CLI_PACKAGES) {
|
|
2694
|
-
packagesToRemove.add(pkg2);
|
|
2695
|
-
}
|
|
2696
|
-
if (!resolved.hasClient) {
|
|
2697
|
-
for (const pkg2 of CLIENT_PACKAGES) {
|
|
2698
|
-
packagesToRemove.add(pkg2);
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
if (result.dependencies && typeof result.dependencies === "object") {
|
|
2702
|
-
const deps = { ...result.dependencies };
|
|
2703
|
-
for (const pkg2 of packagesToRemove) {
|
|
2704
|
-
delete deps[pkg2];
|
|
2705
|
-
}
|
|
2706
|
-
result.dependencies = deps;
|
|
2707
|
-
}
|
|
2708
|
-
if (result.devDependencies && typeof result.devDependencies === "object") {
|
|
2709
|
-
const devDeps = { ...result.devDependencies };
|
|
2710
|
-
if (!resolved.modules.has("admin")) {
|
|
2711
|
-
delete devDeps["@testing-library/user-event"];
|
|
2712
|
-
}
|
|
2713
|
-
if (!resolved.hasClient) {
|
|
2714
|
-
for (const pkg2 of CLIENT_DEV_PACKAGES) {
|
|
2715
|
-
delete devDeps[pkg2];
|
|
2716
|
-
}
|
|
2717
|
-
for (const pkg2 of CLIENT_TYPE_PACKAGES) {
|
|
2718
|
-
delete devDeps[pkg2];
|
|
2719
|
-
}
|
|
2720
|
-
}
|
|
2721
|
-
result.devDependencies = devDeps;
|
|
2722
|
-
}
|
|
2723
|
-
if (!resolved.hasClient && result.scripts && typeof result.scripts === "object") {
|
|
2724
|
-
const scripts = { ...result.scripts };
|
|
2725
|
-
scripts["dev"] = "NODE_ENV=development node --import tsx src/server/entries/node.ts";
|
|
2726
|
-
scripts["build"] = "npm run build:server && npm run build:cli";
|
|
2727
|
-
scripts["build:all"] = "npm run build:server && npm run build:cli";
|
|
2728
|
-
delete scripts["build:client"];
|
|
2729
|
-
delete scripts["build:cloudflare"];
|
|
2730
|
-
delete scripts["preview"];
|
|
2731
|
-
delete scripts["dev:todo"];
|
|
2732
|
-
delete scripts["dev:plugin"];
|
|
2733
|
-
delete scripts["dev:ecommerce"];
|
|
2734
|
-
delete scripts["dev:community"];
|
|
2735
|
-
delete scripts["dev:forum"];
|
|
2736
|
-
delete scripts["dev:saas"];
|
|
2737
|
-
delete scripts["dev:cf"];
|
|
2738
|
-
delete scripts["deploy:cf"];
|
|
2739
|
-
delete scripts["test:e2e"];
|
|
2740
|
-
delete scripts["test:e2e:ui"];
|
|
2741
|
-
delete scripts["test:e2e:debug"];
|
|
2742
|
-
delete scripts["test:full"];
|
|
2743
|
-
result.scripts = scripts;
|
|
2744
|
-
}
|
|
2745
|
-
return result;
|
|
2746
|
-
}
|
|
2747
|
-
function generateViteConfig(resolved, templateDir) {
|
|
2748
|
-
const originalPath = join(templateDir, "vite.config.ts");
|
|
2749
|
-
let content = readFileSync(originalPath, "utf-8");
|
|
2750
|
-
const entriesToRemove = [];
|
|
2751
|
-
const aliasesToRemove = [];
|
|
2752
|
-
if (!resolved.modules.has("admin")) {
|
|
2753
|
-
entriesToRemove.push("admin");
|
|
2754
|
-
aliasesToRemove.push("@admin");
|
|
2755
|
-
}
|
|
2756
|
-
if (!resolved.modules.has("tenant")) {
|
|
2757
|
-
entriesToRemove.push("tenant", "merchant");
|
|
2758
|
-
aliasesToRemove.push("@tenant", "@merchant");
|
|
2759
|
-
}
|
|
2760
|
-
for (const name of entriesToRemove) {
|
|
2761
|
-
const re = new RegExp(
|
|
2762
|
-
`^\\s*${name}:\\s*path\\.resolve\\(__dirname,\\s*['"]${name}\\.html['"]\\),\\s*$\\n?`,
|
|
2763
|
-
"gm"
|
|
2764
|
-
);
|
|
2765
|
-
content = content.replace(re, "");
|
|
2766
|
-
}
|
|
2767
|
-
for (const alias of aliasesToRemove) {
|
|
2768
|
-
const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2769
|
-
const dirName = alias.replace("@", "");
|
|
2770
|
-
const re = new RegExp(
|
|
2771
|
-
`^\\s*'${escapedAlias}':\\s*path\\.resolve\\(__dirname,\\s*['"]src\\/${dirName}['"]\\),\\s*$\\n?`,
|
|
2772
|
-
"gm"
|
|
2773
|
-
);
|
|
2774
|
-
content = content.replace(re, "");
|
|
2775
|
-
}
|
|
2776
|
-
return content;
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
// src/generators/client-preset-ui-config.ts
|
|
2780
|
-
function getPresetType(presetId) {
|
|
2781
|
-
const map = {
|
|
2782
|
-
"todo-app": "todo",
|
|
2783
|
-
"xbrowser-marketplace": "plugin",
|
|
2784
|
-
ecommerce: "ecommerce",
|
|
2785
|
-
"fullstack-admin": "saas",
|
|
2786
|
-
forum: "community",
|
|
2787
|
-
minimal: "todo"
|
|
2788
|
-
};
|
|
2789
|
-
return map[presetId] || "todo";
|
|
2790
|
-
}
|
|
2791
|
-
function getThemeForPresetType(presetType) {
|
|
2792
|
-
const themes = {
|
|
2793
|
-
todo: {
|
|
2794
|
-
constName: "TODO_THEME",
|
|
2795
|
-
theme: `{
|
|
2796
|
-
primaryColor: '#6366f1',
|
|
2797
|
-
primaryHover: '#4f46e5',
|
|
2798
|
-
bgColor: '#ffffff',
|
|
2799
|
-
textColor: '#111827',
|
|
2800
|
-
secondaryBg: '#f9fafb',
|
|
2801
|
-
borderColor: '#e5e7eb',
|
|
2802
|
-
borderRadius: '12px',
|
|
2803
|
-
logoText: 'Biomimic',
|
|
2804
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
2805
|
-
}`
|
|
2806
|
-
},
|
|
2807
|
-
plugin: {
|
|
2808
|
-
constName: "PLUGIN_MARKET_THEME",
|
|
2809
|
-
theme: `{
|
|
2810
|
-
primaryColor: '#3b82f6',
|
|
2811
|
-
primaryHover: '#2563eb',
|
|
2812
|
-
bgColor: '#ffffff',
|
|
2813
|
-
textColor: '#111827',
|
|
2814
|
-
secondaryBg: '#f0f9ff',
|
|
2815
|
-
borderColor: '#bae6fd',
|
|
2816
|
-
borderRadius: '12px',
|
|
2817
|
-
logoText: 'PluginHub',
|
|
2818
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
2819
|
-
}`
|
|
2820
|
-
},
|
|
2821
|
-
ecommerce: {
|
|
2822
|
-
constName: "ECOMMERCE_THEME",
|
|
2823
|
-
theme: `{
|
|
2824
|
-
primaryColor: '#f59e0b',
|
|
2825
|
-
primaryHover: '#d97706',
|
|
2826
|
-
bgColor: '#ffffff',
|
|
2827
|
-
textColor: '#111827',
|
|
2828
|
-
secondaryBg: '#fffbeb',
|
|
2829
|
-
borderColor: '#fde68a',
|
|
2830
|
-
borderRadius: '12px',
|
|
2831
|
-
logoText: 'ShopMart',
|
|
2832
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
2833
|
-
}`
|
|
2834
|
-
},
|
|
2835
|
-
saas: {
|
|
2836
|
-
constName: "SAAS_ADMIN_THEME",
|
|
2837
|
-
theme: `{
|
|
2838
|
-
primaryColor: '#1f2937',
|
|
2839
|
-
primaryHover: '#374151',
|
|
2840
|
-
bgColor: '#f9fafb',
|
|
2841
|
-
textColor: '#111827',
|
|
2842
|
-
secondaryBg: '#f3f4f6',
|
|
2843
|
-
borderColor: '#e5e7eb',
|
|
2844
|
-
borderRadius: '8px',
|
|
2845
|
-
logoText: 'AdminPanel',
|
|
2846
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
2847
|
-
}`
|
|
2848
|
-
},
|
|
2849
|
-
community: {
|
|
2850
|
-
constName: "COMMUNITY_THEME",
|
|
2851
|
-
theme: `{
|
|
2852
|
-
primaryColor: '#f97316',
|
|
2853
|
-
primaryHover: '#ea580c',
|
|
2854
|
-
bgColor: '#ffffff',
|
|
2855
|
-
textColor: '#111827',
|
|
2856
|
-
secondaryBg: '#fff7ed',
|
|
2857
|
-
borderColor: '#fed7aa',
|
|
2858
|
-
borderRadius: '12px',
|
|
2859
|
-
logoText: 'CommunityHub',
|
|
2860
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
2861
|
-
}`
|
|
2862
|
-
}
|
|
2863
|
-
};
|
|
2864
|
-
return themes[presetType] || themes.todo;
|
|
2865
|
-
}
|
|
2866
|
-
function getRoutesForPreset(presetType, resolved) {
|
|
2867
|
-
const hasModule = (m) => resolved.modules.has(m);
|
|
2868
|
-
const loginRoute = {
|
|
2869
|
-
path: "/login",
|
|
2870
|
-
importPath: "./pages/LoginPage",
|
|
2871
|
-
componentName: "LoginPage",
|
|
2872
|
-
label: "Login"
|
|
2873
|
-
};
|
|
2874
|
-
const registerRoute = {
|
|
2875
|
-
path: "/register",
|
|
2876
|
-
importPath: "./pages/RegisterPage",
|
|
2877
|
-
componentName: "RegisterPage",
|
|
2878
|
-
label: "Register"
|
|
2879
|
-
};
|
|
2880
|
-
const maybeAuthRoutes = hasModule("auth") ? [
|
|
2881
|
-
loginRoute,
|
|
2882
|
-
registerRoute,
|
|
2883
|
-
{
|
|
2884
|
-
path: "/profile",
|
|
2885
|
-
importPath: "./pages/ProfilePage",
|
|
2886
|
-
componentName: "ProfilePage",
|
|
2887
|
-
label: "Profile"
|
|
2888
|
-
}
|
|
2889
|
-
] : [];
|
|
2890
|
-
switch (presetType) {
|
|
2891
|
-
case "todo": {
|
|
2892
|
-
const routes = [
|
|
2893
|
-
...maybeAuthRoutes,
|
|
2894
|
-
{
|
|
2895
|
-
path: "/todos",
|
|
2896
|
-
importPath: "./pages/TodoPage",
|
|
2897
|
-
componentName: "TodoPage",
|
|
2898
|
-
label: "Todos"
|
|
2899
|
-
}
|
|
2900
|
-
];
|
|
2901
|
-
if (hasModule("notifications")) {
|
|
2902
|
-
routes.push({
|
|
2903
|
-
path: "/notifications",
|
|
2904
|
-
importPath: "./pages/NotificationPage",
|
|
2905
|
-
componentName: "NotificationPage",
|
|
2906
|
-
label: "Notifications"
|
|
2907
|
-
});
|
|
2908
|
-
}
|
|
2909
|
-
if (hasModule("chat")) {
|
|
2910
|
-
routes.push({
|
|
2911
|
-
path: "/websocket",
|
|
2912
|
-
importPath: "./pages/WebSocketPage",
|
|
2913
|
-
componentName: "WebSocketPage",
|
|
2914
|
-
label: "WebSocket"
|
|
2915
|
-
});
|
|
2916
|
-
}
|
|
2917
|
-
return routes;
|
|
2918
|
-
}
|
|
2919
|
-
case "plugin": {
|
|
2920
|
-
const routes = [];
|
|
2921
|
-
if (hasModule("plugin")) {
|
|
2922
|
-
routes.push(
|
|
2923
|
-
{
|
|
2924
|
-
path: "/",
|
|
2925
|
-
importPath: "./pages/PluginsPage",
|
|
2926
|
-
componentName: "PluginsPage",
|
|
2927
|
-
label: "Home"
|
|
2928
|
-
},
|
|
2929
|
-
{
|
|
2930
|
-
path: "/plugins",
|
|
2931
|
-
importPath: "./pages/PluginsPage",
|
|
2932
|
-
componentName: "PluginsPage",
|
|
2933
|
-
label: "Plugins"
|
|
2934
|
-
},
|
|
2935
|
-
{
|
|
2936
|
-
path: "/plugins/:slug",
|
|
2937
|
-
importPath: "./pages/PluginDetailPage",
|
|
2938
|
-
componentName: "PluginDetailPage",
|
|
2939
|
-
label: "Plugin Detail"
|
|
2940
|
-
},
|
|
2941
|
-
{
|
|
2942
|
-
path: "/categories",
|
|
2943
|
-
importPath: "./pages/CategoriesPage",
|
|
2944
|
-
componentName: "CategoriesPage",
|
|
2945
|
-
label: "Categories"
|
|
2946
|
-
},
|
|
2947
|
-
{
|
|
2948
|
-
path: "/search",
|
|
2949
|
-
importPath: "./pages/SearchPage",
|
|
2950
|
-
componentName: "SearchPage",
|
|
2951
|
-
label: "Search"
|
|
2952
|
-
},
|
|
2953
|
-
{
|
|
2954
|
-
path: "/publish",
|
|
2955
|
-
importPath: "./pages/PublishPage",
|
|
2956
|
-
componentName: "PublishPage",
|
|
2957
|
-
label: "Publish"
|
|
2958
|
-
},
|
|
2959
|
-
{
|
|
2960
|
-
path: "/developer",
|
|
2961
|
-
importPath: "./pages/DeveloperDashboardPage",
|
|
2962
|
-
componentName: "DeveloperDashboardPage",
|
|
2963
|
-
label: "Developer"
|
|
2964
|
-
}
|
|
2965
|
-
);
|
|
2966
|
-
}
|
|
2967
|
-
routes.push(...maybeAuthRoutes);
|
|
2968
|
-
if (hasModule("notifications")) {
|
|
2969
|
-
routes.push({
|
|
2970
|
-
path: "/notifications",
|
|
2971
|
-
importPath: "./pages/NotificationPage",
|
|
2972
|
-
componentName: "NotificationPage",
|
|
2973
|
-
label: "Notifications"
|
|
2974
|
-
});
|
|
2975
|
-
}
|
|
2976
|
-
return routes;
|
|
2977
|
-
}
|
|
2978
|
-
case "ecommerce": {
|
|
2979
|
-
const routes = [];
|
|
2980
|
-
if (hasModule("content")) {
|
|
2981
|
-
routes.push(
|
|
2982
|
-
{
|
|
2983
|
-
path: "/",
|
|
2984
|
-
importPath: "./pages/ContentListPage",
|
|
2985
|
-
componentName: "ContentListPage",
|
|
2986
|
-
label: "Home"
|
|
2987
|
-
},
|
|
2988
|
-
{
|
|
2989
|
-
path: "/products",
|
|
2990
|
-
importPath: "./pages/ContentListPage",
|
|
2991
|
-
componentName: "ContentListPage",
|
|
2992
|
-
label: "Products"
|
|
2993
|
-
},
|
|
2994
|
-
{
|
|
2995
|
-
path: "/products/:id",
|
|
2996
|
-
importPath: "./pages/ContentDetailPage",
|
|
2997
|
-
componentName: "ContentDetailPage",
|
|
2998
|
-
label: "Product Detail"
|
|
2999
|
-
},
|
|
3000
|
-
{
|
|
3001
|
-
path: "/content",
|
|
3002
|
-
importPath: "./pages/ContentListPage",
|
|
3003
|
-
componentName: "ContentListPage",
|
|
3004
|
-
label: "Content"
|
|
3005
|
-
},
|
|
3006
|
-
{
|
|
3007
|
-
path: "/content/:id",
|
|
3008
|
-
importPath: "./pages/ContentDetailPage",
|
|
3009
|
-
componentName: "ContentDetailPage",
|
|
3010
|
-
label: "Content Detail"
|
|
3011
|
-
}
|
|
3012
|
-
);
|
|
3013
|
-
}
|
|
3014
|
-
if (hasModule("order")) {
|
|
3015
|
-
routes.push(
|
|
3016
|
-
{
|
|
3017
|
-
path: "/cart",
|
|
3018
|
-
importPath: "./pages/CartPage",
|
|
3019
|
-
componentName: "CartPage",
|
|
3020
|
-
label: "Cart"
|
|
3021
|
-
},
|
|
3022
|
-
{
|
|
3023
|
-
path: "/orders",
|
|
3024
|
-
importPath: "./pages/OrdersPage",
|
|
3025
|
-
componentName: "OrdersPage",
|
|
3026
|
-
label: "Orders"
|
|
3027
|
-
}
|
|
3028
|
-
);
|
|
3029
|
-
}
|
|
3030
|
-
routes.push(...maybeAuthRoutes);
|
|
3031
|
-
return routes;
|
|
3032
|
-
}
|
|
3033
|
-
case "saas": {
|
|
3034
|
-
const routes = [];
|
|
3035
|
-
if (hasModule("admin")) {
|
|
3036
|
-
routes.push(
|
|
3037
|
-
{
|
|
3038
|
-
path: "/dashboard",
|
|
3039
|
-
importPath: "./pages/DashboardPage",
|
|
3040
|
-
componentName: "DashboardPage",
|
|
3041
|
-
label: "Dashboard"
|
|
3042
|
-
},
|
|
3043
|
-
{
|
|
3044
|
-
path: "/settings",
|
|
3045
|
-
importPath: "./pages/SettingsPage",
|
|
3046
|
-
componentName: "SettingsPage",
|
|
3047
|
-
label: "Settings"
|
|
3048
|
-
}
|
|
3049
|
-
);
|
|
3050
|
-
}
|
|
3051
|
-
routes.push(...maybeAuthRoutes);
|
|
3052
|
-
return routes;
|
|
3053
|
-
}
|
|
3054
|
-
case "community": {
|
|
3055
|
-
const routes = [];
|
|
3056
|
-
if (hasModule("content")) {
|
|
3057
|
-
routes.push(
|
|
3058
|
-
{
|
|
3059
|
-
path: "/",
|
|
3060
|
-
importPath: "./pages/ContentListPage",
|
|
3061
|
-
componentName: "ContentListPage",
|
|
3062
|
-
label: "Home"
|
|
3063
|
-
},
|
|
3064
|
-
{
|
|
3065
|
-
path: "/topics",
|
|
3066
|
-
importPath: "./pages/ContentListPage",
|
|
3067
|
-
componentName: "ContentListPage",
|
|
3068
|
-
label: "Topics"
|
|
3069
|
-
},
|
|
3070
|
-
{
|
|
3071
|
-
path: "/topics/:id",
|
|
3072
|
-
importPath: "./pages/ContentDetailPage",
|
|
3073
|
-
componentName: "ContentDetailPage",
|
|
3074
|
-
label: "Topic Detail"
|
|
3075
|
-
},
|
|
3076
|
-
{
|
|
3077
|
-
path: "/popular",
|
|
3078
|
-
importPath: "./pages/ContentListPage",
|
|
3079
|
-
componentName: "ContentListPage",
|
|
3080
|
-
label: "Popular"
|
|
3081
|
-
},
|
|
3082
|
-
{
|
|
3083
|
-
path: "/content",
|
|
3084
|
-
importPath: "./pages/ContentListPage",
|
|
3085
|
-
componentName: "ContentListPage",
|
|
3086
|
-
label: "Content"
|
|
3087
|
-
},
|
|
3088
|
-
{
|
|
3089
|
-
path: "/content/:id",
|
|
3090
|
-
importPath: "./pages/ContentDetailPage",
|
|
3091
|
-
componentName: "ContentDetailPage",
|
|
3092
|
-
label: "Content Detail"
|
|
3093
|
-
}
|
|
3094
|
-
);
|
|
3095
|
-
}
|
|
3096
|
-
if (hasModule("chat")) {
|
|
3097
|
-
routes.push({
|
|
3098
|
-
path: "/websocket",
|
|
3099
|
-
importPath: "./pages/WebSocketPage",
|
|
3100
|
-
componentName: "WebSocketPage",
|
|
3101
|
-
label: "WebSocket"
|
|
3102
|
-
});
|
|
3103
|
-
}
|
|
3104
|
-
if (hasModule("notifications")) {
|
|
3105
|
-
routes.push({
|
|
3106
|
-
path: "/notifications",
|
|
3107
|
-
importPath: "./pages/NotificationPage",
|
|
3108
|
-
componentName: "NotificationPage",
|
|
3109
|
-
label: "Notifications"
|
|
3110
|
-
});
|
|
3111
|
-
}
|
|
3112
|
-
routes.push(...maybeAuthRoutes);
|
|
3113
|
-
return routes;
|
|
3114
|
-
}
|
|
3115
|
-
default:
|
|
3116
|
-
return [...maybeAuthRoutes];
|
|
3117
|
-
}
|
|
3118
|
-
}
|
|
3119
|
-
function getNavConfigForPreset(presetType, hasAuth) {
|
|
3120
|
-
switch (presetType) {
|
|
3121
|
-
case "todo":
|
|
3122
|
-
return {
|
|
3123
|
-
name: "Todo App",
|
|
3124
|
-
appType: "client",
|
|
3125
|
-
layout: "top-nav",
|
|
3126
|
-
navigationObj: hasAuth ? "{ visible: true, showLogo: true, showSearch: false, showCart: false, authStyle: 'buttons', navItems: 'desktop' }" : "{ visible: true, showLogo: true, showSearch: false, showCart: false, authStyle: 'none', navItems: 'desktop' }",
|
|
3127
|
-
desktopNav: [
|
|
3128
|
-
"{ label: 'Todos', icon: 'CheckSquare', path: '/todos' }",
|
|
3129
|
-
"{ label: 'SSE Demo', icon: 'Bell', path: '/notifications' }",
|
|
3130
|
-
"{ label: 'WebSocket', icon: 'Zap', path: '/websocket' }"
|
|
3131
|
-
],
|
|
3132
|
-
mobileTabs: [
|
|
3133
|
-
"{ label: 'Todos', icon: 'CheckSquare', path: '/todos' }",
|
|
3134
|
-
"{ label: 'SSE', icon: 'Bell', path: '/notifications' }",
|
|
3135
|
-
"{ label: 'WS', icon: 'Zap', path: '/websocket' }"
|
|
3136
|
-
],
|
|
3137
|
-
defaultRoute: "/todos"
|
|
3138
|
-
};
|
|
3139
|
-
case "plugin":
|
|
3140
|
-
return {
|
|
3141
|
-
name: "Plugin Market",
|
|
3142
|
-
appType: "client",
|
|
3143
|
-
layout: "top-nav",
|
|
3144
|
-
navigationObj: "{ visible: true, showLogo: true, showSearch: true, showCart: false, authStyle: 'text-link', navItems: 'desktop' }",
|
|
3145
|
-
desktopNav: [
|
|
3146
|
-
"{ label: 'Discover', icon: 'Compass', path: '/plugins' }",
|
|
3147
|
-
"{ label: 'Plugins', icon: 'Puzzle', path: '/plugins/list' }",
|
|
3148
|
-
"{ label: 'Categories', icon: 'Tags', path: '/categories' }",
|
|
3149
|
-
"{ label: 'Search', icon: 'Search', path: '/search' }",
|
|
3150
|
-
"{ label: 'Publish', icon: 'PlusCircle', path: '/publish' }",
|
|
3151
|
-
"{ label: 'Developer', icon: 'Code', path: '/developer' }"
|
|
3152
|
-
],
|
|
3153
|
-
mobileTabs: [
|
|
3154
|
-
"{ label: 'Discover', icon: 'Compass', path: '/plugins' }",
|
|
3155
|
-
"{ label: 'Plugins', icon: 'Puzzle', path: '/plugins/list' }",
|
|
3156
|
-
"{ label: 'Categories', icon: 'Tags', path: '/categories' }",
|
|
3157
|
-
"{ label: 'Search', icon: 'Search', path: '/search' }",
|
|
3158
|
-
"{ label: 'My', icon: 'User', path: '/developer' }"
|
|
3159
|
-
],
|
|
3160
|
-
defaultRoute: "/plugins"
|
|
3161
|
-
};
|
|
3162
|
-
case "ecommerce":
|
|
3163
|
-
return {
|
|
3164
|
-
name: "E-Commerce",
|
|
3165
|
-
appType: "client",
|
|
3166
|
-
layout: "top-nav",
|
|
3167
|
-
navigationObj: "{ visible: true, showLogo: true, showSearch: true, showCart: true, authStyle: 'icon', navItems: 'desktop' }",
|
|
3168
|
-
desktopNav: [
|
|
3169
|
-
"{ label: 'Home', icon: 'Home', path: '/' }",
|
|
3170
|
-
"{ label: 'Products', icon: 'ShoppingBag', path: '/products' }",
|
|
3171
|
-
"{ label: 'Cart', icon: 'ShoppingCart', path: '/cart' }",
|
|
3172
|
-
"{ label: 'Orders', icon: 'Package', path: '/orders' }",
|
|
3173
|
-
"{ label: 'Account', icon: 'User', path: '/content' }"
|
|
3174
|
-
],
|
|
3175
|
-
mobileTabs: [
|
|
3176
|
-
"{ label: 'Home', icon: 'Home', path: '/' }",
|
|
3177
|
-
"{ label: 'Products', icon: 'ShoppingBag', path: '/products' }",
|
|
3178
|
-
"{ label: 'Cart', icon: 'ShoppingCart', path: '/cart' }",
|
|
3179
|
-
"{ label: 'Orders', icon: 'Package', path: '/orders' }",
|
|
3180
|
-
"{ label: 'Me', icon: 'User', path: '/content' }"
|
|
3181
|
-
],
|
|
3182
|
-
defaultRoute: "/"
|
|
3183
|
-
};
|
|
3184
|
-
case "saas":
|
|
3185
|
-
return {
|
|
3186
|
-
name: "SaaS Admin",
|
|
3187
|
-
appType: "admin",
|
|
3188
|
-
layout: "minimal",
|
|
3189
|
-
navigationObj: "{ visible: false, showLogo: false, showSearch: false, showCart: false, authStyle: 'none', navItems: 'none' }",
|
|
3190
|
-
desktopNav: [
|
|
3191
|
-
"{ label: 'Dashboard', icon: 'LayoutDashboard', path: '/dashboard' }",
|
|
3192
|
-
"{ label: 'Settings', icon: 'Settings', path: '/settings' }"
|
|
3193
|
-
],
|
|
3194
|
-
mobileTabs: [
|
|
3195
|
-
"{ label: 'Dashboard', icon: 'LayoutDashboard', path: '/dashboard' }",
|
|
3196
|
-
"{ label: 'Settings', icon: 'Settings', path: '/settings' }"
|
|
3197
|
-
],
|
|
3198
|
-
defaultRoute: "/dashboard"
|
|
3199
|
-
};
|
|
3200
|
-
case "community":
|
|
3201
|
-
return {
|
|
3202
|
-
name: "Community Forum",
|
|
3203
|
-
appType: "client",
|
|
3204
|
-
layout: "top-nav",
|
|
3205
|
-
navigationObj: "{ visible: true, showLogo: true, showSearch: true, showCart: false, authStyle: 'text-link', navItems: 'desktop' }",
|
|
3206
|
-
desktopNav: [
|
|
3207
|
-
"{ label: 'Home', icon: 'Home', path: '/' }",
|
|
3208
|
-
"{ label: 'Topics', icon: 'MessageSquare', path: '/topics' }",
|
|
3209
|
-
"{ label: 'Popular', icon: 'Flame', path: '/popular' }",
|
|
3210
|
-
"{ label: 'Profile', icon: 'User', path: '/profile' }",
|
|
3211
|
-
"{ label: 'Chat', icon: 'MessageCircle', path: '/websocket' }"
|
|
3212
|
-
],
|
|
3213
|
-
mobileTabs: [
|
|
3214
|
-
"{ label: 'Home', icon: 'Home', path: '/' }",
|
|
3215
|
-
"{ label: 'Topics', icon: 'MessageSquare', path: '/topics' }",
|
|
3216
|
-
"{ label: 'Popular', icon: 'Flame', path: '/popular' }",
|
|
3217
|
-
"{ label: 'Profile', icon: 'User', path: '/profile' }",
|
|
3218
|
-
"{ label: 'Chat', icon: 'MessageCircle', path: '/websocket' }"
|
|
3219
|
-
],
|
|
3220
|
-
defaultRoute: "/"
|
|
3221
|
-
};
|
|
3222
|
-
default:
|
|
3223
|
-
return {
|
|
3224
|
-
name: "App",
|
|
3225
|
-
appType: "client",
|
|
3226
|
-
layout: "top-nav",
|
|
3227
|
-
navigationObj: "{ visible: true, showLogo: true, showSearch: false, showCart: false, authStyle: 'none', navItems: 'desktop' }",
|
|
3228
|
-
desktopNav: ["{ label: 'Home', icon: 'Home', path: '/' }"],
|
|
3229
|
-
mobileTabs: ["{ label: 'Home', icon: 'Home', path: '/' }"],
|
|
3230
|
-
defaultRoute: "/"
|
|
3231
|
-
};
|
|
3232
|
-
}
|
|
3233
|
-
}
|
|
3234
|
-
function filterNavByModules(navItems, resolved) {
|
|
3235
|
-
const hasModule = (path3) => {
|
|
3236
|
-
if (path3 === "/todos") return resolved.modules.has("todos");
|
|
3237
|
-
if (path3 === "/notifications") return resolved.modules.has("notifications");
|
|
3238
|
-
if (path3 === "/websocket") return resolved.modules.has("chat");
|
|
3239
|
-
if (path3.startsWith("/plugins") || path3 === "/categories" || path3 === "/search" || path3 === "/publish" || path3 === "/developer")
|
|
3240
|
-
return resolved.modules.has("plugin");
|
|
3241
|
-
if (path3 === "/cart" || path3 === "/orders") return resolved.modules.has("order");
|
|
3242
|
-
if (path3.startsWith("/content") || path3 === "/products" || path3 === "/")
|
|
3243
|
-
return resolved.modules.has("content");
|
|
3244
|
-
if (path3 === "/topics" || path3 === "/popular" || path3 === "/profile")
|
|
3245
|
-
return resolved.modules.has("content");
|
|
3246
|
-
if (path3 === "/dashboard" || path3 === "/settings") return resolved.modules.has("admin");
|
|
3247
|
-
return true;
|
|
3248
|
-
};
|
|
3249
|
-
return navItems.filter((item) => {
|
|
3250
|
-
const pathMatch = item.match(/path:\s*'([^']+)'/);
|
|
3251
|
-
if (!pathMatch) return true;
|
|
3252
|
-
return hasModule(pathMatch[1]);
|
|
3253
|
-
});
|
|
3254
|
-
}
|
|
3255
|
-
function generatePresetUIConfig(resolved, presetId) {
|
|
3256
|
-
const presetType = getPresetType(presetId);
|
|
3257
|
-
const { constName, theme } = getThemeForPresetType(presetType);
|
|
3258
|
-
const hasAuth = resolved.modules.has("auth");
|
|
3259
|
-
const navConfig = getNavConfigForPreset(presetType, hasAuth);
|
|
3260
|
-
const routes = getRoutesForPreset(presetType, resolved);
|
|
3261
|
-
const aliases = {};
|
|
3262
|
-
if (presetId !== presetType) {
|
|
3263
|
-
aliases[presetId] = presetType;
|
|
3264
|
-
}
|
|
3265
|
-
const allAliases = {
|
|
3266
|
-
"todo-app": "todo",
|
|
3267
|
-
"xbrowser-marketplace": "plugin",
|
|
3268
|
-
ecommerce: "ecommerce",
|
|
3269
|
-
"fullstack-admin": "saas",
|
|
3270
|
-
forum: "community",
|
|
3271
|
-
minimal: "todo",
|
|
3272
|
-
saas: "saas"
|
|
3273
|
-
};
|
|
3274
|
-
for (const [alias, type] of Object.entries(allAliases)) {
|
|
3275
|
-
if (type === presetType && alias !== presetType) {
|
|
3276
|
-
aliases[alias] = presetType;
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
const desktopNav = filterNavByModules(navConfig.desktopNav, resolved);
|
|
3280
|
-
const mobileTabs = filterNavByModules(navConfig.mobileTabs, resolved);
|
|
3281
|
-
const routeDefs = routes.map((r) => {
|
|
3282
|
-
return ` {
|
|
3283
|
-
path: '${r.path}',
|
|
3284
|
-
component: lazy(() => import('${r.importPath}').then(m => ({ default: m.${r.componentName} }))),
|
|
3285
|
-
label: '${r.label}',
|
|
3286
|
-
}`;
|
|
3287
|
-
});
|
|
3288
|
-
return `import { lazy, type ComponentType } from 'react'
|
|
3289
|
-
|
|
3290
|
-
export type PresetType = '${presetType}'
|
|
3291
|
-
|
|
3292
|
-
export type AppType = 'client' | 'admin'
|
|
3293
|
-
export type LayoutType = 'top-nav' | 'minimal'
|
|
3294
|
-
export type AuthStyle = 'buttons' | 'text-link' | 'icon' | 'avatar' | 'none'
|
|
3295
|
-
|
|
3296
|
-
// ClientNavItem is intentionally simpler than admin MenuItem (no permissions/children needed for client nav)
|
|
3297
|
-
// eslint-disable-next-line local-rules/prefer-shared-types
|
|
3298
|
-
export interface ClientNavItem {
|
|
3299
|
-
label: string
|
|
3300
|
-
icon: string
|
|
3301
|
-
path: string
|
|
3302
|
-
}
|
|
3303
|
-
|
|
3304
|
-
export type TabItem = ClientNavItem
|
|
3305
|
-
|
|
3306
|
-
export interface NavigationConfig {
|
|
3307
|
-
visible: boolean
|
|
3308
|
-
showLogo: boolean
|
|
3309
|
-
showSearch: boolean
|
|
3310
|
-
showCart: boolean
|
|
3311
|
-
authStyle: AuthStyle
|
|
3312
|
-
navItems: 'desktop' | 'none'
|
|
3313
|
-
}
|
|
3314
|
-
|
|
3315
|
-
export interface PresetTheme {
|
|
3316
|
-
primaryColor: string
|
|
3317
|
-
primaryHover: string
|
|
3318
|
-
bgColor: string
|
|
3319
|
-
textColor: string
|
|
3320
|
-
secondaryBg: string
|
|
3321
|
-
borderColor: string
|
|
3322
|
-
borderRadius: string
|
|
3323
|
-
logoText: string
|
|
3324
|
-
fontFamily: string
|
|
3325
|
-
}
|
|
3326
|
-
|
|
3327
|
-
export interface RouteDef {
|
|
3328
|
-
path: string
|
|
3329
|
-
component: ComponentType<Record<string, unknown>> | null
|
|
3330
|
-
label: string
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
|
-
export interface PresetUIConfig {
|
|
3334
|
-
id: PresetType
|
|
3335
|
-
name: string
|
|
3336
|
-
appType: AppType
|
|
3337
|
-
layout: LayoutType
|
|
3338
|
-
theme: PresetTheme
|
|
3339
|
-
navigation: NavigationConfig
|
|
3340
|
-
desktopNav: ClientNavItem[]
|
|
3341
|
-
mobileTabs: ClientNavItem[]
|
|
3342
|
-
routes: RouteDef[]
|
|
3343
|
-
defaultRoute: string
|
|
3344
|
-
}
|
|
3345
|
-
|
|
3346
|
-
const ${constName}: PresetTheme = ${theme}
|
|
3347
|
-
|
|
3348
|
-
// Preset ID aliases: allows dev:xxx scripts and VITE_PRESET to use config IDs
|
|
3349
|
-
const PRESET_ALIASES: Record<string, '${presetType}'> = {
|
|
3350
|
-
${Object.entries(aliases).map(([k, v]) => ` '${k}': '${v}',`).join("\n")}
|
|
3351
|
-
}
|
|
3352
|
-
|
|
3353
|
-
export const PRESET_UI_CONFIGS: Record<PresetType, PresetUIConfig> = {
|
|
3354
|
-
${presetType}: {
|
|
3355
|
-
id: '${presetType}',
|
|
3356
|
-
name: '${navConfig.name}',
|
|
3357
|
-
appType: '${navConfig.appType}',
|
|
3358
|
-
layout: '${navConfig.layout}',
|
|
3359
|
-
theme: ${constName},
|
|
3360
|
-
navigation: ${navConfig.navigationObj},
|
|
3361
|
-
desktopNav: [
|
|
3362
|
-
${desktopNav.map((i) => ` ${i}`).join(",\n")}
|
|
3363
|
-
],
|
|
3364
|
-
mobileTabs: [
|
|
3365
|
-
${mobileTabs.map((i) => ` ${i}`).join(",\n")}
|
|
3366
|
-
],
|
|
3367
|
-
defaultRoute: '${navConfig.defaultRoute}',
|
|
3368
|
-
routes: [
|
|
3369
|
-
${routeDefs.join(",\n")}
|
|
3370
|
-
],
|
|
3371
|
-
},
|
|
3372
|
-
}
|
|
3373
|
-
|
|
3374
|
-
export function getPresetUIConfig(id: string): PresetUIConfig {
|
|
3375
|
-
const resolvedId = PRESET_ALIASES[id] ?? (id as PresetType)
|
|
3376
|
-
return PRESET_UI_CONFIGS[resolvedId] ?? PRESET_UI_CONFIGS['${presetType}']
|
|
3377
|
-
}
|
|
3378
|
-
|
|
3379
|
-
export function getPresetUIConfigs(): Record<PresetType, PresetUIConfig> {
|
|
3380
|
-
return PRESET_UI_CONFIGS
|
|
3381
|
-
}
|
|
3382
|
-
`;
|
|
3383
|
-
}
|
|
3384
|
-
|
|
3385
|
-
// src/generators/client-main.ts
|
|
3386
|
-
function getPresetType2(presetId) {
|
|
3387
|
-
const map = {
|
|
3388
|
-
"todo-app": "todo",
|
|
3389
|
-
"xbrowser-marketplace": "plugin",
|
|
3390
|
-
ecommerce: "ecommerce",
|
|
3391
|
-
"fullstack-admin": "saas",
|
|
3392
|
-
forum: "community",
|
|
3393
|
-
minimal: "todo"
|
|
3394
|
-
};
|
|
3395
|
-
return map[presetId] || "todo";
|
|
3396
|
-
}
|
|
3397
|
-
function generateClientMain(resolved, presetId) {
|
|
3398
|
-
const presetType = getPresetType2(presetId);
|
|
3399
|
-
const isSaas = presetType === "saas";
|
|
3400
|
-
const authTokenBlock = isSaas ? "" : `
|
|
3401
|
-
if (preset !== 'saas') {
|
|
3402
|
-
try {
|
|
3403
|
-
const raw = localStorage.getItem('auth-token')
|
|
3404
|
-
const parsed = raw ? JSON.parse(raw) : null
|
|
3405
|
-
if (!parsed?.state?.token) {
|
|
3406
|
-
localStorage.setItem('auth-token', JSON.stringify({
|
|
3407
|
-
state: {
|
|
3408
|
-
token: 'user-token',
|
|
3409
|
-
isAuthenticated: true,
|
|
3410
|
-
user: { id: 'user-1', username: 'Demo User', role: 'USER' },
|
|
3411
|
-
loading: false,
|
|
3412
|
-
error: null,
|
|
3413
|
-
},
|
|
3414
|
-
version: 0,
|
|
3415
|
-
}))
|
|
3416
|
-
}
|
|
3417
|
-
} catch {
|
|
3418
|
-
localStorage.setItem('auth-token', JSON.stringify({
|
|
3419
|
-
state: { token: 'user-token', isAuthenticated: true, user: { id: 'user-1', username: 'Demo User', role: 'USER' }, loading: false, error: null },
|
|
3420
|
-
version: 0,
|
|
3421
|
-
}))
|
|
3422
|
-
}
|
|
3423
|
-
}
|
|
3424
|
-
`;
|
|
3425
|
-
if (isSaas) {
|
|
3426
|
-
return `import React from 'react'
|
|
3427
|
-
import ReactDOM from 'react-dom/client'
|
|
3428
|
-
import './index.css'
|
|
3429
|
-
|
|
3430
|
-
const AdminApp = React.lazy(() => import('@admin/App').then(m => ({ default: m.App })))
|
|
3431
|
-
|
|
3432
|
-
const RootApp = () => {
|
|
3433
|
-
return (
|
|
3434
|
-
<React.Suspense fallback={<div className="flex items-center justify-center h-screen text-gray-400">Loading...</div>}>
|
|
3435
|
-
<AdminApp basePath="/" />
|
|
3436
|
-
</React.Suspense>
|
|
3437
|
-
)
|
|
3438
|
-
}
|
|
3439
|
-
|
|
3440
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
3441
|
-
<React.StrictMode>
|
|
3442
|
-
<RootApp />
|
|
3443
|
-
</React.StrictMode>,
|
|
3444
|
-
)
|
|
3445
|
-
|
|
3446
|
-
if (typeof window !== 'undefined') {
|
|
3447
|
-
requestAnimationFrame(() => {
|
|
3448
|
-
setTimeout(() => {
|
|
3449
|
-
document.dispatchEvent(new CustomEvent('prerender-ready'))
|
|
3450
|
-
}, 100)
|
|
3451
|
-
})
|
|
3452
|
-
}
|
|
3453
|
-
`;
|
|
3454
|
-
}
|
|
3455
|
-
return `import React from 'react'
|
|
3456
|
-
import ReactDOM from 'react-dom/client'
|
|
3457
|
-
import { HelmetProvider } from 'react-helmet-async'
|
|
3458
|
-
import { App as ClientApp } from './App'
|
|
3459
|
-
import './index.css'
|
|
3460
|
-
|
|
3461
|
-
const preset = import.meta.env.VITE_PRESET || '${presetType}'
|
|
3462
|
-
${authTokenBlock}
|
|
3463
|
-
const RootApp = () => {
|
|
3464
|
-
return (
|
|
3465
|
-
<HelmetProvider>
|
|
3466
|
-
<ClientApp presetId={preset} />
|
|
3467
|
-
</HelmetProvider>
|
|
3468
|
-
)
|
|
3469
|
-
}
|
|
3470
|
-
|
|
3471
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
3472
|
-
<React.StrictMode>
|
|
3473
|
-
<RootApp />
|
|
3474
|
-
</React.StrictMode>,
|
|
3475
|
-
)
|
|
3476
|
-
|
|
3477
|
-
if (typeof window !== 'undefined') {
|
|
3478
|
-
requestAnimationFrame(() => {
|
|
3479
|
-
setTimeout(() => {
|
|
3480
|
-
document.dispatchEvent(new CustomEvent('prerender-ready'))
|
|
3481
|
-
}, 100)
|
|
3482
|
-
})
|
|
3483
|
-
}
|
|
3484
|
-
`;
|
|
3485
|
-
}
|
|
3486
|
-
|
|
3487
|
-
// src/commands/create.ts
|
|
3488
|
-
var __filename$1 = fileURLToPath(import.meta.url);
|
|
3489
|
-
var __dirname$1 = path.dirname(__filename$1);
|
|
3490
|
-
var TEMPLATE_PROJECT_NAME = "biomimic-todo-app";
|
|
3491
|
-
var TEMPLATE_DB_NAME = "biomimic-todo-db";
|
|
3492
|
-
var ScaffoldError = class extends Error {
|
|
3493
|
-
constructor(message) {
|
|
3494
|
-
super(message);
|
|
3495
|
-
this.name = "ScaffoldError";
|
|
3496
|
-
}
|
|
3497
|
-
};
|
|
3498
|
-
function validateProjectName(name) {
|
|
3499
|
-
if (!name || name.trim().length === 0) {
|
|
3500
|
-
throw new ScaffoldError("Project name cannot be empty");
|
|
3501
|
-
}
|
|
3502
|
-
const validNameRegex = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
3503
|
-
if (!validNameRegex.test(name)) {
|
|
3504
|
-
throw new ScaffoldError(
|
|
3505
|
-
`Invalid project name "${name}". Use lowercase letters, numbers, hyphens, and underscores only.`
|
|
3506
|
-
);
|
|
3507
|
-
}
|
|
3508
|
-
if (name.length > 214) {
|
|
3509
|
-
throw new ScaffoldError("Project name must be 214 characters or less");
|
|
3510
|
-
}
|
|
3511
|
-
if (name.includes("..") || name.includes("/") || name.includes("\\")) {
|
|
3512
|
-
throw new ScaffoldError("Project name cannot contain path separators");
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
function parseGitignore(content) {
|
|
3516
|
-
const negatePatterns = [];
|
|
3517
|
-
const includePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => {
|
|
3518
|
-
if (line.startsWith("!")) {
|
|
3519
|
-
negatePatterns.push(line.slice(1));
|
|
3520
|
-
return null;
|
|
3521
|
-
}
|
|
3522
|
-
return line;
|
|
3523
|
-
}).filter((line) => line !== null).map((pattern) => pattern.replace(/\/$/, "")).map((pattern) => pattern.replace(/^\*\./, "")).map((pattern) => pattern.replace(/^\/+/, "")).filter((pattern) => !pattern.includes("*"));
|
|
3524
|
-
return [...includePatterns, ...negatePatterns.map((p) => `!${p}`)];
|
|
3525
|
-
}
|
|
3526
|
-
function generateDbName(projectName) {
|
|
3527
|
-
const sanitized = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
3528
|
-
return `${sanitized}-db`;
|
|
3529
|
-
}
|
|
3530
|
-
async function updateWranglerToml(targetDir, projectName) {
|
|
3531
|
-
const wranglerPath = path.join(targetDir, "wrangler.toml");
|
|
3532
|
-
if (!await fs.pathExists(wranglerPath)) {
|
|
3533
|
-
return;
|
|
3534
|
-
}
|
|
3535
|
-
let content = await fs.readFile(wranglerPath, "utf-8");
|
|
3536
|
-
const dbName = generateDbName(projectName);
|
|
3537
|
-
content = content.replace(
|
|
3538
|
-
new RegExp(`^name = "${TEMPLATE_PROJECT_NAME}"`, "m"),
|
|
3539
|
-
`name = "${projectName}"`
|
|
3540
|
-
);
|
|
3541
|
-
content = content.replace(
|
|
3542
|
-
new RegExp(`database_name = "${TEMPLATE_DB_NAME}"`, "g"),
|
|
3543
|
-
`database_name = "${dbName}"`
|
|
3544
|
-
);
|
|
3545
|
-
content = content.replace(
|
|
3546
|
-
/database_id = "[^"]+"/,
|
|
3547
|
-
`database_id = "" # TODO: Run 'wrangler d1 create ${dbName}' and paste the ID here`
|
|
3548
|
-
);
|
|
3549
|
-
await fs.writeFile(wranglerPath, content);
|
|
3550
|
-
}
|
|
3551
|
-
async function updatePackageJson(targetDir, projectName, resolved) {
|
|
3552
|
-
const pkgJsonPath = path.join(targetDir, "package.json");
|
|
3553
|
-
if (!await fs.pathExists(pkgJsonPath)) {
|
|
3554
|
-
return;
|
|
3555
|
-
}
|
|
3556
|
-
let pkgJson = await fs.readJson(pkgJsonPath);
|
|
3557
|
-
pkgJson = filterPackageJson(pkgJson, resolved);
|
|
3558
|
-
pkgJson.name = projectName;
|
|
3559
|
-
if (pkgJson.bin) {
|
|
3560
|
-
delete pkgJson.bin;
|
|
3561
|
-
}
|
|
3562
|
-
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
3563
|
-
}
|
|
3564
|
-
async function updatePackageLockJson(targetDir, projectName) {
|
|
3565
|
-
const lockFilePath = path.join(targetDir, "package-lock.json");
|
|
3566
|
-
if (!await fs.pathExists(lockFilePath)) {
|
|
3567
|
-
return;
|
|
3568
|
-
}
|
|
3569
|
-
const lockFile = await fs.readJson(lockFilePath);
|
|
3570
|
-
if (lockFile.name === TEMPLATE_PROJECT_NAME) {
|
|
3571
|
-
lockFile.name = projectName;
|
|
3572
|
-
}
|
|
3573
|
-
if (lockFile.packages?.[""]?.name === TEMPLATE_PROJECT_NAME) {
|
|
3574
|
-
lockFile.packages[""].name = projectName;
|
|
3575
|
-
}
|
|
3576
|
-
await fs.writeJson(lockFilePath, lockFile, { spaces: 2 });
|
|
3577
|
-
}
|
|
3578
|
-
async function updateReadme(targetDir, projectName) {
|
|
3579
|
-
const readmePath = path.join(targetDir, "README.md");
|
|
3580
|
-
if (!await fs.pathExists(readmePath)) {
|
|
3581
|
-
return;
|
|
3582
|
-
}
|
|
3583
|
-
let content = await fs.readFile(readmePath, "utf-8");
|
|
3584
|
-
content = content.replace(/^# (.+)$/m, `# ${projectName}`);
|
|
3585
|
-
await fs.writeFile(readmePath, content);
|
|
3586
|
-
}
|
|
3587
|
-
async function createProject(projectNameOrOptions, useCurrentDir = false, preset) {
|
|
3588
|
-
let projectName;
|
|
3589
|
-
let currentDir;
|
|
3590
|
-
let presetId;
|
|
3591
|
-
let outputDir;
|
|
3592
|
-
let dryRun;
|
|
3593
|
-
let install;
|
|
3594
|
-
if (typeof projectNameOrOptions === "string") {
|
|
3595
|
-
projectName = projectNameOrOptions;
|
|
3596
|
-
currentDir = useCurrentDir;
|
|
3597
|
-
presetId = preset;
|
|
3598
|
-
dryRun = false;
|
|
3599
|
-
install = true;
|
|
3600
|
-
} else {
|
|
3601
|
-
projectName = projectNameOrOptions.projectName;
|
|
3602
|
-
currentDir = projectNameOrOptions.currentDir;
|
|
3603
|
-
presetId = projectNameOrOptions.preset;
|
|
3604
|
-
outputDir = projectNameOrOptions.outputDir;
|
|
3605
|
-
dryRun = projectNameOrOptions.dryRun ?? false;
|
|
3606
|
-
install = projectNameOrOptions.install ?? true;
|
|
3607
|
-
}
|
|
3608
|
-
if (!currentDir) {
|
|
3609
|
-
validateProjectName(projectName);
|
|
3610
|
-
}
|
|
3611
|
-
const templateDir = path.join(__dirname$1, "../../template");
|
|
3612
|
-
let targetDir;
|
|
3613
|
-
if (currentDir) {
|
|
3614
|
-
targetDir = process.cwd();
|
|
3615
|
-
projectName = path.basename(targetDir);
|
|
3616
|
-
} else if (outputDir) {
|
|
3617
|
-
targetDir = path.resolve(outputDir);
|
|
3618
|
-
if (await fs.pathExists(targetDir)) {
|
|
3619
|
-
throw new ScaffoldError(`Directory ${outputDir} already exists`);
|
|
3620
|
-
}
|
|
3621
|
-
} else {
|
|
3622
|
-
targetDir = path.resolve(process.cwd(), projectName);
|
|
3623
|
-
if (await fs.pathExists(targetDir)) {
|
|
3624
|
-
throw new ScaffoldError(`Directory ${projectName} already exists`);
|
|
3625
|
-
}
|
|
3626
|
-
}
|
|
3627
|
-
try {
|
|
3628
|
-
const manifestSpinner = ora("Loading module manifests...").start();
|
|
3629
|
-
const allManifests = await loadManifests(templateDir);
|
|
3630
|
-
const presets = await loadPresets(templateDir);
|
|
3631
|
-
const selectedPresetId = presetId || "fullstack-admin";
|
|
3632
|
-
const selectedPreset = presets.find((p) => p.id === selectedPresetId);
|
|
3633
|
-
if (!selectedPreset) {
|
|
3634
|
-
throw new ScaffoldError(
|
|
3635
|
-
`Unknown preset: ${selectedPresetId}. Available: ${presets.map((p) => p.id).join(", ")}`
|
|
3636
|
-
);
|
|
3637
|
-
}
|
|
3638
|
-
const resolved = resolvePreset(selectedPreset, allManifests);
|
|
3639
|
-
manifestSpinner.succeed(
|
|
3640
|
-
chalk.green(`Using preset: ${selectedPreset.name} (${resolved.modules.size} modules)`)
|
|
3641
|
-
);
|
|
3642
|
-
if (dryRun) {
|
|
3643
|
-
const resolvedModuleNames = [...resolved.modules.keys()];
|
|
3644
|
-
const generatedFiles2 = getGeneratedFiles(resolved);
|
|
3645
|
-
console.log("");
|
|
3646
|
-
console.log(chalk.blue("\u{1F4CB} Dry Run - Files that would be generated:\n"));
|
|
3647
|
-
for (const file of generatedFiles2) {
|
|
3648
|
-
console.log(` ${chalk.green("\u2713")} ${file}`);
|
|
3649
|
-
}
|
|
3650
|
-
const gitignorePath2 = path.join(templateDir, ".gitignore");
|
|
3651
|
-
let ignorePatterns2 = [];
|
|
3652
|
-
if (await fs.pathExists(gitignorePath2)) {
|
|
3653
|
-
const gitignoreContent = await fs.readFile(gitignorePath2, "utf-8");
|
|
3654
|
-
ignorePatterns2 = parseGitignore(gitignoreContent);
|
|
3655
|
-
}
|
|
3656
|
-
ignorePatterns2.push("node_modules", ".wrangler");
|
|
3657
|
-
const excludePatterns2 = getExcludePatterns(resolved, allManifests);
|
|
3658
|
-
let templateFileCount = 0;
|
|
3659
|
-
const templateFiles = await fs.readdir(templateDir, { recursive: true });
|
|
3660
|
-
for (const file of templateFiles) {
|
|
3661
|
-
const relative = String(file);
|
|
3662
|
-
if (!relative) continue;
|
|
3663
|
-
const negated = ignorePatterns2.filter((p) => p.startsWith("!"));
|
|
3664
|
-
const gitIgnored = ignorePatterns2.filter(
|
|
3665
|
-
(p) => !p.startsWith("!") && relative.startsWith(p)
|
|
3666
|
-
);
|
|
3667
|
-
if (gitIgnored.length > 0) {
|
|
3668
|
-
const allowed = negated.some((p) => relative === p.slice(1));
|
|
3669
|
-
if (!allowed) continue;
|
|
3670
|
-
}
|
|
3671
|
-
const normalizedRelative = relative.replace(/\\/g, "/");
|
|
3672
|
-
let excluded = false;
|
|
3673
|
-
for (const pattern of excludePatterns2) {
|
|
3674
|
-
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
3675
|
-
if (normalizedRelative === normalizedPattern || normalizedRelative.startsWith(normalizedPattern + "/")) {
|
|
3676
|
-
excluded = true;
|
|
3677
|
-
break;
|
|
3678
|
-
}
|
|
3679
|
-
}
|
|
3680
|
-
if (!excluded) templateFileCount++;
|
|
3681
|
-
}
|
|
3682
|
-
console.log("");
|
|
3683
|
-
console.log(chalk.blue("\u{1F4C1} Template files that would be copied:\n"));
|
|
3684
|
-
console.log(` ${chalk.green("\u2713")} ${templateFileCount} template files`);
|
|
3685
|
-
console.log("");
|
|
3686
|
-
console.log(chalk.yellow(` Total generated files: ${generatedFiles2.length}`));
|
|
3687
|
-
console.log(chalk.yellow(` Preset: ${selectedPreset.name}`));
|
|
3688
|
-
console.log(chalk.yellow(` Modules: ${resolvedModuleNames.join(", ")}`));
|
|
3689
|
-
console.log("");
|
|
3690
|
-
return;
|
|
3691
|
-
}
|
|
3692
|
-
if (!currentDir) {
|
|
3693
|
-
const dirSpinner = ora("Creating project directory...").start();
|
|
3694
|
-
await fs.ensureDir(targetDir);
|
|
3695
|
-
dirSpinner.succeed(chalk.green("Project directory created"));
|
|
3696
|
-
}
|
|
3697
|
-
const copySpinner = ora("Copying template files...").start();
|
|
3698
|
-
const gitignorePath = path.join(templateDir, ".gitignore");
|
|
3699
|
-
let ignorePatterns = [];
|
|
3700
|
-
if (await fs.pathExists(gitignorePath)) {
|
|
3701
|
-
const gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
|
|
3702
|
-
ignorePatterns = parseGitignore(gitignoreContent);
|
|
3703
|
-
}
|
|
3704
|
-
ignorePatterns.push("node_modules", ".wrangler");
|
|
3705
|
-
const excludePatterns = getExcludePatterns(resolved, allManifests);
|
|
3706
|
-
await fs.copy(templateDir, targetDir, {
|
|
3707
|
-
filter: (src) => {
|
|
3708
|
-
const relative = path.relative(templateDir, src);
|
|
3709
|
-
if (relative === "") return true;
|
|
3710
|
-
const negated = ignorePatterns.filter((p) => p.startsWith("!"));
|
|
3711
|
-
const gitIgnored = ignorePatterns.filter((p) => !p.startsWith("!") && relative.startsWith(p));
|
|
3712
|
-
if (gitIgnored.length > 0) {
|
|
3713
|
-
const allowed = negated.some((p) => relative === p.slice(1));
|
|
3714
|
-
if (!allowed) return false;
|
|
3715
|
-
}
|
|
3716
|
-
const normalizedRelative = relative.replace(/\\/g, "/");
|
|
3717
|
-
for (const pattern of excludePatterns) {
|
|
3718
|
-
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
3719
|
-
if (normalizedRelative === normalizedPattern || normalizedRelative.startsWith(normalizedPattern + "/")) {
|
|
3720
|
-
return false;
|
|
3721
|
-
}
|
|
3722
|
-
}
|
|
3723
|
-
return true;
|
|
3724
|
-
},
|
|
3725
|
-
dereference: false
|
|
3726
|
-
});
|
|
3727
|
-
copySpinner.succeed(chalk.green("Template files copied"));
|
|
3728
|
-
const genSpinner = ora("Generating module-specific files...").start();
|
|
3729
|
-
const routeRegistryContent = generateRouteRegistry(resolved);
|
|
3730
|
-
await fs.writeFile(path.join(targetDir, "src/server/route-registry.ts"), routeRegistryContent);
|
|
3731
|
-
const dbSchemaContent = generateDbSchemaBarrel(resolved);
|
|
3732
|
-
await fs.writeFile(path.join(targetDir, "src/server/db/schema/index.ts"), dbSchemaContent);
|
|
3733
|
-
if (resolved.hasClient) {
|
|
3734
|
-
const clientNavContent = generateClientNavigation(resolved);
|
|
3735
|
-
await fs.writeFile(
|
|
3736
|
-
path.join(targetDir, "src/client/components/Navigation.tsx"),
|
|
3737
|
-
clientNavContent
|
|
3738
|
-
);
|
|
3739
|
-
const clientAppTestContent = generateClientAppTest(resolved);
|
|
3740
|
-
await fs.ensureDir(path.join(targetDir, "src/client/components/__tests__"));
|
|
3741
|
-
await fs.writeFile(
|
|
3742
|
-
path.join(targetDir, "src/client/components/__tests__/App.test.tsx"),
|
|
3743
|
-
clientAppTestContent
|
|
3744
|
-
);
|
|
3745
|
-
const clientNavTestContent = generateClientNavigationTest(resolved);
|
|
3746
|
-
await fs.writeFile(
|
|
3747
|
-
path.join(targetDir, "src/client/components/__tests__/Navigation.test.tsx"),
|
|
3748
|
-
clientNavTestContent
|
|
3749
|
-
);
|
|
3750
|
-
const presetUIConfigContent = generatePresetUIConfig(resolved, selectedPreset.id);
|
|
3751
|
-
await fs.writeFile(
|
|
3752
|
-
path.join(targetDir, "src/client/preset-ui-config.ts"),
|
|
3753
|
-
presetUIConfigContent
|
|
3754
|
-
);
|
|
3755
|
-
const clientMainContent = generateClientMain(resolved, selectedPreset.id);
|
|
3756
|
-
await fs.writeFile(path.join(targetDir, "src/client/main.tsx"), clientMainContent);
|
|
3757
|
-
}
|
|
3758
|
-
if (resolved.hasClient && resolved.modules.has("admin")) {
|
|
3759
|
-
const adminAppContent = generateAdminApp(resolved);
|
|
3760
|
-
if (adminAppContent) {
|
|
3761
|
-
await fs.ensureDir(path.join(targetDir, "src/admin"));
|
|
3762
|
-
await fs.writeFile(path.join(targetDir, "src/admin/App.tsx"), adminAppContent);
|
|
3763
|
-
}
|
|
3764
|
-
}
|
|
3765
|
-
const serverAppContent = generateServerApp(resolved);
|
|
3766
|
-
await fs.writeFile(path.join(targetDir, "src/server/app.ts"), serverAppContent);
|
|
3767
|
-
const generatedFiles = getGeneratedFiles(resolved);
|
|
3768
|
-
if (generatedFiles.includes("src/server/db/init.ts")) {
|
|
3769
|
-
const dbInitContent = generateDbInit(resolved);
|
|
3770
|
-
await fs.writeFile(path.join(targetDir, "src/server/db/init.ts"), dbInitContent);
|
|
3771
|
-
}
|
|
3772
|
-
const sharedModulesContent = generateSharedModulesIndex(resolved);
|
|
3773
|
-
await fs.writeFile(path.join(targetDir, "src/shared/modules/index.ts"), sharedModulesContent);
|
|
3774
|
-
const sharedSchemasContent = generateSharedSchemasIndex(resolved);
|
|
3775
|
-
await fs.writeFile(path.join(targetDir, "src/shared/schemas/index.ts"), sharedSchemasContent);
|
|
3776
|
-
const middlewareIndexContent = generateMiddlewareIndex(resolved);
|
|
3777
|
-
await fs.writeFile(
|
|
3778
|
-
path.join(targetDir, "src/server/middleware/index.ts"),
|
|
3779
|
-
middlewareIndexContent
|
|
3780
|
-
);
|
|
3781
|
-
if (generatedFiles.includes("src/server/middleware/auth.ts")) {
|
|
3782
|
-
const authMiddlewareContent = generateAuthMiddleware(resolved);
|
|
3783
|
-
await fs.writeFile(
|
|
3784
|
-
path.join(targetDir, "src/server/middleware/auth.ts"),
|
|
3785
|
-
authMiddlewareContent
|
|
3786
|
-
);
|
|
3787
|
-
}
|
|
3788
|
-
if (generatedFiles.includes("src/server/utils/auth.ts")) {
|
|
3789
|
-
const authUtilsContent = generateAuthUtils(resolved);
|
|
3790
|
-
await fs.writeFile(path.join(targetDir, "src/server/utils/auth.ts"), authUtilsContent);
|
|
3791
|
-
}
|
|
3792
|
-
if (resolved.hasClient) {
|
|
3793
|
-
const clientComponentsContent = generateClientComponentsIndex(resolved);
|
|
3794
|
-
await fs.writeFile(
|
|
3795
|
-
path.join(targetDir, "src/client/components/index.ts"),
|
|
3796
|
-
clientComponentsContent
|
|
3797
|
-
);
|
|
3798
|
-
}
|
|
3799
|
-
const cliModulesContent = generateCliModulesIndex(resolved);
|
|
3800
|
-
await fs.writeFile(path.join(targetDir, "src/cli/modules/index.ts"), cliModulesContent);
|
|
3801
|
-
if (resolved.hasClient && generatedFiles.includes("vite.config.ts")) {
|
|
3802
|
-
const viteConfigContent = generateViteConfig(resolved, templateDir);
|
|
3803
|
-
await fs.writeFile(path.join(targetDir, "vite.config.ts"), viteConfigContent);
|
|
3804
|
-
}
|
|
3805
|
-
genSpinner.succeed(chalk.green("Module-specific files generated"));
|
|
3806
|
-
const pkgSpinner = ora("Configuring package.json...").start();
|
|
3807
|
-
await updatePackageJson(targetDir, projectName, resolved);
|
|
3808
|
-
pkgSpinner.succeed(chalk.green("package.json configured"));
|
|
3809
|
-
const lockSpinner = ora("Configuring package-lock.json...").start();
|
|
3810
|
-
await updatePackageLockJson(targetDir, projectName);
|
|
3811
|
-
lockSpinner.succeed(chalk.green("package-lock.json configured"));
|
|
3812
|
-
const wranglerSpinner = ora("Configuring wrangler.toml...").start();
|
|
3813
|
-
await updateWranglerToml(targetDir, projectName);
|
|
3814
|
-
wranglerSpinner.succeed(chalk.green("wrangler.toml configured"));
|
|
3815
|
-
const readmeSpinner = ora("Configuring README.md...").start();
|
|
3816
|
-
await updateReadme(targetDir, projectName);
|
|
3817
|
-
readmeSpinner.succeed(chalk.green("README.md configured"));
|
|
3818
|
-
let installSucceeded = false;
|
|
3819
|
-
if (install && !dryRun) {
|
|
3820
|
-
const installSpinner = ora("Installing dependencies...").start();
|
|
3821
|
-
try {
|
|
3822
|
-
const { execSync } = await import('child_process');
|
|
3823
|
-
execSync("npm install --legacy-peer-deps", {
|
|
3824
|
-
cwd: targetDir,
|
|
3825
|
-
stdio: "pipe",
|
|
3826
|
-
timeout: 3e5
|
|
3827
|
-
});
|
|
3828
|
-
installSpinner.succeed(chalk.green("Dependencies installed"));
|
|
3829
|
-
installSucceeded = true;
|
|
3830
|
-
} catch {
|
|
3831
|
-
installSpinner.warn(chalk.yellow("Dependency installation failed (you can run npm install manually)"));
|
|
3832
|
-
}
|
|
3833
|
-
}
|
|
3834
|
-
if (installSucceeded) {
|
|
3835
|
-
const patchesDir = path.join(targetDir, "patches");
|
|
3836
|
-
if (await fs.pathExists(patchesDir)) {
|
|
3837
|
-
try {
|
|
3838
|
-
const { execSync } = await import('child_process');
|
|
3839
|
-
execSync("npx patch-package", {
|
|
3840
|
-
cwd: targetDir,
|
|
3841
|
-
stdio: "pipe",
|
|
3842
|
-
timeout: 6e4
|
|
3843
|
-
});
|
|
3844
|
-
} catch {
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
console.log("");
|
|
3849
|
-
console.log(chalk.green(" \u2713 Project created successfully!"));
|
|
3850
|
-
console.log(chalk.gray(` Preset: ${selectedPreset.name}`));
|
|
3851
|
-
console.log(chalk.gray(` Modules: ${[...resolved.modules.keys()].join(", ")}`));
|
|
3852
|
-
console.log("");
|
|
3853
|
-
console.log(chalk.cyan(" Next steps:"));
|
|
3854
|
-
if (!currentDir && !outputDir) {
|
|
3855
|
-
console.log(chalk.white(` cd ${projectName}`));
|
|
3856
|
-
}
|
|
3857
|
-
if (!install || !installSucceeded) {
|
|
3858
|
-
console.log(chalk.white(" npm install"));
|
|
3859
|
-
}
|
|
3860
|
-
if (resolved.hasClient) {
|
|
3861
|
-
console.log(chalk.white(" npm run dev"));
|
|
3862
|
-
console.log("");
|
|
3863
|
-
console.log(chalk.yellow(" \u26A0\uFE0F Cloudflare Setup:"));
|
|
3864
|
-
console.log(
|
|
3865
|
-
chalk.white(` 1. Create D1 database: wrangler d1 create ${generateDbName(projectName)}`)
|
|
3866
|
-
);
|
|
3867
|
-
console.log(chalk.white(" 2. Copy the database ID to wrangler.toml"));
|
|
3868
|
-
console.log(chalk.white(" 3. Deploy: npm run deploy:cf"));
|
|
3869
|
-
} else {
|
|
3870
|
-
console.log(chalk.white(" npm run build"));
|
|
3871
|
-
console.log(chalk.white(" npm start"));
|
|
3872
|
-
console.log("");
|
|
3873
|
-
console.log(chalk.cyan(" CLI usage:"));
|
|
3874
|
-
console.log(chalk.white(" node dist/cli/index.js config status"));
|
|
3875
|
-
console.log(chalk.white(" node dist/cli/index.js todo list"));
|
|
3876
|
-
console.log(chalk.white(" node dist/cli/index.js --help"));
|
|
3877
|
-
}
|
|
3878
|
-
console.log("");
|
|
3879
|
-
console.log(chalk.gray(" Happy coding! \u{1F41F}"));
|
|
3880
|
-
console.log("");
|
|
3881
|
-
} catch (error) {
|
|
3882
|
-
if (error instanceof ScaffoldError) throw error;
|
|
3883
|
-
throw new ScaffoldError(`Error creating project: ${error}`);
|
|
3884
|
-
}
|
|
3885
|
-
}
|
|
3886
|
-
|
|
3887
|
-
// src/index.ts
|
|
3888
|
-
var __filename2 = fileURLToPath(import.meta.url);
|
|
3889
|
-
var __dirname2 = path.dirname(__filename2);
|
|
3890
|
-
var rootDir = __dirname2.endsWith(path.join("src")) ? path.resolve(__dirname2, "..") : __dirname2.endsWith(path.join("dist", "cli")) ? path.resolve(__dirname2, "..", "..") : path.resolve(__dirname2, "..", "..");
|
|
3891
|
-
var packageJson = JSON.parse(readFileSync(path.join(rootDir, "package.json"), "utf-8"));
|
|
3892
|
-
var program = new Command();
|
|
3893
|
-
program.name("create-fullstack-scaffold").description("Create a new full-stack scaffold app with Todo List example").version(packageJson.version).argument("[project-name]", "Name of your project").option("-c, --current-dir", "Create project in current directory").option(
|
|
3894
|
-
"-p, --preset <preset>",
|
|
3895
|
-
"Template preset to use (fullstack-admin, todo-app, ecommerce, xbrowser-marketplace, forum, cli-only, minimal, saas)"
|
|
3896
|
-
).option("-o, --output-dir <path>", "Output directory (defaults to project name)").option("--dry-run", "Show what would be generated without creating files").option("--no-install", "Skip automatic dependency installation").action(
|
|
3897
|
-
async (projectName = "my-fullstack-app", options) => {
|
|
3898
|
-
console.log("");
|
|
3899
|
-
console.log(chalk.cyan.bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
3900
|
-
console.log(chalk.cyan.bold(" \u2551 Create Fullstack Scaffold App \u2551"));
|
|
3901
|
-
console.log(chalk.cyan.bold(" \u2551 React + Hono + Vite + Zustand + TS \u2551"));
|
|
3902
|
-
console.log(chalk.cyan.bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
3903
|
-
console.log("");
|
|
3904
|
-
let preset = options.preset;
|
|
3905
|
-
if (!preset && process.stdin.isTTY) {
|
|
3906
|
-
const templateDir = path.join(rootDir, "template");
|
|
3907
|
-
const presets = await loadPresets(templateDir);
|
|
3908
|
-
preset = await select({
|
|
3909
|
-
message: "Choose a template preset:",
|
|
3910
|
-
choices: presets.map((p) => ({
|
|
3911
|
-
value: p.id,
|
|
3912
|
-
name: `${p.name} \u2014 ${p.description}`
|
|
3913
|
-
}))
|
|
3914
|
-
});
|
|
3915
|
-
}
|
|
3916
|
-
if (!preset) {
|
|
3917
|
-
preset = "fullstack-admin";
|
|
3918
|
-
}
|
|
3919
|
-
try {
|
|
3920
|
-
await createProject({
|
|
3921
|
-
projectName,
|
|
3922
|
-
currentDir: options.currentDir ?? false,
|
|
3923
|
-
preset,
|
|
3924
|
-
outputDir: options.outputDir,
|
|
3925
|
-
dryRun: options.dryRun ?? false,
|
|
3926
|
-
install: options.install
|
|
3927
|
-
});
|
|
3928
|
-
} catch (error) {
|
|
3929
|
-
if (error instanceof ScaffoldError) {
|
|
3930
|
-
console.error(chalk.red(` \u2716 ${error.message}`));
|
|
3931
|
-
process.exit(1);
|
|
3932
|
-
}
|
|
3933
|
-
throw error;
|
|
3934
|
-
}
|
|
3935
|
-
}
|
|
3936
|
-
);
|
|
3937
|
-
program.command("presets").description("List available template presets").action(async () => {
|
|
3938
|
-
const templateDir = path.join(rootDir, "template");
|
|
3939
|
-
const presets = await loadPresets(templateDir);
|
|
3940
|
-
console.log(chalk.cyan("\nAvailable presets:\n"));
|
|
3941
|
-
for (const preset of presets) {
|
|
3942
|
-
console.log(` ${chalk.green(preset.id.padEnd(20))} ${preset.name}`);
|
|
3943
|
-
console.log(` ${" ".repeat(20)} ${preset.description}`);
|
|
3944
|
-
console.log(` ${" ".repeat(20)} Modules: ${preset.modules.join(", ")}`);
|
|
3945
|
-
console.log();
|
|
3946
|
-
}
|
|
3947
|
-
});
|
|
3948
|
-
program.parse();
|
|
3949
|
-
//# sourceMappingURL=index.js.map
|
|
3950
|
-
//# sourceMappingURL=index.js.map
|