zuro-cli 0.0.2-beta.14 → 0.0.2-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/{chunk-W36ZIR4Y.mjs → chunk-R3MGV5UR.mjs} +130 -24
- package/dist/chunk-R3MGV5UR.mjs.map +1 -0
- package/dist/{database.handler-D7EVXRJX.mjs → database.handler-5OUD6XJZ.mjs} +6 -2
- package/dist/index.js +957 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +829 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-W36ZIR4Y.mjs.map +0 -1
- /package/dist/{database.handler-D7EVXRJX.mjs.map → database.handler-5OUD6XJZ.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# zuro-cli
|
|
2
2
|
|
|
3
|
-
A CLI for
|
|
3
|
+
A CLI for adding production-ready backend modules you own, without framework lock-in.
|
|
4
4
|
|
|
5
5
|
## init
|
|
6
6
|
|
|
@@ -31,6 +31,7 @@ npx zuro-cli add auth
|
|
|
31
31
|
Available modules include:
|
|
32
32
|
|
|
33
33
|
- `database`
|
|
34
|
+
- `uploads`
|
|
34
35
|
- `auth`
|
|
35
36
|
- `validator`
|
|
36
37
|
- `error-handler`
|
|
@@ -7,27 +7,78 @@ import path from "path";
|
|
|
7
7
|
import fs from "fs-extra";
|
|
8
8
|
import prompts from "prompts";
|
|
9
9
|
import chalk from "chalk";
|
|
10
|
+
var DATABASE_MODULE_MAP = {
|
|
11
|
+
drizzle: {
|
|
12
|
+
postgresql: "database-pg",
|
|
13
|
+
mysql: "database-mysql"
|
|
14
|
+
},
|
|
15
|
+
prisma: {
|
|
16
|
+
postgresql: "database-prisma-pg",
|
|
17
|
+
mysql: "database-prisma-mysql"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
10
20
|
var DEFAULT_DATABASE_URLS = {
|
|
11
21
|
"database-pg": "postgresql://postgres@localhost:5432/mydb",
|
|
12
|
-
"database-mysql": "mysql://root@localhost:3306/mydb"
|
|
22
|
+
"database-mysql": "mysql://root@localhost:3306/mydb",
|
|
23
|
+
"database-prisma-pg": "postgresql://postgres@localhost:5432/mydb",
|
|
24
|
+
"database-prisma-mysql": "mysql://root@localhost:3306/mydb"
|
|
13
25
|
};
|
|
14
26
|
function parseDatabaseDialect(value) {
|
|
15
27
|
const normalized = value?.trim().toLowerCase();
|
|
16
28
|
if (!normalized) {
|
|
17
29
|
return null;
|
|
18
30
|
}
|
|
19
|
-
if (normalized === "pg" || normalized === "postgres" || normalized === "postgresql" || normalized === "database-pg") {
|
|
31
|
+
if (normalized === "pg" || normalized === "postgres" || normalized === "postgresql" || normalized === "database-pg" || normalized === "drizzle-pg" || normalized === "drizzle-postgres" || normalized === "database-drizzle-pg") {
|
|
20
32
|
return "database-pg";
|
|
21
33
|
}
|
|
22
|
-
if (normalized === "mysql" || normalized === "database-mysql") {
|
|
34
|
+
if (normalized === "mysql" || normalized === "database-mysql" || normalized === "drizzle-mysql" || normalized === "database-drizzle-mysql") {
|
|
23
35
|
return "database-mysql";
|
|
24
36
|
}
|
|
37
|
+
if (normalized === "database-prisma-pg" || normalized === "prisma-pg" || normalized === "prisma-postgres" || normalized === "prisma-postgresql" || normalized === "database-prisma") {
|
|
38
|
+
return "database-prisma-pg";
|
|
39
|
+
}
|
|
40
|
+
if (normalized === "database-prisma-mysql" || normalized === "prisma-mysql") {
|
|
41
|
+
return "database-prisma-mysql";
|
|
42
|
+
}
|
|
25
43
|
return null;
|
|
26
44
|
}
|
|
27
45
|
function isDatabaseModule(moduleName) {
|
|
46
|
+
return moduleName === "database-pg" || moduleName === "database-mysql" || moduleName === "database-prisma-pg" || moduleName === "database-prisma-mysql";
|
|
47
|
+
}
|
|
48
|
+
function isDrizzleDatabaseModule(moduleName) {
|
|
28
49
|
return moduleName === "database-pg" || moduleName === "database-mysql";
|
|
29
50
|
}
|
|
51
|
+
function getDatabaseSelection(moduleName) {
|
|
52
|
+
if (moduleName === "database-pg") {
|
|
53
|
+
return { orm: "drizzle", dialect: "postgresql" };
|
|
54
|
+
}
|
|
55
|
+
if (moduleName === "database-mysql") {
|
|
56
|
+
return { orm: "drizzle", dialect: "mysql" };
|
|
57
|
+
}
|
|
58
|
+
if (moduleName === "database-prisma-pg") {
|
|
59
|
+
return { orm: "prisma", dialect: "postgresql" };
|
|
60
|
+
}
|
|
61
|
+
return { orm: "prisma", dialect: "mysql" };
|
|
62
|
+
}
|
|
63
|
+
function getDatabaseModule(orm, dialect) {
|
|
64
|
+
return DATABASE_MODULE_MAP[orm][dialect];
|
|
65
|
+
}
|
|
66
|
+
function parsePrismaProvider(schemaContent) {
|
|
67
|
+
const match = schemaContent.match(/provider\s*=\s*"([^"]+)"/);
|
|
68
|
+
if (!match) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const provider = match[1]?.trim().toLowerCase();
|
|
72
|
+
if (provider === "mysql") {
|
|
73
|
+
return "mysql";
|
|
74
|
+
}
|
|
75
|
+
if (provider === "postgresql" || provider === "postgres") {
|
|
76
|
+
return "postgresql";
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
30
80
|
function validateDatabaseUrl(rawUrl, moduleName) {
|
|
81
|
+
const { dialect } = getDatabaseSelection(moduleName);
|
|
31
82
|
const dbUrl = rawUrl.trim();
|
|
32
83
|
if (!dbUrl) {
|
|
33
84
|
throw new Error("Database URL cannot be empty.");
|
|
@@ -39,25 +90,43 @@ function validateDatabaseUrl(rawUrl, moduleName) {
|
|
|
39
90
|
throw new Error(`Invalid database URL: '${dbUrl}'.`);
|
|
40
91
|
}
|
|
41
92
|
const protocol = parsed.protocol.toLowerCase();
|
|
42
|
-
if (
|
|
93
|
+
if (dialect === "postgresql" && protocol !== "postgresql:" && protocol !== "postgres:") {
|
|
43
94
|
throw new Error("PostgreSQL URL must start with postgres:// or postgresql://");
|
|
44
95
|
}
|
|
45
|
-
if (
|
|
96
|
+
if (dialect === "mysql" && protocol !== "mysql:") {
|
|
46
97
|
throw new Error("MySQL URL must start with mysql://");
|
|
47
98
|
}
|
|
48
99
|
return dbUrl;
|
|
49
100
|
}
|
|
50
101
|
async function detectInstalledDatabaseDialect(projectRoot, srcDir) {
|
|
51
102
|
const dbIndexPath = path.join(projectRoot, srcDir, "db", "index.ts");
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
103
|
+
const prismaSchemaPath = path.join(projectRoot, "prisma", "schema.prisma");
|
|
104
|
+
if (fs.existsSync(dbIndexPath)) {
|
|
105
|
+
const content = await fs.readFile(dbIndexPath, "utf-8");
|
|
106
|
+
if (content.includes("drizzle-orm/node-postgres") || content.includes(`from "pg"`)) {
|
|
107
|
+
return "database-pg";
|
|
108
|
+
}
|
|
109
|
+
if (content.includes("drizzle-orm/mysql2") || content.includes(`from "mysql2`)) {
|
|
110
|
+
return "database-mysql";
|
|
111
|
+
}
|
|
112
|
+
if (content.includes(`from "@prisma/client"`)) {
|
|
113
|
+
if (fs.existsSync(prismaSchemaPath)) {
|
|
114
|
+
const schemaContent = await fs.readFile(prismaSchemaPath, "utf-8");
|
|
115
|
+
const dialect = parsePrismaProvider(schemaContent);
|
|
116
|
+
if (dialect === "mysql") {
|
|
117
|
+
return "database-prisma-mysql";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return "database-prisma-pg";
|
|
121
|
+
}
|
|
58
122
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
123
|
+
if (fs.existsSync(prismaSchemaPath)) {
|
|
124
|
+
const schemaContent = await fs.readFile(prismaSchemaPath, "utf-8");
|
|
125
|
+
const dialect = parsePrismaProvider(schemaContent);
|
|
126
|
+
if (dialect === "mysql") {
|
|
127
|
+
return "database-prisma-mysql";
|
|
128
|
+
}
|
|
129
|
+
return "database-prisma-pg";
|
|
61
130
|
}
|
|
62
131
|
return null;
|
|
63
132
|
}
|
|
@@ -66,7 +135,8 @@ async function backupDatabaseFiles(projectRoot, srcDir) {
|
|
|
66
135
|
const backupRoot = path.join(projectRoot, ".zuro", "backups", `database-${timestamp}`);
|
|
67
136
|
const candidates = [
|
|
68
137
|
path.join(projectRoot, srcDir, "db", "index.ts"),
|
|
69
|
-
path.join(projectRoot, "drizzle.config.ts")
|
|
138
|
+
path.join(projectRoot, "drizzle.config.ts"),
|
|
139
|
+
path.join(projectRoot, "prisma", "schema.prisma")
|
|
70
140
|
];
|
|
71
141
|
let copied = false;
|
|
72
142
|
for (const filePath of candidates) {
|
|
@@ -82,7 +152,10 @@ async function backupDatabaseFiles(projectRoot, srcDir) {
|
|
|
82
152
|
return copied ? backupRoot : null;
|
|
83
153
|
}
|
|
84
154
|
function databaseLabel(moduleName) {
|
|
85
|
-
|
|
155
|
+
const selection = getDatabaseSelection(moduleName);
|
|
156
|
+
const ormLabel = selection.orm === "drizzle" ? "Drizzle" : "Prisma";
|
|
157
|
+
const dialectLabel = selection.dialect === "postgresql" ? "PostgreSQL" : "MySQL";
|
|
158
|
+
return `${ormLabel} (${dialectLabel})`;
|
|
86
159
|
}
|
|
87
160
|
function getDatabaseSetupHint(moduleName, dbUrl) {
|
|
88
161
|
try {
|
|
@@ -122,23 +195,42 @@ async function ensureSchemaExport(projectRoot, srcDir, schemaFileName) {
|
|
|
122
195
|
async function promptDatabaseConfig(initialModuleName, projectRoot, srcDir) {
|
|
123
196
|
let resolvedModuleName;
|
|
124
197
|
if (initialModuleName === "database") {
|
|
198
|
+
const ormResponse = await prompts({
|
|
199
|
+
type: "select",
|
|
200
|
+
name: "orm",
|
|
201
|
+
message: "Which ORM?",
|
|
202
|
+
choices: [
|
|
203
|
+
{ title: "Drizzle", value: "drizzle" },
|
|
204
|
+
{ title: "Prisma", value: "prisma" }
|
|
205
|
+
],
|
|
206
|
+
initial: 0
|
|
207
|
+
});
|
|
208
|
+
if (!ormResponse.orm) {
|
|
209
|
+
console.log(chalk.yellow("Operation cancelled."));
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
125
212
|
const variantResponse = await prompts({
|
|
126
213
|
type: "select",
|
|
127
|
-
name: "
|
|
214
|
+
name: "dialect",
|
|
128
215
|
message: "Which database dialect?",
|
|
129
216
|
choices: [
|
|
130
|
-
{ title: "PostgreSQL", value: "
|
|
131
|
-
{ title: "MySQL", value: "
|
|
217
|
+
{ title: "PostgreSQL", value: "postgresql" },
|
|
218
|
+
{ title: "MySQL", value: "mysql" }
|
|
132
219
|
]
|
|
133
220
|
});
|
|
134
|
-
if (!variantResponse.
|
|
221
|
+
if (!variantResponse.dialect) {
|
|
135
222
|
console.log(chalk.yellow("Operation cancelled."));
|
|
136
223
|
return null;
|
|
137
224
|
}
|
|
138
|
-
resolvedModuleName = variantResponse.
|
|
225
|
+
resolvedModuleName = getDatabaseModule(ormResponse.orm, variantResponse.dialect);
|
|
139
226
|
} else {
|
|
140
|
-
|
|
227
|
+
const parsed = parseDatabaseDialect(initialModuleName);
|
|
228
|
+
if (!parsed) {
|
|
229
|
+
throw new Error(`Unsupported database module '${initialModuleName}'.`);
|
|
230
|
+
}
|
|
231
|
+
resolvedModuleName = parsed;
|
|
141
232
|
}
|
|
233
|
+
const { orm: selectedOrm, dialect: selectedDialect } = getDatabaseSelection(resolvedModuleName);
|
|
142
234
|
let databaseBackupPath = null;
|
|
143
235
|
const installedDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);
|
|
144
236
|
if (installedDialect && installedDialect !== resolvedModuleName) {
|
|
@@ -182,7 +274,14 @@ async function promptDatabaseConfig(initialModuleName, projectRoot, srcDir) {
|
|
|
182
274
|
const enteredUrl = response.dbUrl?.trim() || "";
|
|
183
275
|
const usedDefaultDbUrl = enteredUrl.length === 0;
|
|
184
276
|
const customDbUrl = validateDatabaseUrl(enteredUrl || defaultUrl, resolvedModuleName);
|
|
185
|
-
return {
|
|
277
|
+
return {
|
|
278
|
+
resolvedModuleName,
|
|
279
|
+
selectedOrm,
|
|
280
|
+
selectedDialect,
|
|
281
|
+
customDbUrl,
|
|
282
|
+
usedDefaultDbUrl,
|
|
283
|
+
databaseBackupPath
|
|
284
|
+
};
|
|
186
285
|
}
|
|
187
286
|
function printDatabaseHints(moduleName, customDbUrl, usedDefaultDbUrl, databaseBackupPath) {
|
|
188
287
|
if (databaseBackupPath) {
|
|
@@ -197,13 +296,20 @@ function printDatabaseHints(moduleName, customDbUrl, usedDefaultDbUrl, databaseB
|
|
|
197
296
|
customDbUrl || DEFAULT_DATABASE_URLS[moduleName]
|
|
198
297
|
);
|
|
199
298
|
console.log(chalk.yellow(`\u2139 Ensure DB exists: ${setupHint}`));
|
|
200
|
-
|
|
299
|
+
if (isDrizzleDatabaseModule(moduleName)) {
|
|
300
|
+
console.log(chalk.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
console.log(chalk.yellow("\u2139 Run migrations: npx prisma migrate dev --name init"));
|
|
304
|
+
console.log(chalk.yellow("\u2139 Generate client: npx prisma generate"));
|
|
201
305
|
}
|
|
202
306
|
|
|
203
307
|
export {
|
|
204
308
|
DEFAULT_DATABASE_URLS,
|
|
205
309
|
parseDatabaseDialect,
|
|
206
310
|
isDatabaseModule,
|
|
311
|
+
isDrizzleDatabaseModule,
|
|
312
|
+
getDatabaseSelection,
|
|
207
313
|
validateDatabaseUrl,
|
|
208
314
|
detectInstalledDatabaseDialect,
|
|
209
315
|
backupDatabaseFiles,
|
|
@@ -213,4 +319,4 @@ export {
|
|
|
213
319
|
promptDatabaseConfig,
|
|
214
320
|
printDatabaseHints
|
|
215
321
|
};
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
322
|
+
//# sourceMappingURL=chunk-R3MGV5UR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/handlers/database.handler.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs-extra\";\nimport prompts from \"prompts\";\nimport chalk from \"chalk\";\nimport { escapeRegex } from \"../utils/code-inject\";\n\nexport type DatabaseOrm = \"drizzle\" | \"prisma\";\nexport type DatabaseDialect = \"postgresql\" | \"mysql\";\nexport type DatabaseModuleName =\n | \"database-pg\"\n | \"database-mysql\"\n | \"database-prisma-pg\"\n | \"database-prisma-mysql\";\n\nconst DATABASE_MODULE_MAP: Record<DatabaseOrm, Record<DatabaseDialect, DatabaseModuleName>> = {\n drizzle: {\n postgresql: \"database-pg\",\n mysql: \"database-mysql\",\n },\n prisma: {\n postgresql: \"database-prisma-pg\",\n mysql: \"database-prisma-mysql\",\n },\n};\n\nexport const DEFAULT_DATABASE_URLS: Record<DatabaseModuleName, string> = {\n \"database-pg\": \"postgresql://postgres@localhost:5432/mydb\",\n \"database-mysql\": \"mysql://root@localhost:3306/mydb\",\n \"database-prisma-pg\": \"postgresql://postgres@localhost:5432/mydb\",\n \"database-prisma-mysql\": \"mysql://root@localhost:3306/mydb\",\n};\n\nexport function parseDatabaseDialect(value?: string): DatabaseModuleName | null {\n const normalized = value?.trim().toLowerCase();\n if (!normalized) {\n return null;\n }\n\n if (\n normalized === \"pg\"\n || normalized === \"postgres\"\n || normalized === \"postgresql\"\n || normalized === \"database-pg\"\n || normalized === \"drizzle-pg\"\n || normalized === \"drizzle-postgres\"\n || normalized === \"database-drizzle-pg\"\n ) {\n return \"database-pg\";\n }\n\n if (\n normalized === \"mysql\"\n || normalized === \"database-mysql\"\n || normalized === \"drizzle-mysql\"\n || normalized === \"database-drizzle-mysql\"\n ) {\n return \"database-mysql\";\n }\n\n if (\n normalized === \"database-prisma-pg\"\n || normalized === \"prisma-pg\"\n || normalized === \"prisma-postgres\"\n || normalized === \"prisma-postgresql\"\n || normalized === \"database-prisma\"\n ) {\n return \"database-prisma-pg\";\n }\n\n if (normalized === \"database-prisma-mysql\" || normalized === \"prisma-mysql\") {\n return \"database-prisma-mysql\";\n }\n\n return null;\n}\n\nexport function isDatabaseModule(moduleName: string): moduleName is DatabaseModuleName {\n return (\n moduleName === \"database-pg\"\n || moduleName === \"database-mysql\"\n || moduleName === \"database-prisma-pg\"\n || moduleName === \"database-prisma-mysql\"\n );\n}\n\nexport function isDrizzleDatabaseModule(moduleName: string): moduleName is \"database-pg\" | \"database-mysql\" {\n return moduleName === \"database-pg\" || moduleName === \"database-mysql\";\n}\n\nexport function getDatabaseSelection(moduleName: DatabaseModuleName): { orm: DatabaseOrm; dialect: DatabaseDialect } {\n if (moduleName === \"database-pg\") {\n return { orm: \"drizzle\", dialect: \"postgresql\" };\n }\n\n if (moduleName === \"database-mysql\") {\n return { orm: \"drizzle\", dialect: \"mysql\" };\n }\n\n if (moduleName === \"database-prisma-pg\") {\n return { orm: \"prisma\", dialect: \"postgresql\" };\n }\n\n return { orm: \"prisma\", dialect: \"mysql\" };\n}\n\nfunction getDatabaseModule(orm: DatabaseOrm, dialect: DatabaseDialect): DatabaseModuleName {\n return DATABASE_MODULE_MAP[orm][dialect];\n}\n\nfunction parsePrismaProvider(schemaContent: string): DatabaseDialect | null {\n const match = schemaContent.match(/provider\\s*=\\s*\"([^\"]+)\"/);\n if (!match) {\n return null;\n }\n\n const provider = match[1]?.trim().toLowerCase();\n if (provider === \"mysql\") {\n return \"mysql\";\n }\n\n if (provider === \"postgresql\" || provider === \"postgres\") {\n return \"postgresql\";\n }\n\n return null;\n}\n\nexport function validateDatabaseUrl(rawUrl: string, moduleName: DatabaseModuleName) {\n const { dialect } = getDatabaseSelection(moduleName);\n const dbUrl = rawUrl.trim();\n if (!dbUrl) {\n throw new Error(\"Database URL cannot be empty.\");\n }\n\n let parsed: URL;\n try {\n parsed = new URL(dbUrl);\n } catch {\n throw new Error(`Invalid database URL: '${dbUrl}'.`);\n }\n\n const protocol = parsed.protocol.toLowerCase();\n if (dialect === \"postgresql\" && protocol !== \"postgresql:\" && protocol !== \"postgres:\") {\n throw new Error(\"PostgreSQL URL must start with postgres:// or postgresql://\");\n }\n\n if (dialect === \"mysql\" && protocol !== \"mysql:\") {\n throw new Error(\"MySQL URL must start with mysql://\");\n }\n\n return dbUrl;\n}\n\nexport async function detectInstalledDatabaseDialect(projectRoot: string, srcDir: string): Promise<DatabaseModuleName | null> {\n const dbIndexPath = path.join(projectRoot, srcDir, \"db\", \"index.ts\");\n const prismaSchemaPath = path.join(projectRoot, \"prisma\", \"schema.prisma\");\n\n if (fs.existsSync(dbIndexPath)) {\n const content = await fs.readFile(dbIndexPath, \"utf-8\");\n if (content.includes(\"drizzle-orm/node-postgres\") || content.includes(`from \"pg\"`)) {\n return \"database-pg\";\n }\n\n if (content.includes(\"drizzle-orm/mysql2\") || content.includes(`from \"mysql2`)) {\n return \"database-mysql\";\n }\n\n if (content.includes(`from \"@prisma/client\"`)) {\n if (fs.existsSync(prismaSchemaPath)) {\n const schemaContent = await fs.readFile(prismaSchemaPath, \"utf-8\");\n const dialect = parsePrismaProvider(schemaContent);\n if (dialect === \"mysql\") {\n return \"database-prisma-mysql\";\n }\n }\n\n return \"database-prisma-pg\";\n }\n }\n\n if (fs.existsSync(prismaSchemaPath)) {\n const schemaContent = await fs.readFile(prismaSchemaPath, \"utf-8\");\n const dialect = parsePrismaProvider(schemaContent);\n if (dialect === \"mysql\") {\n return \"database-prisma-mysql\";\n }\n\n return \"database-prisma-pg\";\n }\n\n return null;\n}\n\nexport async function backupDatabaseFiles(projectRoot: string, srcDir: string): Promise<string | null> {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupRoot = path.join(projectRoot, \".zuro\", \"backups\", `database-${timestamp}`);\n const candidates = [\n path.join(projectRoot, srcDir, \"db\", \"index.ts\"),\n path.join(projectRoot, \"drizzle.config.ts\"),\n path.join(projectRoot, \"prisma\", \"schema.prisma\"),\n ];\n\n let copied = false;\n for (const filePath of candidates) {\n if (!fs.existsSync(filePath)) {\n continue;\n }\n\n const relativePath = path.relative(projectRoot, filePath);\n const backupPath = path.join(backupRoot, relativePath);\n await fs.ensureDir(path.dirname(backupPath));\n await fs.copyFile(filePath, backupPath);\n copied = true;\n }\n\n return copied ? backupRoot : null;\n}\n\nexport function databaseLabel(moduleName: DatabaseModuleName) {\n const selection = getDatabaseSelection(moduleName);\n const ormLabel = selection.orm === \"drizzle\" ? \"Drizzle\" : \"Prisma\";\n const dialectLabel = selection.dialect === \"postgresql\" ? \"PostgreSQL\" : \"MySQL\";\n return `${ormLabel} (${dialectLabel})`;\n}\n\nexport function getDatabaseSetupHint(moduleName: DatabaseModuleName, dbUrl: string) {\n try {\n const parsed = new URL(dbUrl);\n const dbName = parsed.pathname.replace(/^\\/+/, \"\") || \"mydb\";\n\n if (moduleName === \"database-pg\") {\n return `createdb ${dbName}`;\n }\n\n return `mysql -e \"CREATE DATABASE IF NOT EXISTS ${dbName};\"`;\n } catch {\n return moduleName === \"database-pg\"\n ? \"createdb <database_name>\"\n : `mysql -e \"CREATE DATABASE IF NOT EXISTS <database_name>;\"`;\n }\n}\n\nexport async function ensureSchemaExport(projectRoot: string, srcDir: string, schemaFileName: string) {\n const schemaIndexPath = path.join(projectRoot, srcDir, \"db\", \"schema\", \"index.ts\");\n if (!await fs.pathExists(schemaIndexPath)) {\n return;\n }\n\n const exportLine = `export * from \"./${schemaFileName}\";`;\n const content = await fs.readFile(schemaIndexPath, \"utf-8\");\n const normalized = content.replace(/\\r\\n/g, \"\\n\");\n const exportPattern = new RegExp(\n `^\\\\s*export\\\\s*\\\\*\\\\s*from\\\\s*[\"']\\\\./${escapeRegex(schemaFileName)}[\"'];?\\\\s*$`,\n \"m\"\n );\n\n if (exportPattern.test(normalized)) {\n return;\n }\n\n let next = normalized\n .replace(/^\\s*export\\s*\\{\\s*\\};?\\s*$/m, \"\")\n .trimEnd();\n\n if (next.length > 0) {\n next += \"\\n\\n\";\n }\n\n next += `${exportLine}\\n`;\n await fs.writeFile(schemaIndexPath, next);\n}\n\nexport interface DatabasePromptResult {\n resolvedModuleName: DatabaseModuleName;\n selectedOrm: DatabaseOrm;\n selectedDialect: DatabaseDialect;\n customDbUrl: string | undefined;\n usedDefaultDbUrl: boolean;\n databaseBackupPath: string | null;\n}\n\n/**\n * Runs all database-specific interactive prompts and validation.\n * Returns null if the user cancels.\n */\nexport async function promptDatabaseConfig(\n initialModuleName: string,\n projectRoot: string,\n srcDir: string,\n): Promise<DatabasePromptResult | null> {\n let resolvedModuleName: DatabaseModuleName;\n\n if (initialModuleName === \"database\") {\n const ormResponse = await prompts({\n type: \"select\",\n name: \"orm\",\n message: \"Which ORM?\",\n choices: [\n { title: \"Drizzle\", value: \"drizzle\" },\n { title: \"Prisma\", value: \"prisma\" },\n ],\n initial: 0,\n });\n\n if (!ormResponse.orm) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n const variantResponse = await prompts({\n type: \"select\",\n name: \"dialect\",\n message: \"Which database dialect?\",\n choices: [\n { title: \"PostgreSQL\", value: \"postgresql\" },\n { title: \"MySQL\", value: \"mysql\" },\n ],\n });\n\n if (!variantResponse.dialect) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n resolvedModuleName = getDatabaseModule(ormResponse.orm as DatabaseOrm, variantResponse.dialect as DatabaseDialect);\n } else {\n const parsed = parseDatabaseDialect(initialModuleName);\n if (!parsed) {\n throw new Error(`Unsupported database module '${initialModuleName}'.`);\n }\n\n resolvedModuleName = parsed;\n }\n\n const { orm: selectedOrm, dialect: selectedDialect } = getDatabaseSelection(resolvedModuleName);\n\n // Check for dialect switch\n let databaseBackupPath: string | null = null;\n const installedDialect = await detectInstalledDatabaseDialect(projectRoot, srcDir);\n\n if (installedDialect && installedDialect !== resolvedModuleName) {\n console.log(\n chalk.yellow(\n `\\n⚠ Existing database setup detected: ${databaseLabel(installedDialect)}.`\n )\n );\n console.log(\n chalk.yellow(\n ` Switching to ${databaseLabel(resolvedModuleName)} will overwrite db files and drizzle config.\\n`\n )\n );\n\n const switchResponse = await prompts({\n type: \"confirm\",\n name: \"proceed\",\n message: \"Continue and switch database dialect?\",\n initial: false,\n });\n\n if (!switchResponse.proceed) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n databaseBackupPath = await backupDatabaseFiles(projectRoot, srcDir);\n }\n\n // Prompt for database URL\n const defaultUrl = DEFAULT_DATABASE_URLS[resolvedModuleName];\n console.log(chalk.dim(` Tip: Leave blank to use ${defaultUrl}\\n`));\n\n const response = await prompts({\n type: \"text\",\n name: \"dbUrl\",\n message: \"Database URL\",\n initial: \"\",\n });\n\n if (response.dbUrl === undefined) {\n console.log(chalk.yellow(\"Operation cancelled.\"));\n return null;\n }\n\n const enteredUrl = response.dbUrl?.trim() || \"\";\n const usedDefaultDbUrl = enteredUrl.length === 0;\n const customDbUrl = validateDatabaseUrl(enteredUrl || defaultUrl, resolvedModuleName);\n\n return {\n resolvedModuleName,\n selectedOrm,\n selectedDialect,\n customDbUrl,\n usedDefaultDbUrl,\n databaseBackupPath,\n };\n}\n\n/**\n * Prints post-install hints for database modules.\n */\nexport function printDatabaseHints(\n moduleName: DatabaseModuleName,\n customDbUrl: string | undefined,\n usedDefaultDbUrl: boolean,\n databaseBackupPath: string | null,\n) {\n if (databaseBackupPath) {\n console.log(chalk.blue(`ℹ Backup created at: ${databaseBackupPath}\\n`));\n }\n\n if (usedDefaultDbUrl) {\n console.log(chalk.yellow(\"ℹ Review DATABASE_URL in .env if your local DB config differs.\"));\n }\n\n const setupHint = getDatabaseSetupHint(\n moduleName,\n customDbUrl || DEFAULT_DATABASE_URLS[moduleName]\n );\n console.log(chalk.yellow(`ℹ Ensure DB exists: ${setupHint}`));\n if (isDrizzleDatabaseModule(moduleName)) {\n console.log(chalk.yellow(\"ℹ Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate\"));\n return;\n }\n\n console.log(chalk.yellow(\"ℹ Run migrations: npx prisma migrate dev --name init\"));\n console.log(chalk.yellow(\"ℹ Generate client: npx prisma generate\"));\n}\n"],"mappings":";;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,aAAa;AACpB,OAAO,WAAW;AAWlB,IAAM,sBAAwF;AAAA,EAC1F,SAAS;AAAA,IACL,YAAY;AAAA,IACZ,OAAO;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACJ,YAAY;AAAA,IACZ,OAAO;AAAA,EACX;AACJ;AAEO,IAAM,wBAA4D;AAAA,EACrE,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,yBAAyB;AAC7B;AAEO,SAAS,qBAAqB,OAA2C;AAC5E,QAAM,aAAa,OAAO,KAAK,EAAE,YAAY;AAC7C,MAAI,CAAC,YAAY;AACb,WAAO;AAAA,EACX;AAEA,MACI,eAAe,QACZ,eAAe,cACf,eAAe,gBACf,eAAe,iBACf,eAAe,gBACf,eAAe,sBACf,eAAe,uBACpB;AACE,WAAO;AAAA,EACX;AAEA,MACI,eAAe,WACZ,eAAe,oBACf,eAAe,mBACf,eAAe,0BACpB;AACE,WAAO;AAAA,EACX;AAEA,MACI,eAAe,wBACZ,eAAe,eACf,eAAe,qBACf,eAAe,uBACf,eAAe,mBACpB;AACE,WAAO;AAAA,EACX;AAEA,MAAI,eAAe,2BAA2B,eAAe,gBAAgB;AACzE,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AAEO,SAAS,iBAAiB,YAAsD;AACnF,SACI,eAAe,iBACZ,eAAe,oBACf,eAAe,wBACf,eAAe;AAE1B;AAEO,SAAS,wBAAwB,YAAoE;AACxG,SAAO,eAAe,iBAAiB,eAAe;AAC1D;AAEO,SAAS,qBAAqB,YAAgF;AACjH,MAAI,eAAe,eAAe;AAC9B,WAAO,EAAE,KAAK,WAAW,SAAS,aAAa;AAAA,EACnD;AAEA,MAAI,eAAe,kBAAkB;AACjC,WAAO,EAAE,KAAK,WAAW,SAAS,QAAQ;AAAA,EAC9C;AAEA,MAAI,eAAe,sBAAsB;AACrC,WAAO,EAAE,KAAK,UAAU,SAAS,aAAa;AAAA,EAClD;AAEA,SAAO,EAAE,KAAK,UAAU,SAAS,QAAQ;AAC7C;AAEA,SAAS,kBAAkB,KAAkB,SAA8C;AACvF,SAAO,oBAAoB,GAAG,EAAE,OAAO;AAC3C;AAEA,SAAS,oBAAoB,eAA+C;AACxE,QAAM,QAAQ,cAAc,MAAM,0BAA0B;AAC5D,MAAI,CAAC,OAAO;AACR,WAAO;AAAA,EACX;AAEA,QAAM,WAAW,MAAM,CAAC,GAAG,KAAK,EAAE,YAAY;AAC9C,MAAI,aAAa,SAAS;AACtB,WAAO;AAAA,EACX;AAEA,MAAI,aAAa,gBAAgB,aAAa,YAAY;AACtD,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AAEO,SAAS,oBAAoB,QAAgB,YAAgC;AAChF,QAAM,EAAE,QAAQ,IAAI,qBAAqB,UAAU;AACnD,QAAM,QAAQ,OAAO,KAAK;AAC1B,MAAI,CAAC,OAAO;AACR,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACnD;AAEA,MAAI;AACJ,MAAI;AACA,aAAS,IAAI,IAAI,KAAK;AAAA,EAC1B,QAAQ;AACJ,UAAM,IAAI,MAAM,0BAA0B,KAAK,IAAI;AAAA,EACvD;AAEA,QAAM,WAAW,OAAO,SAAS,YAAY;AAC7C,MAAI,YAAY,gBAAgB,aAAa,iBAAiB,aAAa,aAAa;AACpF,UAAM,IAAI,MAAM,6DAA6D;AAAA,EACjF;AAEA,MAAI,YAAY,WAAW,aAAa,UAAU;AAC9C,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACxD;AAEA,SAAO;AACX;AAEA,eAAsB,+BAA+B,aAAqB,QAAoD;AAC1H,QAAM,cAAc,KAAK,KAAK,aAAa,QAAQ,MAAM,UAAU;AACnE,QAAM,mBAAmB,KAAK,KAAK,aAAa,UAAU,eAAe;AAEzE,MAAI,GAAG,WAAW,WAAW,GAAG;AAC5B,UAAM,UAAU,MAAM,GAAG,SAAS,aAAa,OAAO;AACtD,QAAI,QAAQ,SAAS,2BAA2B,KAAK,QAAQ,SAAS,WAAW,GAAG;AAChF,aAAO;AAAA,IACX;AAEA,QAAI,QAAQ,SAAS,oBAAoB,KAAK,QAAQ,SAAS,cAAc,GAAG;AAC5E,aAAO;AAAA,IACX;AAEA,QAAI,QAAQ,SAAS,uBAAuB,GAAG;AAC3C,UAAI,GAAG,WAAW,gBAAgB,GAAG;AACjC,cAAM,gBAAgB,MAAM,GAAG,SAAS,kBAAkB,OAAO;AACjE,cAAM,UAAU,oBAAoB,aAAa;AACjD,YAAI,YAAY,SAAS;AACrB,iBAAO;AAAA,QACX;AAAA,MACJ;AAEA,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,MAAI,GAAG,WAAW,gBAAgB,GAAG;AACjC,UAAM,gBAAgB,MAAM,GAAG,SAAS,kBAAkB,OAAO;AACjE,UAAM,UAAU,oBAAoB,aAAa;AACjD,QAAI,YAAY,SAAS;AACrB,aAAO;AAAA,IACX;AAEA,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AAEA,eAAsB,oBAAoB,aAAqB,QAAwC;AACnG,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AAC/D,QAAM,aAAa,KAAK,KAAK,aAAa,SAAS,WAAW,YAAY,SAAS,EAAE;AACrF,QAAM,aAAa;AAAA,IACf,KAAK,KAAK,aAAa,QAAQ,MAAM,UAAU;AAAA,IAC/C,KAAK,KAAK,aAAa,mBAAmB;AAAA,IAC1C,KAAK,KAAK,aAAa,UAAU,eAAe;AAAA,EACpD;AAEA,MAAI,SAAS;AACb,aAAW,YAAY,YAAY;AAC/B,QAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC1B;AAAA,IACJ;AAEA,UAAM,eAAe,KAAK,SAAS,aAAa,QAAQ;AACxD,UAAM,aAAa,KAAK,KAAK,YAAY,YAAY;AACrD,UAAM,GAAG,UAAU,KAAK,QAAQ,UAAU,CAAC;AAC3C,UAAM,GAAG,SAAS,UAAU,UAAU;AACtC,aAAS;AAAA,EACb;AAEA,SAAO,SAAS,aAAa;AACjC;AAEO,SAAS,cAAc,YAAgC;AAC1D,QAAM,YAAY,qBAAqB,UAAU;AACjD,QAAM,WAAW,UAAU,QAAQ,YAAY,YAAY;AAC3D,QAAM,eAAe,UAAU,YAAY,eAAe,eAAe;AACzE,SAAO,GAAG,QAAQ,KAAK,YAAY;AACvC;AAEO,SAAS,qBAAqB,YAAgC,OAAe;AAChF,MAAI;AACA,UAAM,SAAS,IAAI,IAAI,KAAK;AAC5B,UAAM,SAAS,OAAO,SAAS,QAAQ,QAAQ,EAAE,KAAK;AAEtD,QAAI,eAAe,eAAe;AAC9B,aAAO,YAAY,MAAM;AAAA,IAC7B;AAEA,WAAO,2CAA2C,MAAM;AAAA,EAC5D,QAAQ;AACJ,WAAO,eAAe,gBAChB,6BACA;AAAA,EACV;AACJ;AAEA,eAAsB,mBAAmB,aAAqB,QAAgB,gBAAwB;AAClG,QAAM,kBAAkB,KAAK,KAAK,aAAa,QAAQ,MAAM,UAAU,UAAU;AACjF,MAAI,CAAC,MAAM,GAAG,WAAW,eAAe,GAAG;AACvC;AAAA,EACJ;AAEA,QAAM,aAAa,oBAAoB,cAAc;AACrD,QAAM,UAAU,MAAM,GAAG,SAAS,iBAAiB,OAAO;AAC1D,QAAM,aAAa,QAAQ,QAAQ,SAAS,IAAI;AAChD,QAAM,gBAAgB,IAAI;AAAA,IACtB,yCAAyC,YAAY,cAAc,CAAC;AAAA,IACpE;AAAA,EACJ;AAEA,MAAI,cAAc,KAAK,UAAU,GAAG;AAChC;AAAA,EACJ;AAEA,MAAI,OAAO,WACN,QAAQ,+BAA+B,EAAE,EACzC,QAAQ;AAEb,MAAI,KAAK,SAAS,GAAG;AACjB,YAAQ;AAAA,EACZ;AAEA,UAAQ,GAAG,UAAU;AAAA;AACrB,QAAM,GAAG,UAAU,iBAAiB,IAAI;AAC5C;AAeA,eAAsB,qBAClB,mBACA,aACA,QACoC;AACpC,MAAI;AAEJ,MAAI,sBAAsB,YAAY;AAClC,UAAM,cAAc,MAAM,QAAQ;AAAA,MAC9B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACL,EAAE,OAAO,WAAW,OAAO,UAAU;AAAA,QACrC,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,MACvC;AAAA,MACA,SAAS;AAAA,IACb,CAAC;AAED,QAAI,CAAC,YAAY,KAAK;AAClB,cAAQ,IAAI,MAAM,OAAO,sBAAsB,CAAC;AAChD,aAAO;AAAA,IACX;AAEA,UAAM,kBAAkB,MAAM,QAAQ;AAAA,MAClC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACL,EAAE,OAAO,cAAc,OAAO,aAAa;AAAA,QAC3C,EAAE,OAAO,SAAS,OAAO,QAAQ;AAAA,MACrC;AAAA,IACJ,CAAC;AAED,QAAI,CAAC,gBAAgB,SAAS;AAC1B,cAAQ,IAAI,MAAM,OAAO,sBAAsB,CAAC;AAChD,aAAO;AAAA,IACX;AAEA,yBAAqB,kBAAkB,YAAY,KAAoB,gBAAgB,OAA0B;AAAA,EACrH,OAAO;AACH,UAAM,SAAS,qBAAqB,iBAAiB;AACrD,QAAI,CAAC,QAAQ;AACT,YAAM,IAAI,MAAM,gCAAgC,iBAAiB,IAAI;AAAA,IACzE;AAEA,yBAAqB;AAAA,EACzB;AAEA,QAAM,EAAE,KAAK,aAAa,SAAS,gBAAgB,IAAI,qBAAqB,kBAAkB;AAG9F,MAAI,qBAAoC;AACxC,QAAM,mBAAmB,MAAM,+BAA+B,aAAa,MAAM;AAEjF,MAAI,oBAAoB,qBAAqB,oBAAoB;AAC7D,YAAQ;AAAA,MACJ,MAAM;AAAA,QACF;AAAA,2CAAyC,cAAc,gBAAgB,CAAC;AAAA,MAC5E;AAAA,IACJ;AACA,YAAQ;AAAA,MACJ,MAAM;AAAA,QACF,kBAAkB,cAAc,kBAAkB,CAAC;AAAA;AAAA,MACvD;AAAA,IACJ;AAEA,UAAM,iBAAiB,MAAM,QAAQ;AAAA,MACjC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACb,CAAC;AAED,QAAI,CAAC,eAAe,SAAS;AACzB,cAAQ,IAAI,MAAM,OAAO,sBAAsB,CAAC;AAChD,aAAO;AAAA,IACX;AAEA,yBAAqB,MAAM,oBAAoB,aAAa,MAAM;AAAA,EACtE;AAGA,QAAM,aAAa,sBAAsB,kBAAkB;AAC3D,UAAQ,IAAI,MAAM,IAAI,6BAA6B,UAAU;AAAA,CAAI,CAAC;AAElE,QAAM,WAAW,MAAM,QAAQ;AAAA,IAC3B,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,EACb,CAAC;AAED,MAAI,SAAS,UAAU,QAAW;AAC9B,YAAQ,IAAI,MAAM,OAAO,sBAAsB,CAAC;AAChD,WAAO;AAAA,EACX;AAEA,QAAM,aAAa,SAAS,OAAO,KAAK,KAAK;AAC7C,QAAM,mBAAmB,WAAW,WAAW;AAC/C,QAAM,cAAc,oBAAoB,cAAc,YAAY,kBAAkB;AAEpF,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;AAKO,SAAS,mBACZ,YACA,aACA,kBACA,oBACF;AACE,MAAI,oBAAoB;AACpB,YAAQ,IAAI,MAAM,KAAK,6BAAwB,kBAAkB;AAAA,CAAI,CAAC;AAAA,EAC1E;AAEA,MAAI,kBAAkB;AAClB,YAAQ,IAAI,MAAM,OAAO,qEAAgE,CAAC;AAAA,EAC9F;AAEA,QAAM,YAAY;AAAA,IACd;AAAA,IACA,eAAe,sBAAsB,UAAU;AAAA,EACnD;AACA,UAAQ,IAAI,MAAM,OAAO,4BAAuB,SAAS,EAAE,CAAC;AAC5D,MAAI,wBAAwB,UAAU,GAAG;AACrC,YAAQ,IAAI,MAAM,OAAO,4EAAuE,CAAC;AACjG;AAAA,EACJ;AAEA,UAAQ,IAAI,MAAM,OAAO,2DAAsD,CAAC;AAChF,UAAQ,IAAI,MAAM,OAAO,6CAAwC,CAAC;AACtE;","names":[]}
|
|
@@ -4,13 +4,15 @@ import {
|
|
|
4
4
|
databaseLabel,
|
|
5
5
|
detectInstalledDatabaseDialect,
|
|
6
6
|
ensureSchemaExport,
|
|
7
|
+
getDatabaseSelection,
|
|
7
8
|
getDatabaseSetupHint,
|
|
8
9
|
isDatabaseModule,
|
|
10
|
+
isDrizzleDatabaseModule,
|
|
9
11
|
parseDatabaseDialect,
|
|
10
12
|
printDatabaseHints,
|
|
11
13
|
promptDatabaseConfig,
|
|
12
14
|
validateDatabaseUrl
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-R3MGV5UR.mjs";
|
|
14
16
|
import "./chunk-VMOTWTER.mjs";
|
|
15
17
|
export {
|
|
16
18
|
DEFAULT_DATABASE_URLS,
|
|
@@ -18,11 +20,13 @@ export {
|
|
|
18
20
|
databaseLabel,
|
|
19
21
|
detectInstalledDatabaseDialect,
|
|
20
22
|
ensureSchemaExport,
|
|
23
|
+
getDatabaseSelection,
|
|
21
24
|
getDatabaseSetupHint,
|
|
22
25
|
isDatabaseModule,
|
|
26
|
+
isDrizzleDatabaseModule,
|
|
23
27
|
parseDatabaseDialect,
|
|
24
28
|
printDatabaseHints,
|
|
25
29
|
promptDatabaseConfig,
|
|
26
30
|
validateDatabaseUrl
|
|
27
31
|
};
|
|
28
|
-
//# sourceMappingURL=database.handler-
|
|
32
|
+
//# sourceMappingURL=database.handler-5OUD6XJZ.mjs.map
|