stackkit 0.1.2 → 0.1.4

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 (37) hide show
  1. package/README.md +4 -2
  2. package/dist/cli/add.d.ts +2 -1
  3. package/dist/cli/add.js +318 -110
  4. package/dist/cli/create.js +8 -45
  5. package/dist/cli/doctor.js +0 -10
  6. package/dist/cli/list.js +3 -5
  7. package/dist/index.js +23 -4
  8. package/dist/lib/conversion/js-conversion.js +3 -2
  9. package/dist/lib/discovery/module-discovery.d.ts +0 -1
  10. package/dist/lib/discovery/module-discovery.js +27 -5
  11. package/dist/lib/generation/code-generator.d.ts +14 -0
  12. package/dist/lib/generation/code-generator.js +69 -65
  13. package/dist/lib/generation/generator-utils.d.ts +11 -0
  14. package/dist/lib/generation/generator-utils.js +116 -0
  15. package/dist/lib/git-utils.js +20 -0
  16. package/dist/lib/project/detect.js +2 -4
  17. package/dist/lib/utils/package-root.d.ts +1 -0
  18. package/dist/lib/utils/package-root.js +45 -0
  19. package/dist/types/index.d.ts +0 -10
  20. package/modules/auth/authjs/generator.json +10 -0
  21. package/modules/auth/authjs/module.json +0 -9
  22. package/modules/auth/better-auth/files/lib/auth.ts +7 -7
  23. package/modules/auth/better-auth/generator.json +12 -12
  24. package/modules/auth/better-auth/module.json +0 -24
  25. package/modules/database/mongoose/files/models/health.ts +34 -0
  26. package/modules/database/mongoose/generator.json +4 -4
  27. package/modules/database/mongoose/module.json +1 -2
  28. package/modules/database/prisma/files/lib/prisma.ts +2 -1
  29. package/modules/database/prisma/generator.json +33 -8
  30. package/modules/database/prisma/module.json +1 -4
  31. package/package.json +1 -2
  32. package/templates/express/tsconfig.json +2 -23
  33. package/dist/lib/database/database-config.d.ts +0 -6
  34. package/dist/lib/database/database-config.js +0 -9
  35. package/modules/database/mongoose/files/models/User.ts +0 -34
  36. package/templates/react-vite/README.md +0 -56
  37. /package/modules/database/mongoose/files/lib/{db.ts → mongoose.ts} +0 -0
@@ -17,6 +17,7 @@ const logger_1 = require("../lib/ui/logger");
17
17
  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
