stackkit 0.2.2 → 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/bin/stackkit.js +10 -3
- package/dist/cli/add.js +188 -61
- package/dist/cli/create.js +75 -35
- package/dist/cli/doctor.js +36 -161
- package/dist/cli/list.js +10 -40
- package/dist/index.js +6 -6
- package/dist/lib/conversion/js-conversion.js +27 -11
- package/dist/lib/discovery/installed-detection.d.ts +7 -0
- package/dist/lib/discovery/installed-detection.js +134 -0
- package/dist/lib/discovery/module-discovery.d.ts +1 -1
- package/dist/lib/discovery/module-discovery.js +26 -17
- package/dist/lib/discovery/shared.js +9 -7
- package/dist/lib/generation/code-generator.d.ts +2 -0
- package/dist/lib/generation/code-generator.js +98 -4
- package/dist/lib/generation/generator-utils.d.ts +1 -1
- package/dist/lib/generation/generator-utils.js +5 -3
- package/dist/lib/project/detect.js +59 -29
- package/dist/types/index.d.ts +21 -17
- package/modules/auth/better-auth/files/lib/auth.ts +2 -2
- package/package.json +13 -10
- package/templates/express/gitignore +23 -0
- package/templates/nextjs/gitignore +42 -0
- package/templates/nextjs/next-env.d.ts +6 -0
- package/templates/react/dist/assets/index-D4AHT4dU.js +193 -0
- package/templates/react/dist/assets/index-rpwj5ZOX.css +1 -0
- package/templates/react/dist/index.html +14 -0
- package/templates/react/dist/vite.svg +1 -0
- package/templates/react/gitignore +44 -0
- package/templates/react/src/utils/utils.ts +1 -1
- /package/templates/express/{.env.example → env.example} +0 -0
- /package/templates/nextjs/{.env.example → env.example} +0 -0
- /package/templates/react/{.env.example → env.example} +0 -0
- /package/templates/react/{.prettierignore → prettierignore} +0 -0
- /package/templates/react/{.prettierrc → prettierrc} +0 -0
package/dist/cli/doctor.js
CHANGED
|
@@ -7,12 +7,13 @@ exports.doctorCommand = doctorCommand;
|
|
|
7
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
|
+
const installed_detection_1 = require("../lib/discovery/installed-detection");
|
|
10
11
|
const logger_1 = require("../lib/ui/logger");
|
|
11
12
|
const package_root_1 = require("../lib/utils/package-root");
|
|
12
13
|
// Constants for consistent messaging
|
|
13
14
|
const MESSAGES = {
|
|
14
15
|
NO_PACKAGE_JSON: "No package.json found in current directory or any parent directory.",
|
|
15
|
-
UNSUPPORTED_PROJECT: "Unsupported project type
|
|
16
|
+
UNSUPPORTED_PROJECT: "Unsupported project type or unable to detect framework.",
|
|
16
17
|
NODE_TOO_OLD: (version) => `Node.js version ${version} is not supported. Minimum required: Node 18.`,
|
|
17
18
|
NODE_WARNING: (version) => `Node.js version ${version} is supported but Node 20+ is recommended.`,
|
|
18
19
|
NODE_SUCCESS: (version) => `Node.js version ${version} is supported.`,
|
|
@@ -84,8 +85,8 @@ async function runDoctorChecks() {
|
|
|
84
85
|
}
|
|
85
86
|
const nodeVersionCheck = checkNodeVersion();
|
|
86
87
|
checks.push(nodeVersionCheck);
|
|
87
|
-
const authModules = detectAuthModules(packageJson);
|
|
88
|
-
const databaseModules = detectDatabaseModules(packageJson);
|
|
88
|
+
const authModules = await (0, installed_detection_1.detectAuthModules)(packageJson);
|
|
89
|
+
const databaseModules = await (0, installed_detection_1.detectDatabaseModules)(packageJson);
|
|
89
90
|
const filesCheck = await checkKeyFiles(projectRoot, projectType, authModules, databaseModules);
|
|
90
91
|
checks.push(...filesCheck);
|
|
91
92
|
const envCheck = await checkEnvFiles(projectRoot, authModules, databaseModules);
|
|
@@ -124,7 +125,7 @@ async function runDoctorChecks() {
|
|
|
124
125
|
prismaSchema: databaseModules.includes("prisma")
|
|
125
126
|
? await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"))
|
|
126
127
|
: undefined,
|
|
127
|
-
authRoutes: authModules.length > 0 ? await checkAuthRoutesExist(projectRoot
|
|
128
|
+
authRoutes: authModules.length > 0 ? await checkAuthRoutesExist(projectRoot) : undefined,
|
|
128
129
|
tsconfig: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "tsconfig.json")),
|
|
129
130
|
eslintConfig: await checkEslintConfigExists(projectRoot),
|
|
130
131
|
git: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".git")),
|
|
@@ -173,6 +174,29 @@ async function detectPackageManager(projectRoot) {
|
|
|
173
174
|
}
|
|
174
175
|
function detectProjectType(packageJson) {
|
|
175
176
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
177
|
+
// Try to detect using available templates
|
|
178
|
+
try {
|
|
179
|
+
const templatesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "templates");
|
|
180
|
+
if (fs_extra_1.default.existsSync(templatesDir)) {
|
|
181
|
+
const dirs = fs_extra_1.default.readdirSync(templatesDir);
|
|
182
|
+
for (const d of dirs) {
|
|
183
|
+
const tplPath = path_1.default.join(templatesDir, d, "template.json");
|
|
184
|
+
if (!fs_extra_1.default.existsSync(tplPath))
|
|
185
|
+
continue;
|
|
186
|
+
try {
|
|
187
|
+
const tpl = JSON.parse(fs_extra_1.default.readFileSync(tplPath, "utf-8"));
|
|
188
|
+
if (tpl && tpl.framework && deps[tpl.framework])
|
|
189
|
+
return tpl.framework;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// ignore
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// ignore template-based detection errors
|
|
199
|
+
}
|
|
176
200
|
if (deps.next) {
|
|
177
201
|
return "nextjs";
|
|
178
202
|
}
|
|
@@ -209,129 +233,7 @@ function checkNodeVersion() {
|
|
|
209
233
|
};
|
|
210
234
|
}
|
|
211
235
|
}
|
|
212
|
-
|
|
213
|
-
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
214
|
-
const modules = [];
|
|
215
|
-
try {
|
|
216
|
-
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "auth");
|
|
217
|
-
if (fs_extra_1.default.existsSync(modulesDir)) {
|
|
218
|
-
const authDirs = fs_extra_1.default.readdirSync(modulesDir);
|
|
219
|
-
for (const authDir of authDirs) {
|
|
220
|
-
try {
|
|
221
|
-
const genPath = path_1.default.join(modulesDir, authDir, "generator.json");
|
|
222
|
-
const modJson = path_1.default.join(modulesDir, authDir, "module.json");
|
|
223
|
-
let pkgNames = [];
|
|
224
|
-
if (fs_extra_1.default.existsSync(genPath)) {
|
|
225
|
-
const gen = JSON.parse(fs_extra_1.default.readFileSync(genPath, "utf-8"));
|
|
226
|
-
if (Array.isArray(gen.operations)) {
|
|
227
|
-
for (const op of gen.operations) {
|
|
228
|
-
if (op.dependencies && typeof op.dependencies === "object") {
|
|
229
|
-
pkgNames.push(...Object.keys(op.dependencies));
|
|
230
|
-
}
|
|
231
|
-
if (op.devDependencies && typeof op.devDependencies === "object") {
|
|
232
|
-
pkgNames.push(...Object.keys(op.devDependencies));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
// Fallback: check module.json provider/name
|
|
238
|
-
let moduleName = authDir;
|
|
239
|
-
if (fs_extra_1.default.existsSync(modJson)) {
|
|
240
|
-
try {
|
|
241
|
-
const m = JSON.parse(fs_extra_1.default.readFileSync(modJson, "utf-8"));
|
|
242
|
-
if (m && m.name)
|
|
243
|
-
moduleName = m.name;
|
|
244
|
-
}
|
|
245
|
-
catch {
|
|
246
|
-
/* ignore */
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
for (const pkg of pkgNames) {
|
|
250
|
-
if (deps[pkg]) {
|
|
251
|
-
modules.push(moduleName);
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
// ignore per-module errors
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
catch {
|
|
263
|
-
// ignore discovery errors
|
|
264
|
-
}
|
|
265
|
-
// Fallback to original simple checks if nothing found
|
|
266
|
-
if (modules.length === 0) {
|
|
267
|
-
if (deps["better-auth"])
|
|
268
|
-
modules.push("better-auth");
|
|
269
|
-
if (deps["next-auth"])
|
|
270
|
-
modules.push("authjs");
|
|
271
|
-
}
|
|
272
|
-
return Array.from(new Set(modules));
|
|
273
|
-
}
|
|
274
|
-
function detectDatabaseModules(packageJson) {
|
|
275
|
-
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
276
|
-
const modules = [];
|
|
277
|
-
try {
|
|
278
|
-
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "database");
|
|
279
|
-
if (fs_extra_1.default.existsSync(modulesDir)) {
|
|
280
|
-
const dbDirs = fs_extra_1.default.readdirSync(modulesDir);
|
|
281
|
-
for (const dbDir of dbDirs) {
|
|
282
|
-
try {
|
|
283
|
-
const genPath = path_1.default.join(modulesDir, dbDir, "generator.json");
|
|
284
|
-
const modJson = path_1.default.join(modulesDir, dbDir, "module.json");
|
|
285
|
-
let pkgNames = [];
|
|
286
|
-
if (fs_extra_1.default.existsSync(genPath)) {
|
|
287
|
-
const gen = JSON.parse(fs_extra_1.default.readFileSync(genPath, "utf-8"));
|
|
288
|
-
if (Array.isArray(gen.operations)) {
|
|
289
|
-
for (const op of gen.operations) {
|
|
290
|
-
if (op.dependencies && typeof op.dependencies === "object") {
|
|
291
|
-
pkgNames.push(...Object.keys(op.dependencies));
|
|
292
|
-
}
|
|
293
|
-
if (op.devDependencies && typeof op.devDependencies === "object") {
|
|
294
|
-
pkgNames.push(...Object.keys(op.devDependencies));
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
let moduleName = dbDir;
|
|
300
|
-
if (fs_extra_1.default.existsSync(modJson)) {
|
|
301
|
-
try {
|
|
302
|
-
const m = JSON.parse(fs_extra_1.default.readFileSync(modJson, "utf-8"));
|
|
303
|
-
if (m && m.name)
|
|
304
|
-
moduleName = m.name;
|
|
305
|
-
}
|
|
306
|
-
catch {
|
|
307
|
-
/* ignore */
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
for (const pkg of pkgNames) {
|
|
311
|
-
if (deps[pkg]) {
|
|
312
|
-
modules.push(moduleName);
|
|
313
|
-
break;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
catch {
|
|
318
|
-
// ignore per-module errors
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
catch {
|
|
324
|
-
// ignore discovery errors
|
|
325
|
-
}
|
|
326
|
-
// Fallback to original checks if nothing found
|
|
327
|
-
if (modules.length === 0) {
|
|
328
|
-
if (deps["@prisma/client"] || deps["prisma"])
|
|
329
|
-
modules.push("prisma");
|
|
330
|
-
if (deps["mongoose"])
|
|
331
|
-
modules.push("mongoose");
|
|
332
|
-
}
|
|
333
|
-
return Array.from(new Set(modules));
|
|
334
|
-
}
|
|
236
|
+
// Module detection is delegated to shared utilities in `lib/discovery`.
|
|
335
237
|
async function checkKeyFiles(projectRoot, projectType, authModules, databaseModules) {
|
|
336
238
|
const checks = [];
|
|
337
239
|
const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
|
|
@@ -348,8 +250,8 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
|
|
|
348
250
|
message: schemaExists ? "Prisma schema found" : "Prisma schema missing (required for Prisma)",
|
|
349
251
|
});
|
|
350
252
|
}
|
|
351
|
-
if (authModules.length > 0
|
|
352
|
-
const authRoutesExist = await checkAuthRoutesExist(projectRoot
|
|
253
|
+
if (authModules.length > 0) {
|
|
254
|
+
const authRoutesExist = await checkAuthRoutesExist(projectRoot);
|
|
353
255
|
checks.push({
|
|
354
256
|
status: authRoutesExist ? "success" : "warning",
|
|
355
257
|
message: authRoutesExist
|
|
@@ -359,9 +261,7 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
|
|
|
359
261
|
}
|
|
360
262
|
return checks;
|
|
361
263
|
}
|
|
362
|
-
async function checkAuthRoutesExist(projectRoot
|
|
363
|
-
if (projectType !== "nextjs")
|
|
364
|
-
return true; // Skip for non-Next.js
|
|
264
|
+
async function checkAuthRoutesExist(projectRoot) {
|
|
365
265
|
// Build candidate auth route paths from generator.json files in modules/auth
|
|
366
266
|
const candidates = new Set();
|
|
367
267
|
try {
|
|
@@ -396,23 +296,8 @@ async function checkAuthRoutesExist(projectRoot, projectType) {
|
|
|
396
296
|
catch {
|
|
397
297
|
// ignore discovery errors
|
|
398
298
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
"app/api/auth/[...nextauth]/route.ts",
|
|
402
|
-
"app/api/auth/[...nextauth]/route.js",
|
|
403
|
-
"src/app/api/auth/[...nextauth]/route.ts",
|
|
404
|
-
"src/app/api/auth/[...nextauth]/route.js",
|
|
405
|
-
"pages/api/auth/[...nextauth].ts",
|
|
406
|
-
"pages/api/auth/[...nextauth].js",
|
|
407
|
-
"src/pages/api/auth/[...nextauth].ts",
|
|
408
|
-
"src/pages/api/auth/[...nextauth].js",
|
|
409
|
-
"app/api/auth/[...all]/route.ts",
|
|
410
|
-
"app/api/auth/[...all]/route.js",
|
|
411
|
-
"src/app/api/auth/[...all]/route.ts",
|
|
412
|
-
"src/app/api/auth/[...all]/route.js",
|
|
413
|
-
];
|
|
414
|
-
for (const p of fallback)
|
|
415
|
-
candidates.add(p);
|
|
299
|
+
if (candidates.size === 0)
|
|
300
|
+
return false;
|
|
416
301
|
for (const routePath of candidates) {
|
|
417
302
|
if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, routePath))) {
|
|
418
303
|
return true;
|
|
@@ -468,16 +353,7 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
|
|
|
468
353
|
}
|
|
469
354
|
}
|
|
470
355
|
catch {
|
|
471
|
-
//
|
|
472
|
-
if (databaseModules.includes("prisma")) {
|
|
473
|
-
requiredKeys.push("DATABASE_URL");
|
|
474
|
-
}
|
|
475
|
-
if (authModules.includes("authjs")) {
|
|
476
|
-
requiredKeys.push("NEXTAUTH_SECRET", "NEXTAUTH_URL");
|
|
477
|
-
}
|
|
478
|
-
if (authModules.includes("better-auth")) {
|
|
479
|
-
requiredKeys.push("BETTER_AUTH_SECRET", "BETTER_AUTH_URL");
|
|
480
|
-
}
|
|
356
|
+
// If discovery fails, keep requiredKeys empty rather than using hard-coded defaults.
|
|
481
357
|
}
|
|
482
358
|
const envPaths = [".env", ".env.local"];
|
|
483
359
|
let envContent = "";
|
|
@@ -563,7 +439,6 @@ async function checkDependencies(packageJson) {
|
|
|
563
439
|
// Assume up to date if using flexible versioning
|
|
564
440
|
}
|
|
565
441
|
else {
|
|
566
|
-
// Assume outdated if using exact versions (simplified check)
|
|
567
442
|
outdated.push(name);
|
|
568
443
|
}
|
|
569
444
|
}
|
|
@@ -603,7 +478,7 @@ function generateSuggestions(authModules, databaseModules) {
|
|
|
603
478
|
suggestions.push("stackkit add auth - Add authentication module");
|
|
604
479
|
}
|
|
605
480
|
if (databaseModules.length === 0) {
|
|
606
|
-
suggestions.push("stackkit add
|
|
481
|
+
suggestions.push("stackkit add database - Add database module");
|
|
607
482
|
}
|
|
608
483
|
// Always show available commands
|
|
609
484
|
suggestions.push("stackkit list - View available modules");
|
package/dist/cli/list.js
CHANGED
|
@@ -7,7 +7,6 @@ exports.listCommand = listCommand;
|
|
|
7
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
|
-
const module_discovery_1 = require("../lib/discovery/module-discovery");
|
|
11
10
|
const shared_1 = require("../lib/discovery/shared");
|
|
12
11
|
const logger_1 = require("../lib/ui/logger");
|
|
13
12
|
const package_root_1 = require("../lib/utils/package-root");
|
|
@@ -28,7 +27,7 @@ async function listCommand(options) {
|
|
|
28
27
|
frameworks.forEach((framework, index) => {
|
|
29
28
|
const isLast = index === frameworks.length - 1;
|
|
30
29
|
const prefix = isLast ? "└──" : "├──";
|
|
31
|
-
logger_1.logger.log(` ${chalk_1.default.gray(prefix)} ${chalk_1.default.cyan(framework.displayName)}`);
|
|
30
|
+
logger_1.logger.log(` ${chalk_1.default.gray(prefix)} ${chalk_1.default.cyan(framework.displayName || framework.name)}`);
|
|
32
31
|
});
|
|
33
32
|
logger_1.logger.newLine();
|
|
34
33
|
}
|
|
@@ -36,22 +35,14 @@ async function listCommand(options) {
|
|
|
36
35
|
if (showModules) {
|
|
37
36
|
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
38
37
|
const modules = await getAvailableModules(modulesDir);
|
|
39
|
-
// Discover modules to derive provider lists dynamically
|
|
40
|
-
let discovered;
|
|
41
|
-
try {
|
|
42
|
-
discovered = await (0, module_discovery_1.discoverModules)(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"));
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
discovered = { frameworks: [], databases: [], auth: [] };
|
|
46
|
-
}
|
|
47
38
|
if (modules.length > 0) {
|
|
48
39
|
hasModules = true;
|
|
49
40
|
logger_1.logger.log(chalk_1.default.bold.magenta("MODULES"));
|
|
50
41
|
const grouped = modules.reduce((acc, mod) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
acc[
|
|
42
|
+
const cat = mod.category || "other";
|
|
43
|
+
if (!acc[cat])
|
|
44
|
+
acc[cat] = [];
|
|
45
|
+
acc[cat].push(mod);
|
|
55
46
|
return acc;
|
|
56
47
|
}, {});
|
|
57
48
|
const categories = Object.keys(grouped);
|
|
@@ -78,32 +69,11 @@ async function listCommand(options) {
|
|
|
78
69
|
: isLastMod
|
|
79
70
|
? "│ └──"
|
|
80
71
|
: "│ ├──";
|
|
81
|
-
//
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
const m = c.name.match(/\(([^)]+)\)/);
|
|
87
|
-
return m ? m[1] : c.name;
|
|
88
|
-
});
|
|
89
|
-
const providersText = prismaProviders.length > 0
|
|
90
|
-
? prismaProviders.join(", ")
|
|
91
|
-
: (() => {
|
|
92
|
-
const detected = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)()).map((p) => {
|
|
93
|
-
if (p === "postgresql")
|
|
94
|
-
return "PostgreSQL";
|
|
95
|
-
if (p === "mongodb")
|
|
96
|
-
return "MongoDB";
|
|
97
|
-
if (p === "mysql")
|
|
98
|
-
return "MySQL";
|
|
99
|
-
if (p === "sqlite")
|
|
100
|
-
return "SQLite";
|
|
101
|
-
return p;
|
|
102
|
-
});
|
|
103
|
-
return detected.length > 0
|
|
104
|
-
? detected.join(", ")
|
|
105
|
-
: "PostgreSQL, MongoDB, MySQL, SQLite";
|
|
106
|
-
})();
|
|
72
|
+
// Derive provider list directly from the Prisma generator metadata
|
|
73
|
+
const providers = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)());
|
|
74
|
+
const providersText = providers.length
|
|
75
|
+
? providers.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(", ")
|
|
76
|
+
: "None";
|
|
107
77
|
logger_1.logger.log(` ${chalk_1.default.gray(providerPrefix)} ${chalk_1.default.dim(`Providers: ${providersText}`)}`);
|
|
108
78
|
}
|
|
109
79
|
});
|
package/dist/index.js
CHANGED
|
@@ -78,7 +78,7 @@ function buildOptionHints() {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
catch {
|
|
81
|
-
|
|
81
|
+
// ignore malformed module.json
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -93,18 +93,18 @@ function buildOptionHints() {
|
|
|
93
93
|
auths.push(m.name);
|
|
94
94
|
}
|
|
95
95
|
catch {
|
|
96
|
-
|
|
96
|
+
// ignore malformed module.json
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
return {
|
|
102
|
-
databaseHint: dbs.length > 0 ? dbs.join(", ") : "
|
|
103
|
-
authHint: auths.length > 0 ? auths.join(", ") : "
|
|
102
|
+
databaseHint: dbs.length > 0 ? dbs.join(", ") : "none",
|
|
103
|
+
authHint: auths.length > 0 ? auths.join(", ") : "none",
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
catch {
|
|
107
|
-
return { databaseHint: "
|
|
107
|
+
return { databaseHint: "none", authHint: "none" };
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
const hints = buildOptionHints();
|
|
@@ -134,7 +134,7 @@ program
|
|
|
134
134
|
.command("create [project-name]")
|
|
135
135
|
.description("Create a new StackKit project")
|
|
136
136
|
.usage("[project-name] [options]")
|
|
137
|
-
.option("-f, --framework <framework>", "Framework
|
|
137
|
+
.option("-f, --framework <framework>", "Framework (discovered)")
|
|
138
138
|
.option("-d, --database <database>", `Database: ${hints.databaseHint}`)
|
|
139
139
|
.option("--prisma-provider <provider>", "Prisma provider")
|
|
140
140
|
.option("-a, --auth <auth>", `Auth: ${hints.authHint}`)
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface PackageJsonLike {
|
|
2
|
+
dependencies?: Record<string, string>;
|
|
3
|
+
devDependencies?: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
export declare function detectAuthModules(packageJson: PackageJsonLike): Promise<string[]>;
|
|
6
|
+
export declare function detectDatabaseModules(packageJson: PackageJsonLike): Promise<string[]>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectAuthModules = detectAuthModules;
|
|
7
|
+
exports.detectDatabaseModules = detectDatabaseModules;
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const package_root_1 = require("../utils/package-root");
|
|
11
|
+
async function collectModulePkgNames(modulePath) {
|
|
12
|
+
const pkgNames = [];
|
|
13
|
+
const genPath = path_1.default.join(modulePath, "generator.json");
|
|
14
|
+
if (await fs_extra_1.default.pathExists(genPath)) {
|
|
15
|
+
try {
|
|
16
|
+
const gen = await fs_extra_1.default.readJson(genPath);
|
|
17
|
+
if (Array.isArray(gen.operations)) {
|
|
18
|
+
for (const op of gen.operations) {
|
|
19
|
+
if (op.dependencies && typeof op.dependencies === "object") {
|
|
20
|
+
pkgNames.push(...Object.keys(op.dependencies));
|
|
21
|
+
}
|
|
22
|
+
if (op.devDependencies && typeof op.devDependencies === "object") {
|
|
23
|
+
pkgNames.push(...Object.keys(op.devDependencies));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// ignore malformed generator
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Also check module.json for declared deps
|
|
33
|
+
const modJson = path_1.default.join(modulePath, "module.json");
|
|
34
|
+
if (await fs_extra_1.default.pathExists(modJson)) {
|
|
35
|
+
try {
|
|
36
|
+
const m = (await fs_extra_1.default.readJson(modJson));
|
|
37
|
+
if (m && typeof m === "object") {
|
|
38
|
+
const deps = m.dependencies || {};
|
|
39
|
+
if (typeof deps === "object")
|
|
40
|
+
pkgNames.push(...Object.keys(deps));
|
|
41
|
+
const devDeps = m.devDependencies || {};
|
|
42
|
+
if (typeof devDeps === "object")
|
|
43
|
+
pkgNames.push(...Object.keys(devDeps));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// ignore malformed module.json
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return Array.from(new Set(pkgNames));
|
|
51
|
+
}
|
|
52
|
+
async function detectAuthModules(packageJson) {
|
|
53
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
54
|
+
const modules = [];
|
|
55
|
+
try {
|
|
56
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "auth");
|
|
57
|
+
if (await fs_extra_1.default.pathExists(modulesDir)) {
|
|
58
|
+
const authDirs = await fs_extra_1.default.readdir(modulesDir);
|
|
59
|
+
for (const authDir of authDirs) {
|
|
60
|
+
try {
|
|
61
|
+
const modulePath = path_1.default.join(modulesDir, authDir);
|
|
62
|
+
const pkgNames = await collectModulePkgNames(modulePath);
|
|
63
|
+
// Fallback: check module.json provider/name for display
|
|
64
|
+
let moduleName = authDir;
|
|
65
|
+
const modJson = path_1.default.join(modulePath, "module.json");
|
|
66
|
+
if (await fs_extra_1.default.pathExists(modJson)) {
|
|
67
|
+
try {
|
|
68
|
+
const m = (await fs_extra_1.default.readJson(modJson));
|
|
69
|
+
if (m && m.name)
|
|
70
|
+
moduleName = m.name;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// ignore malformed module.json
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const pkg of pkgNames) {
|
|
77
|
+
if (deps[pkg]) {
|
|
78
|
+
modules.push(moduleName);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ignore per-module errors
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// ignore discovery errors
|
|
91
|
+
}
|
|
92
|
+
return Array.from(new Set(modules));
|
|
93
|
+
}
|
|
94
|
+
async function detectDatabaseModules(packageJson) {
|
|
95
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
96
|
+
const modules = [];
|
|
97
|
+
try {
|
|
98
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "database");
|
|
99
|
+
if (await fs_extra_1.default.pathExists(modulesDir)) {
|
|
100
|
+
const dbDirs = await fs_extra_1.default.readdir(modulesDir);
|
|
101
|
+
for (const dbDir of dbDirs) {
|
|
102
|
+
try {
|
|
103
|
+
const modulePath = path_1.default.join(modulesDir, dbDir);
|
|
104
|
+
const pkgNames = await collectModulePkgNames(modulePath);
|
|
105
|
+
let moduleName = dbDir;
|
|
106
|
+
const modJson = path_1.default.join(modulePath, "module.json");
|
|
107
|
+
if (await fs_extra_1.default.pathExists(modJson)) {
|
|
108
|
+
try {
|
|
109
|
+
const m = await fs_extra_1.default.readJson(modJson);
|
|
110
|
+
if (m && m.name)
|
|
111
|
+
moduleName = m.name;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// ignore malformed module.json
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const pkg of pkgNames) {
|
|
118
|
+
if (deps[pkg]) {
|
|
119
|
+
modules.push(moduleName);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// ignore per-module errors
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// ignore discovery errors
|
|
132
|
+
}
|
|
133
|
+
return Array.from(new Set(modules));
|
|
134
|
+
}
|
|
@@ -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
|
}>;
|