stackkit-cli 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +26 -2
  2. package/bin/stackkit.js +1 -1
  3. package/dist/commands/add.js +20 -39
  4. package/dist/commands/doctor.d.ts +11 -0
  5. package/dist/commands/doctor.js +483 -0
  6. package/dist/commands/list.d.ts +1 -1
  7. package/dist/commands/list.js +59 -38
  8. package/dist/index.js +11 -13
  9. package/dist/types/index.d.ts +15 -0
  10. package/dist/utils/config-utils.d.ts +2 -0
  11. package/dist/utils/config-utils.js +88 -0
  12. package/dist/utils/detect.js +12 -2
  13. package/dist/utils/env-editor.d.ts +0 -1
  14. package/dist/utils/env-editor.js +50 -25
  15. package/dist/utils/files.d.ts +8 -0
  16. package/dist/utils/files.js +51 -0
  17. package/dist/utils/js-conversion.d.ts +1 -0
  18. package/dist/utils/js-conversion.js +244 -0
  19. package/dist/utils/module-utils.d.ts +2 -0
  20. package/dist/utils/module-utils.js +461 -0
  21. package/dist/utils/package-manager.js +15 -31
  22. package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
  23. package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
  24. package/modules/auth/authjs/files/lib/auth.ts +41 -0
  25. package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
  26. package/modules/auth/authjs/module.json +95 -0
  27. package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
  28. package/modules/auth/better-auth/files/lib/auth.ts +62 -0
  29. package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
  30. package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
  31. package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +1 -1
  32. package/modules/auth/better-auth/module.json +164 -27
  33. package/modules/database/mongoose/files/lib/db.ts +68 -0
  34. package/modules/database/{mongoose-mongodb → mongoose}/files/models/User.ts +0 -5
  35. package/modules/database/{mongoose-mongodb → mongoose}/module.json +9 -24
  36. package/modules/database/prisma/files/lib/prisma.ts +1 -3
  37. package/modules/database/prisma/files/prisma/schema.prisma +1 -1
  38. package/modules/database/prisma/files/prisma.config.ts +2 -2
  39. package/modules/database/prisma/module.json +5 -23
  40. package/package.json +1 -1
  41. package/templates/express/.env.example +0 -1
  42. package/templates/express/package.json +4 -4
  43. package/templates/express/src/app.ts +2 -2
  44. package/templates/express/src/features/health/health.controller.ts +18 -0
  45. package/templates/express/src/features/health/health.route.ts +9 -0
  46. package/templates/express/src/features/health/health.service.ts +6 -0
  47. package/templates/nextjs/lib/env.ts +8 -0
  48. package/templates/nextjs/package.json +7 -7
  49. package/templates/react-vite/.env.example +1 -2
  50. package/templates/react-vite/.prettierignore +4 -0
  51. package/templates/react-vite/.prettierrc +9 -0
  52. package/templates/react-vite/README.md +22 -0
  53. package/templates/react-vite/package.json +16 -16
  54. package/templates/react-vite/src/router.tsx +0 -12
  55. package/templates/react-vite/vite.config.ts +0 -6
  56. package/dist/commands/init.d.ts +0 -10
  57. package/dist/commands/init.js +0 -157
  58. package/dist/utils/code-inject.d.ts +0 -14
  59. package/dist/utils/code-inject.js +0 -70
  60. package/dist/utils/json-editor.d.ts +0 -8
  61. package/dist/utils/json-editor.js +0 -45
  62. package/modules/auth/clerk/files/express/auth.ts +0 -7
  63. package/modules/auth/clerk/files/nextjs/auth-provider.tsx +0 -5
  64. package/modules/auth/clerk/files/nextjs/middleware.ts +0 -9
  65. package/modules/auth/clerk/files/react/auth-provider.tsx +0 -15
  66. package/modules/auth/clerk/module.json +0 -115
  67. package/modules/database/mongoose-mongodb/files/lib/db.ts +0 -78
  68. package/templates/express/src/features/auth/auth.controller.ts +0 -48
  69. package/templates/express/src/features/auth/auth.route.ts +0 -10
  70. package/templates/express/src/features/auth/auth.service.ts +0 -21
  71. package/templates/react-vite/src/api/services/user.service.ts +0 -18
  72. package/templates/react-vite/src/pages/UserProfile.tsx +0 -40
  73. package/templates/react-vite/src/types/user.d.ts +0 -6
@@ -0,0 +1,461 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.mergeDatabaseConfig = mergeDatabaseConfig;
40
+ exports.mergeAuthConfig = mergeAuthConfig;
41
+ const fs_extra_1 = __importDefault(require("fs-extra"));
42
+ const path_1 = __importStar(require("path"));
43
+ const config_utils_1 = require("./config-utils");
44
+ const files_1 = require("./files");
45
+ const logger_1 = require("./logger");
46
+ /**
47
+ * Process environment variables with variable replacement
48
+ */
49
+ function processEnvVars(envVarList, variables) {
50
+ const envVars = {};
51
+ for (const envVar of envVarList) {
52
+ let value = envVar.value;
53
+ for (const [key, val] of Object.entries(variables)) {
54
+ value = value.replace(new RegExp(`{{${key}}}`, "g"), val);
55
+ }
56
+ envVars[envVar.key] = value;
57
+ }
58
+ return envVars;
59
+ }
60
+ /**
61
+ * Get framework-specific paths for file placement
62
+ */
63
+ function getFrameworkPaths(framework) {
64
+ switch (framework) {
65
+ case "nextjs":
66
+ return { lib: "lib", router: "app", models: "lib/models" };
67
+ case "express":
68
+ return { lib: "lib", router: "src", models: "lib/models" };
69
+ case "react-vite":
70
+ return { lib: "src/lib", router: "src", models: "src/models" };
71
+ default:
72
+ return { lib: "src/lib", router: "src", models: "src/models" };
73
+ }
74
+ }
75
+ /**
76
+ * Generate variables based on database, framework, and provider
77
+ */
78
+ function generateVariables(database, framework, dbProvider, auth) {
79
+ const variables = {};
80
+ // Database-specific variables
81
+ variables.dbFile = database === "prisma" ? "prisma.ts" : "db.ts";
82
+ variables.dbDescription =
83
+ database === "prisma"
84
+ ? "Create Prisma client singleton"
85
+ : "Create MongoDB connection with Mongoose";
86
+ // Auth-specific variables
87
+ if (auth) {
88
+ variables.authFile = "auth.ts";
89
+ variables.authDescription = `Create ${auth} authentication configuration`;
90
+ // Dynamic dbImport for auth modules
91
+ const libPath = framework === "nextjs" ? "@/lib" : ".";
92
+ const adapterImport = 'import { prismaAdapter } from "better-auth/adapters/prisma";';
93
+ if (database === "prisma") {
94
+ variables.dbImport = `import { prisma } from "${libPath}/prisma";
95
+ ${adapterImport}`;
96
+ }
97
+ else {
98
+ variables.dbImport =
99
+ database === "prisma"
100
+ ? `import { prisma } from "${libPath}/prisma";`
101
+ : `import { client } from "${libPath}/db";`;
102
+ }
103
+ }
104
+ else {
105
+ // Framework-specific database import for database modules
106
+ const libPath = framework === "nextjs" ? "@/lib" : ".";
107
+ variables.dbImport = database === "prisma" ? `${libPath}/prisma` : `${libPath}/db`;
108
+ }
109
+ // Provider-specific variables
110
+ if (dbProvider) {
111
+ variables.provider = dbProvider;
112
+ switch (dbProvider) {
113
+ case "postgresql":
114
+ variables.connectionString = "postgresql://user:password@localhost:5432/mydb?schema=public";
115
+ variables.prismaClientInit = `import { PrismaPg } from "@prisma/adapter-pg";
116
+
117
+ const globalForPrisma = global as unknown as {
118
+ prisma: PrismaClient | undefined;
119
+ };
120
+
121
+ const adapter = new PrismaPg({
122
+ connectionString: process.env.DATABASE_URL!,
123
+ });
124
+
125
+ const prisma = globalForPrisma.prisma ?? new PrismaClient({
126
+ adapter,
127
+ });
128
+
129
+ if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
130
+ `;
131
+ break;
132
+ case "mongodb":
133
+ variables.connectionString = "mongodb+srv://username:password@cluster.mongodb.net/mydb";
134
+ variables.prismaClientInit = `
135
+ const prisma = new PrismaClient()
136
+ `;
137
+ break;
138
+ case "mysql":
139
+ variables.connectionString = "mysql://user:password@localhost:3306/mydb";
140
+ variables.prismaClientInit = `import { PrismaMariaDb } from '@prisma/adapter-mariadb';
141
+
142
+ const adapter = new PrismaMariaDb({
143
+ host: process.env.DATABASE_HOST,
144
+ user: process.env.DATABASE_USER,
145
+ password: process.env.DATABASE_PASSWORD,
146
+ database: process.env.DATABASE_NAME,
147
+ connectionLimit: 5
148
+ });
149
+ const prisma = new PrismaClient({ adapter });
150
+ `;
151
+ break;
152
+ case "sqlite":
153
+ variables.connectionString = "file:./dev.db";
154
+ variables.prismaClientInit = `import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
155
+
156
+ const adapter = new PrismaBetterSqlite3({ url: process.env.DATABASE_URL });
157
+ const prisma = new PrismaClient({ adapter });
158
+ `;
159
+ break;
160
+ }
161
+ }
162
+ return variables;
163
+ }
164
+ /**
165
+ * Process a single patch with variable replacement
166
+ */
167
+ async function processPatch(patch, filesDir, targetDir, variables, frameworkPaths) {
168
+ if (patch.type === "create-file") {
169
+ if (!patch.source || !patch.destination)
170
+ return;
171
+ try {
172
+ // Replace variables in source and destination
173
+ const sourcePath = patch.source.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] || match);
174
+ const sourceFile = (0, path_1.join)(filesDir, sourcePath);
175
+ let destFile = (0, path_1.join)(targetDir, patch.destination.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] || match));
176
+ // Apply framework-specific path replacements
177
+ destFile = destFile
178
+ .replace("{{lib}}", frameworkPaths.lib)
179
+ .replace("{{router}}", frameworkPaths.router)
180
+ .replace("{{models}}", frameworkPaths.models);
181
+ if (!(await fs_extra_1.default.pathExists(sourceFile))) {
182
+ logger_1.logger.warn(`Source file not found: ${sourceFile}`);
183
+ return;
184
+ }
185
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(destFile));
186
+ const ext = path_1.default.extname(sourceFile);
187
+ if ([".ts", ".js", ".tsx", ".prisma", ".json"].includes(ext)) {
188
+ let content = await fs_extra_1.default.readFile(sourceFile, "utf-8");
189
+ for (const [key, value] of Object.entries(variables)) {
190
+ content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
191
+ }
192
+ await fs_extra_1.default.writeFile(destFile, content);
193
+ }
194
+ else {
195
+ await fs_extra_1.default.copy(sourceFile, destFile, { overwrite: false });
196
+ }
197
+ }
198
+ catch (error) {
199
+ logger_1.logger.error(`Failed to process patch ${patch.description}: ${error.message}`);
200
+ throw error;
201
+ }
202
+ }
203
+ else if (patch.type === "patch-file") {
204
+ if (!patch.file)
205
+ return;
206
+ try {
207
+ const filePath = (0, path_1.join)(targetDir, patch.file);
208
+ if (!(await fs_extra_1.default.pathExists(filePath))) {
209
+ logger_1.logger.warn(`File to patch not found: ${filePath}`);
210
+ return;
211
+ }
212
+ let content = await fs_extra_1.default.readFile(filePath, "utf-8");
213
+ for (const operation of patch.operations || []) {
214
+ if (operation.type === "add-import" && operation.imports) {
215
+ // Add imports at the top of the file, after existing imports
216
+ const imports = operation.imports.join("\n");
217
+ // Find the last import statement
218
+ const importRegex = /^import\s+.*$/gm;
219
+ const matches = [...content.matchAll(importRegex)];
220
+ if (matches.length > 0) {
221
+ const lastImport = matches[matches.length - 1];
222
+ const insertIndex = (lastImport.index ?? 0) + lastImport[0].length;
223
+ content = content.slice(0, insertIndex) + "\n" + imports + content.slice(insertIndex);
224
+ }
225
+ else {
226
+ // No imports found, add at the beginning
227
+ content = imports + "\n\n" + content;
228
+ }
229
+ }
230
+ else if (operation.type === "add-code") {
231
+ if (operation.after && operation.code) {
232
+ const insertIndex = content.indexOf(operation.after);
233
+ if (insertIndex !== -1) {
234
+ const afterIndex = insertIndex + operation.after.length;
235
+ content = content.slice(0, afterIndex) + operation.code + content.slice(afterIndex);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ await fs_extra_1.default.writeFile(filePath, content);
241
+ }
242
+ catch (error) {
243
+ logger_1.logger.error(`Failed to process patch-file ${patch.description}: ${error.message}`);
244
+ throw error;
245
+ }
246
+ }
247
+ }
248
+ /**
249
+ * Validate and extract module data
250
+ */
251
+ async function loadModuleData(modulePath) {
252
+ const moduleJsonPath = (0, path_1.join)(modulePath, "module.json");
253
+ if (!(await fs_extra_1.default.pathExists(moduleJsonPath))) {
254
+ throw new Error(`Module configuration not found: ${moduleJsonPath}`);
255
+ }
256
+ const moduleData = await fs_extra_1.default.readJson(moduleJsonPath);
257
+ if (!moduleData) {
258
+ throw new Error(`Invalid module configuration: ${moduleJsonPath}`);
259
+ }
260
+ return moduleData;
261
+ }
262
+ async function mergeDatabaseConfig(templatesDir, targetDir, database, framework, dbProvider) {
263
+ try {
264
+ const modulesDir = (0, path_1.join)(templatesDir, "..", "modules");
265
+ const dbModulePath = (0, path_1.join)(modulesDir, "database", database);
266
+ if (!(await fs_extra_1.default.pathExists(dbModulePath))) {
267
+ logger_1.logger.warn(`Database module not found: ${database}`);
268
+ return [];
269
+ }
270
+ const moduleData = await loadModuleData(dbModulePath);
271
+ const variables = generateVariables(database, framework, dbProvider);
272
+ const frameworkPaths = getFrameworkPaths(framework);
273
+ const filesDir = (0, path_1.join)(dbModulePath, "files");
274
+ if (await fs_extra_1.default.pathExists(filesDir)) {
275
+ for (const patch of moduleData.patches || []) {
276
+ await processPatch(patch, filesDir, targetDir, variables, frameworkPaths);
277
+ }
278
+ }
279
+ // Merge dependencies
280
+ let dependencies = {};
281
+ let devDependencies = {};
282
+ if (moduleData.dependencies) {
283
+ if (moduleData.dependencies.common || moduleData.dependencies.providers) {
284
+ // Structured dependencies (common + providers)
285
+ dependencies = {
286
+ ...moduleData.dependencies.common,
287
+ ...(dbProvider ? moduleData.dependencies.providers?.[dbProvider] : {}),
288
+ };
289
+ }
290
+ else {
291
+ // Flat dependencies structure
292
+ dependencies = { ...moduleData.dependencies };
293
+ }
294
+ }
295
+ if (moduleData.devDependencies) {
296
+ if (moduleData.devDependencies.common || moduleData.devDependencies.providers) {
297
+ // Structured devDependencies
298
+ devDependencies = {
299
+ ...moduleData.devDependencies.common,
300
+ ...(dbProvider ? moduleData.devDependencies.providers?.[dbProvider] : {}),
301
+ };
302
+ }
303
+ else {
304
+ // Flat devDependencies structure
305
+ devDependencies = { ...moduleData.devDependencies };
306
+ }
307
+ }
308
+ await (0, files_1.mergePackageJson)(targetDir, { dependencies, devDependencies });
309
+ // Process environment variables
310
+ const envVars = {};
311
+ const commonEnvVars = Array.isArray(moduleData.envVars)
312
+ ? moduleData.envVars
313
+ : moduleData.envVars?.common || [];
314
+ const providerEnvVars = dbProvider && moduleData.envVars?.providers?.[dbProvider]
315
+ ? moduleData.envVars.providers[dbProvider]
316
+ : [];
317
+ const allEnvVars = [...commonEnvVars, ...providerEnvVars];
318
+ Object.assign(envVars, processEnvVars(allEnvVars, variables));
319
+ await (0, files_1.mergeEnvFile)(targetDir, envVars);
320
+ // Apply framework-specific patches
321
+ if (moduleData.frameworkPatches) {
322
+ const frameworkKey = framework === "react-vite" ? "react" : framework;
323
+ const patches = moduleData.frameworkPatches[frameworkKey];
324
+ if (patches) {
325
+ await (0, config_utils_1.applyFrameworkPatches)(targetDir, patches);
326
+ }
327
+ }
328
+ return moduleData.postInstall || [];
329
+ }
330
+ catch (error) {
331
+ logger_1.logger.error(`Failed to merge database config for ${database}: ${error.message}`);
332
+ throw error;
333
+ }
334
+ }
335
+ async function mergeAuthConfig(templatesDir, targetDir, framework, auth, database = "none", dbProvider) {
336
+ try {
337
+ const modulesDir = (0, path_1.join)(templatesDir, "..", "modules");
338
+ const authModulePath = (0, path_1.join)(modulesDir, "auth", auth);
339
+ if (!(await fs_extra_1.default.pathExists(authModulePath))) {
340
+ logger_1.logger.warn(`Auth module not found: ${auth}`);
341
+ return;
342
+ }
343
+ const moduleData = await loadModuleData(authModulePath);
344
+ if (moduleData.supportedFrameworks && !moduleData.supportedFrameworks.includes(framework)) {
345
+ logger_1.logger.warn(`Auth ${auth} does not support framework ${framework}`);
346
+ return;
347
+ }
348
+ let frameworkConfig = null;
349
+ if (moduleData.frameworkConfigs) {
350
+ frameworkConfig = moduleData.frameworkConfigs[framework];
351
+ if (!frameworkConfig) {
352
+ logger_1.logger.warn(`No config for framework ${framework} in ${auth}`);
353
+ return;
354
+ }
355
+ const shared = moduleData.frameworkConfigs.shared;
356
+ if (shared) {
357
+ frameworkConfig = {
358
+ ...shared,
359
+ ...frameworkConfig,
360
+ };
361
+ }
362
+ }
363
+ const variables = generateVariables(database, framework, dbProvider, auth);
364
+ const frameworkPaths = getFrameworkPaths(framework);
365
+ // Handle database adapters first to set variables
366
+ if (database !== "none" && moduleData.databaseAdapters) {
367
+ let adapterKey = database;
368
+ if (database === "prisma" && dbProvider) {
369
+ adapterKey = `prisma-${dbProvider}`;
370
+ }
371
+ const adapterConfig = moduleData.databaseAdapters[adapterKey];
372
+ if (adapterConfig) {
373
+ // Generate adapter code based on database type
374
+ if (database === "prisma" && dbProvider) {
375
+ variables.databaseAdapter = `database: prismaAdapter(prisma, {\n provider: "${dbProvider}",\n }),`;
376
+ }
377
+ if (adapterConfig.schema && adapterConfig.schemaDestination) {
378
+ const schemaSource = (0, path_1.join)(authModulePath, adapterConfig.schema);
379
+ const schemaDest = (0, path_1.join)(targetDir, adapterConfig.schemaDestination);
380
+ if (await fs_extra_1.default.pathExists(schemaSource)) {
381
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(schemaDest));
382
+ let content = await fs_extra_1.default.readFile(schemaSource, "utf-8");
383
+ // Set schema variables for Prisma
384
+ if (dbProvider === "postgresql") {
385
+ variables.provider = "postgresql";
386
+ variables.idDefault = "@default(cuid())";
387
+ variables.userIdType = "";
388
+ }
389
+ else if (dbProvider === "mongodb") {
390
+ variables.provider = "mongodb";
391
+ variables.idDefault = '@default(auto()) @map("_id") @db.ObjectId';
392
+ variables.userIdType = "@db.ObjectId";
393
+ }
394
+ else if (dbProvider === "mysql") {
395
+ variables.provider = "mysql";
396
+ variables.idDefault = "@default(cuid())";
397
+ variables.userIdType = "";
398
+ }
399
+ else if (dbProvider === "sqlite") {
400
+ variables.provider = "sqlite";
401
+ variables.idDefault = "@default(cuid())";
402
+ variables.userIdType = "";
403
+ }
404
+ for (const [key, value] of Object.entries(variables)) {
405
+ content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
406
+ }
407
+ await fs_extra_1.default.writeFile(schemaDest, content, { flag: "a" }); // append
408
+ }
409
+ }
410
+ if (adapterConfig.dependencies) {
411
+ const commonDeps = moduleData.databaseAdapters.common?.dependencies || {};
412
+ const commonDevDeps = moduleData.databaseAdapters.common?.devDependencies || {};
413
+ const specificDeps = adapterConfig.dependencies;
414
+ const specificDevDeps = adapterConfig.devDependencies || {};
415
+ await (0, files_1.mergePackageJson)(targetDir, {
416
+ dependencies: { ...commonDeps, ...specificDeps },
417
+ devDependencies: { ...commonDevDeps, ...specificDevDeps },
418
+ });
419
+ }
420
+ }
421
+ }
422
+ const filesDir = (0, path_1.join)(authModulePath, "files");
423
+ if (await fs_extra_1.default.pathExists(filesDir)) {
424
+ const patches = frameworkConfig?.patches || moduleData.patches || [];
425
+ for (const patch of patches) {
426
+ await processPatch(patch, filesDir, targetDir, variables, frameworkPaths);
427
+ }
428
+ }
429
+ // Add framework-specific patches
430
+ if (framework === "nextjs") {
431
+ const apiSource = (0, path_1.join)(filesDir, "api/auth/[...all]/route.ts");
432
+ const apiDest = (0, path_1.join)(targetDir, "app/api/auth/[...all]/route.ts");
433
+ if (await fs_extra_1.default.pathExists(apiSource)) {
434
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(apiDest));
435
+ await fs_extra_1.default.copy(apiSource, apiDest, { overwrite: false });
436
+ }
437
+ }
438
+ // Merge package.json
439
+ await (0, files_1.mergePackageJson)(targetDir, {
440
+ dependencies: frameworkConfig?.dependencies || moduleData.dependencies,
441
+ devDependencies: frameworkConfig?.devDependencies || moduleData.devDependencies,
442
+ });
443
+ // Process environment variables
444
+ const envVars = {};
445
+ const envVarList = frameworkConfig?.envVars || moduleData.envVars || [];
446
+ Object.assign(envVars, processEnvVars(envVarList, variables));
447
+ await (0, files_1.mergeEnvFile)(targetDir, envVars);
448
+ // Apply framework-specific patches
449
+ if (moduleData.frameworkPatches) {
450
+ const frameworkKey = framework === "react-vite" ? "react" : framework;
451
+ const patches = moduleData.frameworkPatches[frameworkKey];
452
+ if (patches) {
453
+ await (0, config_utils_1.applyFrameworkPatches)(targetDir, patches);
454
+ }
455
+ }
456
+ }
457
+ catch (error) {
458
+ logger_1.logger.error(`Failed to merge auth config for ${auth}: ${error.message}`);
459
+ throw error;
460
+ }
461
+ }
@@ -20,28 +20,20 @@ async function detectPackageManager(cwd) {
20
20
  }
21
21
  }