+ const package_root_1 = require("../lib/utils/package-root");
20
21
  async function createProject(projectName, options) {
21
22
  logger_1.logger.newLine();
22
23
  logger_1.logger.log(chalk_1.default.bold.cyan("📦 Create StackKit App"));
@@ -32,20 +33,8 @@ async function createProject(projectName, options) {
32
33
  showNextSteps(config);
33
34
  }
34
35
  async function getProjectConfig(projectName, options) {
35
- // Resolve modules directory (dist first, then package root)
36
- const modulesCandidates = [
37
- path_1.default.join(__dirname, "..", "..", "..", "modules"),
38
- path_1.default.join(__dirname, "..", "..", "..", "..", "modules"),
39
- ];
40
- let modulesDir;
41
- for (const c of modulesCandidates) {
42
- if (await fs_extra_1.default.pathExists(c)) {
43
- modulesDir = c;
44
- break;
45
- }
46
- }
47
- const discoveredModules = await (0, module_discovery_1.discoverModules)(modulesDir || modulesCandidates[1]);
48
- // Detect flags or `--yes` after `create`; project-name-only remains interactive
36
+ const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules');
37
+ const discoveredModules = await (0, module_discovery_1.discoverModules)(modulesDir);
49
38
  const argv = process.argv.slice(2);
50
39
  const createIndex = argv.indexOf('create');
51
40
  const argsAfterCreate = createIndex >= 0 ? argv.slice(createIndex + 1) : [];
@@ -63,7 +52,6 @@ async function getProjectConfig(projectName, options) {
63
52
  packageManager: "pnpm",
64
53
  };
65
54
  }
66
- // Validate options using discovered modules (if any discovered)
67
55
  const framework = (options && (options.framework || options.f)) || undefined;
68
56
  if (discoveredModules.frameworks && discoveredModules.frameworks.length > 0) {
69
57
  const validFrameworks = discoveredModules.frameworks.map(f => f.name);
@@ -75,7 +63,6 @@ async function getProjectConfig(projectName, options) {
75
63
  let allValidDatabases = [];
76
64
  if (discoveredModules.databases && discoveredModules.databases.length > 0) {
77
65
  const validDatabases = (0, module_discovery_1.getValidDatabaseOptions)(discoveredModules.databases);
78
- // Also allow base database names like 'prisma', 'mongoose'
79
66
  const validBaseDatabases = discoveredModules.databases.map(db => db.name);
80
67
  allValidDatabases = [...validDatabases, ...validBaseDatabases];
81
68
  if (db && !allValidDatabases.includes(db)) {
@@ -111,7 +98,6 @@ async function getProjectConfig(projectName, options) {
111
98
  auth = authOpt;
112
99
  }
113
100
  const finalFramework = (framework || "nextjs");
114
- // Validate auth compatibility
115
101
  if (auth === "authjs" && (database !== "prisma" || finalFramework !== "nextjs")) {
116
102
  throw new Error("Auth.js is only supported with Next.js and Prisma database");
117
103
  }
@@ -128,7 +114,6 @@ async function getProjectConfig(projectName, options) {
128
114
  packageManager: (pm || "pnpm"),
129
115
  };
130
116
  }
131
- // Interactive prompts
132
117
  const answers = (await inquirer_1.default.prompt([
133
118
  {
134
119
  type: "input",
@@ -235,7 +220,6 @@ async function generateProject(config, targetDir, options) {
235
220
  copySpinner.fail("Failed to create project files");
236
221
  throw error;
237
222
  }
238
- // Install dependencies
239
223
  if (options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
240
224
  const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
241
225
  try {
@@ -247,7 +231,6 @@ async function generateProject(config, targetDir, options) {
247
231
  throw error;
248
232
  }
249
233
  }
250
- // Run post-install commands (skip if install was skipped)
251
234
  if (postInstallCommands.length > 0 && options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
252
235
  const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
253
236
  try {
@@ -261,41 +244,25 @@ async function generateProject(config, targetDir, options) {
261
244
  throw error;
262
245
  }
263
246
  }
264
- // Initialize git
265
247
  if (options?.git !== false && !(options?.['no-git'] || options?.noGit)) {
266
248
  const gitSpinner = logger_1.logger.startSpinner("Initializing git repository...");
267
249
  try {
268
250
  await (0, git_utils_1.initGit)(targetDir);
269
251
  gitSpinner.succeed("Git repository initialized");
270
252
  }
271
- catch {
272
- gitSpinner.warn("Failed to initialize git repository");
253
+ catch (error) {
254
+ gitSpinner.warn(`Failed to initialize git repository: ${error.message}`);
273
255
  }
274
256
  }
275
257
  }
276
258
  async function composeTemplate(config, targetDir) {
277
- // Resolve templates/modules paths
278
- const templatesCandidates = [
279
- path_1.default.join(__dirname, "..", "..", "..", "templates"),
280
- path_1.default.join(__dirname, "..", "..", "..", "..", "templates"),
281
- ];
282
- const modulesCandidates2 = [
283
- path_1.default.join(__dirname, "..", "..", "..", "modules"),
284
- path_1.default.join(__dirname, "..", "..", "..", "..", "modules"),
285
- ];
286
- const templatesDir = (await (async () => { for (const c of templatesCandidates)
287
- if (await fs_extra_1.default.pathExists(c))
288
- return c; return templatesCandidates[1]; })());
289
- const modulesDirForGenerator = (await (async () => { for (const c of modulesCandidates2)
290
- if (await fs_extra_1.default.pathExists(c))
291
- return c; return modulesCandidates2[1]; })());
259
+ 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');
292
262
  await fs_extra_1.default.ensureDir(targetDir);
293
- // Load framework configuration
294
263
  const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(config.framework, templatesDir);
295
- // Initialize advanced code generator
296
264
  const generator = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
297
265
  await generator.loadGenerators(modulesDirForGenerator);
298
- // Generate project using advanced code generator
299
266
  const features = [];
300
267
  const postInstallCommands = await generator.generate({
301
268
  framework: config.framework,
@@ -303,14 +270,12 @@ async function composeTemplate(config, targetDir) {
303
270
  auth: config.auth === 'none' ? undefined : config.auth,
304
271
  prismaProvider: config.prismaProvider,
305
272
  }, features, targetDir);
306
- // Update project name in package.json
307
273
  const packageJsonPath = path_1.default.join(targetDir, "package.json");
308
274
  if (await fs_extra_1.default.pathExists(packageJsonPath)) {
309
275
  const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
310
276
  packageJson.name = config.projectName;
311
277
  await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
312
278
  }
313
- // Ensure .env exists: if .env.example was copied from the template, create .env from it
314
279
  try {
315
280
  const envExamplePath = path_1.default.join(targetDir, ".env.example");
316
281
  const envPath = path_1.default.join(targetDir, ".env");
@@ -320,12 +285,10 @@ async function composeTemplate(config, targetDir) {
320
285
  }
321
286
  }
322
287
  catch {
323
- // non-fatal
324
288
  }
325
289
  if (config.language === "javascript") {
326
290
  await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
327
291
  }
328
- // For now, return empty array as post-install commands are handled by the generator
329
292
  return postInstallCommands;
330
293
  }
331
294
  function showNextSteps(config) {
@@ -229,13 +229,11 @@ function detectDatabaseModules(packageJson) {
229
229
  }
230
230
  async function checkKeyFiles(projectRoot, projectType, authModules, databaseModules) {
231
231
  const checks = [];
232
- // Check .env.example
233
232
  const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
234
233
  checks.push({
235
234
  status: envExampleExists ? "success" : "warning",
236
235
  message: envExampleExists ? ".env.example file found" : ".env.example file missing (recommended for documentation)",
237
236
  });
238
- // Check Prisma schema
239
237
  if (databaseModules.includes("prisma")) {
240
238
  const schemaExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"));
241
239
  checks.push({
@@ -243,7 +241,6 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
243
241
  message: schemaExists ? "Prisma schema found" : "Prisma schema missing (required for Prisma)",
244
242
  });
245
243
  }
246
- // Check auth routes
247
244
  if (authModules.length > 0 && projectType === "nextjs") {
248
245
  const authRoutesExist = await checkAuthRoutesExist(projectRoot, projectType);
249
246
  checks.push({
@@ -278,7 +275,6 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
278
275
  const requiredKeys = [];
279
276
  const missing = [];
280
277
  const present = [];
281
- // Determine required env keys
282
278
  if (databaseModules.includes("prisma")) {
283
279
  requiredKeys.push("DATABASE_URL");
284
280
  }
@@ -288,7 +284,6 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
288
284
  if (authModules.includes("better-auth")) {
289
285
  requiredKeys.push("BETTER_AUTH_SECRET", "BETTER_AUTH_URL");
290
286
  }
291
- // Check env files
292
287
  const envPaths = [".env", ".env.local"];
293
288
  let envContent = "";
294
289
  for (const envPath of envPaths) {
@@ -305,7 +300,6 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
305
300
  });
306
301
  return { checks, envStatus: { missing: requiredKeys, present: [] } };
307
302
  }
308
- // Parse env content
309
303
  const envLines = envContent.split("\n").map(line => line.trim()).filter(line => line && !line.startsWith("#"));
310
304
  const envVars = new Set(envLines.map(line => line.split("=")[0]));
311
305
  for (const key of requiredKeys) {
@@ -342,19 +336,16 @@ function checkConflicts(authModules, databaseModules) {
342
336
  }
343
337
  async function checkConfigFiles(projectRoot, projectType, packageJson) {
344
338
  const checks = [];
345
- // Check tsconfig.json
346
339
  const tsconfigExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "tsconfig.json"));
347
340
  checks.push({
348
341
  status: tsconfigExists ? "success" : "error",
349
342
  message: tsconfigExists ? MESSAGES.TSCONFIG_FOUND : MESSAGES.TSCONFIG_MISSING,
350
343
  });
351
- // Check ESLint config
352
344
  const eslintExists = await checkEslintConfigExists(projectRoot);
353
345
  checks.push({
354
346
  status: eslintExists ? "success" : "warning",
355
347
  message: eslintExists ? MESSAGES.ESLINT_CONFIG_FOUND : MESSAGES.ESLINT_CONFIG_MISSING,
356
348
  });
357
- // Check build script
358
349
  const hasBuildScript = packageJson.scripts && typeof packageJson.scripts === "object" && "build" in packageJson.scripts;
359
350
  checks.push({
360
351
  status: hasBuildScript ? "success" : "warning",
@@ -369,7 +360,6 @@ async function checkDependencies(packageJson) {
369
360
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
370
361
  for (const [name, version] of Object.entries(deps || {})) {
371
362
  if (typeof version === "string" && (version.startsWith("^") || version.startsWith("~"))) {
372
- // Assume up to date
373
363
  }
374
364
  else {
375
365
  outdated.push(name);
package/dist/cli/list.js CHANGED
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const logger_1 = require("../lib/ui/logger");
11
+ const package_root_1 = require("../lib/utils/package-root");
11
12
  async function listCommand(options) {
12
13
  const showFrameworks = !options.modules || options.frameworks;
13
14
  const showModules = !options.frameworks || options.modules;
@@ -17,7 +18,7 @@ async function listCommand(options) {
17
18
  let hasFrameworks = false;
18
19
  let hasModules = false;
19
20
  if (showFrameworks) {
20
- const templatesDir = path_1.default.join(__dirname, "..", "..", "..", "..", "templates");
21
+ const templatesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "templates");
21
22
  const frameworks = await getAvailableFrameworks(templatesDir);
22
23
  if (frameworks.length > 0) {
23
24
  hasFrameworks = true;
@@ -30,14 +31,12 @@ async function listCommand(options) {
30
31
  logger_1.logger.newLine();
31
32
  }
32
33
  }
33
- // List modules
34
34
  if (showModules) {
35
- const modulesDir = path_1.default.join(__dirname, "..", "..", "..", "modules");
35
+ const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
36
36
  const modules = await getAvailableModules(modulesDir);
37
37
  if (modules.length > 0) {
38
38
  hasModules = true;
39
39
  logger_1.logger.log(chalk_1.default.bold.magenta("MODULES"));
40
- // Group by category
41
40
  const grouped = modules.reduce((acc, mod) => {
42
41
  if (!acc[mod.category]) {
43
42
  acc[mod.category] = [];
@@ -55,7 +54,6 @@ async function listCommand(options) {
55
54
  const isLastMod = modIndex === mods.length - 1;
56
55
  const modPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
57
56
  logger_1.logger.log(` ${chalk_1.default.gray(modPrefix)} ${chalk_1.default.green(mod.displayName)}`);
58
- // Show additional details for database modules
59
57
  if (mod.category === "database" && mod.name === "prisma") {
60
58
  const providerPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
61
59
  logger_1.logger.log(` ${chalk_1.default.gray(providerPrefix)} ${chalk_1.default.dim("Providers: PostgreSQL, MongoDB, MySQL, SQLite")}`);
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.4")
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
- .command("create [project-name]")
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);
@@ -7,6 +7,7 @@ exports.convertToJavaScript = convertToJavaScript;
7
7
  /* eslint-disable @typescript-eslint/no-require-imports */
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const path_1 = __importDefault(require("path"));
10
+ const package_root_1 = require("../utils/package-root");
10
11
  const baseDirs = {
11
12
  express: "./src",
12
13
  "react": "./src",
@@ -76,7 +77,7 @@ async function convertToJavaScript(targetDir, framework) {
76
77
  presets.push([
77
78
  require.resolve("@babel/preset-react"),
78
79
  {
79
- runtime: "classic",
80
+ runtime: "automatic",
80
81
  },
81
82
  ]);
82
83
  }
@@ -165,7 +166,7 @@ async function convertToJavaScript(targetDir, framework) {
165
166
  };
166
167
  await replaceAliases(targetDir);
167
168
  }
168
- const templatesRoot = path_1.default.join(__dirname, "..", "..", "..", "templates");
169
+ const templatesRoot = path_1.default.join((0, package_root_1.getPackageRoot)(), "templates");
169
170
  const templateName = framework;
170
171
  let fileReplacements = [];
171
172
  let jsScripts = null;
@@ -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>;
@@ -11,6 +11,7 @@ exports.getCompatibleAuthOptions = getCompatibleAuthOptions;
11
11
  exports.getDatabaseChoices = getDatabaseChoices;
12
12
  const fs_extra_1 = __importDefault(require("fs-extra"));
13
13
  const path_1 = __importDefault(require("path"));
14
+ const package_root_1 = require("../utils/package-root");
14
15
  /**
15
16
  * Discover all available modules from the modules directory
16
17
  */
@@ -20,9 +21,21 @@ async function discoverModules(modulesDir) {
20
21
  databases: [],
21
22
  auth: [],
22
23
  };
23
- if (!(await fs_extra_1.default.pathExists(modulesDir))) {
24
- return discovered;
24
+ // If modulesDir isn't provided or doesn't exist, try common locations
25
+ const candidates = [];
26
+ if (modulesDir)
27
+ candidates.push(modulesDir);
28
+ candidates.push(path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules')); // package root
29
+ let resolvedModulesDir;
30
+ for (const c of candidates) {
31
+ if (await fs_extra_1.default.pathExists(c)) {
32
+ resolvedModulesDir = c;
33
+ break;
34
+ }
25
35
  }
36
+ if (!resolvedModulesDir)
37
+ return discovered;
38
+ modulesDir = resolvedModulesDir;
26
39
  // Discover frameworks from templates directory
27
40
  const templatesDir = path_1.default.join(modulesDir, '..', 'templates');
28
41
  if (await fs_extra_1.default.pathExists(templatesDir)) {
@@ -62,6 +75,11 @@ async function discoverModules(modulesDir) {
62
75
  if (await fs_extra_1.default.pathExists(moduleJsonPath)) {
63
76
  try {
64
77
  const metadata = await fs_extra_1.default.readJson(moduleJsonPath);
78
+ // Ensure name/displayName fallbacks
79
+ if (!metadata.name)
80
+ metadata.name = moduleName;
81
+ if (!metadata.displayName)
82
+ metadata.displayName = moduleName;
65
83
  discovered.databases.push(metadata);
66
84
  }
67
85
  catch {
@@ -80,6 +98,10 @@ async function discoverModules(modulesDir) {
80
98
  if (await fs_extra_1.default.pathExists(moduleJsonPath)) {
81
99
  try {
82
100
  const metadata = await fs_extra_1.default.readJson(moduleJsonPath);
101
+ if (!metadata.name)
102
+ metadata.name = moduleName;
103
+ if (!metadata.displayName)
104
+ metadata.displayName = moduleName;
83
105
  discovered.auth.push(metadata);
84
106
  }
85
107
  catch {
@@ -101,7 +123,7 @@ function getValidDatabaseOptions(databases) {
101
123
  options.push('prisma-postgresql', 'prisma-mongodb', 'prisma-mysql', 'prisma-sqlite');
102
124
  }
103
125
  else if (db.name === 'mongoose') {
104
- options.push('mongoose-mongodb', 'mongoose');
126
+ options.push('mongoose', 'mongoose');
105
127
  }
106
128
  else {
107
129
  // For other databases, add the name directly
@@ -131,7 +153,7 @@ function parseDatabaseOption(dbOption) {
131
153
  const provider = dbOption.split('-')[1];
132
154
  return { database: 'prisma', provider };
133
155
  }
134
- if (dbOption === 'mongoose-mongodb' || dbOption === 'mongoose') {
156
+ if (dbOption === 'mongoose' || dbOption === 'mongoose') {
135
157
  return { database: 'mongoose' };
136
158
  }
137
159
  return { database: dbOption };
@@ -176,7 +198,7 @@ function getDatabaseChoices(databases, framework) {
176
198
  choices.push({ name: 'Prisma (PostgreSQL)', value: 'prisma-postgresql' }, { name: 'Prisma (MongoDB)', value: 'prisma-mongodb' }, { name: 'Prisma (MySQL)', value: 'prisma-mysql' }, { name: 'Prisma (SQLite)', value: 'prisma-sqlite' });
177
199
  }
178
200
  else if (db.name === 'mongoose') {
179
- choices.push({ name: 'Mongoose (MongoDB)', value: 'mongoose-mongodb' });
201
+ choices.push({ name: 'Mongoose', value: 'mongoose' });
180
202
  }
181
203
  else {
182
204
  choices.push({ name: db.displayName, value: db.name });
@@ -75,9 +75,23 @@ export declare class AdvancedCodeGenerator {
75
75
  private executeAddEnv;
76
76
  private executeRunCommand;
77
77
  private generatePackageJson;
78
+ /**
79
+ * Apply generators to an existing project directory instead of creating a new template.
80
+ * This executes applicable operations and updates package.json in-place.
81
+ */
82
+ applyToProject(selectedModules: {
83
+ framework: string;
84
+ database?: string;
85
+ auth?: string;
86
+ prismaProvider?: string;
87
+ }, features: string[], projectPath: string): Promise<string[]>;
78
88
  getAvailableGenerators(): {
79
89
  frameworks: string[];
80
90
  databases: string[];
81
91
  auths: string[];
82
92
  };
93
+ /**
94
+ * Register a generator config dynamically (used for modules that only provide module.json patches).
95
+ */
96
+ registerGenerator(type: 'framework' | 'database' | 'auth', name: string, config: GeneratorConfig): void;
83
97
  }
@@ -36,6 +36,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AdvancedCodeGenerator = void 0;
37
37
  const fs = __importStar(require("fs-extra"));
38
38
  const path = __importStar(require("path"));
39
+ const package_root_1 = require("../utils/package-root");
40
+ const generator_utils_1 = require("./generator-utils");
39
41
  class AdvancedCodeGenerator {
40
42
  constructor(frameworkConfig) {
41
43
  this.generators = new Map();
@@ -44,7 +46,6 @@ class AdvancedCodeGenerator {
44
46
  }
45
47
  async loadGenerators(modulesPath) {
46
48
  const moduleTypes = ['auth', 'database'];
47
- // Load module generators
48
49
  for (const type of moduleTypes) {
49
50
  const typePath = path.join(modulesPath, type);
50
51
  if (await fs.pathExists(typePath)) {
@@ -54,20 +55,8 @@ class AdvancedCodeGenerator {
54
55
  if (await fs.pathExists(generatorPath)) {
55
56
  try {
56
57
  const config = await fs.readJson(generatorPath);
57
- // Also load module.json for additional metadata like postInstall
58
- const modulePath = path.join(typePath, moduleName, 'module.json');
59
- if (await fs.pathExists(modulePath)) {
60
- try {
61
- const moduleConfig = await fs.readJson(modulePath);
62
- if (moduleConfig.postInstall && Array.isArray(moduleConfig.postInstall)) {
63
- // Store postInstall commands with the generator for later use
64
- config.postInstall = moduleConfig.postInstall;
65
- }
66
- }
67
- catch {
68
- // Silently skip if module.json is invalid
69
- }
70
- }
58
+ const modulePath = path.join(typePath, moduleName);
59
+ await (0, generator_utils_1.mergeModuleIntoGeneratorConfig)(config, modulePath);
71
60
  this.generators.set(`${type}:${moduleName}`, config);
72
61
  }
73
62
  catch {
@@ -375,28 +364,18 @@ class AdvancedCodeGenerator {
375
364
  }
376
365
  }
377
366
  async copyTemplate(frameworkName, outputPath) {
378
- const candidates = [
379
- path.resolve(__dirname, '..', '..', 'templates'), // dist/templates (when bundled)
380
- path.resolve(__dirname, '..', '..', '..', 'templates'), // package root templates
381
- ];
382
- let templateBase;
383
- for (const c of candidates) {
384
- const p = path.join(c, frameworkName);
385
- if (await fs.pathExists(p)) {
386
- templateBase = p;
387
- break;
388
- }
367
+ const packageRoot = (0, package_root_1.getPackageRoot)();
368
+ const templatePath = path.join(packageRoot, 'templates', frameworkName);
369
+ if (await fs.pathExists(templatePath)) {
370
+ await fs.copy(templatePath, outputPath, {
371
+ filter: (src) => {
372
+ const relativePath = path.relative(templatePath, src);
373
+ return relativePath !== 'template.json' &&
374
+ relativePath !== 'node_modules' &&
375
+ !relativePath.startsWith('node_modules/');
376
+ }
377
+ });
389
378
  }
390
- if (!templateBase)
391
- return;
392
- await fs.copy(templateBase, outputPath, {
393
- filter: (src) => {
394
- const relativePath = path.relative(templateBase, src);
395
- return relativePath !== 'template.json' &&
396
- relativePath !== 'node_modules' &&
397
- !relativePath.startsWith('node_modules/');
398
- }
399
- });
400
379
  }
401
380
  processOperationTemplates(operation, context) {
402
381
  const processed = { ...operation };
@@ -455,34 +434,9 @@ class AdvancedCodeGenerator {
455
434
  content = this.processTemplate(operation.content, context);
456
435
  }
457
436
  else if (operation.source) {
458
- // Find the source file path relative to the module/template directory
459
- // Resolve modules/templates base paths (try both dist and package root)
460
- const modulesCandidates = [
461
- path.resolve(__dirname, '..', '..', 'modules'),
462
- path.resolve(__dirname, '..', '..', '..', 'modules'),
463
- ];
464
- const templatesCandidates = [
465
- path.resolve(__dirname, '..', '..', 'templates'),
466
- path.resolve(__dirname, '..', '..', '..', 'templates'),
467
- ];
468
- const resolveExisting = async (cands) => {
469
- for (const c of cands) {
470
- if (await fs.pathExists(c))
471
- return c;
472
- }
473
- return undefined;
474
- };
475
- const modulesPathResolved = await resolveExisting(modulesCandidates);
476
- const templatesPathResolved = await resolveExisting(templatesCandidates);
477
- const moduleBasePath = operation.generatorType === 'framework'
478
- ? path.join(templatesPathResolved || templatesCandidates[0], operation.generator)
479
- : path.join(modulesPathResolved || modulesCandidates[0], operation.generatorType, operation.generator);
480
- const sourcePath = path.join(moduleBasePath, 'files', operation.source);
481
- // Check if source file exists
482
- if (await fs.pathExists(sourcePath)) {
483
- // Read source file
437
+ const sourcePath = (0, generator_utils_1.locateOperationSource)(operation.generatorType, operation.generator, operation.source || '');
438
+ if (sourcePath && await fs.pathExists(sourcePath)) {
484
439
  content = await fs.readFile(sourcePath, 'utf-8');
485
- // Process template content
486
440
  content = this.processTemplate(content, context);
487
441
  }
488
442
  else {
@@ -551,7 +505,7 @@ class AdvancedCodeGenerator {
551
505
  processedContentTop = this.processTemplate(patchOp.content, context).trim();
552
506
  }
553
507
  else if (patchOp.source) {
554
- const modulesPath = path.join(__dirname, '..', '..', 'modules');
508
+ const modulesPath = path.join((0, package_root_1.getPackageRoot)(), 'modules');
555
509
  const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
556
510
  if (await fs.pathExists(sourcePath)) {
557
511
  processedContentTop = await fs.readFile(sourcePath, 'utf-8');
@@ -569,7 +523,7 @@ class AdvancedCodeGenerator {
569
523
  processedContentBottom = this.processTemplate(patchOp.content, context).trim();
570
524
  }
571
525
  else if (patchOp.source) {
572
- const modulesPath = path.join(__dirname, '..', '..', 'modules');
526
+ const modulesPath = path.join((0, package_root_1.getPackageRoot)(), 'modules');
573
527
  const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
574
528
  if (await fs.pathExists(sourcePath)) {
575
529
  processedContentBottom = await fs.readFile(sourcePath, 'utf-8');
@@ -657,6 +611,50 @@ class AdvancedCodeGenerator {
657
611
  packageJson.scripts = { ...(packageJson.scripts || {}), ...allScripts };
658
612
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
659
613
  }
614
+ /**
615
+ * Apply generators to an existing project directory instead of creating a new template.
616
+ * This executes applicable operations and updates package.json in-place.
617
+ */
618
+ async applyToProject(selectedModules, features, projectPath) {
619
+ const context = {
620
+ ...selectedModules,
621
+ features,
622
+ };
623
+ if (selectedModules.database === 'prisma' && !context.prismaProvider) {
624
+ context.prismaProvider = 'postgresql';
625
+ }
626
+ const applicableOperations = [];
627
+ for (const [key, generator] of this.generators) {
628
+ const [genType, name] = key.split(':');
629
+ if (genType === 'framework' && name === selectedModules.framework) {
630
+ // framework
631
+ }
632
+ else if (genType === 'database' && name === selectedModules.database) {
633
+ // database
634
+ }
635
+ else if (genType === 'auth' && name === selectedModules.auth) {
636
+ // auth
637
+ }
638
+ else {
639
+ continue;
640
+ }
641
+ if (generator.postInstall && Array.isArray(generator.postInstall)) {
642
+ this.postInstallCommands.push(...generator.postInstall);
643
+ }
644
+ const items = generator.operations || [];
645
+ for (const item of items) {
646
+ if (this.evaluateCondition(item.condition, context)) {
647
+ applicableOperations.push({ ...item, generator: name, generatorType: genType, priority: generator.priority });
648
+ }
649
+ }
650
+ }
651
+ applicableOperations.sort((a, b) => (a.priority || 0) - (b.priority || 0));
652
+ for (const operation of applicableOperations) {
653
+ await this.executeOperation(operation, context, projectPath);
654
+ }
655
+ await this.generatePackageJson(selectedModules, features, projectPath);
656
+ return this.postInstallCommands;
657
+ }
660
658
  getAvailableGenerators() {
661
659
  const frameworks = [];
662
660
  const databases = [];
@@ -677,5 +675,11 @@ class AdvancedCodeGenerator {
677
675
  }
678
676
  return { frameworks, databases, auths };
679
677
  }
678
+ /**
679
+ * Register a generator config dynamically (used for modules that only provide module.json patches).
680
+ */
681
+ registerGenerator(type, name, config) {
682
+ this.generators.set(`${type}:${name}`, config);
683
+ }
680
684
  }
681
685
  exports.AdvancedCodeGenerator = AdvancedCodeGenerator;