stackkit 0.2.3 → 0.2.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/dist/cli/add.js CHANGED
@@ -31,10 +31,7 @@ async function addCommand(module, options) {
31
31
  await addModuleToProject(projectRoot, refreshedProjectInfo, config, options);
32
32
  logger_1.logger.newLine();
33
33
  if (config.preAdded && config.preAdded.length > 0) {
34
- const addedNames = [
35
- ...config.preAdded.map((p) => p.displayName),
36
- config.displayName,
37
- ].map((s) => chalk_1.default.bold(s));
34
+ const addedNames = [...config.preAdded.map((p) => p.displayName), config.displayName].map((s) => chalk_1.default.bold(s));
38
35
  logger_1.logger.success(`Added ${addedNames.join(" and ")}`);
39
36
  }
40
37
  else {
@@ -121,7 +118,7 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
121
118
  const discovered = await (0, module_discovery_1.discoverModules)(modulesDir);
122
119
  // Prefer discovered framework, then projectInfo.framework; leave empty if unknown
123
120
  const defaultFramework = (discovered.frameworks && discovered.frameworks[0]?.name) || projectInfo?.framework || "";
124
- const compatibleAuths = (0, module_discovery_1.getCompatibleAuthOptions)(discovered.auth || [], projectInfo?.framework || defaultFramework, projectInfo?.hasPrisma ? "prisma" : "none");
121
+ const compatibleAuths = (0, module_discovery_1.getCompatibleAuthOptions)(discovered.auth || [], projectInfo?.framework || defaultFramework, projectInfo?.hasPrisma ? "prisma" : "none", discovered.frameworks);
125
122
  const categories = [
126
123
  { name: "Database", value: "database" },
127
124
  ];
@@ -171,7 +168,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
171
168
  }
172
169
  else if (category === "auth") {
173
170
  let preAddedForReturn;
174
- // If no database detected, require the user to select/add a database first
175
171
  if (!projectInfo?.hasDatabase) {
176
172
  logger_1.logger.warn("No database detected in the project. Authentication requires a database.");
177
173
  const dbChoices = (0, module_discovery_1.getDatabaseChoices)(discovered.databases || [], projectInfo?.framework || defaultFramework);
@@ -188,7 +184,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
188
184
  logger_1.logger.info("Cancelled — authentication requires a database");
189
185
  process.exit(0);
190
186
  }
191
- // Build a database AddConfig and add it immediately, then refresh projectInfo
192
187
  let dbConfig;
193
188
  if (selectedDb.startsWith("prisma-")) {
194
189
  const provider = selectedDb.split("-")[1];
@@ -210,13 +205,9 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
210
205
  metadata: meta,
211
206
  };
212
207
  }
213
- // Add the database first (suppress its top-level success message)
214
208
  await addModuleToProject(projectRoot, projectInfo || (await (0, detect_1.detectProjectInfo)(projectRoot)), dbConfig, options);
215
- // Refresh project info after database install and record pre-added
216
209
  projectInfo = await (0, detect_1.detectProjectInfo)(projectRoot);
217
- // attach preAdded so caller can summarize chained additions
218
210
  dbConfig.preAdded = dbConfig.preAdded || [];
219
- // store for returning later
220
211
  preAddedForReturn = dbConfig;
221
212
  }
222
213
  const dbString = projectInfo?.hasPrisma ? "prisma" : "none";
@@ -249,7 +240,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
249
240
  displayName: metadata.displayName || selectedAuth,
250
241
  metadata,
251
242
  };
252
- // If we added a DB earlier in this flow, include it for grouped messaging
253
243
  if (typeof preAddedForReturn !== "undefined" && preAddedForReturn) {
254
244
  result.preAdded = [preAddedForReturn];
255
245
  }
@@ -257,7 +247,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
257
247
  }
258
248
  throw new Error("Invalid selection");
259
249
  }
260
- /* removed unused getProviderConfig — discovery-based flows handle providers */
261
250
  async function addModuleToProject(projectRoot, projectInfo, config, options) {
262
251
  const moduleMetadata = config.metadata;
263
252
  const selectedProvider = config.provider;
@@ -329,14 +318,11 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
329
318
  }
330
319
  }
331
320
  const selectedModules = { framework: projectInfo.framework };
