stackkit 0.1.2 → 0.1.4
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 +4 -2
- package/dist/cli/add.d.ts +2 -1
- package/dist/cli/add.js +318 -110
- package/dist/cli/create.js +8 -45
- package/dist/cli/doctor.js +0 -10
- package/dist/cli/list.js +3 -5
- package/dist/index.js +23 -4
- package/dist/lib/conversion/js-conversion.js +3 -2
- package/dist/lib/discovery/module-discovery.d.ts +0 -1
- package/dist/lib/discovery/module-discovery.js +27 -5
- package/dist/lib/generation/code-generator.d.ts +14 -0
- package/dist/lib/generation/code-generator.js +69 -65
- package/dist/lib/generation/generator-utils.d.ts +11 -0
- package/dist/lib/generation/generator-utils.js +116 -0
- package/dist/lib/git-utils.js +20 -0
- package/dist/lib/project/detect.js +2 -4
- package/dist/lib/utils/package-root.d.ts +1 -0
- package/dist/lib/utils/package-root.js +45 -0
- package/dist/types/index.d.ts +0 -10
- package/modules/auth/authjs/generator.json +10 -0
- package/modules/auth/authjs/module.json +0 -9
- package/modules/auth/better-auth/files/lib/auth.ts +7 -7
- package/modules/auth/better-auth/generator.json +12 -12
- package/modules/auth/better-auth/module.json +0 -24
- package/modules/database/mongoose/files/models/health.ts +34 -0
- package/modules/database/mongoose/generator.json +4 -4
- package/modules/database/mongoose/module.json +1 -2
- package/modules/database/prisma/files/lib/prisma.ts +2 -1
- package/modules/database/prisma/generator.json +33 -8
- package/modules/database/prisma/module.json +1 -4
- package/package.json +1 -2
- package/templates/express/tsconfig.json +2 -23
- package/dist/lib/database/database-config.d.ts +0 -6
- package/dist/lib/database/database-config.js +0 -9
- package/modules/database/mongoose/files/models/User.ts +0 -34
- package/templates/react-vite/README.md +0 -56
- /package/modules/database/mongoose/files/lib/{db.ts → mongoose.ts} +0 -0
package/dist/cli/create.js
CHANGED
|
@@ -17,6 +17,7 @@ const logger_1 = require("../lib/ui/logger");
|
|
|
17
17
|
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
|
+
const package_root_1 = require("../lib/utils/package-root");
|
|
20
21
|
async function createProject(projectName, options) {
|
|
21
22
|
logger_1.logger.newLine();
|
|
22
23
|
logger_1.logger.log(chalk_1.default.bold.cyan("📦 Create StackKit App"));
|
|
@@ -32,20 +33,8 @@ async function createProject(projectName, options) {
|
|
|
32
33
|
showNextSteps(config);
|
|
33
34
|
}
|
|
34
35
|
async function getProjectConfig(projectName, options) {
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
path_1.default.join(__dirname, "..", "..", "..", "modules"),
|
|
38
|
-
path_1.default.join(__dirname, "..", "..", "..", "..", "modules"),
|
|
39
|
-
];
|
|
40
|
-
let modulesDir;
|
|
41
|
-
for (const c of modulesCandidates) {
|
|
42
|
-
if (await fs_extra_1.default.pathExists(c)) {
|
|
43
|
-
modulesDir = c;
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const discoveredModules = await (0, module_discovery_1.discoverModules)(modulesDir || modulesCandidates[1]);
|
|
48
|
-
// Detect flags or `--yes` after `create`; project-name-only remains interactive
|
|
36
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules');
|
|
37
|
+
const discoveredModules = await (0, module_discovery_1.discoverModules)(modulesDir);
|
|
49
38
|
const argv = process.argv.slice(2);
|
|
50
39
|
const createIndex = argv.indexOf('create');
|
|
51
40
|
const argsAfterCreate = createIndex >= 0 ? argv.slice(createIndex + 1) : [];
|
|
@@ -63,7 +52,6 @@ async function getProjectConfig(projectName, options) {
|
|
|
63
52
|
packageManager: "pnpm",
|
|
64
53
|
};
|
|
65
54
|
}
|
|
66
|
-
// Validate options using discovered modules (if any discovered)
|
|
67
55
|
const framework = (options && (options.framework || options.f)) || undefined;
|
|
68
56
|
if (discoveredModules.frameworks && discoveredModules.frameworks.length > 0) {
|
|
69
57
|
const validFrameworks = discoveredModules.frameworks.map(f => f.name);
|
|
@@ -75,7 +63,6 @@ async function getProjectConfig(projectName, options) {
|
|
|
75
63
|
let allValidDatabases = [];
|
|
76
64
|
if (discoveredModules.databases && discoveredModules.databases.length > 0) {
|
|
77
65
|
const validDatabases = (0, module_discovery_1.getValidDatabaseOptions)(discoveredModules.databases);
|
|
78
|
-
// Also allow base database names like 'prisma', 'mongoose'
|
|
79
66
|
const validBaseDatabases = discoveredModules.databases.map(db => db.name);
|
|
80
67
|
allValidDatabases = [...validDatabases, ...validBaseDatabases];
|
|
81
68
|
if (db && !allValidDatabases.includes(db)) {
|
|
@@ -111,7 +98,6 @@ async function getProjectConfig(projectName, options) {
|
|
|
111
98
|
auth = authOpt;
|
|
112
99
|
}
|
|
113
100
|
const finalFramework = (framework || "nextjs");
|
|
114
|
-
// Validate auth compatibility
|
|
115
101
|
if (auth === "authjs" && (database !== "prisma" || finalFramework !== "nextjs")) {
|
|
116
102
|
throw new Error("Auth.js is only supported with Next.js and Prisma database");
|
|
117
103
|
}
|
|
@@ -128,7 +114,6 @@ async function getProjectConfig(projectName, options) {
|
|
|
128
114
|
packageManager: (pm || "pnpm"),
|
|
129
115
|
};
|
|
130
116
|
}
|
|
131
|
-
// Interactive prompts
|
|
132
117
|
const answers = (await inquirer_1.default.prompt([
|
|
133
118
|
{
|
|
134
119
|
type: "input",
|
|
@@ -235,7 +220,6 @@ async function generateProject(config, targetDir, options) {
|
|
|
235
220
|
copySpinner.fail("Failed to create project files");
|
|
236
221
|
throw error;
|
|
237
222
|
}
|
|
238
|
-
// Install dependencies
|
|
239
223
|
if (options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
|
|
240
224
|
const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
|
|
241
225
|
try {
|
|
@@ -247,7 +231,6 @@ async function generateProject(config, targetDir, options) {
|
|
|
247
231
|
throw error;
|
|
248
232
|
}
|
|
249
233
|
}
|
|
250
|
-
// Run post-install commands (skip if install was skipped)
|
|
251
234
|
if (postInstallCommands.length > 0 && options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
|
|
252
235
|
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
253
236
|
try {
|
|
@@ -261,41 +244,25 @@ async function generateProject(config, targetDir, options) {
|
|
|
261
244
|
throw error;
|
|
262
245
|
}
|
|
263
246
|
}
|
|
264
|
-
// Initialize git
|
|
265
247
|
if (options?.git !== false && !(options?.['no-git'] || options?.noGit)) {
|
|
266
248
|
const gitSpinner = logger_1.logger.startSpinner("Initializing git repository...");
|
|
267
249
|
try {
|
|
268
250
|
await (0, git_utils_1.initGit)(targetDir);
|
|
269
251
|
gitSpinner.succeed("Git repository initialized");
|
|
270
252
|
}
|
|
271
|
-
catch {
|
|
272
|
-
gitSpinner.warn(
|
|
253
|
+
catch (error) {
|
|
254
|
+
gitSpinner.warn(`Failed to initialize git repository: ${error.message}`);
|
|
273
255
|
}
|
|
274
256
|
}
|
|
275
257
|
}
|
|
276
258
|
async function composeTemplate(config, targetDir) {
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
path_1.default.join(__dirname, "..", "..", "..", "..", "templates"),
|
|
281
|
-
];
|
|
282
|
-
const modulesCandidates2 = [
|
|
283
|
-
path_1.default.join(__dirname, "..", "..", "..", "modules"),
|
|
284
|
-
path_1.default.join(__dirname, "..", "..", "..", "..", "modules"),
|
|
285
|
-
];
|
|
286
|
-
const templatesDir = (await (async () => { for (const c of templatesCandidates)
|
|
287
|
-
if (await fs_extra_1.default.pathExists(c))
|
|
288
|
-
return c; return templatesCandidates[1]; })());
|
|
289
|
-
const modulesDirForGenerator = (await (async () => { for (const c of modulesCandidates2)
|
|
290
|
-
if (await fs_extra_1.default.pathExists(c))
|
|
291
|
-
return c; return modulesCandidates2[1]; })());
|
|
259
|
+
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');
|
|
292
262
|
await fs_extra_1.default.ensureDir(targetDir);
|
|
293
|
-
// Load framework configuration
|
|
294
263
|
const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(config.framework, templatesDir);
|
|
295
|
-
// Initialize advanced code generator
|
|
296
264
|
const generator = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
|
|
297
265
|
await generator.loadGenerators(modulesDirForGenerator);
|
|
298
|
-
// Generate project using advanced code generator
|
|
299
266
|
const features = [];
|
|
300
267
|
const postInstallCommands = await generator.generate({
|
|
301
268
|
framework: config.framework,
|
|
@@ -303,14 +270,12 @@ async function composeTemplate(config, targetDir) {
|
|
|
303
270
|
auth: config.auth === 'none' ? undefined : config.auth,
|
|
304
271
|
prismaProvider: config.prismaProvider,
|
|
305
272
|
}, features, targetDir);
|
|
306
|
-
// Update project name in package.json
|
|
307
273
|
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
308
274
|
if (await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
309
275
|
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
310
276
|
packageJson.name = config.projectName;
|
|
311
277
|
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
312
278
|
}
|
|
313
|
-
// Ensure .env exists: if .env.example was copied from the template, create .env from it
|
|
314
279
|
try {
|
|
315
280
|
const envExamplePath = path_1.default.join(targetDir, ".env.example");
|
|
316
281
|
const envPath = path_1.default.join(targetDir, ".env");
|
|
@@ -320,12 +285,10 @@ async function composeTemplate(config, targetDir) {
|
|
|
320
285
|
}
|
|
321
286
|
}
|
|
322
287
|
catch {
|
|
323
|
-
// non-fatal
|
|
324
288
|
}
|
|
325
289
|
if (config.language === "javascript") {
|
|
326
290
|
await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
|
|
327
291
|
}
|
|
328
|
-
// For now, return empty array as post-install commands are handled by the generator
|
|
329
292
|
return postInstallCommands;
|
|
330
293
|
}
|
|
331
294
|
function showNextSteps(config) {
|
package/dist/cli/doctor.js
CHANGED
|
@@ -229,13 +229,11 @@ function detectDatabaseModules(packageJson) {
|
|
|
229
229
|
}
|
|
230
230
|
async function checkKeyFiles(projectRoot, projectType, authModules, databaseModules) {
|
|
231
231
|
const checks = [];
|
|
232
|
-
// Check .env.example
|
|
233
232
|
const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
|
|
234
233
|
checks.push({
|
|
235
234
|
status: envExampleExists ? "success" : "warning",
|
|
236
235
|
message: envExampleExists ? ".env.example file found" : ".env.example file missing (recommended for documentation)",
|
|
237
236
|
});
|
|
238
|
-
// Check Prisma schema
|
|
239
237
|
if (databaseModules.includes("prisma")) {
|
|
240
238
|
const schemaExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"));
|
|
241
239
|
checks.push({
|
|
@@ -243,7 +241,6 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
|
|
|
243
241
|
message: schemaExists ? "Prisma schema found" : "Prisma schema missing (required for Prisma)",
|
|
244
242
|
});
|
|
245
243
|
}
|
|
246
|
-
// Check auth routes
|
|
247
244
|
if (authModules.length > 0 && projectType === "nextjs") {
|
|
248
245
|
const authRoutesExist = await checkAuthRoutesExist(projectRoot, projectType);
|
|
249
246
|
checks.push({
|
|
@@ -278,7 +275,6 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
|
|
|
278
275
|
const requiredKeys = [];
|
|
279
276
|
const missing = [];
|
|
280
277
|
const present = [];
|
|
281
|
-
// Determine required env keys
|
|
282
278
|
if (databaseModules.includes("prisma")) {
|
|
283
279
|
requiredKeys.push("DATABASE_URL");
|
|
284
280
|
}
|
|
@@ -288,7 +284,6 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
|
|
|
288
284
|
if (authModules.includes("better-auth")) {
|
|
289
285
|
requiredKeys.push("BETTER_AUTH_SECRET", "BETTER_AUTH_URL");
|
|
290
286
|
}
|
|
291
|
-
// Check env files
|
|
292
287
|
const envPaths = [".env", ".env.local"];
|
|
293
288
|
let envContent = "";
|
|
294
289
|
for (const envPath of envPaths) {
|
|
@@ -305,7 +300,6 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
|
|
|
305
300
|
});
|
|
306
301
|
return { checks, envStatus: { missing: requiredKeys, present: [] } };
|
|
307
302
|
}
|
|
308
|
-
// Parse env content
|
|
309
303
|
const envLines = envContent.split("\n").map(line => line.trim()).filter(line => line && !line.startsWith("#"));
|
|
310
304
|
const envVars = new Set(envLines.map(line => line.split("=")[0]));
|
|
311
305
|
for (const key of requiredKeys) {
|
|
@@ -342,19 +336,16 @@ function checkConflicts(authModules, databaseModules) {
|
|
|
342
336
|
}
|
|
343
337
|
async function checkConfigFiles(projectRoot, projectType, packageJson) {
|
|
344
338
|
const checks = [];
|
|
345
|
-
// Check tsconfig.json
|
|
346
339
|
const tsconfigExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "tsconfig.json"));
|
|
347
340
|
checks.push({
|
|
348
341
|
status: tsconfigExists ? "success" : "error",
|
|
349
342
|
message: tsconfigExists ? MESSAGES.TSCONFIG_FOUND : MESSAGES.TSCONFIG_MISSING,
|
|
350
343
|
});
|
|
351
|
-
// Check ESLint config
|
|
352
344
|
const eslintExists = await checkEslintConfigExists(projectRoot);
|
|
353
345
|
checks.push({
|
|
354
346
|
status: eslintExists ? "success" : "warning",
|
|
355
347
|
message: eslintExists ? MESSAGES.ESLINT_CONFIG_FOUND : MESSAGES.ESLINT_CONFIG_MISSING,
|
|
356
348
|
});
|
|
357
|
-
// Check build script
|
|
358
349
|
const hasBuildScript = packageJson.scripts && typeof packageJson.scripts === "object" && "build" in packageJson.scripts;
|
|
359
350
|
checks.push({
|
|
360
351
|
status: hasBuildScript ? "success" : "warning",
|
|
@@ -369,7 +360,6 @@ async function checkDependencies(packageJson) {
|
|
|
369
360
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
370
361
|
for (const [name, version] of Object.entries(deps || {})) {
|
|
371
362
|
if (typeof version === "string" && (version.startsWith("^") || version.startsWith("~"))) {
|
|
372
|
-
// Assume up to date
|
|
373
363
|
}
|
|
374
364
|
else {
|
|
375
365
|
outdated.push(name);
|
package/dist/cli/list.js
CHANGED
|
@@ -8,6 +8,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
10
|
const logger_1 = require("../lib/ui/logger");
|
|
11
|
+
const package_root_1 = require("../lib/utils/package-root");
|
|
11
12
|
async function listCommand(options) {
|
|
12
13
|
const showFrameworks = !options.modules || options.frameworks;
|
|
13
14
|
const showModules = !options.frameworks || options.modules;
|
|
@@ -17,7 +18,7 @@ async function listCommand(options) {
|
|
|
17
18
|
let hasFrameworks = false;
|
|
18
19
|
let hasModules = false;
|
|
19
20
|
if (showFrameworks) {
|
|
20
|
-
const templatesDir = path_1.default.join(
|
|
21
|
+
const templatesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "templates");
|
|
21
22
|
const frameworks = await getAvailableFrameworks(templatesDir);
|
|
22
23
|
if (frameworks.length > 0) {
|
|
23
24
|
hasFrameworks = true;
|
|
@@ -30,14 +31,12 @@ async function listCommand(options) {
|
|
|
30
31
|
logger_1.logger.newLine();
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
// List modules
|
|
34
34
|
if (showModules) {
|
|
35
|
-
const modulesDir = path_1.default.join(
|
|
35
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
36
36
|
const modules = await getAvailableModules(modulesDir);
|
|
37
37
|
if (modules.length > 0) {
|
|
38
38
|
hasModules = true;
|
|
39
39
|
logger_1.logger.log(chalk_1.default.bold.magenta("MODULES"));
|
|
40
|
-
// Group by category
|
|
41
40
|
const grouped = modules.reduce((acc, mod) => {
|
|
42
41
|
if (!acc[mod.category]) {
|
|
43
42
|
acc[mod.category] = [];
|
|
@@ -55,7 +54,6 @@ async function listCommand(options) {
|
|
|
55
54
|
const isLastMod = modIndex === mods.length - 1;
|
|
56
55
|
const modPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
|
|
57
56
|
logger_1.logger.log(` ${chalk_1.default.gray(modPrefix)} ${chalk_1.default.green(mod.displayName)}`);
|
|
58
|
-
// Show additional details for database modules
|
|
59
57
|
if (mod.category === "database" && mod.name === "prisma") {
|
|
60
58
|
const providerPrefix = isLastCategory ? (isLastMod ? " └──" : " ├──") : (isLastMod ? "│ └──" : "│ ├──");
|
|
61
59
|
logger_1.logger.log(` ${chalk_1.default.gray(providerPrefix)} ${chalk_1.default.dim("Providers: PostgreSQL, MongoDB, MySQL, SQLite")}`);
|
package/dist/index.js
CHANGED
|
@@ -11,11 +11,28 @@ const program = new commander_1.Command();
|
|
|
11
11
|
program
|
|
12
12
|
.name("stackkit")
|
|
13
13
|
.description("CLI for creating and managing StackKit projects")
|
|
14
|
-
.version("0.1.
|
|
14
|
+
.version("0.1.4")
|
|
15
|
+
.configureHelp({
|
|
16
|
+
subcommandTerm: (cmd) => {
|
|
17
|
+
const name = cmd.name();
|
|
18
|
+
if (name === 'create')
|
|
19
|
+
return 'create [project-name] [options]';
|
|
20
|
+
if (name === 'add')
|
|
21
|
+
return 'add <module> [options]';
|
|
22
|
+
if (name === 'doctor')
|
|
23
|
+
return 'doctor [options]';
|
|
24
|
+
if (name === 'list')
|
|
25
|
+
return 'list [options]';
|
|
26
|
+
if (name === 'help')
|
|
27
|
+
return 'help [command]';
|
|
28
|
+
return name + ' [options]';
|
|
29
|
+
}
|
|
30
|
+
});
|
|
15
31
|
// Create command
|
|
16
32
|
program
|
|
17
|
-
.command(
|
|
33
|
+
.command('create [project-name]')
|
|
18
34
|
.description("Create a new StackKit project")
|
|
35
|
+
.usage('[project-name] [options]')
|
|
19
36
|
.option("-f, --framework <framework>", "Framework: nextjs, express, react")
|
|
20
37
|
.option("-d, --database <database>", "Database: prisma, mongoose, none")
|
|
21
38
|
.option("--prisma-provider <provider>", "Prisma provider: postgresql, mongodb, mysql, sqlite")
|
|
@@ -36,12 +53,14 @@ program
|
|
|
36
53
|
});
|
|
37
54
|
// Add command
|
|
38
55
|
program
|
|
39
|
-
.command("add
|
|
40
|
-
.description("Add a module to your existing project")
|
|
56
|
+
.command("add [module]")
|
|
57
|
+
.description("Add a module or category to your existing project")
|
|
58
|
+
.usage('[module] [options]')
|
|
41
59
|
.option("--provider <provider>", "Specific provider/variant to use")
|
|
42
60
|
.option("--force", "Overwrite existing files")
|
|
43
61
|
.option("--dry-run", "Show what would be changed without making changes")
|
|
44
62
|
.option("--no-install", "Skip installing dependencies")
|
|
63
|
+
.option("-y, --yes", "Use default options")
|
|
45
64
|
.action(async (module, options) => {
|
|
46
65
|
try {
|
|
47
66
|
await (0, add_1.addCommand)(module, options);
|
|
@@ -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 package_root_1 = require("../utils/package-root");
|
|
10
11
|
const baseDirs = {
|
|
11
12
|
express: "./src",
|
|
12
13
|
"react": "./src",
|
|
@@ -76,7 +77,7 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
76
77
|
presets.push([
|
|
77
78
|
require.resolve("@babel/preset-react"),
|
|
78
79
|
{
|
|
79
|
-
runtime: "
|
|
80
|
+
runtime: "automatic",
|
|
80
81
|
},
|
|
81
82
|
]);
|
|
82
83
|
}
|
|
@@ -165,7 +166,7 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
165
166
|
};
|
|
166
167
|
await replaceAliases(targetDir);
|
|
167
168
|
}
|
|
168
|
-
const templatesRoot = path_1.default.join(
|
|
169
|
+
const templatesRoot = path_1.default.join((0, package_root_1.getPackageRoot)(), "templates");
|
|
169
170
|
const templateName = framework;
|
|
170
171
|
let fileReplacements = [];
|
|
171
172
|
let jsScripts = null;
|
|
@@ -5,7 +5,6 @@ export interface ModuleMetadata {
|
|
|
5
5
|
category: string;
|
|
6
6
|
provider?: string;
|
|
7
7
|
supportedFrameworks?: string[];
|
|
8
|
-
databaseAdapters?: Record<string, unknown>;
|
|
9
8
|
frameworkConfigs?: Record<string, unknown>;
|
|
10
9
|
dependencies?: Record<string, unknown>;
|
|
11
10
|
devDependencies?: Record<string, unknown>;
|
|
@@ -11,6 +11,7 @@ exports.getCompatibleAuthOptions = getCompatibleAuthOptions;
|
|
|
11
11
|
exports.getDatabaseChoices = getDatabaseChoices;
|
|
12
12
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
13
13
|
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const package_root_1 = require("../utils/package-root");
|
|
14
15
|
/**
|
|
15
16
|
* Discover all available modules from the modules directory
|
|
16
17
|
*/
|
|
@@ -20,9 +21,21 @@ async function discoverModules(modulesDir) {
|
|
|
20
21
|
databases: [],
|
|
21
22
|
auth: [],
|
|
22
23
|
};
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
// If modulesDir isn't provided or doesn't exist, try common locations
|
|
25
|
+
const candidates = [];
|
|
26
|
+
if (modulesDir)
|
|
27
|
+
candidates.push(modulesDir);
|
|
28
|
+
candidates.push(path_1.default.join((0, package_root_1.getPackageRoot)(), 'modules')); // package root
|
|
29
|
+
let resolvedModulesDir;
|
|
30
|
+
for (const c of candidates) {
|
|
31
|
+
if (await fs_extra_1.default.pathExists(c)) {
|
|
32
|
+
resolvedModulesDir = c;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
25
35
|
}
|
|
36
|
+
if (!resolvedModulesDir)
|
|
37
|
+
return discovered;
|
|
38
|
+
modulesDir = resolvedModulesDir;
|
|
26
39
|
// Discover frameworks from templates directory
|
|
27
40
|
const templatesDir = path_1.default.join(modulesDir, '..', 'templates');
|
|
28
41
|
if (await fs_extra_1.default.pathExists(templatesDir)) {
|
|
@@ -62,6 +75,11 @@ async function discoverModules(modulesDir) {
|
|
|
62
75
|
if (await fs_extra_1.default.pathExists(moduleJsonPath)) {
|
|
63
76
|
try {
|
|
64
77
|
const metadata = await fs_extra_1.default.readJson(moduleJsonPath);
|
|
78
|
+
// Ensure name/displayName fallbacks
|
|
79
|
+
if (!metadata.name)
|
|
80
|
+
metadata.name = moduleName;
|
|
81
|
+
if (!metadata.displayName)
|
|
82
|
+
metadata.displayName = moduleName;
|
|
65
83
|
discovered.databases.push(metadata);
|
|
66
84
|
}
|
|
67
85
|
catch {
|
|
@@ -80,6 +98,10 @@ async function discoverModules(modulesDir) {
|
|
|
80
98
|
if (await fs_extra_1.default.pathExists(moduleJsonPath)) {
|
|
81
99
|
try {
|
|
82
100
|
const metadata = await fs_extra_1.default.readJson(moduleJsonPath);
|
|
101
|
+
if (!metadata.name)
|
|
102
|
+
metadata.name = moduleName;
|
|
103
|
+
if (!metadata.displayName)
|
|
104
|
+
metadata.displayName = moduleName;
|
|
83
105
|
discovered.auth.push(metadata);
|
|
84
106
|
}
|
|
85
107
|
catch {
|
|
@@ -101,7 +123,7 @@ function getValidDatabaseOptions(databases) {
|
|
|
101
123
|
options.push('prisma-postgresql', 'prisma-mongodb', 'prisma-mysql', 'prisma-sqlite');
|
|
102
124
|
}
|
|
103
125
|
else if (db.name === 'mongoose') {
|
|
104
|
-
options.push('mongoose
|
|
126
|
+
options.push('mongoose', 'mongoose');
|
|
105
127
|
}
|
|
106
128
|
else {
|
|
107
129
|
// For other databases, add the name directly
|
|
@@ -131,7 +153,7 @@ function parseDatabaseOption(dbOption) {
|
|
|
131
153
|
const provider = dbOption.split('-')[1];
|
|
132
154
|
return { database: 'prisma', provider };
|
|
133
155
|
}
|
|
134
|
-
if (dbOption === 'mongoose
|
|
156
|
+
if (dbOption === 'mongoose' || dbOption === 'mongoose') {
|
|
135
157
|
return { database: 'mongoose' };
|
|
136
158
|
}
|
|
137
159
|
return { database: dbOption };
|
|
@@ -176,7 +198,7 @@ function getDatabaseChoices(databases, framework) {
|
|
|
176
198
|
choices.push({ name: 'Prisma (PostgreSQL)', value: 'prisma-postgresql' }, { name: 'Prisma (MongoDB)', value: 'prisma-mongodb' }, { name: 'Prisma (MySQL)', value: 'prisma-mysql' }, { name: 'Prisma (SQLite)', value: 'prisma-sqlite' });
|
|
177
199
|
}
|
|
178
200
|
else if (db.name === 'mongoose') {
|
|
179
|
-
choices.push({ name: 'Mongoose
|
|
201
|
+
choices.push({ name: 'Mongoose', value: 'mongoose' });
|
|
180
202
|
}
|
|
181
203
|
else {
|
|
182
204
|
choices.push({ name: db.displayName, value: db.name });
|
|
@@ -75,9 +75,23 @@ export declare class AdvancedCodeGenerator {
|
|
|
75
75
|
private executeAddEnv;
|
|
76
76
|
private executeRunCommand;
|
|
77
77
|
private generatePackageJson;
|
|
78
|
+
/**
|
|
79
|
+
* Apply generators to an existing project directory instead of creating a new template.
|
|
80
|
+
* This executes applicable operations and updates package.json in-place.
|
|
81
|
+
*/
|
|
82
|
+
applyToProject(selectedModules: {
|
|
83
|
+
framework: string;
|
|
84
|
+
database?: string;
|
|
85
|
+
auth?: string;
|
|
86
|
+
prismaProvider?: string;
|
|
87
|
+
}, features: string[], projectPath: string): Promise<string[]>;
|
|
78
88
|
getAvailableGenerators(): {
|
|
79
89
|
frameworks: string[];
|
|
80
90
|
databases: string[];
|
|
81
91
|
auths: string[];
|
|
82
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Register a generator config dynamically (used for modules that only provide module.json patches).
|
|
95
|
+
*/
|
|
96
|
+
registerGenerator(type: 'framework' | 'database' | 'auth', name: string, config: GeneratorConfig): void;
|
|
83
97
|
}
|
|
@@ -36,6 +36,8 @@ 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 package_root_1 = require("../utils/package-root");
|
|
40
|
+
const generator_utils_1 = require("./generator-utils");
|
|
39
41
|
class AdvancedCodeGenerator {
|
|
40
42
|
constructor(frameworkConfig) {
|
|
41
43
|
this.generators = new Map();
|
|
@@ -44,7 +46,6 @@ class AdvancedCodeGenerator {
|
|
|
44
46
|
}
|
|
45
47
|
async loadGenerators(modulesPath) {
|
|
46
48
|
const moduleTypes = ['auth', 'database'];
|
|
47
|
-
// Load module generators
|
|
48
49
|
for (const type of moduleTypes) {
|
|
49
50
|
const typePath = path.join(modulesPath, type);
|
|
50
51
|
if (await fs.pathExists(typePath)) {
|
|
@@ -54,20 +55,8 @@ class AdvancedCodeGenerator {
|
|
|
54
55
|
if (await fs.pathExists(generatorPath)) {
|
|
55
56
|
try {
|
|
56
57
|
const config = await fs.readJson(generatorPath);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (await fs.pathExists(modulePath)) {
|
|
60
|
-
try {
|
|
61
|
-
const moduleConfig = await fs.readJson(modulePath);
|
|
62
|
-
if (moduleConfig.postInstall && Array.isArray(moduleConfig.postInstall)) {
|
|
63
|
-
// Store postInstall commands with the generator for later use
|
|
64
|
-
config.postInstall = moduleConfig.postInstall;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
// Silently skip if module.json is invalid
|
|
69
|
-
}
|
|
70
|
-
}
|
|
58
|
+
const modulePath = path.join(typePath, moduleName);
|
|
59
|
+
await (0, generator_utils_1.mergeModuleIntoGeneratorConfig)(config, modulePath);
|
|
71
60
|
this.generators.set(`${type}:${moduleName}`, config);
|
|
72
61
|
}
|
|
73
62
|
catch {
|
|
@@ -375,28 +364,18 @@ class AdvancedCodeGenerator {
|
|
|
375
364
|
}
|
|
376
365
|
}
|
|
377
366
|
async copyTemplate(frameworkName, outputPath) {
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
367
|
+
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
368
|
+
const templatePath = path.join(packageRoot, 'templates', frameworkName);
|
|
369
|
+
if (await fs.pathExists(templatePath)) {
|
|
370
|
+
await fs.copy(templatePath, outputPath, {
|
|
371
|
+
filter: (src) => {
|
|
372
|
+
const relativePath = path.relative(templatePath, src);
|
|
373
|
+
return relativePath !== 'template.json' &&
|
|
374
|
+
relativePath !== 'node_modules' &&
|
|
375
|
+
!relativePath.startsWith('node_modules/');
|
|
376
|
+
}
|
|
377
|
+
});
|
|
389
378
|
}
|
|
390
|
-
if (!templateBase)
|
|
391
|
-
return;
|
|
392
|
-
await fs.copy(templateBase, outputPath, {
|
|
393
|
-
filter: (src) => {
|
|
394
|
-
const relativePath = path.relative(templateBase, src);
|
|
395
|
-
return relativePath !== 'template.json' &&
|
|
396
|
-
relativePath !== 'node_modules' &&
|
|
397
|
-
!relativePath.startsWith('node_modules/');
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
379
|
}
|
|
401
380
|
processOperationTemplates(operation, context) {
|
|
402
381
|
const processed = { ...operation };
|
|
@@ -455,34 +434,9 @@ class AdvancedCodeGenerator {
|
|
|
455
434
|
content = this.processTemplate(operation.content, context);
|
|
456
435
|
}
|
|
457
436
|
else if (operation.source) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const modulesCandidates = [
|
|
461
|
-
path.resolve(__dirname, '..', '..', 'modules'),
|
|
462
|
-
path.resolve(__dirname, '..', '..', '..', 'modules'),
|
|
463
|
-
];
|
|
464
|
-
const templatesCandidates = [
|
|
465
|
-
path.resolve(__dirname, '..', '..', 'templates'),
|
|
466
|
-
path.resolve(__dirname, '..', '..', '..', 'templates'),
|
|
467
|
-
];
|
|
468
|
-
const resolveExisting = async (cands) => {
|
|
469
|
-
for (const c of cands) {
|
|
470
|
-
if (await fs.pathExists(c))
|
|
471
|
-
return c;
|
|
472
|
-
}
|
|
473
|
-
return undefined;
|
|
474
|
-
};
|
|
475
|
-
const modulesPathResolved = await resolveExisting(modulesCandidates);
|
|
476
|
-
const templatesPathResolved = await resolveExisting(templatesCandidates);
|
|
477
|
-
const moduleBasePath = operation.generatorType === 'framework'
|
|
478
|
-
? path.join(templatesPathResolved || templatesCandidates[0], operation.generator)
|
|
479
|
-
: path.join(modulesPathResolved || modulesCandidates[0], operation.generatorType, operation.generator);
|
|
480
|
-
const sourcePath = path.join(moduleBasePath, 'files', operation.source);
|
|
481
|
-
// Check if source file exists
|
|
482
|
-
if (await fs.pathExists(sourcePath)) {
|
|
483
|
-
// Read source file
|
|
437
|
+
const sourcePath = (0, generator_utils_1.locateOperationSource)(operation.generatorType, operation.generator, operation.source || '');
|
|
438
|
+
if (sourcePath && await fs.pathExists(sourcePath)) {
|
|
484
439
|
content = await fs.readFile(sourcePath, 'utf-8');
|
|
485
|
-
// Process template content
|
|
486
440
|
content = this.processTemplate(content, context);
|
|
487
441
|
}
|
|
488
442
|
else {
|
|
@@ -551,7 +505,7 @@ class AdvancedCodeGenerator {
|
|
|
551
505
|
processedContentTop = this.processTemplate(patchOp.content, context).trim();
|
|
552
506
|
}
|
|
553
507
|
else if (patchOp.source) {
|
|
554
|
-
const modulesPath = path.join(
|
|
508
|
+
const modulesPath = path.join((0, package_root_1.getPackageRoot)(), 'modules');
|
|
555
509
|
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
|
|
556
510
|
if (await fs.pathExists(sourcePath)) {
|
|
557
511
|
processedContentTop = await fs.readFile(sourcePath, 'utf-8');
|
|
@@ -569,7 +523,7 @@ class AdvancedCodeGenerator {
|
|
|
569
523
|
processedContentBottom = this.processTemplate(patchOp.content, context).trim();
|
|
570
524
|
}
|
|
571
525
|
else if (patchOp.source) {
|
|
572
|
-
const modulesPath = path.join(
|
|
526
|
+
const modulesPath = path.join((0, package_root_1.getPackageRoot)(), 'modules');
|
|
573
527
|
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
|
|
574
528
|
if (await fs.pathExists(sourcePath)) {
|
|
575
529
|
processedContentBottom = await fs.readFile(sourcePath, 'utf-8');
|
|
@@ -657,6 +611,50 @@ class AdvancedCodeGenerator {
|
|
|
657
611
|
packageJson.scripts = { ...(packageJson.scripts || {}), ...allScripts };
|
|
658
612
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
659
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Apply generators to an existing project directory instead of creating a new template.
|
|
616
|
+
* This executes applicable operations and updates package.json in-place.
|
|
617
|
+
*/
|
|
618
|
+
async applyToProject(selectedModules, features, projectPath) {
|
|
619
|
+
const context = {
|
|
620
|
+
...selectedModules,
|
|
621
|
+
features,
|
|
622
|
+
};
|
|
623
|
+
if (selectedModules.database === 'prisma' && !context.prismaProvider) {
|
|
624
|
+
context.prismaProvider = 'postgresql';
|
|
625
|
+
}
|
|
626
|
+
const applicableOperations = [];
|
|
627
|
+
for (const [key, generator] of this.generators) {
|
|
628
|
+
const [genType, name] = key.split(':');
|
|
629
|
+
if (genType === 'framework' && name === selectedModules.framework) {
|
|
630
|
+
// framework
|
|
631
|
+
}
|
|
632
|
+
else if (genType === 'database' && name === selectedModules.database) {
|
|
633
|
+
// database
|
|
634
|
+
}
|
|
635
|
+
else if (genType === 'auth' && name === selectedModules.auth) {
|
|
636
|
+
// auth
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
if (generator.postInstall && Array.isArray(generator.postInstall)) {
|
|
642
|
+
this.postInstallCommands.push(...generator.postInstall);
|
|
643
|
+
}
|
|
644
|
+
const items = generator.operations || [];
|
|
645
|
+
for (const item of items) {
|
|
646
|
+
if (this.evaluateCondition(item.condition, context)) {
|
|
647
|
+
applicableOperations.push({ ...item, generator: name, generatorType: genType, priority: generator.priority });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
applicableOperations.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
652
|
+
for (const operation of applicableOperations) {
|
|
653
|
+
await this.executeOperation(operation, context, projectPath);
|
|
654
|
+
}
|
|
655
|
+
await this.generatePackageJson(selectedModules, features, projectPath);
|
|
656
|
+
return this.postInstallCommands;
|
|
657
|
+
}
|
|
660
658
|
getAvailableGenerators() {
|
|
661
659
|
const frameworks = [];
|
|
662
660
|
const databases = [];
|
|
@@ -677,5 +675,11 @@ class AdvancedCodeGenerator {
|
|
|
677
675
|
}
|
|
678
676
|
return { frameworks, databases, auths };
|
|
679
677
|
}
|
|
678
|
+
/**
|
|
679
|
+
* Register a generator config dynamically (used for modules that only provide module.json patches).
|
|
680
|
+
*/
|
|
681
|
+
registerGenerator(type, name, config) {
|
|
682
|
+
this.generators.set(`${type}:${name}`, config);
|
|
683
|
+
}
|
|
680
684
|
}
|
|
681
685
|
exports.AdvancedCodeGenerator = AdvancedCodeGenerator;
|