stackkit 0.1.3 → 0.1.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 (43) hide show
  1. package/README.md +5 -3
  2. package/dist/cli/add.d.ts +2 -1
  3. package/dist/cli/add.js +325 -102
  4. package/dist/cli/create.d.ts +2 -2
  5. package/dist/cli/create.js +96 -30
  6. package/dist/cli/doctor.js +25 -17
  7. package/dist/cli/list.js +14 -2
  8. package/dist/index.js +22 -3
  9. package/dist/lib/conversion/js-conversion.js +2 -2
  10. package/dist/lib/discovery/module-discovery.d.ts +0 -1
  11. package/dist/lib/discovery/module-discovery.js +35 -35
  12. package/dist/lib/env/env-editor.js +1 -1
  13. package/dist/lib/framework/framework-utils.d.ts +1 -1
  14. package/dist/lib/framework/framework-utils.js +3 -3
  15. package/dist/lib/generation/code-generator.d.ts +18 -4
  16. package/dist/lib/generation/code-generator.js +212 -147
  17. package/dist/lib/generation/generator-utils.d.ts +11 -0
  18. package/dist/lib/generation/generator-utils.js +124 -0
  19. package/dist/lib/git-utils.js +20 -0
  20. package/dist/lib/project/detect.js +2 -4
  21. package/dist/lib/utils/package-root.js +2 -2
  22. package/dist/types/index.d.ts +0 -10
  23. package/modules/auth/authjs/generator.json +10 -0
  24. package/modules/auth/authjs/module.json +0 -9
  25. package/modules/auth/better-auth/files/lib/auth.ts +38 -31
  26. package/modules/auth/better-auth/files/prisma/schema.prisma +3 -3
  27. package/modules/auth/better-auth/generator.json +58 -28
  28. package/modules/auth/better-auth/module.json +0 -24
  29. package/modules/database/mongoose/files/models/health.ts +34 -0
  30. package/modules/database/mongoose/generator.json +27 -8
  31. package/modules/database/mongoose/module.json +1 -2
  32. package/modules/database/prisma/files/lib/prisma.ts +27 -21
  33. package/modules/database/prisma/files/prisma.config.ts +17 -0
  34. package/modules/database/prisma/generator.json +79 -15
  35. package/modules/database/prisma/module.json +1 -4
  36. package/package.json +1 -1
  37. package/templates/express/src/server.ts +9 -3
  38. package/templates/express/tsconfig.json +2 -23
  39. package/templates/nextjs/lib/env.ts +1 -1
  40. package/dist/lib/database/database-config.d.ts +0 -6
  41. package/dist/lib/database/database-config.js +0 -9
  42. package/modules/database/mongoose/files/models/User.ts +0 -34
  43. /package/modules/database/mongoose/files/lib/{db.ts → mongoose.ts} +0 -0
@@ -18,6 +18,7 @@ const framework_utils_1 = require("../lib/framework/framework-utils");
18
18
  const module_discovery_1 = require("../lib/discovery/module-discovery");
19
19
  const code_generator_1 = require("../lib/generation/code-generator");
20
20
  const package_root_1 = require("../lib/utils/package-root");
21
+ const env_editor_1 = require("../lib/env/env-editor");
21
22
  async function createProject(projectName, options) {
22
23
  logger_1.logger.newLine();
23
24
  logger_1.logger.log(chalk_1.default.bold.cyan("📦 Create StackKit App"));
@@ -30,18 +31,19 @@ async function createProject(projectName, options) {
30
31
  process.exit(1);
31
32
  }
32
33
  await generateProject(config, targetDir, options);
34
+ await processGeneratorEnvVars(config, targetDir);
33
35
  showNextSteps(config);
34
36
  }
