stackkit 0.2.1 → 0.2.2

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/dist/cli/add.js CHANGED
@@ -78,6 +78,20 @@ async function getAddConfig(module, options, projectInfo) {
78
78
  if (!moduleMetadata) {
79
79
  throw new Error(`Auth provider "${provider}" not found`);
80
80
  }
81
+ // Validate compatibility with existing project
82
+ if (projectInfo) {
83
+ // Auth.js requires Next.js + Prisma
84
+ if (provider === "authjs" &&
85
+ (projectInfo.framework !== "nextjs" || !projectInfo.hasPrisma)) {
86
+ throw new Error("Auth.js is only supported with Next.js and Prisma database in this project");
87
+ }
88
+ // Better-auth requires a database for server frameworks (non-react)
89
+ if (provider === "better-auth" &&
90
+ !projectInfo.hasDatabase &&
91
+ projectInfo.framework !== "react") {
92
+ throw new Error("Better Auth requires a database for server frameworks in this project");
93
+ }
94
+ }
81
95
  return {
82
96
  module: "auth",
83
97
  provider,
@@ -90,19 +104,25 @@ async function getAddConfig(module, options, projectInfo) {
90
104
  throw new Error(`Unknown module type "${module}". Use "database" or "auth", or specify a provider directly.`);
91
105
  }
92
106
  async function getInteractiveConfig(modulesDir, projectInfo) {
107
+ // Discover modules once and use compatibility info to decide which categories to show
108
+ const discovered = await (0, module_discovery_1.discoverModules)(modulesDir);
109
+ const compatibleAuths = (0, module_discovery_1.getCompatibleAuthOptions)(discovered.auth || [], projectInfo?.framework || "nextjs", projectInfo?.hasPrisma ? "prisma" : "none");
110
+ const categories = [
111
+ { name: "Database", value: "database" },
112
+ ];
113
+ // Only show Auth category if there is at least one compatible auth option
114
+ if (compatibleAuths.length > 1) {
115
+ categories.push({ name: "Auth", value: "auth" });
116
+ }
93
117
  const answers = await inquirer_1.default.prompt([
94
118
  {
95
119
  type: "list",
96
120
  name: "category",
97
121
  message: "What would you like to add?",
98
- choices: [
99
- { name: "Database", value: "database" },
100
- { name: "Auth", value: "auth" },
101
- ],
122
+ choices: categories,
102
123
  },
103
124
  ]);
104
125
  const category = answers.category;
105
- const discovered = await (0, module_discovery_1.discoverModules)(modulesDir);
106
126
  if (category === "database") {
107
127
  const dbChoices = (0, module_discovery_1.getDatabaseChoices)(discovered.databases || [], projectInfo?.framework || "nextjs");
108
128
  const dbAnswers = await inquirer_1.default.prompt([
@@ -135,7 +155,9 @@ async function getInteractiveConfig(modulesDir, projectInfo) {
135
155
  };
136
156
  }
137
157
  else if (category === "auth") {
138
- const authChoices = (discovered.auth || []).map((a) => ({ name: a.displayName, value: a.name }));
158
+ // Filter auth choices based on project compatibility (framework + database)
159
+ const dbString = projectInfo?.hasPrisma ? "prisma" : "none";
160
+ const authChoices = (0, module_discovery_1.getCompatibleAuthOptions)(discovered.auth || [], projectInfo?.framework || "nextjs", dbString);
139
161
  const authAnswers = await inquirer_1.default.prompt([
140
162
  {
141
163
  type: "list",
@@ -145,11 +167,17 @@ async function getInteractiveConfig(modulesDir, projectInfo) {
145
167
  },
146
168
  ]);
147
169
  const selectedAuth = authAnswers.auth;
170
+ if (selectedAuth === "none") {
171
+ logger_1.logger.info("Cancelled");
172
+ process.exit(0);
173
+ }
148
174
  const metadata = await loadModuleMetadata(modulesDir, selectedAuth, selectedAuth);
149
175
  if (!metadata) {
150
176
  throw new Error(`Auth provider "${selectedAuth}" not found`);
151
177
  }
152
- if (projectInfo && metadata.supportedFrameworks && !metadata.supportedFrameworks.includes(projectInfo.framework)) {
178
+ if (projectInfo &&
179
+ metadata.supportedFrameworks &&
180
+ !metadata.supportedFrameworks.includes(projectInfo.framework)) {
153
181
  throw new Error(`Auth provider "${selectedAuth}" does not support ${projectInfo.framework}`);
154
182
  }
155
183
  return {
@@ -244,6 +272,18 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
244
272
  selectedModules.auth = config.provider;
245
273
  }
246
274
  const postInstall = await gen.applyToProject(selectedModules, [], projectRoot);
275
+ // Install dependencies first, then run post-install commands (e.g. prisma generate)
276
+ if (!options?.dryRun && options?.install !== false) {
277
+ const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
278
+ try {
279
+ await (0, package_manager_1.installDependencies)(projectRoot, projectInfo.packageManager);
280
+ installSpinner.succeed("Dependencies installed");
281
+ }
282
+ catch (err) {
283
+ installSpinner.fail("Failed to install dependencies");
284
+ throw err;
285
+ }
286
+ }
247
287
  if (postInstall && postInstall.length > 0 && !options?.dryRun) {
248
288
  const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
249
289
  try {
@@ -257,17 +297,6 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
257
297
  throw error;
258
298
  }
259
299
  }
260
- if (!options?.dryRun && options?.install !== false) {
261
- const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
262
- try {
263
- await (0, package_manager_1.installDependencies)(projectRoot, projectInfo.packageManager);
264
- installSpinner.succeed("Dependencies installed");
265
- }
266
- catch (err) {
267
- installSpinner.fail("Failed to install dependencies");
268
- throw err;
269
- }
270
- }
271
300
  return;
272
301
  }
273
302
  const mergedDeps = {};
@@ -390,6 +390,24 @@ class AdvancedCodeGenerator {
390
390
  catch {
391
391
  // ignore failures here — not critical
392
392
  }
393
+ // Ensure gitignore is present in target even if template authors
394
+ // renamed it to avoid npm/package issues (e.g. 'gitignore' or '_gitignore')
395
+ try {
396
+ const gitCandidates = [".gitignore", "gitignore", "_gitignore"];
397
+ for (const g of gitCandidates) {
398
+ const src = path.join(templatePath, g);
399
+ if (await fs.pathExists(src)) {
400
+ const dest = path.join(outputPath, ".gitignore");
401
+ if (!(await fs.pathExists(dest))) {
402
+ await fs.copy(src, dest);
403
+ }
404
+ break;
405
+ }
406
+ }
407
+ }
408
+ catch {
409
+ // ignore
410
+ }
393
411
  }
394
412
  }
395
413
  processOperationTemplates(operation, context) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stackkit",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Production-ready CLI to create and extend JavaScript or TypeScript apps with modular stacks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {