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
|
@@ -52,7 +52,7 @@ async function discoverModules(modulesDir) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
catch {
|
|
55
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
108
|
+
// ignore invalid module
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -128,11 +128,8 @@ function getValidDatabaseOptions(databases) {
|
|
|
128
128
|
options.push("prisma");
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
else if (db.name === "mongoose") {
|
|
132
|
-
options.push("mongoose");
|
|
133
|
-
}
|
|
134
131
|
else {
|
|
135
|
-
// For other databases, add the name directly
|
|
132
|
+
// For other databases, add the module name directly (generic handling)
|
|
136
133
|
options.push(db.name);
|
|
137
134
|
}
|
|
138
135
|
}
|
|
@@ -152,7 +149,7 @@ function getValidAuthOptions(authModules) {
|
|
|
152
149
|
/**
|
|
153
150
|
* Get compatible auth options for given framework and database
|
|
154
151
|
*/
|
|
155
|
-
function getCompatibleAuthOptions(authModules, framework, database) {
|
|
152
|
+
function getCompatibleAuthOptions(authModules, framework, database, frameworksMeta) {
|
|
156
153
|
const compatible = [];
|
|
157
154
|
for (const auth of authModules) {
|
|
158
155
|
// Check if auth supports the framework
|
|
@@ -161,13 +158,27 @@ function getCompatibleAuthOptions(authModules, framework, database) {
|
|
|
161
158
|
}
|
|
162
159
|
// Normalize database option (handle prisma-<provider> values)
|
|
163
160
|
const parsedDb = (0, shared_1.parseDatabaseOption)(database || "").database;
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
// If module provides explicit compatibility matrix, use it
|
|
162
|
+
let dbCompatible = true;
|
|
163
|
+
if (auth.compatibility && auth.compatibility.databases) {
|
|
164
|
+
dbCompatible = auth.compatibility.databases.includes(parsedDb);
|
|
167
165
|
}
|
|
168
|
-
|
|
169
|
-
|
|
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
|
+
}
|
|
170
179
|
}
|
|
180
|
+
if (!dbCompatible && !explicitlyAllowedByFramework)
|
|
181
|
+
continue;
|
|
171
182
|
compatible.push({
|
|
172
183
|
name: auth.displayName,
|
|
173
184
|
value: auth.name,
|
|
@@ -200,11 +211,9 @@ function getDatabaseChoices(databases, framework) {
|
|
|
200
211
|
choices.push({ name: "Prisma", value: "prisma" });
|
|
201
212
|
}
|
|
202
213
|
}
|
|
203
|
-
else if (db.name === "mongoose") {
|
|
204
|
-
choices.push({ name: "Mongoose", value: "mongoose" });
|
|
205
|
-
}
|
|
206
214
|
else {
|
|
207
|
-
|
|
215
|
+
// Generic handling for other database modules
|
|
216
|
+
choices.push({ name: db.displayName || db.name, value: db.name });
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
// Add "None" at the end
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.parseDatabaseOption = parseDatabaseOption;
|
|
7
7
|
exports.getPrismaProvidersFromGenerator = getPrismaProvidersFromGenerator;
|
|
8
8
|
exports.isPrismaOption = isPrismaOption;
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const package_root_1 = require("../utils/package-root");
|
|
11
12
|
function parseDatabaseOption(dbOption) {
|
|
@@ -19,20 +20,21 @@ function parseDatabaseOption(dbOption) {
|
|
|
19
20
|
}
|
|
20
21
|
if (dbOption === "prisma")
|
|
21
22
|
return { database: "prisma" };
|
|
22
|
-
if (dbOption === "mongoose")
|
|
23
|
-
return { database: "mongoose" };
|
|
24
23
|
return { database: dbOption };
|
|
25
24
|
}
|
|
26
25
|
function getPrismaProvidersFromGenerator(modulesDir) {
|
|
27
26
|
const pkgRoot = modulesDir || (0, package_root_1.getPackageRoot)();
|
|
28
27
|
const genPath = path_1.default.join(pkgRoot, "modules", "database", "prisma", "generator.json");
|
|
29
28
|
try {
|
|
30
|
-
const gen =
|
|
29
|
+
const gen = fs_extra_1.default.readJsonSync(genPath, { throws: false });
|
|
31
30
|
const providers = new Set();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const ops = gen && typeof gen === "object" ? gen["operations"] : undefined;
|
|
32
|
+
if (Array.isArray(ops)) {
|
|
33
|
+
for (const op of ops) {
|
|
34
|
+
const cond = op && typeof op === "object" ? op["condition"] : undefined;
|
|
35
|
+
if (cond && typeof cond["prismaProvider"] === "string") {
|
|
36
|
+
providers.add(String(cond["prismaProvider"]));
|
|
37
|
+
}
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
return Array.from(providers);
|
|
@@ -53,6 +53,7 @@ export declare class AdvancedCodeGenerator {
|
|
|
53
53
|
private generators;
|
|
54
54
|
private frameworkConfig;
|
|
55
55
|
private postInstallCommands;
|
|
56
|
+
private createdFiles;
|
|
56
57
|
constructor(frameworkConfig: FrameworkConfig);
|
|
57
58
|
loadGenerators(modulesPath: string): Promise<void>;
|
|
58
59
|
private evaluateCondition;
|
|
@@ -65,6 +66,7 @@ export declare class AdvancedCodeGenerator {
|
|
|
65
66
|
auth?: string;
|
|
66
67
|
prismaProvider?: string;
|
|
67
68
|
}, features: string[], outputPath: string): Promise<string[]>;
|
|
69
|
+
getCreatedFiles(): string[];
|
|
68
70
|
private executeOperation;
|
|
69
71
|
private copyTemplate;
|
|
70
72
|
private processOperationTemplates;
|
|
@@ -36,12 +36,14 @@ 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 shared_1 = require("../discovery/shared");
|
|
39
40
|
const package_root_1 = require("../utils/package-root");
|
|
40
41
|
const generator_utils_1 = require("./generator-utils");
|
|
41
42
|
class AdvancedCodeGenerator {
|
|
42
43
|
constructor(frameworkConfig) {
|
|
43
44
|
this.generators = new Map();
|
|
44
45
|
this.postInstallCommands = [];
|
|
46
|
+
this.createdFiles = [];
|
|
45
47
|
this.frameworkConfig = frameworkConfig;
|
|
46
48
|
}
|
|
47
49
|
async loadGenerators(modulesPath) {
|
|
@@ -60,7 +62,7 @@ class AdvancedCodeGenerator {
|
|
|
60
62
|
this.generators.set(`${type}:${moduleName}`, config);
|
|
61
63
|
}
|
|
62
64
|
catch {
|
|
63
|
-
//
|
|
65
|
+
// ignore invalid generator files
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -290,7 +292,10 @@ class AdvancedCodeGenerator {
|
|
|
290
292
|
};
|
|
291
293
|
// Set default prismaProvider if database is prisma but no provider specified
|
|
292
294
|
if (selectedModules.database === "prisma" && !context.prismaProvider) {
|
|
293
|
-
|
|
295
|
+
const providers = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)());
|
|
296
|
+
if (providers && providers.length > 0) {
|
|
297
|
+
context.prismaProvider = providers[0];
|
|
298
|
+
}
|
|
294
299
|
}
|
|
295
300
|
// Collect all applicable operations
|
|
296
301
|
const applicableOperations = [];
|
|
@@ -340,6 +345,9 @@ class AdvancedCodeGenerator {
|
|
|
340
345
|
await this.generatePackageJson(selectedModules, features, outputPath);
|
|
341
346
|
return this.postInstallCommands;
|
|
342
347
|
}
|
|
348
|
+
getCreatedFiles() {
|
|
349
|
+
return this.createdFiles.slice();
|
|
350
|
+
}
|
|
343
351
|
async executeOperation(operation, context, outputPath) {
|
|
344
352
|
// Process templates in operation content
|
|
345
353
|
const processedOperation = this.processOperationTemplates(operation, context);
|
|
@@ -388,7 +396,7 @@ class AdvancedCodeGenerator {
|
|
|
388
396
|
}
|
|
389
397
|
}
|
|
390
398
|
catch {
|
|
391
|
-
// ignore
|
|
399
|
+
// ignore (not critical)
|
|
392
400
|
}
|
|
393
401
|
// Ensure gitignore is present in target even if template authors
|
|
394
402
|
// renamed it to avoid npm/package issues (e.g. 'gitignore' or '_gitignore')
|
|
@@ -408,6 +416,81 @@ class AdvancedCodeGenerator {
|
|
|
408
416
|
catch {
|
|
409
417
|
// ignore
|
|
410
418
|
}
|
|
419
|
+
// Ensure any top-level dotfiles declared in template.json are created
|
|
420
|
+
try {
|
|
421
|
+
const templateJsonPath = path.join(templatePath, "template.json");
|
|
422
|
+
if (await fs.pathExists(templateJsonPath)) {
|
|
423
|
+
const tpl = await fs.readJson(templateJsonPath);
|
|
424
|
+
if (tpl && Array.isArray(tpl.files)) {
|
|
425
|
+
for (const f of tpl.files) {
|
|
426
|
+
if (typeof f === "string" && f.startsWith(".")) {
|
|
427
|
+
const targetDest = path.join(outputPath, f);
|
|
428
|
+
if (await fs.pathExists(targetDest))
|
|
429
|
+
continue; // already present
|
|
430
|
+
// Special-case: allow creating .gitignore from non-dot fallbacks
|
|
431
|
+
if (f === ".gitignore") {
|
|
432
|
+
const nameWithoutDot = f.slice(1);
|
|
433
|
+
const candidates = [f, nameWithoutDot, `_${nameWithoutDot}`];
|
|
434
|
+
for (const cand of candidates) {
|
|
435
|
+
const src = path.join(templatePath, cand);
|
|
436
|
+
if (await fs.pathExists(src)) {
|
|
437
|
+
await fs.copy(src, targetDest);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
// For other dotfiles (e.g. .env.example), only create if the template
|
|
444
|
+
// actually contains a dotfile source. Don't synthesize from non-dot fallbacks
|
|
445
|
+
// to avoid creating files unintentionally.
|
|
446
|
+
const srcDot = path.join(templatePath, f);
|
|
447
|
+
if (await fs.pathExists(srcDot)) {
|
|
448
|
+
await fs.copy(srcDot, targetDest);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// ignore
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
const templateJsonPath2 = path.join(templatePath, "template.json");
|
|
460
|
+
if (await fs.pathExists(templateJsonPath2)) {
|
|
461
|
+
const tpl = await fs.readJson(templateJsonPath2);
|
|
462
|
+
if (tpl && Array.isArray(tpl.files)) {
|
|
463
|
+
for (const f of tpl.files) {
|
|
464
|
+
if (typeof f === "string" && f.startsWith(".")) {
|
|
465
|
+
const dotDest = path.join(outputPath, f);
|
|
466
|
+
const nameWithoutDot = f.slice(1);
|
|
467
|
+
const nonDot = path.join(outputPath, nameWithoutDot);
|
|
468
|
+
const underscore = path.join(outputPath, `_${nameWithoutDot}`);
|
|
469
|
+
// If dot already exists, remove non-dot fallbacks
|
|
470
|
+
if (await fs.pathExists(dotDest)) {
|
|
471
|
+
if (await fs.pathExists(nonDot)) {
|
|
472
|
+
await fs.remove(nonDot);
|
|
473
|
+
}
|
|
474
|
+
if (await fs.pathExists(underscore)) {
|
|
475
|
+
await fs.remove(underscore);
|
|
476
|
+
}
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
// If dot doesn't exist but a non-dot fallback was copied, rename it
|
|
480
|
+
if (await fs.pathExists(nonDot)) {
|
|
481
|
+
await fs.move(nonDot, dotDest, { overwrite: true });
|
|
482
|
+
}
|
|
483
|
+
else if (await fs.pathExists(underscore)) {
|
|
484
|
+
await fs.move(underscore, dotDest, { overwrite: true });
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
// ignore
|
|
493
|
+
}
|
|
411
494
|
}
|
|
412
495
|
}
|
|
413
496
|
processOperationTemplates(operation, context) {
|
|
@@ -481,6 +564,14 @@ class AdvancedCodeGenerator {
|
|
|
481
564
|
}
|
|
482
565
|
// Write destination file
|
|
483
566
|
await fs.writeFile(destinationPath, content, "utf-8");
|
|
567
|
+
try {
|
|
568
|
+
const rel = path.relative(outputPath, destinationPath);
|
|
569
|
+
if (rel && !this.createdFiles.includes(rel))
|
|
570
|
+
this.createdFiles.push(rel);
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
// ignore logging failures
|
|
574
|
+
}
|
|
484
575
|
}
|
|
485
576
|
async executePatchFile(operation, context, outputPath) {
|
|
486
577
|
if (!operation.destination)
|
|
@@ -755,7 +846,10 @@ class AdvancedCodeGenerator {
|
|
|
755
846
|
features,
|
|
756
847
|
};
|
|
757
848
|
if (selectedModules.database === "prisma" && !context.prismaProvider) {
|
|
758
|
-
|
|
849
|
+
const providers = (0, shared_1.getPrismaProvidersFromGenerator)((0, package_root_1.getPackageRoot)());
|
|
850
|
+
if (providers && providers.length > 0) {
|
|
851
|
+
context.prismaProvider = providers[0];
|
|
852
|
+
}
|
|
759
853
|
}
|
|
760
854
|
const applicableOperations = [];
|
|
761
855
|
for (const [key, generator] of this.generators) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { GeneratorConfig } from "./code-generator";
|
|
2
1
|
import type { ModuleMetadata } from "../../types";
|
|
2
|
+
import type { GeneratorConfig } from "./code-generator";
|
|
3
3
|
export declare function mergeModuleIntoGeneratorConfig(config: GeneratorConfig, modulePath: string): Promise<GeneratorConfig>;
|
|
4
4
|
export declare function mergeGeneratorIntoModuleMetadata(metadata: ModuleMetadata, modulePath: string): Promise<ModuleMetadata>;
|
|
5
5
|
export declare function locateOperationSource(generatorType: string, generatorName: string, sourceRel: string): string | null;
|
|
@@ -78,11 +78,13 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
|
|
|
78
78
|
if (generator.operations && Array.isArray(generator.operations)) {
|
|
79
79
|
for (const operation of generator.operations) {
|
|
80
80
|
if (operation.type === "add-env" && operation.envVars) {
|
|
81
|
-
|
|
81
|
+
if (!Array.isArray(metadata.envVars))
|
|
82
|
+
metadata.envVars = [];
|
|
83
|
+
const arr = metadata.envVars;
|
|
82
84
|
for (const [key, value] of Object.entries(operation.envVars)) {
|
|
83
|
-
|
|
85
|
+
arr.push({
|
|
84
86
|
key,
|
|
85
|
-
value: value,
|
|
87
|
+
value: String(value),
|
|
86
88
|
description: `Environment variable for ${key}`,
|
|
87
89
|
required: true,
|
|
88
90
|
});
|
|
@@ -8,35 +8,68 @@ exports.getRouterBasePath = getRouterBasePath;
|
|
|
8
8
|
exports.getLibPath = getLibPath;
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const installed_detection_1 = require("../discovery/installed-detection");
|
|
12
|
+
const package_root_1 = require("../utils/package-root");
|
|
11
13
|
async function detectProjectInfo(targetDir) {
|
|
12
14
|
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
13
15
|
if (!(await fs_extra_1.default.pathExists(packageJsonPath))) {
|
|
14
16
|
throw new Error("No package.json found. This does not appear to be a Node.js project.");
|
|
15
17
|
}
|
|
16
18
|
const packageJson = await fs_extra_1.default.readJSON(packageJsonPath);
|
|
17
|
-
// Detect framework
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
// Detect framework by matching available templates' characteristic files
|
|
20
|
+
// Framework is dynamic and driven by templates; keep as string for discovery
|
|
21
|
+
let framework = "unknown";
|
|
22
|
+
try {
|
|
23
|
+
const templatesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "templates");
|
|
24
|
+
if (await fs_extra_1.default.pathExists(templatesDir)) {
|
|
25
|
+
const dirs = await fs_extra_1.default.readdir(templatesDir);
|
|
26
|
+
let bestMatch = null;
|
|
27
|
+
for (const d of dirs) {
|
|
28
|
+
const tplPath = path_1.default.join(templatesDir, d, "template.json");
|
|
29
|
+
if (!(await fs_extra_1.default.pathExists(tplPath)))
|
|
30
|
+
continue;
|
|
31
|
+
try {
|
|
32
|
+
const tpl = await fs_extra_1.default.readJSON(tplPath);
|
|
33
|
+
const files = Array.isArray(tpl.files) ? tpl.files : [];
|
|
34
|
+
let score = 0;
|
|
35
|
+
for (const f of files) {
|
|
36
|
+
const candidate = path_1.default.join(targetDir, f.replace(/\\/g, "/"));
|
|
37
|
+
if (await fs_extra_1.default.pathExists(candidate))
|
|
38
|
+
score++;
|
|
39
|
+
}
|
|
40
|
+
if (!bestMatch || score > bestMatch.score)
|
|
41
|
+
bestMatch = { name: d, score };
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// ignore
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (bestMatch && bestMatch.score > 0) {
|
|
48
|
+
// Use the template folder name as the framework identifier
|
|
49
|
+
framework = bestMatch.name;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
31
52
|
}
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
catch {
|
|
54
|
+
// fall back to dependency heuristics below
|
|
34
55
|
}
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
// Fallback: simple dependency-based detection
|
|
57
|
+
if (framework === "unknown") {
|
|
58
|
+
const isNextJs = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
59
|
+
const isExpress = packageJson.dependencies?.express || packageJson.devDependencies?.express;
|
|
60
|
+
const isReact = packageJson.dependencies?.react || packageJson.devDependencies?.react;
|
|
61
|
+
const isVite = packageJson.dependencies?.vite || packageJson.devDependencies?.vite;
|
|
62
|
+
if (isNextJs)
|
|
63
|
+
framework = "nextjs";
|
|
64
|
+
else if (isExpress)
|
|
65
|
+
framework = "express";
|
|
66
|
+
else if (isReact && isVite)
|
|
67
|
+
framework = "react";
|
|
68
|
+
else if (isReact)
|
|
69
|
+
framework = "react";
|
|
37
70
|
}
|
|
38
71
|
if (framework === "unknown") {
|
|
39
|
-
throw new Error("
|
|
72
|
+
throw new Error("Unsupported project type or unable to detect framework from templates.");
|
|
40
73
|
}
|
|
41
74
|
// Detect router type (only for Next.js)
|
|
42
75
|
let router = "unknown";
|
|
@@ -79,16 +112,13 @@ async function detectProjectInfo(targetDir) {
|
|
|
79
112
|
else if (bunLockExists) {
|
|
80
113
|
packageManager = "bun";
|
|
81
114
|
}
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const hasPrisma =
|
|
88
|
-
const hasDatabase = hasPrisma ||
|
|
89
|
-
!!(packageJson.dependencies?.["mongoose"] ||
|
|
90
|
-
packageJson.dependencies?.["typeorm"] ||
|
|
91
|
-
packageJson.dependencies?.["drizzle-orm"]);
|
|
115
|
+
// Detect installed modules by comparing project dependencies against
|
|
116
|
+
// declared dependencies in `modules/*/generator.json` and `module.json`.
|
|
117
|
+
const detectedAuth = await (0, installed_detection_1.detectAuthModules)(packageJson);
|
|
118
|
+
const detectedDbs = await (0, installed_detection_1.detectDatabaseModules)(packageJson);
|
|
119
|
+
const hasAuth = detectedAuth.length > 0;
|
|
120
|
+
const hasPrisma = detectedDbs.includes("prisma");
|
|
121
|
+
const hasDatabase = hasPrisma || detectedDbs.length > 0;
|
|
92
122
|
return {
|
|
93
123
|
framework,
|
|
94
124
|
router,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,32 +7,36 @@ export interface TemplateMetadata {
|
|
|
7
7
|
features: string[];
|
|
8
8
|
}
|
|
9
9
|
export interface ModuleMetadata {
|
|
10
|
-
name
|
|
11
|
-
displayName
|
|
12
|
-
description
|
|
13
|
-
category
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
name?: string;
|
|
11
|
+
displayName?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
category?: string;
|
|
14
|
+
provider?: string;
|
|
15
|
+
supportedFrameworks?: string[];
|
|
16
|
+
frameworkConfigs?: Record<string, unknown>;
|
|
17
|
+
dependencies?: Record<string, unknown> | {
|
|
16
18
|
common?: Record<string, string>;
|
|
17
19
|
providers?: Record<string, Record<string, string>>;
|
|
18
|
-
}
|
|
19
|
-
devDependencies?: {
|
|
20
|
+
};
|
|
21
|
+
devDependencies?: Record<string, unknown> | {
|
|
20
22
|
common?: Record<string, string>;
|
|
21
23
|
providers?: Record<string, Record<string, string>>;
|
|
22
|
-
}
|
|
23
|
-
envVars
|
|
24
|
-
patches
|
|
24
|
+
};
|
|
25
|
+
envVars?: EnvVar[] | Record<string, string>;
|
|
26
|
+
patches?: ModulePatch[];
|
|
25
27
|
frameworkPatches?: Record<string, {
|
|
26
28
|
[file: string]: {
|
|
27
29
|
merge?: Record<string, unknown>;
|
|
28
30
|
};
|
|
29
31
|
}>;
|
|
30
32
|
postInstall?: string[];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
33
|
+
compatibility?: {
|
|
34
|
+
databases?: string[];
|
|
35
|
+
auth?: string[];
|
|
36
|
+
languages?: string[];
|
|
37
|
+
};
|
|
38
|
+
files?: string[];
|
|
39
|
+
scripts?: Record<string, string>;
|
|
36
40
|
}
|
|
37
41
|
export interface EnvVar {
|
|
38
42
|
key: string;
|
|
@@ -64,7 +68,7 @@ export interface ModifyJsonPatch extends ModulePatch {
|
|
|
64
68
|
}[];
|
|
65
69
|
}
|
|
66
70
|
export interface ProjectInfo {
|
|
67
|
-
framework:
|
|
71
|
+
framework: string;
|
|
68
72
|
router: "app" | "pages" | "unknown";
|
|
69
73
|
language: "ts" | "js";
|
|
70
74
|
packageManager: "npm" | "yarn" | "pnpm" | "bun";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { betterAuth
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
2
|
import { sendEmail } from "./email/email-service";
|
|
3
3
|
import { getVerificationEmailTemplate, getPasswordResetEmailTemplate } from "./email/email-templates";
|
|
4
4
|
{{#switch database}}
|
|
@@ -32,7 +32,7 @@ return betterAuth({
|
|
|
32
32
|
{{/switch}}
|
|
33
33
|
baseURL: process.env.BETTER_AUTH_URL,
|
|
34
34
|
secret: process.env.BETTER_AUTH_SECRET,
|
|
35
|
-
trustedOrigins: [process.env.APP_URL],
|
|
35
|
+
trustedOrigins: [process.env.APP_URL!],
|
|
36
36
|
user: {
|
|
37
37
|
additionalFields: {
|
|
38
38
|
role: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stackkit",
|
|
3
|
-
"version": "0.2.
|
|
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": {
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"dev": "tsc --watch",
|
|
27
27
|
"build": "npm run clean && tsc && npm run copy-assets",
|
|
28
|
-
"copy-assets": "
|
|
29
|
-
"clean": "
|
|
28
|
+
"copy-assets": "node ./scripts/copy-assets.js",
|
|
29
|
+
"clean": "node ./scripts/clean.js",
|
|
30
30
|
"typecheck": "tsc --noEmit",
|
|
31
31
|
"lint": "eslint src --ext .ts",
|
|
32
32
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
],
|
|
53
53
|
"author": "Tariqul Islam",
|
|
54
54
|
"license": "MIT",
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18"
|
|
57
|
+
},
|
|
55
58
|
"dependencies": {
|
|
56
59
|
"chalk": "^4.1.2",
|
|
57
60
|
"commander": "^12.0.0",
|
|
@@ -64,17 +67,17 @@
|
|
|
64
67
|
"@babel/core": "^7.28.5",
|
|
65
68
|
"@babel/preset-env": "^7.28.5",
|
|
66
69
|
"@babel/preset-typescript": "^7.28.5",
|
|
67
|
-
"@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"
|
|
68
75
|
},
|
|
69
76
|
"devDependencies": {
|
|
70
77
|
"@types/fs-extra": "^11.0.4",
|
|
71
78
|
"@types/inquirer": "^9.0.7",
|
|
72
79
|
"@types/node": "^25.0.8",
|
|
73
80
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
74
|
-
"typescript": "^5.3.3"
|
|
75
|
-
"recast": "^0.20.5",
|
|
76
|
-
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
77
|
-
"@babel/parser": "^7.28.5",
|
|
78
|
-
"@babel/plugin-transform-react-jsx": "^7.27.1"
|
|
81
|
+
"typescript": "^5.3.3"
|
|
79
82
|
}
|
|
80
|
-
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Build output
|
|
5
|
+
dist/
|
|
6
|
+
|
|
7
|
+
# Environment
|
|
8
|
+
.env*
|
|
9
|
+
!.env.example
|
|
10
|
+
|
|
11
|
+
# Logs
|
|
12
|
+
logs/
|
|
13
|
+
*.log
|
|
14
|
+
npm-debug.log*
|
|
15
|
+
yarn-debug.log*
|
|
16
|
+
yarn-error.log*
|
|
17
|
+
|
|
18
|
+
# OS
|
|
19
|
+
.DS_Store
|
|
20
|
+
*.pem
|
|
21
|
+
|
|
22
|
+
# TypeScript
|
|
23
|
+
*.tsbuildinfo
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.*
|
|
7
|
+
.yarn/*
|
|
8
|
+
!.yarn/patches
|
|
9
|
+
!.yarn/plugins
|
|
10
|
+
!.yarn/releases
|
|
11
|
+
!.yarn/versions
|
|
12
|
+
|
|
13
|
+
# testing
|
|
14
|
+
/coverage
|
|
15
|
+
|
|
16
|
+
# next.js
|
|
17
|
+
/.next/
|
|
18
|
+
/out/
|
|
19
|
+
|
|
20
|
+
# production
|
|
21
|
+
/build
|
|
22
|
+
|
|
23
|
+
# misc
|
|
24
|
+
.DS_Store
|
|
25
|
+
*.pem
|
|
26
|
+
|
|
27
|
+
# debug
|
|
28
|
+
npm-debug.log*
|
|
29
|
+
yarn-debug.log*
|
|
30
|
+
yarn-error.log*
|
|
31
|
+
.pnpm-debug.log*
|
|
32
|
+
|
|
33
|
+
# env files
|
|
34
|
+
.env*
|
|
35
|
+
!.env.example
|
|
36
|
+
|
|
37
|
+
# vercel
|
|
38
|
+
.vercel
|
|
39
|
+
|
|
40
|
+
# typescript
|
|
41
|
+
*.tsbuildinfo
|
|
42
|
+
next-env.d.ts
|