332
- // Populate database context from detected project state so generator conditions work
333
321
  try {
334
322
  const pkg = await fs_extra_1.default.readJson(path_1.default.join(projectRoot, "package.json"));
335
323
  const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
336
324
  if (projectInfo.hasPrisma) {
337
325
  selectedModules.database = "prisma";
338
- // Parse provider specifically from the `datasource` block to avoid
339
- // capturing the generator block (which uses provider = "prisma-client-js").
340
326
  const prismaSchema = path_1.default.join(projectRoot, "prisma", "schema.prisma");
341
327
  if (await fs_extra_1.default.pathExists(prismaSchema)) {
342
328
  const content = await fs_extra_1.default.readFile(prismaSchema, "utf-8");
@@ -205,8 +205,6 @@ async function getProjectConfig(projectName, options) {
205
205
  return [{ name: "None", value: "none" }];
206
206
  },
207
207
  },
208
- // If a prisma-* choice is selected above, `prismaProvider` will be derived from it,
209
- // otherwise prompt for provider when `prisma` is selected directly.
210
208
  {
211
209
  type: "list",
212
210
  name: "prismaProvider",
@@ -222,7 +220,7 @@ async function getProjectConfig(projectName, options) {
222
220
  name: "auth",
223
221
  message: "Select authentication:",
224
222
  when: (answers) => answers.database !== "none" || answers.framework === "react",
225
- choices: (answers) => (0, module_discovery_1.getCompatibleAuthOptions)(discoveredModules.auth, answers.framework, answers.database || "none"),
223
+ choices: (answers) => (0, module_discovery_1.getCompatibleAuthOptions)(discoveredModules.auth, answers.framework, answers.database || "none", discoveredModules.frameworks),
226
224
  },
227
225
  {
228
226
  type: "list",
@@ -296,9 +296,6 @@ async function checkAuthRoutesExist(projectRoot) {
296
296
  catch {
297
297
  // ignore discovery errors
298
298
  }
299
- // Fallback to known common paths if generators don't provide any
300
- // If generators provided candidate routes, check them. Otherwise
301
- // do not assume any hard-coded route locations.
302
299
  if (candidates.size === 0)
303
300
  return false;
304
301
  for (const routePath of candidates) {
@@ -442,7 +439,6 @@ async function checkDependencies(packageJson) {
442
439
  // Assume up to date if using flexible versioning
443
440
  }
444
441
  else {
445
- // Assume outdated if using exact versions (simplified check)
446
442
  outdated.push(name);
447
443
  }
448
444
  }
package/dist/index.js CHANGED
@@ -78,7 +78,7 @@ function buildOptionHints() {
78
78
  }
79
79
  }
80
80
  catch {
81
- /* ignore */
81
+ // ignore malformed module.json
82
82
  }
83
83
  }
84
84
  }
@@ -93,7 +93,7 @@ function buildOptionHints() {
93
93
  auths.push(m.name);
94
94
  }
95
95
  catch {
96
- /* ignore */
96
+ // ignore malformed module.json
97
97
  }
98
98
  }
99
99
  }
@@ -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 logger_1 = require("../ui/logger");
10
11
  const package_root_1 = require("../utils/package-root");
11
12
  const baseDirs = {
12
13
  express: "./src",
@@ -41,6 +42,32 @@ async function convertToJavaScript(targetDir, framework) {
41
42
  };
42
43
  await removeDtsFiles(targetDir);
43
44
  const babel = require("@babel/core");
45
+ const checkModule = (name) => {
46
+ try {
47
+ require.resolve(name);
48
+ return true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ };
54
+ const hasRecast = checkModule("recast");
55
+ const hasBabelParser = checkModule("@babel/parser");
56
+ const hasTransformTypescript = checkModule("@babel/plugin-transform-typescript");
57
+ if (!hasRecast || !hasBabelParser || !hasTransformTypescript) {
58
+ const packageRoot = (0, package_root_1.getPackageRoot)();
59
+ const missing = [];
60
+ if (!hasRecast)
61
+ missing.push("recast");
62
+ if (!hasBabelParser)
63
+ missing.push("@babel/parser");
64
+ if (!hasTransformTypescript)
65
+ missing.push("@babel/plugin-transform-typescript");
66
+ logger_1.logger.warn(`Optional tooling missing: ${missing.join(", ")}. Generated JavaScript may contain transformed JSX.`);
67
+ if (process.env.STACKKIT_VERBOSE === "1" || process.env.DEBUG) {
68
+ logger_1.logger.info(`To ensure generated JavaScript preserves JSX, add these to the CLI package dependencies or run 'pnpm install' in ${packageRoot}`);
69
+ }
70
+ }
44
71
  const transpileAllTsFiles = async (dir) => {
45
72
  const entries = await fs_extra_1.default.readdir(dir, { withFileTypes: true });
46
73
  for (const entry of entries) {
@@ -73,20 +100,10 @@ async function convertToJavaScript(targetDir, framework) {
73
100
  },
74
101
  ],
75
102
  ];
76
- if (isTsx) {
77
- presets.push([
78
- require.resolve("@babel/preset-react"),
79
- {
80
- runtime: "automatic",
81
- },
82
- ]);
83
- }
84
- // Use recast + Babel AST transform (same approach as transform.tools)
85
103
  try {
86
104
  const recast = require("recast");
87
105
  const { transformFromAstSync } = require("@babel/core");
88
106
  const transformTypescript = require("@babel/plugin-transform-typescript");
89
- // getBabelOptions may be exported as default or directly
90
107
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
108
  let getBabelOptions = require("recast/parsers/_babel_options");
92
109
  if (getBabelOptions && getBabelOptions.default)
@@ -96,7 +113,6 @@ async function convertToJavaScript(targetDir, framework) {
96
113
  parser: {
97
114
  parse: (source, options) => {
98
115
  const babelOptions = getBabelOptions(options || {});
99
- // ensure typescript and jsx handling
100
116
  if (isTsx) {
101
117
  babelOptions.plugins.push("typescript", "jsx");
102
118
  }
@@ -44,7 +44,7 @@ async function collectModulePkgNames(modulePath) {
44
44
  }
45
45
  }
46
46
  catch {
47
- // ignore
47
+ // ignore malformed module.json
48
48
  }
49
49
  }
50
50
  return Array.from(new Set(pkgNames));
@@ -70,7 +70,7 @@ async function detectAuthModules(packageJson) {
70
70
  moduleName = m.name;
71
71
  }
72
72
  catch {
73
- /* ignore */
73
+ // ignore malformed module.json
74
74
  }
75
75
  }
76
76
  for (const pkg of pkgNames) {
@@ -111,7 +111,7 @@ async function detectDatabaseModules(packageJson) {
111
111
  moduleName = m.name;
112
112
  }
113
113
  catch {
114
- /* ignore */
114
+ // ignore malformed module.json
115
115
  }
116
116
  }
117
117
  for (const pkg of pkgNames) {
@@ -41,7 +41,7 @@ export declare function getValidAuthOptions(authModules: ModuleMetadata[]): stri
41
41
  /**
42
42
  * Get compatible auth options for given framework and database
43
43
  */
44
- export declare function getCompatibleAuthOptions(authModules: ModuleMetadata[], framework: string, database: string): Array<{
44
+ export declare function getCompatibleAuthOptions(authModules: ModuleMetadata[], framework: string, database: string, frameworksMeta?: ModuleMetadata[]): Array<{
45
45
  name: string;
46
46
  value: string;
47
47
  }>;
@@ -52,7 +52,7 @@ async function discoverModules(modulesDir) {
52
52
  });
53
53
  }
54
54
  catch {
55
- // Silently skip invalid templates
55
+ // ignore invalid template
56
56
  }
57
57
  }
58
58
  }
@@ -83,7 +83,7 @@ async function discoverModules(modulesDir) {
83
83
  discovered.databases.push(metadata);
84
84
  }
85
85
  catch {
86
- // Silently skip invalid modules
86
+ // ignore invalid module
87
87
  }
88
88
  }
89
89
  }
@@ -105,7 +105,7 @@ async function discoverModules(modulesDir) {
105
105
  discovered.auth.push(metadata);
106
106
  }
107
107
  catch {
108
- // Silently skip invalid modules
108
+ // ignore invalid module
109
109
  }
110
110
  }
111
111
  }
