stackkit 0.1.4 → 0.1.6

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 CHANGED
@@ -40,4 +40,4 @@ npx stackkit doctor
40
40
  ## Documentation
41
41
 
42
42
  - [StackKit Docs](https://stackkit.tariqul.dev)
43
- - [GitHub Repository](https://github.com/tariqul420/stackkit)
43
+ - [GitHub Repository](https://github.com/tariqul420/stackkit)
package/dist/cli/add.js CHANGED
@@ -17,7 +17,6 @@ const logger_1 = require("../lib/ui/logger");
17
17
  const package_manager_1 = require("../lib/pm/package-manager");
18
18
  const package_root_1 = require("../lib/utils/package-root");
19
19
  const framework_utils_1 = require("../lib/framework/framework-utils");
20
- const generator_utils_1 = require("../lib/generation/generator-utils");
21
20
  async function addCommand(module, options) {
22
21
  try {
23
22
  const projectRoot = process.cwd();
@@ -29,10 +28,6 @@ async function addCommand(module, options) {
29
28
  logger_1.logger.newLine();
30
29
  logger_1.logger.success(`Added ${chalk_1.default.bold(config.displayName)}`);
31
30
  logger_1.logger.newLine();
32
- if (config.metadata.envVars && config.metadata.envVars.length > 0) {
33
- logger_1.logger.log("Next: Fill in environment variables in .env");
34
- }
35
- logger_1.logger.newLine();
36
31
  }
37
32
  catch (error) {
38
33
  logger_1.logger.error(`Failed to add module: ${error.message}`);
@@ -43,87 +38,60 @@ async function addCommand(module, options) {
43
38
  }
44
39
  }
45
40
  async function getAddConfig(module, options, projectInfo) {
46
- const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules');
47
- const argv = process.argv.slice(2);
48
- const addIndex = argv.indexOf('add');
49
- const argsAfterAdd = addIndex >= 0 ? argv.slice(addIndex + 1) : [];
50
- const flagsProvided = argsAfterAdd.some(arg => arg.startsWith('-'));
51
- const optionsProvided = flagsProvided || !!(options && (options.yes || options.provider || options.force || options.dryRun));
52
- if (optionsProvided) {
53
- if (!module) {
54
- throw new Error("Module name is required when using flags");
55
- }
56
- if (module === "database" || module === "auth") {
41
+ const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
42
+ // If no module provided, go interactive
43
+ if (!module) {
44
+ return await getInteractiveConfig(modulesDir, projectInfo);
45
+ }
46
+ // Only allow: no-arg interactive, or explicit category + --provider.
47
+ // Disallow positional provider names like `npx stackkit add better-auth` or
48
+ // `npx stackkit add auth prisma-postgresql` — require `--provider` flag.
49
+ if (module === "database" || module === "auth") {
50
+ if (!options?.provider) {
57
51
  if (module === "database") {
58
- if (!options?.provider) {
59
- throw new Error("Provider is required for database. Use --provider <provider>");
60
- }
61
- let baseProvider = options.provider;
62
- let adapterProvider = options.provider;
63
- if (options.provider.includes('-')) {
64
- const parts = options.provider.split('-');
65
- baseProvider = parts[0]; // e.g., "prisma"
66
- adapterProvider = options.provider; // e.g., "prisma-postgresql"
67
- }
68
- const moduleMetadata = await loadModuleMetadata(modulesDir, baseProvider, baseProvider);
69
- if (!moduleMetadata) {
70
- throw new Error(`Database provider "${baseProvider}" not found`);
71
- }
72
- return {
73
- module: "database",
74
- provider: adapterProvider,
75
- displayName: `${moduleMetadata.displayName} (${adapterProvider.split('-')[1] || adapterProvider})`,
76
- metadata: moduleMetadata,
77
- };
52
+ throw new Error('Provider is required for database. Use: `npx stackkit add database --provider <provider>`');
78
53
  }
79
- else if (module === "auth") {
80
- const provider = options?.provider || "better-auth";
81
- const moduleMetadata = await loadModuleMetadata(modulesDir, provider, provider);
82
- if (!moduleMetadata) {
83
- throw new Error(`Auth provider "${provider}" not found`);
84
- }
85
- return {
86
- module: "auth",
87
- provider,
88
- displayName: moduleMetadata.displayName,
89
- metadata: moduleMetadata,
90
- };
54
+ else {
55
+ throw new Error('Provider is required for auth. Use: `npx stackkit add auth --provider <provider>`');
91
56
  }
92
57
  }
93
- const moduleMetadata = await loadModuleMetadata(modulesDir, module, options?.provider);
94
- if (!moduleMetadata) {
95
- throw new Error(`Module "${module}" not found`);
96
- }
97
- let selectedProvider = options?.provider;
98
- if (!selectedProvider && moduleMetadata.category !== module) {
99
- selectedProvider = module;
100
- }
101
- if (moduleMetadata.category === "database" && !selectedProvider) {
102
- if (typeof moduleMetadata.dependencies === "object" && "providers" in moduleMetadata.dependencies) {
103
- const providers = Object.keys(moduleMetadata.dependencies.providers || {});
104
- if (providers.length > 0) {
105
- const { provider } = await inquirer_1.default.prompt([
106
- {
107
- type: "list",
108
- name: "provider",
109
- message: "Select database provider:",
110
- choices: providers.map((p) => ({ name: p, value: p })),
111
- },
112
- ]);
113
- selectedProvider = provider;
114
- }
58
+ if (module === "database") {
59
+ let baseProvider = options.provider;
60
+ let adapterProvider = options.provider;
61
+ if (options.provider.includes("-")) {
62
+ const parts = options.provider.split("-");
63
+ baseProvider = parts[0]; // e.g., "prisma"
64
+ adapterProvider = options.provider; // e.g., "prisma-postgresql"
65
+ }
66
+ const moduleMetadata = await loadModuleMetadata(modulesDir, baseProvider, baseProvider);
67
+ if (!moduleMetadata) {
68
+ throw new Error(`Database provider "${baseProvider}" not found`);
115
69
  }
70
+ return {
71
+ module: "database",
72
+ provider: adapterProvider,
73
+ displayName: `${moduleMetadata.displayName} (${adapterProvider.split("-")[1] || adapterProvider})`,
74
+ metadata: moduleMetadata,
75
+ };
116
76
  }
117
- if (projectInfo && !moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
118
- throw new Error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(", ")}`);
77
+ else if (module === "auth") {
78
+ const provider = options.provider;
79
+ const moduleMetadata = await loadModuleMetadata(modulesDir, provider, provider);
80
+ if (!moduleMetadata) {
81
+ throw new Error(`Auth provider "${provider}" not found`);
82
+ }
83
+ return {
84
+ module: "auth",
85
+ provider,
86
+ displayName: moduleMetadata.displayName,
87
+ metadata: moduleMetadata,
88
+ };
119
89
  }
120
- return {
121
- module,
122
- provider: selectedProvider,
123
- displayName: moduleMetadata.displayName,
124
- metadata: moduleMetadata,
125
- };
126
90
  }
91
+ // Unknown module type
92
+ throw new Error(`Unknown module type "${module}". Use "database" or "auth", or specify a provider directly.`);
93
+ }
94
+ async function getInteractiveConfig(modulesDir, projectInfo) {
127
95
  const answers = await inquirer_1.default.prompt([
128
96
  {
129
97
  type: "list",
@@ -131,7 +99,7 @@ async function getAddConfig(module, options, projectInfo) {
131
99
  message: "What would you like to add?",
132
100
  choices: [
133
101
  { name: "Database", value: "database" },
134
- { name: "Authentication", value: "auth" },
102
+ { name: "Auth", value: "auth" },
135
103
  ],
136
104
  },
137
105
  ]);
@@ -165,9 +133,9 @@ async function getAddConfig(module, options, projectInfo) {
165
133
  ]);
166
134
  return {
167
135
  module: "database",
168
- provider: "prisma",
136
+ provider: `prisma-${providerAnswers.provider}`,
169
137
  displayName: `Prisma (${providerAnswers.provider})`,
170
- metadata: await loadModuleMetadata(modulesDir, "prisma", "prisma"),
138
+ metadata: (await loadModuleMetadata(modulesDir, "prisma", "prisma")),
171
139
  };
172
140
  }
173
141
  else {
@@ -175,7 +143,7 @@ async function getAddConfig(module, options, projectInfo) {
175
143
  module: "database",
176
144
  provider: "mongoose",
177
145
  displayName: "Mongoose",
178
- metadata: await loadModuleMetadata(modulesDir, "mongoose", "mongoose"),
146
+ metadata: (await loadModuleMetadata(modulesDir, "mongoose", "mongoose")),
179
147
  };
180
148
  }
181
149
  }
@@ -208,6 +176,55 @@ async function getAddConfig(module, options, projectInfo) {
208
176
  }
209
177
  throw new Error("Invalid selection");
210
178
  }
179
+ async function getProviderConfig(modulesDir, provider, projectInfo) {
180
+ if (provider.includes("-")) {
181
+ const parts = provider.split("-");
182
+ const baseProvider = parts[0];
183
+ const specificProvider = provider;
184
+ if (baseProvider === "prisma") {
185
+ const metadata = await loadModuleMetadata(modulesDir, "database", baseProvider);
186
+ if (!metadata) {
187
+ throw new Error(`Database provider "${baseProvider}" not found`);
188
+ }
189
+ return {
190
+ module: "database",
191
+ provider: specificProvider,
192
+ displayName: `Prisma (${parts[1]})`,
193
+ metadata,
194
+ };
195
+ }
196
+ }
197
+ else {
198
+ if (provider === "mongoose") {
199
+ const metadata = await loadModuleMetadata(modulesDir, "database", "mongoose");
200
+ if (!metadata) {
201
+ throw new Error(`Database provider "${provider}" not found`);
202
+ }
203
+ return {
204
+ module: "database",
205
+ provider: "mongoose",
206
+ displayName: "Mongoose",
207
+ metadata,
208
+ };
209
+ }
210
+ else if (provider === "better-auth" || provider === "authjs") {
211
+ const metadata = await loadModuleMetadata(modulesDir, provider, provider);
212
+ if (!metadata) {
213
+ throw new Error(`Auth provider "${provider}" not found`);
214
+ }
215
+ if (projectInfo && !metadata.supportedFrameworks.includes(projectInfo.framework)) {
216
+ throw new Error(`Auth provider "${provider}" does not support ${projectInfo.framework}`);
217
+ }
218
+ return {
219
+ module: "auth",
220
+ provider,
221
+ displayName: provider === "better-auth" ? "Better Auth" : "Auth.js",
222
+ metadata,
223
+ };
224
+ }
225
+ }
226
+ throw new Error(`Unknown provider "${provider}". Available providers: better-auth, authjs, mongoose, prisma-postgresql, prisma-mongodb, prisma-mysql, prisma-sqlite`);
227
+ }
211
228
  async function addModuleToProject(projectRoot, projectInfo, config, options) {
212
229
  const moduleMetadata = config.metadata;
213
230
  const selectedProvider = config.provider;
@@ -226,24 +243,45 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
226
243
  return;
227
244
  }
228
245
  }
246
+ if (config.module === "database" && projectInfo.hasDatabase && !options?.force) {
247
+ logger_1.logger.warn("Database library already detected in this project");
248
+ const { proceed } = await inquirer_1.default.prompt([
249
+ {
250
+ type: "confirm",
251
+ name: "proceed",
252
+ message: "Continue anyway? (use --force to skip this prompt)",
253
+ default: false,
254
+ },
255
+ ]);
256
+ if (!proceed) {
257
+ logger_1.logger.info("Cancelled");
258
+ return;
259
+ }
260
+ }
229
261
  if (options?.dryRun) {
230
262
  logger_1.logger.warn("Dry run mode - no changes will be made");
231
263
  logger_1.logger.newLine();
232
264
  }
233
- const moduleBasePath = await findModulePath(path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules'), config.module, config.provider);
265
+ const moduleBasePath = await findModulePath(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"), config.module, config.provider);
234
266
  if (moduleBasePath) {
235
- const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(projectInfo.framework, path_1.default.join((0, package_root_1.getPackageRoot)(), 'templates'));
267
+ const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(projectInfo.framework, path_1.default.join((0, package_root_1.getPackageRoot)(), "templates"));
236
268
  const gen = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
237
- await gen.loadGenerators(path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules'));
269
+ await gen.loadGenerators(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"));
238
270
  const moduleName = path_1.default.basename(moduleBasePath);
239
271
  const available = gen.getAvailableGenerators();
240
- const alreadyRegistered = (config.module === 'database' && available.databases.includes(moduleName)) || (config.module === 'auth' && available.auths.includes(moduleName));
272
+ const alreadyRegistered = (config.module === "database" && available.databases.includes(moduleName)) ||
273
+ (config.module === "auth" && available.auths.includes(moduleName));
241
274
  if (!alreadyRegistered) {
242
275
  const ops = [];
243
276
  if (Array.isArray(moduleMetadata.patches)) {
244
277
  for (const p of moduleMetadata.patches) {
245
- if (p.type === 'create-file') {
246
- ops.push({ type: 'create-file', source: p.source, destination: p.destination, condition: p.condition });
278
+ if (p.type === "create-file") {
279
+ ops.push({
280
+ type: "create-file",
281
+ source: p.source,
282
+ destination: p.destination,
283
+ condition: p.condition,
284
+ });
247
285
  }
248
286
  }
249
287
  }
@@ -258,40 +296,40 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
258
296
  }
259
297
  }
260
298
  const selectedModules = { framework: projectInfo.framework };
261
- if (config.module === 'database' && config.provider) {
262
- if (config.provider.startsWith('prisma-')) {
263
- selectedModules.database = 'prisma';
264
- selectedModules.prismaProvider = config.provider.split('-')[1];
299
+ if (config.module === "database" && config.provider) {
300
+ if (config.provider.startsWith("prisma-")) {
301
+ selectedModules.database = "prisma";
302
+ selectedModules.prismaProvider = config.provider.split("-")[1];
265
303
  }
266
304
  else {
267
305
  selectedModules.database = config.provider;
268
306
  }
269
307
  }
270
- if (config.module === 'auth' && config.provider) {
308
+ if (config.module === "auth" && config.provider) {
271
309
  selectedModules.auth = config.provider;
272
310
  }
273
311
  const postInstall = await gen.applyToProject(selectedModules, [], projectRoot);
274
312
  if (postInstall && postInstall.length > 0 && !options?.dryRun) {
275
- const postInstallSpinner = logger_1.logger.startSpinner('Running post-install commands...');
313
+ const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
276
314
  try {
277
315
  for (const command of postInstall) {
278
- (0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: 'pipe' });
316
+ (0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
279
317
  }
280
- postInstallSpinner.succeed('Post-install commands completed');
318
+ postInstallSpinner.succeed("Post-install commands completed");
281
319
  }
282
320
  catch (error) {
283
- postInstallSpinner.fail('Failed to run post-install commands');
321
+ postInstallSpinner.fail("Failed to run post-install commands");
284
322
  throw error;
285
323
  }
286
324
  }
287
325
  if (!options?.dryRun && options?.install !== false) {
288
- const installSpinner = logger_1.logger.startSpinner('Installing dependencies...');
326
+ const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
289
327
  try {
290
328
  await (0, package_manager_1.installDependencies)(projectRoot, projectInfo.packageManager);
291
- installSpinner.succeed('Dependencies installed');
329
+ installSpinner.succeed("Dependencies installed");
292
330
  }
293
331
  catch (err) {
294
- installSpinner.fail('Failed to install dependencies');
332
+ installSpinner.fail("Failed to install dependencies");
295
333
  throw err;
296
334
  }
297
335
  }
@@ -314,15 +352,18 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
314
352
  if (moduleMetadata.envVars) {
315
353
  const envArray = Array.isArray(moduleMetadata.envVars)
316
354
  ? moduleMetadata.envVars
317
- : Object.entries(moduleMetadata.envVars).map(([k, v]) => ({ key: k, value: String(v) }));
355
+ : Object.entries(moduleMetadata.envVars).map(([k, v]) => ({
356
+ key: k,
357
+ value: String(v),
358
+ }));
318
359
  for (const ev of envArray) {
319
- if (ev.key && typeof ev.value === 'string')
360
+ if (ev.key && typeof ev.value === "string")
320
361
  variables[ev.key] = ev.value;
321
362
  }
322
363
  for (let pass = 0; pass < 5; pass++) {
323
364
  let changed = false;
324
365
  for (const ev of envArray) {
325
- if (!ev.key || typeof ev.value !== 'string')
366
+ if (!ev.key || typeof ev.value !== "string")
326
367
  continue;
327
368
  const resolved = ev.value.replace(/\{\{(\w+)\}\}/g, (_m, k) => variables[k] ?? _m);
328
369
  if (variables[ev.key] !== resolved) {
@@ -402,10 +443,10 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
402
443
  if (await fs_extra_1.default.pathExists(metadataPath)) {
403
444
  const metadata = await fs_extra_1.default.readJSON(metadataPath);
404
445
  if (provider && moduleDir === provider) {
405
- return await (0, generator_utils_1.mergeGeneratorIntoModuleMetadata)(metadata, modulePath);
446
+ return metadata;
406
447
  }
407
448
  if (!provider && (metadata.category === moduleName || moduleDir === moduleName)) {
408
- return await (0, generator_utils_1.mergeGeneratorIntoModuleMetadata)(metadata, modulePath);
449
+ return metadata;
409
450
  }
410
451
  }
411
452
  }
@@ -10,10 +10,10 @@ interface CliOptions {
10
10
  packageManager?: string;
11
11
  p?: string;
12
12
  install?: boolean;
13
- 'skip-install'?: boolean;
13
+ "skip-install"?: boolean;
14
14
  skipInstall?: boolean;
15
15
  git?: boolean;
16
- 'no-git'?: boolean;
16
+ "no-git"?: boolean;
17
17
  noGit?: boolean;
18
18
  yes?: boolean;
19
19
  y?: boolean;
@@ -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,7 +248,7 @@ 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);
@@ -257,8 +261,8 @@ async function generateProject(config, targetDir, options) {
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,12 +289,75 @@ async function composeTemplate(config, targetDir) {
285
289
  }
286
290
  }
287
291
  catch {
292
+ // env copy failed.
288
293
  }
289
294
  if (config.language === "javascript") {
290
295
  await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
291
296
  }
292
297
  return postInstallCommands;
293
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
+ }
294
361
  function showNextSteps(config) {
295
362
  logger_1.logger.newLine();
296
363
  logger_1.logger.success(`Created ${config.projectName}`);