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.
- package/README.md +26 -2
- package/bin/stackkit.js +1 -1
- package/dist/commands/add.js +20 -39
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +483 -0
- package/dist/commands/list.d.ts +1 -1
- package/dist/commands/list.js +59 -38
- package/dist/index.js +11 -13
- package/dist/types/index.d.ts +15 -0
- package/dist/utils/config-utils.d.ts +2 -0
- package/dist/utils/config-utils.js +88 -0
- package/dist/utils/detect.js +12 -2
- package/dist/utils/env-editor.d.ts +0 -1
- package/dist/utils/env-editor.js +50 -25
- package/dist/utils/files.d.ts +8 -0
- package/dist/utils/files.js +51 -0
- package/dist/utils/js-conversion.d.ts +1 -0
- package/dist/utils/js-conversion.js +244 -0
- package/dist/utils/module-utils.d.ts +2 -0
- package/dist/utils/module-utils.js +461 -0
- package/dist/utils/package-manager.js +15 -31
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
- package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
- package/modules/auth/authjs/files/lib/auth.ts +41 -0
- package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
- package/modules/auth/authjs/module.json +95 -0
- package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
- package/modules/auth/better-auth/files/lib/auth.ts +62 -0
- package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
- package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
- package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +1 -1
- package/modules/auth/better-auth/module.json +164 -27
- package/modules/database/mongoose/files/lib/db.ts +68 -0
- package/modules/database/{mongoose-mongodb → mongoose}/files/models/User.ts +0 -5
- package/modules/database/{mongoose-mongodb → mongoose}/module.json +9 -24
- package/modules/database/prisma/files/lib/prisma.ts +1 -3
- package/modules/database/prisma/files/prisma/schema.prisma +1 -1
- package/modules/database/prisma/files/prisma.config.ts +2 -2
- package/modules/database/prisma/module.json +5 -23
- package/package.json +1 -1
- package/templates/express/.env.example +0 -1
- package/templates/express/package.json +4 -4
- package/templates/express/src/app.ts +2 -2
- package/templates/express/src/features/health/health.controller.ts +18 -0
- package/templates/express/src/features/health/health.route.ts +9 -0
- package/templates/express/src/features/health/health.service.ts +6 -0
- package/templates/nextjs/lib/env.ts +8 -0
- package/templates/nextjs/package.json +7 -7
- package/templates/react-vite/.env.example +1 -2
- package/templates/react-vite/.prettierignore +4 -0
- package/templates/react-vite/.prettierrc +9 -0
- package/templates/react-vite/README.md +22 -0
- package/templates/react-vite/package.json +16 -16
- package/templates/react-vite/src/router.tsx +0 -12
- package/templates/react-vite/vite.config.ts +0 -6
- package/dist/commands/init.d.ts +0 -10
- package/dist/commands/init.js +0 -157
- package/dist/utils/code-inject.d.ts +0 -14
- package/dist/utils/code-inject.js +0 -70
- package/dist/utils/json-editor.d.ts +0 -8
- package/dist/utils/json-editor.js +0 -45
- package/modules/auth/clerk/files/express/auth.ts +0 -7
- package/modules/auth/clerk/files/nextjs/auth-provider.tsx +0 -5
- package/modules/auth/clerk/files/nextjs/middleware.ts +0 -9
- package/modules/auth/clerk/files/react/auth-provider.tsx +0 -15
- package/modules/auth/clerk/module.json +0 -115
- package/modules/database/mongoose-mongodb/files/lib/db.ts +0 -78
- package/templates/express/src/features/auth/auth.controller.ts +0 -48
- package/templates/express/src/features/auth/auth.route.ts +0 -10
- package/templates/express/src/features/auth/auth.service.ts +0 -21
- package/templates/react-vite/src/api/services/user.service.ts +0 -18
- package/templates/react-vite/src/pages/UserProfile.tsx +0 -40
- 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
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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,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
|
+
}
|