stackkit 0.1.1 → 0.1.3
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 +2 -2
- package/dist/cli/add.js +3 -7
- package/dist/cli/create.js +14 -47
- package/dist/cli/doctor.js +0 -6
- package/dist/cli/list.js +3 -5
- package/dist/lib/conversion/js-conversion.js +2 -1
- package/dist/lib/discovery/module-discovery.js +24 -2
- package/dist/lib/generation/code-generator.js +19 -46
- package/dist/lib/utils/package-root.d.ts +1 -0
- package/dist/lib/utils/package-root.js +45 -0
- package/package.json +1 -2
- package/templates/react/README.md +1 -1
- package/templates/react-vite/README.md +0 -56
package/README.md
CHANGED
|
@@ -31,11 +31,11 @@ npx stackkit doctor
|
|
|
31
31
|
|
|
32
32
|
## Supported Technologies
|
|
33
33
|
|
|
34
|
-
- **Frameworks**: Next.js, React
|
|
34
|
+
- **Frameworks**: Next.js, React, Express
|
|
35
35
|
- **Databases**: Prisma (PostgreSQL, MySQL, SQLite), Mongoose (MongoDB)
|
|
36
36
|
- **Auth**: Better Auth, Auth.js
|
|
37
37
|
|
|
38
38
|
## Documentation
|
|
39
39
|
|
|
40
|
-
- [StackKit Docs](https://stack-kit.dev
|
|
40
|
+
- [StackKit Docs](https://stack-kit.com)[https://stackkit.tariqul.dev]
|
|
41
41
|
- [GitHub Repository](https://github.com/tariqul420/stackkit)
|
package/dist/cli/add.js
CHANGED
|
@@ -15,13 +15,14 @@ const files_1 = require("../lib/fs/files");
|
|
|
15
15
|
const logger_1 = require("../lib/ui/logger");
|
|
16
16
|
const package_manager_1 = require("../lib/pm/package-manager");
|
|
17
17
|
const database_config_1 = require("../lib/database/database-config");
|
|
18
|
+
const package_root_1 = require("../lib/utils/package-root");
|
|
18
19
|
async function addCommand(module, options) {
|
|
19
20
|
try {
|
|
20
21
|
const projectRoot = process.cwd();
|
|
21
22
|
const spinner = logger_1.logger.startSpinner("Detecting project...");
|
|
22
23
|
const projectInfo = await (0, detect_1.detectProjectInfo)(projectRoot);
|
|
23
24
|
spinner.succeed(`Detected ${projectInfo.framework} (${projectInfo.router} router, ${projectInfo.language})`);
|
|
24
|
-
const moduleMetadata = await loadModuleMetadata(path_1.default.join(
|
|
25
|
+
const moduleMetadata = await loadModuleMetadata(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"), module, options.provider);
|
|
25
26
|
if (!moduleMetadata) {
|
|
26
27
|
logger_1.logger.error(`Module "${module}" not found`);
|
|
27
28
|
process.exit(1);
|
|
@@ -87,7 +88,7 @@ async function addCommand(module, options) {
|
|
|
87
88
|
logger_1.logger.warn("Dry run mode - no changes will be made");
|
|
88
89
|
logger_1.logger.newLine();
|
|
89
90
|
}
|
|
90
|
-
await applyModulePatches(projectRoot, projectInfo, moduleMetadata, path_1.default.join(
|
|
91
|
+
await applyModulePatches(projectRoot, projectInfo, moduleMetadata, path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"), module, options);
|
|
91
92
|
if (moduleMetadata.frameworkPatches && !options.dryRun) {
|
|
92
93
|
await applyFrameworkPatches(projectRoot, moduleMetadata.frameworkPatches, projectInfo.framework);
|
|
93
94
|
}
|
|
@@ -184,7 +185,6 @@ async function loadGeneratorAndMerge(metadata, modulePath) {
|
|
|
184
185
|
const generatorPath = path_1.default.join(modulePath, "generator.json");
|
|
185
186
|
if (await fs_extra_1.default.pathExists(generatorPath)) {
|
|
186
187
|
const generator = await fs_extra_1.default.readJSON(generatorPath);
|
|
187
|
-
// Merge envVars, dependencies, etc.
|
|
188
188
|
if (generator.envVars) {
|
|
189
189
|
metadata.envVars = metadata.envVars || [];
|
|
190
190
|
for (const [key, value] of Object.entries(generator.envVars)) {
|
|
@@ -197,10 +197,6 @@ async function loadGeneratorAndMerge(metadata, modulePath) {
|
|
|
197
197
|
if (generator.devDependencies) {
|
|
198
198
|
metadata.devDependencies = { ...metadata.devDependencies, ...generator.devDependencies };
|
|
199
199
|
}
|
|
200
|
-
if (generator.scripts) {
|
|
201
|
-
// Perhaps add to metadata, but currently not used
|
|
202
|
-
}
|
|
203
|
-
// For operations, perhaps add to patches or something, but for now, keep manual
|
|
204
200
|
}
|
|
205
201
|
return metadata;
|
|
206
202
|
}
|
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",
|
|
@@ -151,10 +136,13 @@ async function getProjectConfig(projectName, options) {
|
|
|
151
136
|
type: "list",
|
|
152
137
|
name: "framework",
|
|
153
138
|
message: "Select framework:",
|
|
154
|
-
choices: discoveredModules.frameworks.
|
|
155
|
-
name: f.displayName,
|
|
156
|
-
|
|
157
|
-
|
|
139
|
+
choices: (discoveredModules.frameworks && discoveredModules.frameworks.length > 0)
|
|
140
|
+
? discoveredModules.frameworks.map(f => ({ name: f.displayName, value: f.name }))
|
|
141
|
+
: [
|
|
142
|
+
{ name: 'Next.js', value: 'nextjs' },
|
|
143
|
+
{ name: 'Express.js', value: 'express' },
|
|
144
|
+
{ name: 'React (Vite)', value: 'react' },
|
|
145
|
+
],
|
|
158
146
|
},
|
|
159
147
|
{
|
|
160
148
|
type: "list",
|
|
@@ -232,7 +220,6 @@ async function generateProject(config, targetDir, options) {
|
|
|
232
220
|
copySpinner.fail("Failed to create project files");
|
|
233
221
|
throw error;
|
|
234
222
|
}
|
|
235
|
-
// Install dependencies
|
|
236
223
|
if (options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
|
|
237
224
|
const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
|
|
238
225
|
try {
|
|
@@ -244,7 +231,6 @@ async function generateProject(config, targetDir, options) {
|
|
|
244
231
|
throw error;
|
|
245
232
|
}
|
|
246
233
|
}
|
|
247
|
-
// Run post-install commands (skip if install was skipped)
|
|
248
234
|
if (postInstallCommands.length > 0 && options?.install !== false && !(options?.['skip-install'] || options?.skipInstall)) {
|
|
249
235
|
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
250
236
|
try {
|
|
@@ -258,7 +244,6 @@ async function generateProject(config, targetDir, options) {
|
|
|
258
244
|
throw error;
|
|
259
245
|
}
|
|
260
246
|
}
|
|
261
|
-
// Initialize git
|
|
262
247
|
if (options?.git !== false && !(options?.['no-git'] || options?.noGit)) {
|
|
263
248
|
const gitSpinner = logger_1.logger.startSpinner("Initializing git repository...");
|
|
264
249
|
try {
|
|
@@ -271,28 +256,13 @@ async function generateProject(config, targetDir, options) {
|
|
|
271
256
|
}
|
|
272
257
|
}
|
|
273
258
|
async function composeTemplate(config, targetDir) {
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
path_1.default.join(__dirname, "..", "..", "..", "..", "templates"),
|
|
278
|
-
];
|
|
279
|
-
const modulesCandidates2 = [
|
|
280
|
-
path_1.default.join(__dirname, "..", "..", "..", "modules"),
|
|
281
|
-
path_1.default.join(__dirname, "..", "..", "..", "..", "modules"),
|
|
282
|
-
];
|
|
283
|
-
const templatesDir = (await (async () => { for (const c of templatesCandidates)
|
|
284
|
-
if (await fs_extra_1.default.pathExists(c))
|
|
285
|
-
return c; return templatesCandidates[1]; })());
|
|
286
|
-
const modulesDirForGenerator = (await (async () => { for (const c of modulesCandidates2)
|
|
287
|
-
if (await fs_extra_1.default.pathExists(c))
|
|
288
|
-
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');
|
|
289
262
|
await fs_extra_1.default.ensureDir(targetDir);
|
|
290
|
-
// Load framework configuration
|
|
291
263
|
const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(config.framework, templatesDir);
|
|
292
|
-
// Initialize advanced code generator
|
|
293
264
|
const generator = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
|
|
294
265
|
await generator.loadGenerators(modulesDirForGenerator);
|
|
295
|
-
// Generate project using advanced code generator
|
|
296
266
|
const features = [];
|
|
297
267
|
const postInstallCommands = await generator.generate({
|
|
298
268
|
framework: config.framework,
|
|
@@ -300,14 +270,12 @@ async function composeTemplate(config, targetDir) {
|
|
|
300
270
|
auth: config.auth === 'none' ? undefined : config.auth,
|
|
301
271
|
prismaProvider: config.prismaProvider,
|
|
302
272
|
}, features, targetDir);
|
|
303
|
-
// Update project name in package.json
|
|
304
273
|
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
305
274
|
if (await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
306
275
|
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
307
276
|
packageJson.name = config.projectName;
|
|
308
277
|
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
309
278
|
}
|
|
310
|
-
// Ensure .env exists: if .env.example was copied from the template, create .env from it
|
|
311
279
|
try {
|
|
312
280
|
const envExamplePath = path_1.default.join(targetDir, ".env.example");
|
|
313
281
|
const envPath = path_1.default.join(targetDir, ".env");
|
|
@@ -317,12 +285,11 @@ async function composeTemplate(config, targetDir) {
|
|
|
317
285
|
}
|
|
318
286
|
}
|
|
319
287
|
catch {
|
|
320
|
-
//
|
|
288
|
+
// Non-fatal: .env creation from .env.example is optional
|
|
321
289
|
}
|
|
322
290
|
if (config.language === "javascript") {
|
|
323
291
|
await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
|
|
324
292
|
}
|
|
325
|
-
// For now, return empty array as post-install commands are handled by the generator
|
|
326
293
|
return postInstallCommands;
|
|
327
294
|
}
|
|
328
295
|
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) {
|
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")}`);
|
|
@@ -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",
|
|
@@ -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;
|
|
@@ -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 {
|
|
@@ -36,6 +36,7 @@ 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");
|
|
39
40
|
class AdvancedCodeGenerator {
|
|
40
41
|
constructor(frameworkConfig) {
|
|
41
42
|
this.generators = new Map();
|
|
@@ -44,7 +45,6 @@ class AdvancedCodeGenerator {
|
|
|
44
45
|
}
|
|
45
46
|
async loadGenerators(modulesPath) {
|
|
46
47
|
const moduleTypes = ['auth', 'database'];
|
|
47
|
-
// Load module generators
|
|
48
48
|
for (const type of moduleTypes) {
|
|
49
49
|
const typePath = path.join(modulesPath, type);
|
|
50
50
|
if (await fs.pathExists(typePath)) {
|
|
@@ -54,13 +54,11 @@ class AdvancedCodeGenerator {
|
|
|
54
54
|
if (await fs.pathExists(generatorPath)) {
|
|
55
55
|
try {
|
|
56
56
|
const config = await fs.readJson(generatorPath);
|
|
57
|
-
// Also load module.json for additional metadata like postInstall
|
|
58
57
|
const modulePath = path.join(typePath, moduleName, 'module.json');
|
|
59
58
|
if (await fs.pathExists(modulePath)) {
|
|
60
59
|
try {
|
|
61
60
|
const moduleConfig = await fs.readJson(modulePath);
|
|
62
61
|
if (moduleConfig.postInstall && Array.isArray(moduleConfig.postInstall)) {
|
|
63
|
-
// Store postInstall commands with the generator for later use
|
|
64
62
|
config.postInstall = moduleConfig.postInstall;
|
|
65
63
|
}
|
|
66
64
|
}
|
|
@@ -375,28 +373,18 @@ class AdvancedCodeGenerator {
|
|
|
375
373
|
}
|
|
376
374
|
}
|
|
377
375
|
async copyTemplate(frameworkName, outputPath) {
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
376
|
+
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
377
|
+
const templatePath = path.join(packageRoot, 'templates', frameworkName);
|
|
378
|
+
if (await fs.pathExists(templatePath)) {
|
|
379
|
+
await fs.copy(templatePath, outputPath, {
|
|
380
|
+
filter: (src) => {
|
|
381
|
+
const relativePath = path.relative(templatePath, src);
|
|
382
|
+
return relativePath !== 'template.json' &&
|
|
383
|
+
relativePath !== 'node_modules' &&
|
|
384
|
+
!relativePath.startsWith('node_modules/');
|
|
385
|
+
}
|
|
386
|
+
});
|
|
389
387
|
}
|
|
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
388
|
}
|
|
401
389
|
processOperationTemplates(operation, context) {
|
|
402
390
|
const processed = { ...operation };
|
|
@@ -456,27 +444,12 @@ class AdvancedCodeGenerator {
|
|
|
456
444
|
}
|
|
457
445
|
else if (operation.source) {
|
|
458
446
|
// Find the source file path relative to the module/template directory
|
|
459
|
-
|
|
460
|
-
const
|
|
461
|
-
|
|
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);
|
|
447
|
+
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
448
|
+
const modulesPath = path.join(packageRoot, 'modules');
|
|
449
|
+
const templatesPath = path.join(packageRoot, 'templates');
|
|
477
450
|
const moduleBasePath = operation.generatorType === 'framework'
|
|
478
|
-
? path.join(
|
|
479
|
-
: path.join(
|
|
451
|
+
? path.join(templatesPath, operation.generator)
|
|
452
|
+
: path.join(modulesPath, operation.generatorType, operation.generator);
|
|
480
453
|
const sourcePath = path.join(moduleBasePath, 'files', operation.source);
|
|
481
454
|
// Check if source file exists
|
|
482
455
|
if (await fs.pathExists(sourcePath)) {
|
|
@@ -551,7 +524,7 @@ class AdvancedCodeGenerator {
|
|
|
551
524
|
processedContentTop = this.processTemplate(patchOp.content, context).trim();
|
|
552
525
|
}
|
|
553
526
|
else if (patchOp.source) {
|
|
554
|
-
const modulesPath = path.join(
|
|
527
|
+
const modulesPath = path.join((0, package_root_1.getPackageRoot)(), 'modules');
|
|
555
528
|
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
|
|
556
529
|
if (await fs.pathExists(sourcePath)) {
|
|
557
530
|
processedContentTop = await fs.readFile(sourcePath, 'utf-8');
|
|
@@ -569,7 +542,7 @@ class AdvancedCodeGenerator {
|
|
|
569
542
|
processedContentBottom = this.processTemplate(patchOp.content, context).trim();
|
|
570
543
|
}
|
|
571
544
|
else if (patchOp.source) {
|
|
572
|
-
const modulesPath = path.join(
|
|
545
|
+
const modulesPath = path.join((0, package_root_1.getPackageRoot)(), 'modules');
|
|
573
546
|
const sourcePath = path.join(modulesPath, operation.generatorType, operation.generator, 'files', patchOp.source);
|
|
574
547
|
if (await fs.pathExists(sourcePath)) {
|
|
575
548
|
processedContentBottom = await fs.readFile(sourcePath, 'utf-8');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getPackageRoot(): string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getPackageRoot = getPackageRoot;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
function getPackageRoot() {
|
|
39
|
+
try {
|
|
40
|
+
return path.dirname(require.resolve('stackkit/package.json'));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return path.resolve(__dirname, '..', '..', '..');
|
|
44
|
+
}
|
|
45
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stackkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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": {
|
|
@@ -71,7 +71,6 @@
|
|
|
71
71
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
72
72
|
"typescript": "^5.3.3",
|
|
73
73
|
"recast": "^0.20.5",
|
|
74
|
-
"@babel/core": "^7.28.5",
|
|
75
74
|
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
76
75
|
"@babel/parser": "^7.28.5",
|
|
77
76
|
"@babel/plugin-transform-react-jsx": "^7.27.1"
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# React + Vite Template
|
|
2
|
-
|
|
3
|
-
Production-ready React starter with TypeScript, Vite, and essential libraries.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pnpm install
|
|
9
|
-
pnpm dev
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Features
|
|
13
|
-
|
|
14
|
-
- React 19 with TypeScript
|
|
15
|
-
- Vite for fast development
|
|
16
|
-
- React Router for routing
|
|
17
|
-
- TanStack Query for data fetching
|
|
18
|
-
- Tailwind CSS for styling
|
|
19
|
-
- ESLint for code quality
|
|
20
|
-
|
|
21
|
-
## Scripts
|
|
22
|
-
|
|
23
|
-
- `pnpm dev` - Start development server
|
|
24
|
-
- `pnpm build` - Build for production
|
|
25
|
-
- `pnpm preview` - Preview production build
|
|
26
|
-
- `pnpm lint` - Run linter
|
|
27
|
-
|
|
28
|
-
## Environment Variables
|
|
29
|
-
|
|
30
|
-
Copy `.env.example` to `.env` and configure:
|
|
31
|
-
|
|
32
|
-
```env
|
|
33
|
-
VITE_API_URL=http://localhost:3000/api
|
|
34
|
-
VITE_APP_NAME=My App
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Project Structure
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
src/
|
|
41
|
-
├── api/ # API client
|
|
42
|
-
├── components/ # UI components
|
|
43
|
-
├── hooks/ # Custom hooks
|
|
44
|
-
├── lib/ # Utilities
|
|
45
|
-
├── pages/ # Route pages
|
|
46
|
-
├── types/ # TypeScript types
|
|
47
|
-
└── utils/ # Helper functions
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Deployment
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
pnpm build
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Deploy the `dist` folder to Vercel, Netlify, or any static hosting service.
|