22
22
  async function installDependencies(cwd, pm) {
23
- const spinner = logger_1.logger.startSpinner(`Installing dependencies with ${pm}...`);
24
- try {
25
- const args = [];
26
- if (pm === "npm") {
27
- args.push("install");
28
- }
29
- else if (pm === "yarn") {
30
- args.push("install");
31
- }
32
- else if (pm === "pnpm") {
33
- args.push("install");
34
- }
35
- else if (pm === "bun") {
36
- args.push("install");
37
- }
38
- await (0, execa_1.default)(pm, args, { cwd, stdio: "pipe" });
39
- spinner.succeed(`Dependencies installed successfully`);
23
+ const args = [];
24
+ if (pm === "npm") {
25
+ args.push("install");
40
26
  }
41
- catch (error) {
42
- spinner.fail(`Failed to install dependencies`);
43
- throw new Error(`Failed to install dependencies: ${error}`);
27
+ else if (pm === "yarn") {
28
+ args.push("install");
29
+ }
30
+ else if (pm === "pnpm") {
31
+ args.push("install");
32
+ }
33
+ else if (pm === "bun") {
34
+ args.push("install");
44
35
  }
36
+ await (0, execa_1.default)(pm, args, { cwd, stdio: "pipe" });
45
37
  }
46
38
  async function addDependencies(cwd, pm, packages, dev = false) {
47
39
  if (packages.length === 0)
@@ -71,15 +63,7 @@ async function addDependencies(cwd, pm, packages, dev = false) {
71
63
  }
72
64
  }
73
65
  async function initGit(cwd) {
74
- const spinner = logger_1.logger.startSpinner("Initializing git repository...");
75
- try {
76
- await (0, execa_1.default)("git", ["init"], { cwd });
77
- await (0, execa_1.default)("git", ["add", "."], { cwd });
78
- await (0, execa_1.default)("git", ["commit", "-m", "Initial commit from StackKit"], { cwd });
79
- spinner.succeed("Git repository initialized");
80
- }
81
- catch {
82
- spinner.fail("Failed to initialize git repository");
83
- // Don't throw - git init is optional
84
- }
66
+ await (0, execa_1.default)("git", ["init"], { cwd });
67
+ await (0, execa_1.default)("git", ["add", "."], { cwd });
68
+ await (0, execa_1.default)("git", ["commit", "-m", "Initial commit from StackKit"], { cwd });
85
69
  }
@@ -0,0 +1,6 @@
1
+ import NextAuth from "next-auth";
2
+ import { authOptions } from "@/lib/auth";
3
+
4
+ const handler = NextAuth(authOptions);
5
+
6
+ export { handler as GET, handler as POST };
@@ -0,0 +1,11 @@
1
+ import { getServerSession } from "next-auth/next";
2
+ import { authOptions } from "@/lib/auth";
3
+
4
+ export async function getSession() {
5
+ return await getServerSession(authOptions);
6
+ }
7
+
8
+ export async function getCurrentUser() {
9
+ const session = await getSession();
10
+ return session?.user;
11
+ }
@@ -0,0 +1,41 @@
1
+ import { NextAuthOptions } from "next-auth";
2
+ import { PrismaAdapter } from "@auth/prisma-adapter";
3
+ import { prisma } from "@/lib/prisma";
4
+ import GoogleProvider from "next-auth/providers/google";
5
+ import GitHubProvider from "next-auth/providers/github";
6
+
7
+ export const authOptions: NextAuthOptions = {
8
+ adapter: PrismaAdapter(prisma),
9
+ providers: [
10
+ GoogleProvider({
11
+ clientId: process.env.GOOGLE_CLIENT_ID!,
12
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
13
+ }),
14
+ GitHubProvider({
15
+ clientId: process.env.GITHUB_ID!,
16
+ clientSecret: process.env.GITHUB_SECRET!,
17
+ }),
18
+ ],
19
+ session: {
20
+ strategy: "jwt",
21
+ },
22
+ callbacks: {
23
+ async jwt({ token, user }) {
24
+ if (user) {
25
+ token.id = user.id;
26
+ }
27
+ return token;
28
+ },
29
+ async session({ session, token }) {
30
+ if (token) {
31
+ session.user.id = token.id as string;
32
+ }
33
+ return session;
34
+ },
35
+ },
36
+ pages: {
37
+ signIn: "/auth/signin",
38
+ signOut: "/auth/signout",
39
+ error: "/auth/error",
40
+ },
41
+ };
@@ -0,0 +1,45 @@
1
+
2
+ model Account {
3
+ id String @id {{idDefault}}
4
+ userId String {{userIdType}}
5
+ type String
6
+ provider String
7
+ providerAccountId String
8
+ refresh_token String? @db.Text
9
+ access_token String? @db.Text
10
+ expires_at Int?
11
+ token_type String?
12
+ scope String?
13
+ id_token String? @db.Text
14
+ session_state String?
15
+
16
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
17
+
18
+ @@unique([provider, providerAccountId])
19
+ }
20
+
21
+ model Session {
22
+ id String @id {{idDefault}}
23
+ sessionToken String @unique
24
+ userId String {{userIdType}}
25
+ expires DateTime
26
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
27
+ }
28
+
29
+ model User {
30
+ id String @id {{idDefault}}
31
+ name String?
32
+ email String @unique
33
+ emailVerified DateTime?
34
+ image String?
35
+ accounts Account[]
36
+ sessions Session[]
37
+ }
38
+
39
+ model VerificationToken {
40
+ identifier String
41
+ token String @unique
42
+ expires DateTime
43
+
44
+ @@unique([identifier, token])
45
+ }