35
37
  async function getProjectConfig(projectName, options) {
36
- const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules');
38
+ const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
37
39
  const discoveredModules = await (0, module_discovery_1.discoverModules)(modulesDir);
38
40
  const argv = process.argv.slice(2);
39
- const createIndex = argv.indexOf('create');
41
+ const createIndex = argv.indexOf("create");
40
42
  const argsAfterCreate = createIndex >= 0 ? argv.slice(createIndex + 1) : [];
41
- const flagsProvided = argsAfterCreate.some(arg => arg.startsWith('-'));
43
+ const flagsProvided = argsAfterCreate.some((arg) => arg.startsWith("-"));
42
44
  const optionsProvided = flagsProvided || !!(options && (options.yes || options.y));
43
45
  if (optionsProvided) {
44
- if ((options && (options.yes || options.y))) {
46
+ if (options && (options.yes || options.y) && !flagsProvided) {
45
47
  return {
46
48
  projectName: projectName || "my-app",
47
49
  framework: "nextjs",
@@ -54,37 +56,37 @@ async function getProjectConfig(projectName, options) {
54
56
  }
55
57
  const framework = (options && (options.framework || options.f)) || undefined;
56
58
  if (discoveredModules.frameworks && discoveredModules.frameworks.length > 0) {
57
- const validFrameworks = discoveredModules.frameworks.map(f => f.name);
59
+ const validFrameworks = discoveredModules.frameworks.map((f) => f.name);
58
60
  if (framework && !validFrameworks.includes(framework)) {
59
- throw new Error(`Invalid framework: ${framework}. Valid options: ${validFrameworks.join(', ')}`);
61
+ throw new Error(`Invalid framework: ${framework}. Valid options: ${validFrameworks.join(", ")}`);
60
62
  }
61
63
  }
62
64
  const db = (options && (options.database || options.d)) || undefined;
63
65
  let allValidDatabases = [];
64
66
  if (discoveredModules.databases && discoveredModules.databases.length > 0) {
65
67
  const validDatabases = (0, module_discovery_1.getValidDatabaseOptions)(discoveredModules.databases);
66
- const validBaseDatabases = discoveredModules.databases.map(db => db.name);
68
+ const validBaseDatabases = discoveredModules.databases.map((db) => db.name);
67
69
  allValidDatabases = [...validDatabases, ...validBaseDatabases];
68
70
  if (db && !allValidDatabases.includes(db)) {
69
- throw new Error(`Invalid database: ${db}. Valid options: ${allValidDatabases.filter((v, i, arr) => arr.indexOf(v) === i).join(', ')}`);
71
+ throw new Error(`Invalid database: ${db}. Valid options: ${allValidDatabases.filter((v, i, arr) => arr.indexOf(v) === i).join(", ")}`);
70
72
  }
71
73
  }
72
74
  const authOpt = (options && (options.auth || options.a)) || undefined;
73
75
  if (discoveredModules.auth && discoveredModules.auth.length > 0) {
74
76
  const validAuth = (0, module_discovery_1.getValidAuthOptions)(discoveredModules.auth);
75
77
  if (authOpt && !validAuth.includes(authOpt)) {
76
- throw new Error(`Invalid auth: ${authOpt}. Valid options: ${validAuth.join(', ')}`);
78
+ throw new Error(`Invalid auth: ${authOpt}. Valid options: ${validAuth.join(", ")}`);
77
79
  }
78
80
  }
79
- const validLanguages = ['typescript', 'javascript'];
81
+ const validLanguages = ["typescript", "javascript"];
80
82
  const language = (options && (options.language || options.l)) || undefined;
81
83
  if (language && !validLanguages.includes(language)) {
82
- throw new Error(`Invalid language: ${language}. Valid options: ${validLanguages.join(', ')}`);
84
+ throw new Error(`Invalid language: ${language}. Valid options: ${validLanguages.join(", ")}`);
83
85
  }
84
- const validPackageManagers = ['pnpm', 'npm', 'yarn', 'bun'];
86
+ const validPackageManagers = ["pnpm", "npm", "yarn", "bun"];
85
87
  const pm = (options && (options.packageManager || options.p)) || undefined;
86
88
  if (pm && !validPackageManagers.includes(pm)) {
87
- throw new Error(`Invalid package manager: ${pm}. Valid options: ${validPackageManagers.join(', ')}`);
89
+ throw new Error(`Invalid package manager: ${pm}. Valid options: ${validPackageManagers.join(", ")}`);
88
90
  }
89
91
  let database = "none";
90
92
  let prismaProvider;
@@ -136,12 +138,12 @@ async function getProjectConfig(projectName, options) {
136
138
  type: "list",
137
139
  name: "framework",
138
140
  message: "Select framework:",
139
- choices: (discoveredModules.frameworks && discoveredModules.frameworks.length > 0)
140
- ? discoveredModules.frameworks.map(f => ({ name: f.displayName, value: f.name }))
141
+ choices: discoveredModules.frameworks && discoveredModules.frameworks.length > 0
142
+ ? discoveredModules.frameworks.map((f) => ({ name: f.displayName, value: f.name }))
141
143
  : [
142
- { name: 'Next.js', value: 'nextjs' },
143
- { name: 'Express.js', value: 'express' },
144
- { name: 'React (Vite)', value: 'react' },
144
+ { name: "Next.js", value: "nextjs" },
145
+ { name: "Express.js", value: "express" },
146
+ { name: "React (Vite)", value: "react" },
145
147
  ],
146
148
  },
147
149
  {
@@ -171,7 +173,7 @@ async function getProjectConfig(projectName, options) {
171
173
  type: "list",
172
174
  name: "auth",
173
175
  message: "Select authentication:",
174
- when: (answers) => (answers.database !== "none" || answers.framework === "react"),
176
+ when: (answers) => answers.database !== "none" || answers.framework === "react",
175
177
  choices: (answers) => (0, module_discovery_1.getCompatibleAuthOptions)(discoveredModules.auth, answers.framework, answers.database || "none"),
176
178
  },
177
179
  {
@@ -204,7 +206,7 @@ async function getProjectConfig(projectName, options) {
204
206
  ? "none"
205
207
  : answers.database),
206
208
  prismaProvider: answers.prismaProvider,
207
- auth: (answers.auth || "none"),
209
+ auth: answers.auth || "none",
208
210
  language: answers.language,
209
211
  packageManager: answers.packageManager,
210
212
  };
@@ -220,7 +222,7 @@ async function generateProject(config, targetDir, options) {
220
222
  copySpinner.fail("Failed to create project files");
221
223
  throw error;
222
224
  }
223
- if (options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
225
+ if (options?.install !== false && !(options?.["skip-install"] || options?.skipInstall)) {
224
226
  const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
225
227
  try {
226
228
  await (0, package_manager_1.installDependencies)(targetDir, config.packageManager);
@@ -231,7 +233,9 @@ async function generateProject(config, targetDir, options) {
231
233
  throw error;
232
234
  }
233
235
  }
234
- if (postInstallCommands.length > 0 && options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
236
+ if (postInstallCommands.length > 0 &&
237
+ options?.install !== false &&
238
+ !(options?.["skip-install"] || options?.skipInstall)) {
235
239
  const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
236
240
  try {
237
241
  for (const command of postInstallCommands) {
@@ -244,21 +248,21 @@ async function generateProject(config, targetDir, options) {
244
248
  throw error;
245
249
  }
246
250
  }
247
- if (options?.git !== false && !(options?.['no-git'] || options?.noGit)) {
251
+ if (options?.git !== false && !(options?.["no-git"] || options?.noGit)) {
248
252
  const gitSpinner = logger_1.logger.startSpinner("Initializing git repository...");
249
253
  try {
250
254
  await (0, git_utils_1.initGit)(targetDir);
251
255
  gitSpinner.succeed("Git repository initialized");
252
256
  }
253
- catch {
254
- gitSpinner.warn("Failed to initialize git repository");
257
+ catch (error) {
258
+ gitSpinner.warn(`Failed to initialize git repository: ${error.message}`);
255
259
  }
256
260
  }
257
261
  }
258
262
  async function composeTemplate(config, targetDir) {
259
263
  const packageRoot = (0, package_root_1.getPackageRoot)();
260
- const templatesDir = path_1.default.join(packageRoot, 'templates');
261
- const modulesDirForGenerator = path_1.default.join(packageRoot, 'modules');
264
+ const templatesDir = path_1.default.join(packageRoot, "templates");
265
+ const modulesDirForGenerator = path_1.default.join(packageRoot, "modules");
262
266
  await fs_extra_1.default.ensureDir(targetDir);
263
267
  const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(config.framework, templatesDir);
264
268
  const generator = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
@@ -266,8 +270,8 @@ async function composeTemplate(config, targetDir) {
266
270
  const features = [];
267
271
  const postInstallCommands = await generator.generate({
268
272
  framework: config.framework,
269
- database: config.database === 'none' ? undefined : config.database,
270
- auth: config.auth === 'none' ? undefined : config.auth,
273
+ database: config.database === "none" ? undefined : config.database,
274
+ auth: config.auth === "none" ? undefined : config.auth,
271
275
  prismaProvider: config.prismaProvider,
272
276
  }, features, targetDir);
273
277
  const packageJsonPath = path_1.default.join(targetDir, "package.json");
@@ -285,13 +289,75 @@ async function composeTemplate(config, targetDir) {
285
289
  }
286
290
  }
287
291
  catch {
288
- // Non-fatal: .env creation from .env.example is optional
292
+ // env copy failed.
289
293
  }
290
294
  if (config.language === "javascript") {
291
295
  await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
292
296
  }
293
297
  return postInstallCommands;
294
298
  }
299
+ async function processGeneratorEnvVars(config, targetDir) {
300
+ const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
301
+ const envVars = [];
302
+ // Process database generator env vars
303
+ if (config.database && config.database !== "none") {
304
+ const dbGeneratorPath = path_1.default.join(modulesDir, "database", config.database, "generator.json");
305
+ if (await fs_extra_1.default.pathExists(dbGeneratorPath)) {
306
+ const generator = await fs_extra_1.default.readJson(dbGeneratorPath);
307
+ if (generator.operations) {
308
+ for (const operation of generator.operations) {
309
+ if (operation.type === "add-env" && (!operation.condition || checkCondition(operation.condition, config))) {
310
+ for (const [key, value] of Object.entries(operation.envVars)) {
311
+ envVars.push({
312
+ key,
313
+ value: value,
314
+ required: true,
315
+ });
316
+ }
317
+ }
318
+ }
319
+ }
320
+ }
321
+ }
322
+ // Process auth generator env vars
323
+ if (config.auth && config.auth !== "none") {
324
+ const authGeneratorPath = path_1.default.join(modulesDir, "auth", config.auth, "generator.json");
325
+ if (await fs_extra_1.default.pathExists(authGeneratorPath)) {
326
+ const generator = await fs_extra_1.default.readJson(authGeneratorPath);
327
+ if (generator.operations) {
328
+ for (const operation of generator.operations) {
329
+ if (operation.type === "add-env" && (!operation.condition || checkCondition(operation.condition, config))) {
330
+ for (const [key, value] of Object.entries(operation.envVars)) {
331
+ envVars.push({
332
+ key,
333
+ value: value,
334
+ required: true,
335
+ });
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ }
342
+ if (envVars.length > 0) {
343
+ await (0, env_editor_1.addEnvVariables)(targetDir, envVars, { force: true });
344
+ }
345
+ }
346
+ function checkCondition(condition, config) {
347
+ for (const [key, value] of Object.entries(condition)) {
348
+ if (Array.isArray(value)) {
349
+ if (!value.includes(config[key])) {
350
+ return false;
351
+ }
352
+ }
353
+ else {
354
+ if (config[key] !== value) {
355
+ return false;
356
+ }
357
+ }
358
+ }
359
+ return true;
360
+ }
295
361
  function showNextSteps(config) {
296
362
  logger_1.logger.newLine();
297
363
  logger_1.logger.success(`Created ${config.projectName}`);
@@ -96,7 +96,7 @@ async function runDoctorChecks() {
96
96
  const gitCheck = await checkGitRepo(projectRoot);
97
97
  checks.push(gitCheck);
98
98
  const conflicts = checkConflicts(authModules, databaseModules);
99
- conflicts.forEach(conflict => {
99
+ conflicts.forEach((conflict) => {
100
100
  checks.push({
101
101
  status: "warning",
102
102
  message: conflict,
@@ -118,8 +118,11 @@ async function runDoctorChecks() {
118
118
  },
119
119
  files: {
120
120
  envExample: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example")),
121
- 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")),
122
- prismaSchema: databaseModules.includes("prisma") ? await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma")) : undefined,
121
+ env: (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env"))) ||
122
+ (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.local"))),
123
+ prismaSchema: databaseModules.includes("prisma")
124
+ ? await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"))
125
+ : undefined,
123
126
  authRoutes: authModules.length > 0 ? await checkAuthRoutesExist(projectRoot, projectType) : undefined,
124
127
  tsconfig: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "tsconfig.json")),
125
128
  eslintConfig: await checkEslintConfigExists(projectRoot),
@@ -132,8 +135,8 @@ async function runDoctorChecks() {
132
135
  conflicts,
133
136
  checks,
134
137
  summary: {
135
- errors: checks.filter(c => c.status === "error").length,
136
- warnings: checks.filter(c => c.status === "warning").length,
138
+ errors: checks.filter((c) => c.status === "error").length,
139
+ warnings: checks.filter((c) => c.status === "warning").length,
137
140
  suggestions: generateSuggestions(),
138
141
  },
139
142
  };
@@ -232,7 +235,9 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
232
235
  const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
233
236
  checks.push({
234
237
  status: envExampleExists ? "success" : "warning",
235
- message: envExampleExists ? ".env.example file found" : ".env.example file missing (recommended for documentation)",
238
+ message: envExampleExists
239
+ ? ".env.example file found"
240
+ : ".env.example file missing (recommended for documentation)",
236
241
  });
237
242
  if (databaseModules.includes("prisma")) {
238
243
  const schemaExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"));
@@ -245,7 +250,9 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
245
250
  const authRoutesExist = await checkAuthRoutesExist(projectRoot, projectType);
246
251
  checks.push({
247
252
  status: authRoutesExist ? "success" : "warning",
248
- message: authRoutesExist ? "Auth routes configured" : "Auth routes not found (may need configuration)",
253
+ message: authRoutesExist
254
+ ? "Auth routes configured"
255
+ : "Auth routes not found (may need configuration)",
249
256
  });
250
257
  }
251
258
  return checks;
@@ -300,8 +307,11 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
300
307
  });
301
308
  return { checks, envStatus: { missing: requiredKeys, present: [] } };
302
309
  }
303
- const envLines = envContent.split("\n").map(line => line.trim()).filter(line => line && !line.startsWith("#"));
304
- const envVars = new Set(envLines.map(line => line.split("=")[0]));
310
+ const envLines = envContent
311
+ .split("\n")
312
+ .map((line) => line.trim())
313
+ .filter((line) => line && !line.startsWith("#"));
314
+ const envVars = new Set(envLines.map((line) => line.split("=")[0]));
305
315
  for (const key of requiredKeys) {
306
316
  if (envVars.has(key)) {
307
317
  present.push(key);
@@ -336,20 +346,19 @@ function checkConflicts(authModules, databaseModules) {
336
346
  }
337
347
  async function checkConfigFiles(projectRoot, projectType, packageJson) {
338
348
  const checks = [];
339
- // Check tsconfig.json
340
349
  const tsconfigExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "tsconfig.json"));
341
350
  checks.push({
342
351
  status: tsconfigExists ? "success" : "error",
343
352
  message: tsconfigExists ? MESSAGES.TSCONFIG_FOUND : MESSAGES.TSCONFIG_MISSING,
344
353
  });
345
- // Check ESLint config
346
354
  const eslintExists = await checkEslintConfigExists(projectRoot);
347
355
  checks.push({
348
356
  status: eslintExists ? "success" : "warning",
349
357
  message: eslintExists ? MESSAGES.ESLINT_CONFIG_FOUND : MESSAGES.ESLINT_CONFIG_MISSING,
350
358
  });
351
- // Check build script
352
- const hasBuildScript = packageJson.scripts && typeof packageJson.scripts === "object" && "build" in packageJson.scripts;
359
+ const hasBuildScript = packageJson.scripts &&
360
+ typeof packageJson.scripts === "object" &&
361
+ "build" in packageJson.scripts;
353
362
  checks.push({
354
363
  status: hasBuildScript ? "success" : "warning",
355
364
  message: hasBuildScript ? MESSAGES.BUILD_SCRIPT_FOUND : MESSAGES.BUILD_SCRIPT_MISSING,
@@ -363,7 +372,6 @@ async function checkDependencies(packageJson) {
363
372
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
364
373
  for (const [name, version] of Object.entries(deps || {})) {
365
374
  if (typeof version === "string" && (version.startsWith("^") || version.startsWith("~"))) {
366
- // Assume up to date
367
375
  }
368
376
  else {
369
377
  outdated.push(name);
@@ -525,7 +533,7 @@ function printDoctorReport(report, verbose) {
525
533
  // Conflicts
526
534
  if (report.conflicts.length > 0) {
527
535
  logger_1.logger.log(chalk_1.default.bold("Conflicts"));
528
- report.conflicts.forEach(conflict => {
536
+ report.conflicts.forEach((conflict) => {
529
537
  logger_1.logger.warn(conflict);
530
538
  });
531
539
  logger_1.logger.newLine();
@@ -533,7 +541,7 @@ function printDoctorReport(report, verbose) {
533
541
  // Detailed checks if verbose
534
542
  if (verbose) {
535
543
  logger_1.logger.log(chalk_1.default.bold("Detailed Checks"));
536
- report.checks.forEach(check => {
544
+ report.checks.forEach((check) => {
537
545
  if (check.status === "success") {
538
546
  logger_1.logger.success(check.message);
539
547
  }
@@ -556,7 +564,7 @@ function printDoctorReport(report, verbose) {
556
564
  logger_1.logger.newLine();
557
565
  if (report.summary.suggestions.length > 0) {
558
566
  logger_1.logger.log(chalk_1.default.bold("Suggestions"));
559
- report.summary.suggestions.forEach(suggestion => {
567
+ report.summary.suggestions.forEach((suggestion) => {
560
568
  logger_1.logger.log(` • ${suggestion}`);
561
569
  });
562
570
  }
package/dist/cli/list.js CHANGED
@@ -52,10 +52,22 @@ async function listCommand(options) {
52
52
  logger_1.logger.log(` ${chalk_1.default.gray(categoryPrefix)} ${chalk_1.default.yellow(formatCategoryName(category))} ${chalk_1.default.dim(`(${mods.length})`)}`);
53
53
  mods.forEach((mod, modIndex) => {
54
54
  const isLastMod = modIndex === mods.length - 1;
55
- const modPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
55
+ const modPrefix = isLastCategory
56
+ ? isLastMod
57
+ ? " └──"
58
+ : " ├──"
59
+ : isLastMod
60
+ ? "│ └──"
61
+ : "│ ├──";
56
62
  logger_1.logger.log(` ${chalk_1.default.gray(modPrefix)} ${chalk_1.default.green(mod.displayName)}`);
57
63
  if (mod.category === "database" && mod.name === "prisma") {
58
- const providerPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
64
+ const providerPrefix = isLastCategory
65
+ ? isLastMod
66
+ ? " └──"
67
+ : " ├──"
68
+ : isLastMod
69
+ ? "│ └──"
70
+ : "│ ├──";
59
71
  logger_1.logger.log(` ${chalk_1.default.gray(providerPrefix)} ${chalk_1.default.dim("Providers: PostgreSQL, MongoDB, MySQL, SQLite")}`);
60
72
  }
61
73
  });
package/dist/index.js CHANGED
@@ -11,11 +11,28 @@ const program = new commander_1.Command();
11
11
  program
12
12
  .name("stackkit")
13
13
  .description("CLI for creating and managing StackKit projects")
14
- .version("0.1.0");
14
+ .version("0.1.5")
15
+ .configureHelp({
16
+ subcommandTerm: (cmd) => {
17
+ const name = cmd.name();
18
+ if (name === "create")
19
+ return "create [project-name] [options]";
20
+ if (name === "add")
21
+ return "add <module> [options]";
22
+ if (name === "doctor")
23
+ return "doctor [options]";
24
+ if (name === "list")
25
+ return "list [options]";
26
+ if (name === "help")
27
+ return "help [command]";
28
+ return name + " [options]";
29
+ },
30
+ });
15
31
  // Create command
16
32
  program
17
33
  .command("create [project-name]")
18
34
  .description("Create a new StackKit project")
35
+ .usage("[project-name] [options]")
19
36
  .option("-f, --framework <framework>", "Framework: nextjs, express, react")
20
37
  .option("-d, --database <database>", "Database: prisma, mongoose, none")
21
38
  .option("--prisma-provider <provider>", "Prisma provider: postgresql, mongodb, mysql, sqlite")
@@ -36,12 +53,14 @@ program
36
53
  });
37
54
  // Add command
38
55
  program
39
- .command("add <module>")
40
- .description("Add a module to your existing project")
56
+ .command("add [module]")
57
+ .description("Add a module or category to your existing project")
58
+ .usage("[module] [options]")
41
59
  .option("--provider <provider>", "Specific provider/variant to use")
42
60
  .option("--force", "Overwrite existing files")
43
61
  .option("--dry-run", "Show what would be changed without making changes")
44
62
  .option("--no-install", "Skip installing dependencies")
63
+ .option("-y, --yes", "Use default options")
45
64
  .action(async (module, options) => {
46
65
  try {
47
66
  await (0, add_1.addCommand)(module, options);
@@ -10,7 +10,7 @@ const path_1 = __importDefault(require("path"));
10
10
  const package_root_1 = require("../utils/package-root");
11
11
  const baseDirs = {
12
12
  express: "./src",
13
- "react": "./src",
13
+ react: "./src",
14
14
  nextjs: ".",
15
15
  };
16
16
  async function convertToJavaScript(targetDir, framework) {
@@ -77,7 +77,7 @@ async function convertToJavaScript(targetDir, framework) {
77
77
  presets.push([
78
78
  require.resolve("@babel/preset-react"),
79
79
  {
80
- runtime: "classic",
80
+ runtime: "automatic",
81
81
  },
82
82
  ]);
83
83
  }
@@ -5,7 +5,6 @@ export interface ModuleMetadata {
5
5
  category: string;
6
6
  provider?: string;
7
7
  supportedFrameworks?: string[];
8
- databaseAdapters?: Record<string, unknown>;
9
8
  frameworkConfigs?: Record<string, unknown>;
10
9
  dependencies?: Record<string, unknown>;
11
10
  devDependencies?: Record<string, unknown>;