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
package/README.md CHANGED
@@ -5,6 +5,9 @@ Add authentication and database modules to existing projects.
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
+ # Check project health and compatibility
9
+ npx stackkit-cli doctor
10
+
8
11
  # Add authentication
9
12
  npx stackkit-cli add auth
10
13
 
@@ -21,19 +24,40 @@ npx stackkit-cli list
21
24
  - **Shows compatible modules** for your project
22
25
  - **Installs dependencies** automatically
23
26
  - **Configures everything** - files, env vars, and setup
27
+ - **Health checks** with `doctor` command
24
28
 
25
29
  ## Available Modules
26
30
 
27
31
  ### Authentication
28
32
 
29
33
  - Better Auth (Next.js, Express, React)
30
- - Clerk (Next.js, Express, React)
31
34
 
32
35
  ### Database
33
36
 
34
37
  - Prisma with PostgreSQL or MongoDB (Next.js, Express)
35
38
  - Mongoose with MongoDB (Next.js, Express)
36
39
 
40
+ ## Commands
41
+
42
+ ### `doctor`
43
+
44
+ Check your project's health and compatibility with StackKit modules.
45
+
46
+ ```bash
47
+ stackkit doctor # Basic health check
48
+ stackkit doctor --verbose # Detailed output
49
+ stackkit doctor --json # Machine-readable JSON output
50
+ stackkit doctor --strict # Treat warnings as errors
51
+ ```
52
+
53
+ The doctor command:
54
+ - Detects your project type and framework
55
+ - Checks Node.js version compatibility
56
+ - Identifies installed StackKit modules
57
+ - Validates environment variables
58
+ - Checks for configuration conflicts
59
+ - Provides actionable next steps
60
+
37
61
  ## Documentation
38
62
 
39
- Full documentation: [stackkit.dev](https://stackkit.dev) | [GitHub](https://github.com/tariqul420/stackkit)
63
+ Full documentation: [stackkit.dev](https://stackkit.dev) | [GitHub](https://github.com/tariqul420/stackkit/issues)
package/bin/stackkit.js CHANGED
@@ -8,4 +8,4 @@ try {
8
8
  } catch {
9
9
  // eslint-disable-next-line
10
10
  require("../dist/index.js");
11
- }
11
+ }
@@ -50,38 +50,19 @@ async function addCommand(module, options) {
50
50
  // Merge dependencies based on provider
51
51
  const mergedDeps = {};
52
52
  const mergedDevDeps = {};
53
- if (typeof moduleMetadata.dependencies === "object" &&
54
- !("common" in moduleMetadata.dependencies)) {
55
- // Already flat
56
- Object.assign(mergedDeps, moduleMetadata.dependencies);
53
+ // Add shared dependencies
54
+ if (moduleMetadata.frameworkConfigs?.shared?.dependencies) {
55
+ Object.assign(mergedDeps, moduleMetadata.frameworkConfigs.shared.dependencies);
57
56
  }
58
- else if (typeof moduleMetadata.dependencies === "object" &&
59
- "common" in moduleMetadata.dependencies) {
60
- Object.assign(mergedDeps, moduleMetadata.dependencies.common);
61
- if (selectedProvider &&
62
- typeof moduleMetadata.dependencies === "object" &&
63
- "providers" in moduleMetadata.dependencies &&
64
- typeof moduleMetadata.dependencies.providers === "object" &&
65
- selectedProvider in moduleMetadata.dependencies.providers) {
66
- Object.assign(mergedDeps, moduleMetadata.dependencies.providers[selectedProvider]);
67
- }
57
+ if (moduleMetadata.frameworkConfigs?.shared?.devDependencies) {
58
+ Object.assign(mergedDevDeps, moduleMetadata.frameworkConfigs.shared.devDependencies);
68
59
  }
69
- if (moduleMetadata.devDependencies) {
70
- if (typeof moduleMetadata.devDependencies === "object" &&
71
- !("common" in moduleMetadata.devDependencies)) {
72
- Object.assign(mergedDevDeps, moduleMetadata.devDependencies);
73
- }
74
- else if (typeof moduleMetadata.devDependencies === "object" &&
75
- "common" in moduleMetadata.devDependencies) {
76
- Object.assign(mergedDevDeps, moduleMetadata.devDependencies.common);
77
- if (selectedProvider &&
78
- typeof moduleMetadata.devDependencies === "object" &&
79
- "providers" in moduleMetadata.devDependencies &&
80
- typeof moduleMetadata.devDependencies.providers === "object" &&
81
- selectedProvider in moduleMetadata.devDependencies.providers) {
82
- Object.assign(mergedDevDeps, moduleMetadata.devDependencies.providers[selectedProvider]);
83
- }
84
- }
60
+ // Add provider specific dependencies
61
+ if (selectedProvider && moduleMetadata.databaseAdapters?.providers?.[selectedProvider]?.dependencies) {
62
+ Object.assign(mergedDeps, moduleMetadata.databaseAdapters.providers[selectedProvider].dependencies);
63
+ }
64
+ if (selectedProvider && moduleMetadata.databaseAdapters?.providers?.[selectedProvider]?.devDependencies) {
65
+ Object.assign(mergedDevDeps, moduleMetadata.databaseAdapters.providers[selectedProvider].devDependencies);
85
66
  }
86
67
  // Update metadata with merged deps
87
68
  moduleMetadata.dependencies = mergedDeps;
@@ -149,8 +130,8 @@ async function addCommand(module, options) {
149
130
  }
150
131
  }
151
132
  // Add dependencies
152
- if (Object.keys(moduleMetadata.dependencies).length > 0 && options.install !== false) {
153
- const deps = Object.entries(moduleMetadata.dependencies).map(([name, version]) => `${name}@${version}`);
133
+ if (Object.keys(mergedDeps).length > 0 && options.install !== false) {
134
+ const deps = Object.entries(mergedDeps).map(([name, version]) => `${name}@${version}`);
154
135
  if (!options.dryRun) {
155
136
  await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, deps, false);
156
137
  }
@@ -159,10 +140,8 @@ async function addCommand(module, options) {
159
140
  }
160
141
  }
161
142
  // Add dev dependencies
162
- if (moduleMetadata.devDependencies &&
163
- Object.keys(moduleMetadata.devDependencies).length > 0 &&
164
- options.install !== false) {
165
- const devDeps = Object.entries(moduleMetadata.devDependencies).map(([name, version]) => `${name}@${version}`);
143
+ if (Object.keys(mergedDevDeps).length > 0 && options.install !== false) {
144
+ const devDeps = Object.entries(mergedDevDeps).map(([name, version]) => `${name}@${version}`);
166
145
  if (!options.dryRun) {
167
146
  await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, devDeps, true);
168
147
  }
@@ -171,7 +150,7 @@ async function addCommand(module, options) {
171
150
  }
172
151
  }
173
152
  // Add environment variables
174
- if (moduleMetadata.envVars.length > 0) {
153
+ if (moduleMetadata.envVars && moduleMetadata.envVars.length > 0) {
175
154
  // Replace variables in envVars
176
155
  const processedEnvVars = moduleMetadata.envVars.map((envVar) => ({
177
156
  ...envVar,
@@ -188,7 +167,7 @@ async function addCommand(module, options) {
188
167
  logger_1.logger.success(`Added ${chalk_1.default.bold(moduleMetadata.displayName)}`);
189
168
  logger_1.logger.newLine();
190
169
  // Print next steps
191
- if (moduleMetadata.envVars.some((v) => v.required)) {
170
+ if (moduleMetadata.envVars && moduleMetadata.envVars.some((v) => v.required)) {
192
171
  logger_1.logger.log("Next: Fill in environment variables in .env");
193
172
  }
194
173
  logger_1.logger.newLine();
@@ -235,8 +214,10 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
235
214
  }
236
215
  return null;
237
216
  }
238
- // (removed duplicate import)
239
217
  async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, moduleName, options) {
218
+ if (!moduleMetadata.patches || !Array.isArray(moduleMetadata.patches)) {
219
+ return;
220
+ }
240
221
  // Find the module path
241
222
  const moduleBasePath = await findModulePath(modulesDir, moduleName, options.provider);
242
223
  if (!moduleBasePath) {
@@ -0,0 +1,11 @@
1
+ interface DoctorOptions {
2
+ json?: boolean;
3
+ verbose?: boolean;
4
+ strict?: boolean;
5
+ }
6
+ /**
7
+ * Main doctor command function that performs health checks on a StackKit project
8
+ * @param options Command line options
9
+ */
10
+ export declare function doctorCommand(options: DoctorOptions): Promise<void>;
11
+ export {};
@@ -0,0 +1,483 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.doctorCommand = doctorCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const logger_1 = require("../utils/logger");
11
+ // Constants for consistent messaging
12
+ const MESSAGES = {
13
+ NO_PACKAGE_JSON: "No package.json found in current directory or any parent directory.",
14
+ UNSUPPORTED_PROJECT: "Unsupported project type. Only Next.js, Express, and React projects are supported.",
15
+ NODE_TOO_OLD: (version) => `Node.js version ${version} is not supported. Minimum required: Node 18.`,
16
+ NODE_WARNING: (version) => `Node.js version ${version} is supported but Node 20+ is recommended.`,
17
+ NODE_SUCCESS: (version) => `Node.js version ${version} is supported.`,
18
+ ENV_EXAMPLE_MISSING: ".env.example file missing (recommended for documentation)",
19
+ ENV_EXAMPLE_FOUND: ".env.example file found",
20
+ ENV_MISSING: "No .env or .env.local file found",
21
+ ENV_FOUND: ".env/.env.local file found",
22
+ PRISMA_SCHEMA_MISSING: "Prisma schema missing (required for Prisma)",
23
+ PRISMA_SCHEMA_FOUND: "Prisma schema found",
24
+ AUTH_ROUTES_MISSING: "Auth routes not found (may need configuration)",
25
+ AUTH_ROUTES_FOUND: "Auth routes configured",
26
+ ENV_VARS_MISSING: (vars) => `Missing: ${vars.join(", ")}`,
27
+ ENV_VARS_PRESENT: (vars) => `Present: ${vars.join(", ")}`,
28
+ };
29
+ /**
30
+ * Main doctor command function that performs health checks on a StackKit project
31
+ * @param options Command line options
32
+ */
33
+ async function doctorCommand(options) {
34
+ try {
35
+ const report = await runDoctorChecks();
36
+ if (options.json) {
37
+ process.stdout.write(JSON.stringify(report, null, 2) + "\n");
38
+ return;
39
+ }
40
+ printDoctorReport(report, options.verbose || false);
41
+ // Exit with proper codes
42
+ const hasErrors = report.summary.errors > 0;
43
+ const hasWarnings = report.summary.warnings > 0;
44
+ const strictMode = options.strict || false;
45
+ if (hasErrors || (strictMode && hasWarnings)) {
46
+ process.exit(1);
47
+ }
48
+ else {
49
+ process.exit(0);
50
+ }
51
+ }
52
+ catch (error) {
53
+ logger_1.logger.error(`Doctor check failed: ${error instanceof Error ? error.message : String(error)}`);
54
+ process.exit(1);
55
+ }
56
+ }
57
+ /**
58
+ * Runs all doctor checks and generates a comprehensive health report
59
+ * @returns Promise resolving to a DoctorReport
60
+ */
61
+ async function runDoctorChecks() {
62
+ const checks = [];
63
+ // Find project root
64
+ const projectRoot = await findProjectRoot();
65
+ checks.push({
66
+ status: "success",
67
+ message: `Found project root: ${projectRoot}`,
68
+ });
69
+ // Read package.json
70
+ const packageJson = await readPackageJson(projectRoot);
71
+ // Detect package manager
72
+ const packageManager = await detectPackageManager(projectRoot);
73
+ // Detect project type
74
+ const projectType = detectProjectType(packageJson);
75
+ if (projectType === "unknown") {
76
+ checks.push({
77
+ status: "error",
78
+ message: MESSAGES.UNSUPPORTED_PROJECT,
79
+ });
80
+ }
81
+ else {
82
+ checks.push({
83
+ status: "success",
84
+ message: `Detected project type: ${projectType}`,
85
+ });
86
+ }
87
+ // Check Node version
88
+ const nodeVersionCheck = checkNodeVersion();
89
+ checks.push(nodeVersionCheck);
90
+ // Detect installed modules
91
+ const authModules = detectAuthModules(packageJson);
92
+ const databaseModules = detectDatabaseModules(packageJson);
93
+ // Check key files
94
+ const filesCheck = await checkKeyFiles(projectRoot, projectType, authModules, databaseModules);
95
+ checks.push(...filesCheck);
96
+ // Check env files
97
+ const envCheck = await checkEnvFiles(projectRoot, authModules, databaseModules);
98
+ checks.push(...envCheck.checks);
99
+ // Check conflicts
100
+ const conflicts = checkConflicts(authModules, databaseModules);
101
+ conflicts.forEach(conflict => {
102
+ checks.push({
103
+ status: "warning",
104
+ message: conflict,
105
+ });
106
+ });
107
+ // Build report
108
+ const report = {
109
+ project: {
110
+ type: projectType,
111
+ root: projectRoot,
112
+ packageManager,
113
+ },
114
+ runtime: {
115
+ nodeVersion: process.version,
116
+ nodeVersionStatus: nodeVersionCheck.status,
117
+ },
118
+ modules: {
119
+ auth: authModules,
120
+ database: databaseModules,
121
+ },
122
+ files: {
123
+ envExample: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example")),
124
+ env: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env")) || await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.local")),
125
+ prismaSchema: databaseModules.includes("prisma") ? await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma")) : undefined,
126
+ authRoutes: authModules.length > 0 ? await checkAuthRoutesExist(projectRoot, projectType) : undefined,
127
+ },
128
+ env: envCheck.envStatus,
129
+ conflicts,
130
+ checks,
131
+ summary: {
132
+ errors: checks.filter(c => c.status === "error").length,
133
+ warnings: checks.filter(c => c.status === "warning").length,
134
+ suggestions: generateSuggestions(),
135
+ },
136
+ };
137
+ return report;
138
+ }
139
+ /**
140
+ * Finds the project root by walking up directories until package.json is found
141
+ * @returns Promise resolving to the absolute path of the project root
142
+ * @throws Error if no package.json is found
143
+ */
144
+ async function findProjectRoot() {
145
+ let currentDir = process.cwd();
146
+ while (currentDir !== path_1.default.dirname(currentDir)) {
147
+ const packageJsonPath = path_1.default.join(currentDir, "package.json");
148
+ if (await fs_extra_1.default.pathExists(packageJsonPath)) {
149
+ return currentDir;
150
+ }
151
+ currentDir = path_1.default.dirname(currentDir);
152
+ }
153
+ throw new Error(MESSAGES.NO_PACKAGE_JSON);
154
+ }
155
+ /**
156
+ * Reads and parses package.json from the project root
157
+ * @param projectRoot Absolute path to the project root
158
+ * @returns Promise resolving to the parsed package.json object
159
+ */
160
+ async function readPackageJson(projectRoot) {
161
+ const packageJsonPath = path_1.default.join(projectRoot, "package.json");
162
+ return await fs_extra_1.default.readJSON(packageJsonPath);
163
+ }
164
+ async function detectPackageManager(projectRoot) {
165
+ const checks = [
166
+ { file: "pnpm-lock.yaml", manager: "pnpm" },
167
+ { file: "yarn.lock", manager: "yarn" },
168
+ { file: "package-lock.json", manager: "npm" },
169
+ ];
170
+ for (const check of checks) {
171
+ if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, check.file))) {
172
+ return check.manager;
173
+ }
174
+ }
175
+ return "npm";
176
+ }
177
+ function detectProjectType(packageJson) {
178
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
179
+ if (deps.next) {
180
+ return "nextjs";
181
+ }
182
+ else if (deps.express) {
183
+ return "express";
184
+ }
185
+ else if (deps.vite && deps.react) {
186
+ return "react-vite";
187
+ }
188
+ else if (deps.react) {
189
+ return "react";
190
+ }
191
+ return "unknown";
192
+ }
193
+ function checkNodeVersion() {
194
+ const version = process.version;
195
+ const majorVersion = parseInt(version.slice(1).split(".")[0]);
196
+ if (majorVersion < 18) {
197
+ return {
198
+ status: "error",
199
+ message: MESSAGES.NODE_TOO_OLD(version),
200
+ };
201
+ }
202
+ else if (majorVersion < 20) {
203
+ return {
204
+ status: "warning",
205
+ message: MESSAGES.NODE_WARNING(version),
206
+ };
207
+ }
208
+ else {
209
+ return {
210
+ status: "success",
211
+ message: MESSAGES.NODE_SUCCESS(version),
212
+ };
213
+ }
214
+ }
215
+ function detectAuthModules(packageJson) {
216
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
217
+ const modules = [];
218
+ if (deps["better-auth"]) {
219
+ modules.push("better-auth");
220
+ }
221
+ if (deps["next-auth"]) {
222
+ modules.push("authjs");
223
+ }
224
+ return modules;
225
+ }
226
+ function detectDatabaseModules(packageJson) {
227
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
228
+ const modules = [];
229
+ if (deps["@prisma/client"] || deps["prisma"]) {
230
+ modules.push("prisma");
231
+ }
232
+ if (deps["mongoose"]) {
233
+ modules.push("mongoose");
234
+ }
235
+ return modules;
236
+ }
237
+ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModules) {
238
+ const checks = [];
239
+ // Check .env.example
240
+ const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
241
+ checks.push({
242
+ status: envExampleExists ? "success" : "warning",
243
+ message: envExampleExists ? ".env.example file found" : ".env.example file missing (recommended for documentation)",
244
+ });
245
+ // Check Prisma schema
246
+ if (databaseModules.includes("prisma")) {
247
+ const schemaExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"));
248
+ checks.push({
249
+ status: schemaExists ? "success" : "error",
250
+ message: schemaExists ? "Prisma schema found" : "Prisma schema missing (required for Prisma)",
251
+ });
252
+ }
253
+ // Check auth routes
254
+ if (authModules.length > 0 && projectType === "nextjs") {
255
+ const authRoutesExist = await checkAuthRoutesExist(projectRoot, projectType);
256
+ checks.push({
257
+ status: authRoutesExist ? "success" : "warning",
258
+ message: authRoutesExist ? "Auth routes configured" : "Auth routes not found (may need configuration)",
259
+ });
260
+ }
261
+ return checks;
262
+ }
263
+ async function checkAuthRoutesExist(projectRoot, projectType) {
264
+ if (projectType !== "nextjs")
265
+ return true; // Skip for non-Next.js
266
+ const possiblePaths = [
267
+ "app/api/auth/[...nextauth]/route.ts",
268
+ "app/api/auth/[...nextauth]/route.js",
269
+ "src/app/api/auth/[...nextauth]/route.ts",
270
+ "src/app/api/auth/[...nextauth]/route.js",
271
+ "pages/api/auth/[...nextauth].ts",
272
+ "pages/api/auth/[...nextauth].js",
273
+ "src/pages/api/auth/[...nextauth].ts",
274
+ "src/pages/api/auth/[...nextauth].js",
275
+ ];
276
+ for (const routePath of possiblePaths) {
277
+ if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, routePath))) {
278
+ return true;
279
+ }
280
+ }
281
+ return false;
282
+ }
283
+ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
284
+ const checks = [];
285
+ const requiredKeys = [];
286
+ const missing = [];
287
+ const present = [];
288
+ // Determine required env keys
289
+ if (databaseModules.includes("prisma")) {
290
+ requiredKeys.push("DATABASE_URL");
291
+ }
292
+ if (authModules.includes("authjs")) {
293
+ requiredKeys.push("NEXTAUTH_SECRET", "NEXTAUTH_URL");
294
+ }
295
+ if (authModules.includes("better-auth")) {
296
+ requiredKeys.push("BETTER_AUTH_SECRET", "BETTER_AUTH_URL");
297
+ }
298
+ // Check env files
299
+ const envPaths = [".env", ".env.local"];
300
+ let envContent = "";
301
+ for (const envPath of envPaths) {
302
+ const fullPath = path_1.default.join(projectRoot, envPath);
303
+ if (await fs_extra_1.default.pathExists(fullPath)) {
304
+ envContent = await fs_extra_1.default.readFile(fullPath, "utf-8");
305
+ break;
306
+ }
307
+ }
308
+ if (!envContent) {
309
+ checks.push({
310
+ status: "warning",
311
+ message: "No .env or .env.local file found",
312
+ });
313
+ return { checks, envStatus: { missing: requiredKeys, present: [] } };
314
+ }
315
+ // Parse env content
316
+ const envLines = envContent.split("\n").map(line => line.trim()).filter(line => line && !line.startsWith("#"));
317
+ const envVars = new Set(envLines.map(line => line.split("=")[0]));
318
+ for (const key of requiredKeys) {
319
+ if (envVars.has(key)) {
320
+ present.push(key);
321
+ }
322
+ else {
323
+ missing.push(key);
324
+ }
325
+ }
326
+ if (missing.length > 0) {
327
+ checks.push({
328
+ status: "error",
329
+ message: `Missing required environment variables: ${missing.join(", ")}`,
330
+ });
331
+ }
332
+ else if (requiredKeys.length > 0) {
333
+ checks.push({
334
+ status: "success",
335
+ message: "All required environment variables are present",
336
+ });
337
+ }
338
+ return { checks, envStatus: { missing, present } };
339
+ }
340
+ function checkConflicts(authModules, databaseModules) {
341
+ const conflicts = [];
342
+ if (authModules.length > 1) {
343
+ conflicts.push(`Multiple auth providers detected: ${authModules.join(", ")}. Consider using only one.`);
344
+ }
345
+ if (databaseModules.length > 1) {
346
+ conflicts.push(`Multiple database providers detected: ${databaseModules.join(", ")}. Consider using only one.`);
347
+ }
348
+ return conflicts;
349
+ }
350
+ function generateSuggestions() {
351
+ const suggestions = [];
352
+ // Always show available commands
353
+ suggestions.push("stackkit add auth - Add authentication module");
354
+ suggestions.push("stackkit add db - Add database module");
355
+ suggestions.push("stackkit list - View available modules");
356
+ return suggestions;
357
+ }
358
+ /**
359
+ * Prints the doctor report to the console with appropriate formatting
360
+ * @param report The doctor report to print
361
+ * @param verbose Whether to show detailed check information
362
+ */
363
+ function printDoctorReport(report, verbose) {
364
+ logger_1.logger.header("🔍 StackKit Doctor Report");
365
+ logger_1.logger.newLine();
366
+ // Project info
367
+ logger_1.logger.log(chalk_1.default.bold("Project"));
368
+ logger_1.logger.log(` Type: ${report.project.type}`);
369
+ logger_1.logger.log(` Root: ${report.project.root}`);
370
+ logger_1.logger.log(` Package Manager: ${report.project.packageManager}`);
371
+ logger_1.logger.newLine();
372
+ // Runtime
373
+ logger_1.logger.log(chalk_1.default.bold("Runtime"));
374
+ if (report.runtime.nodeVersionStatus === "success") {
375
+ logger_1.logger.success(`Node.js: ${report.runtime.nodeVersion}`);
376
+ }
377
+ else if (report.runtime.nodeVersionStatus === "warning") {
378
+ logger_1.logger.warn(`Node.js: ${report.runtime.nodeVersion}`);
379
+ }
380
+ else {
381
+ logger_1.logger.error(`Node.js: ${report.runtime.nodeVersion}`);
382
+ }
383
+ logger_1.logger.newLine();
384
+ // Modules
385
+ logger_1.logger.log(chalk_1.default.bold("Modules"));
386
+ if (report.modules.auth.length > 0) {
387
+ logger_1.logger.log(` Auth: ${report.modules.auth.join(", ")}`);
388
+ }
389
+ else {
390
+ logger_1.logger.info("Auth: None");
391
+ }
392
+ if (report.modules.database.length > 0) {
393
+ logger_1.logger.log(` Database: ${report.modules.database.join(", ")}`);
394
+ }
395
+ else {
396
+ logger_1.logger.info("Database: None");
397
+ }
398
+ logger_1.logger.newLine();
399
+ // Files
400
+ logger_1.logger.log(chalk_1.default.bold("Files"));
401
+ if (report.files.envExample) {
402
+ logger_1.logger.success(MESSAGES.ENV_EXAMPLE_FOUND);
403
+ }
404
+ else {
405
+ logger_1.logger.warn(MESSAGES.ENV_EXAMPLE_MISSING);
406
+ logger_1.logger.log(" Hint: Helps other developers understand required environment variables");
407
+ }
408
+ if (report.files.env) {
409
+ logger_1.logger.success(MESSAGES.ENV_FOUND);
410
+ }
411
+ else {
412
+ logger_1.logger.warn(MESSAGES.ENV_MISSING);
413
+ logger_1.logger.log(" Hint: Required for local development and production deployment");
414
+ }
415
+ if (report.files.prismaSchema !== undefined) {
416
+ if (report.files.prismaSchema) {
417
+ logger_1.logger.success(MESSAGES.PRISMA_SCHEMA_FOUND);
418
+ }
419
+ else {
420
+ logger_1.logger.error(MESSAGES.PRISMA_SCHEMA_MISSING);
421
+ logger_1.logger.log(" Hint: Defines your database schema and is required for Prisma to work");
422
+ }
423
+ }
424
+ if (report.files.authRoutes !== undefined) {
425
+ if (report.files.authRoutes) {
426
+ logger_1.logger.success(MESSAGES.AUTH_ROUTES_FOUND);
427
+ }
428
+ else {
429
+ logger_1.logger.warn(MESSAGES.AUTH_ROUTES_MISSING);
430
+ logger_1.logger.log(" Hint: Authentication routes handle login/logout flows");
431
+ }
432
+ }
433
+ logger_1.logger.newLine();
434
+ // Environment
435
+ if (report.env.missing.length > 0 || report.env.present.length > 0) {
436
+ logger_1.logger.log(chalk_1.default.bold("Environment Variables"));
437
+ if (report.env.present.length > 0) {
438
+ logger_1.logger.success(MESSAGES.ENV_VARS_PRESENT(report.env.present));
439
+ }
440
+ if (report.env.missing.length > 0) {
441
+ logger_1.logger.error(MESSAGES.ENV_VARS_MISSING(report.env.missing));
442
+ }
443
+ logger_1.logger.newLine();
444
+ }
445
+ // Conflicts
446
+ if (report.conflicts.length > 0) {
447
+ logger_1.logger.log(chalk_1.default.bold("Conflicts"));
448
+ report.conflicts.forEach(conflict => {
449
+ logger_1.logger.warn(conflict);
450
+ });
451
+ logger_1.logger.newLine();
452
+ }
453
+ // Detailed checks if verbose
454
+ if (verbose) {
455
+ logger_1.logger.log(chalk_1.default.bold("Detailed Checks"));
456
+ report.checks.forEach(check => {
457
+ if (check.status === "success") {
458
+ logger_1.logger.success(check.message);
459
+ }
460
+ else if (check.status === "warning") {
461
+ logger_1.logger.warn(check.message);
462
+ }
463
+ else {
464
+ logger_1.logger.error(check.message);
465
+ }
466
+ if (check.details) {
467
+ logger_1.logger.log(` ${chalk_1.default.gray(check.details)}`);
468
+ }
469
+ });
470
+ logger_1.logger.newLine();
471
+ }
472
+ // Summary
473
+ logger_1.logger.log(chalk_1.default.bold("Summary"));
474
+ logger_1.logger.log(` Errors: ${report.summary.errors}`);
475
+ logger_1.logger.log(` Warnings: ${report.summary.warnings}`);
476
+ logger_1.logger.newLine();
477
+ if (report.summary.suggestions.length > 0) {
478
+ logger_1.logger.log(chalk_1.default.bold("Suggestions"));
479
+ report.summary.suggestions.forEach(suggestion => {
480
+ logger_1.logger.log(` • ${suggestion}`);
481
+ });
482
+ }
483
+ }
@@ -1,5 +1,5 @@
1
1
  interface ListOptions {
2
- templates?: boolean;
2
+ frameworks?: boolean;
3
3
  modules?: boolean;
4
4
  }
5
5
  export declare function listCommand(options: ListOptions): Promise<void>;