@@ -149,7 +149,7 @@ function getValidAuthOptions(authModules) {
149
149
  /**
150
150
  * Get compatible auth options for given framework and database
151
151
  */
152
- function getCompatibleAuthOptions(authModules, framework, database) {
152
+ function getCompatibleAuthOptions(authModules, framework, database, frameworksMeta) {
153
153
  const compatible = [];
154
154
  for (const auth of authModules) {
155
155
  // Check if auth supports the framework
@@ -159,10 +159,26 @@ function getCompatibleAuthOptions(authModules, framework, database) {
159
159
  // Normalize database option (handle prisma-<provider> values)
160
160
  const parsedDb = (0, shared_1.parseDatabaseOption)(database || "").database;
161
161
  // If module provides explicit compatibility matrix, use it
162
+ let dbCompatible = true;
162
163
  if (auth.compatibility && auth.compatibility.databases) {
163
- if (!auth.compatibility.databases.includes(parsedDb))
164
- continue;
164
+ dbCompatible = auth.compatibility.databases.includes(parsedDb);
165
165
  }
166
+ // If the framework template explicitly lists this auth as compatible,
167
+ // allow it even if the auth module's database compatibility would normally
168
+ // exclude the current database selection (covers cases like React where
169
+ // the framework can support auth without a DB).
170
+ let explicitlyAllowedByFramework = false;
171
+ if (frameworksMeta && Array.isArray(frameworksMeta)) {
172
+ const fw = frameworksMeta.find((f) => f.name === framework);
173
+ if (fw && fw.compatibility) {
174
+ const authList = fw.compatibility.auth;
175
+ if (Array.isArray(authList) && authList.includes(auth.name)) {
176
+ explicitlyAllowedByFramework = true;
177
+ }
178
+ }
179
+ }
180
+ if (!dbCompatible && !explicitlyAllowedByFramework)
181
+ continue;
166
182
  compatible.push({
167
183
  name: auth.displayName,
168
184
  value: auth.name,
@@ -62,7 +62,7 @@ class AdvancedCodeGenerator {
62
62
  this.generators.set(`${type}:${moduleName}`, config);
63
63
  }
64
64
  catch {
65
- // Silently skip invalid generator files
65
+ // ignore invalid generator files
66
66
  }
67
67
  }
68
68
  }
@@ -396,7 +396,7 @@ class AdvancedCodeGenerator {
396
396
  }
397
397
  }
398
398
  catch {
399
- // ignore failures here — not critical
399
+ // ignore (not critical)
400
400
  }
401
401
  // Ensure gitignore is present in target even if template authors
402
402
  // renamed it to avoid npm/package issues (e.g. 'gitignore' or '_gitignore')
@@ -453,7 +453,7 @@ class AdvancedCodeGenerator {
453
453
  }
454
454
  }
455
455
  catch {
456
- // ignore failures here
456
+ // ignore
457
457
  }
458
458
  try {
459
459
  const templateJsonPath2 = path.join(templatePath, "template.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stackkit",
3
- "version": "0.2.3",
3
+ "version": "0.2.6",
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": {
@@ -67,17 +67,17 @@
67
67
  "@babel/core": "^7.28.5",
68
68
  "@babel/preset-env": "^7.28.5",
69
69
  "@babel/preset-typescript": "^7.28.5",
70
- "@babel/preset-react": "^7.28.5"
70
+ "@babel/preset-react": "^7.28.5",
71
+ "recast": "^0.20.5",
72
+ "@babel/parser": "^7.28.5",
73
+ "@babel/plugin-transform-typescript": "^7.28.5",
74
+ "@babel/plugin-transform-react-jsx": "^7.27.1"
71
75
  },
72
76
  "devDependencies": {
73
77
  "@types/fs-extra": "^11.0.4",
74
78
  "@types/inquirer": "^9.0.7",
75
79
  "@types/node": "^25.0.8",
76
80
  "@types/validate-npm-package-name": "^4.0.2",
77
- "typescript": "^5.3.3",
78
- "recast": "^0.20.5",
79
- "@babel/plugin-transform-typescript": "^7.28.5",
80
- "@babel/parser": "^7.28.5",
81
- "@babel/plugin-transform-react-jsx": "^7.27.1"
81
+ "typescript": "^5.3.3"
82
82
  }
83
- }
83
+ }
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.