stackkit 0.1.4 → 0.1.5
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 +1 -1
- package/dist/cli/add.js +42 -31
- package/dist/cli/create.d.ts +2 -2
- package/dist/cli/create.js +94 -27
- package/dist/cli/doctor.js +25 -13
- package/dist/cli/list.js +14 -2
- package/dist/index.js +16 -16
- package/dist/lib/conversion/js-conversion.js +1 -1
- package/dist/lib/discovery/module-discovery.js +35 -35
- package/dist/lib/env/env-editor.js +1 -1
- package/dist/lib/framework/framework-utils.d.ts +1 -1
- package/dist/lib/framework/framework-utils.js +3 -3
- package/dist/lib/generation/code-generator.d.ts +5 -5
- package/dist/lib/generation/code-generator.js +166 -132
- package/dist/lib/generation/generator-utils.d.ts +2 -2
- package/dist/lib/generation/generator-utils.js +20 -12
- package/dist/lib/utils/package-root.js +2 -2
- package/modules/auth/better-auth/files/lib/auth.ts +37 -30
- package/modules/auth/better-auth/files/prisma/schema.prisma +3 -3
- package/modules/auth/better-auth/generator.json +46 -16
- package/modules/database/mongoose/generator.json +23 -4
- package/modules/database/prisma/files/lib/prisma.ts +26 -21
- package/modules/database/prisma/files/prisma.config.ts +17 -0
- package/modules/database/prisma/generator.json +50 -11
- package/package.json +1 -1
- package/templates/express/src/server.ts +9 -3
- package/templates/express/tsconfig.json +2 -2
- package/templates/nextjs/lib/env.ts +1 -1
package/README.md
CHANGED
package/dist/cli/add.js
CHANGED
|
@@ -43,12 +43,13 @@ async function addCommand(module, options) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
async function getAddConfig(module, options, projectInfo) {
|
|
46
|
-
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(),
|
|
46
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
47
47
|
const argv = process.argv.slice(2);
|
|
48
|
-
const addIndex = argv.indexOf(
|
|
48
|
+
const addIndex = argv.indexOf("add");
|
|
49
49
|
const argsAfterAdd = addIndex >= 0 ? argv.slice(addIndex + 1) : [];
|
|
50
|
-
const flagsProvided = argsAfterAdd.some(arg => arg.startsWith(
|
|
51
|
-
const optionsProvided = flagsProvided ||
|
|
50
|
+
const flagsProvided = argsAfterAdd.some((arg) => arg.startsWith("-"));
|
|
51
|
+
const optionsProvided = flagsProvided ||
|
|
52
|
+
!!(options && (options.yes || options.provider || options.force || options.dryRun));
|
|
52
53
|
if (optionsProvided) {
|
|
53
54
|
if (!module) {
|
|
54
55
|
throw new Error("Module name is required when using flags");
|
|
@@ -60,8 +61,8 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
60
61
|
}
|
|
61
62
|
let baseProvider = options.provider;
|
|
62
63
|
let adapterProvider = options.provider;
|
|
63
|
-
if (options.provider.includes(
|
|
64
|
-
const parts = options.provider.split(
|
|
64
|
+
if (options.provider.includes("-")) {
|
|
65
|
+
const parts = options.provider.split("-");
|
|
65
66
|
baseProvider = parts[0]; // e.g., "prisma"
|
|
66
67
|
adapterProvider = options.provider; // e.g., "prisma-postgresql"
|
|
67
68
|
}
|
|
@@ -72,7 +73,7 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
72
73
|
return {
|
|
73
74
|
module: "database",
|
|
74
75
|
provider: adapterProvider,
|
|
75
|
-
displayName: `${moduleMetadata.displayName} (${adapterProvider.split(
|
|
76
|
+
displayName: `${moduleMetadata.displayName} (${adapterProvider.split("-")[1] || adapterProvider})`,
|
|
76
77
|
metadata: moduleMetadata,
|
|
77
78
|
};
|
|
78
79
|
}
|
|
@@ -99,7 +100,8 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
99
100
|
selectedProvider = module;
|
|
100
101
|
}
|
|
101
102
|
if (moduleMetadata.category === "database" && !selectedProvider) {
|
|
102
|
-
if (typeof moduleMetadata.dependencies === "object" &&
|
|
103
|
+
if (typeof moduleMetadata.dependencies === "object" &&
|
|
104
|
+
"providers" in moduleMetadata.dependencies) {
|
|
103
105
|
const providers = Object.keys(moduleMetadata.dependencies.providers || {});
|
|
104
106
|
if (providers.length > 0) {
|
|
105
107
|
const { provider } = await inquirer_1.default.prompt([
|
|
@@ -167,7 +169,7 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
167
169
|
module: "database",
|
|
168
170
|
provider: "prisma",
|
|
169
171
|
displayName: `Prisma (${providerAnswers.provider})`,
|
|
170
|
-
metadata: await loadModuleMetadata(modulesDir, "prisma", "prisma"),
|
|
172
|
+
metadata: (await loadModuleMetadata(modulesDir, "prisma", "prisma")),
|
|
171
173
|
};
|
|
172
174
|
}
|
|
173
175
|
else {
|
|
@@ -175,7 +177,7 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
175
177
|
module: "database",
|
|
176
178
|
provider: "mongoose",
|
|
177
179
|
displayName: "Mongoose",
|
|
178
|
-
metadata: await loadModuleMetadata(modulesDir, "mongoose", "mongoose"),
|
|
180
|
+
metadata: (await loadModuleMetadata(modulesDir, "mongoose", "mongoose")),
|
|
179
181
|
};
|
|
180
182
|
}
|
|
181
183
|
}
|
|
@@ -230,20 +232,26 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
|
230
232
|
logger_1.logger.warn("Dry run mode - no changes will be made");
|
|
231
233
|
logger_1.logger.newLine();
|
|
232
234
|
}
|
|
233
|
-
const moduleBasePath = await findModulePath(path_1.default.join((0, package_root_1.getPackageRoot)(),
|
|
235
|
+
const moduleBasePath = await findModulePath(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"), config.module, config.provider);
|
|
234
236
|
if (moduleBasePath) {
|
|
235
|
-
const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(projectInfo.framework, path_1.default.join((0, package_root_1.getPackageRoot)(),
|
|
237
|
+
const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(projectInfo.framework, path_1.default.join((0, package_root_1.getPackageRoot)(), "templates"));
|
|
236
238
|
const gen = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
|
|
237
|
-
await gen.loadGenerators(path_1.default.join((0, package_root_1.getPackageRoot)(),
|
|
239
|
+
await gen.loadGenerators(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"));
|
|
238
240
|
const moduleName = path_1.default.basename(moduleBasePath);
|
|
239
241
|
const available = gen.getAvailableGenerators();
|
|
240
|
-
const alreadyRegistered = (config.module ===
|
|
242
|
+
const alreadyRegistered = (config.module === "database" && available.databases.includes(moduleName)) ||
|
|
243
|
+
(config.module === "auth" && available.auths.includes(moduleName));
|
|
241
244
|
if (!alreadyRegistered) {
|
|
242
245
|
const ops = [];
|
|
243
246
|
if (Array.isArray(moduleMetadata.patches)) {
|
|
244
247
|
for (const p of moduleMetadata.patches) {
|
|
245
|
-
if (p.type ===
|
|
246
|
-
ops.push({
|
|
248
|
+
if (p.type === "create-file") {
|
|
249
|
+
ops.push({
|
|
250
|
+
type: "create-file",
|
|
251
|
+
source: p.source,
|
|
252
|
+
destination: p.destination,
|
|
253
|
+
condition: p.condition,
|
|
254
|
+
});
|
|
247
255
|
}
|
|
248
256
|
}
|
|
249
257
|
}
|
|
@@ -258,40 +266,40 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
|
258
266
|
}
|
|
259
267
|
}
|
|
260
268
|
const selectedModules = { framework: projectInfo.framework };
|
|
261
|
-
if (config.module ===
|
|
262
|
-
if (config.provider.startsWith(
|
|
263
|
-
selectedModules.database =
|
|
264
|
-
selectedModules.prismaProvider = config.provider.split(
|
|
269
|
+
if (config.module === "database" && config.provider) {
|
|
270
|
+
if (config.provider.startsWith("prisma-")) {
|
|
271
|
+
selectedModules.database = "prisma";
|
|
272
|
+
selectedModules.prismaProvider = config.provider.split("-")[1];
|
|
265
273
|
}
|
|
266
274
|
else {
|
|
267
275
|
selectedModules.database = config.provider;
|
|
268
276
|
}
|
|
269
277
|
}
|
|
270
|
-
if (config.module ===
|
|
278
|
+
if (config.module === "auth" && config.provider) {
|
|
271
279
|
selectedModules.auth = config.provider;
|
|
272
280
|
}
|
|
273
281
|
const postInstall = await gen.applyToProject(selectedModules, [], projectRoot);
|
|
274
282
|
if (postInstall && postInstall.length > 0 && !options?.dryRun) {
|
|
275
|
-
const postInstallSpinner = logger_1.logger.startSpinner(
|
|
283
|
+
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
276
284
|
try {
|
|
277
285
|
for (const command of postInstall) {
|
|
278
|
-
(0, child_process_1.execSync)(command, { cwd: projectRoot, stdio:
|
|
286
|
+
(0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
|
|
279
287
|
}
|
|
280
|
-
postInstallSpinner.succeed(
|
|
288
|
+
postInstallSpinner.succeed("Post-install commands completed");
|
|
281
289
|
}
|
|
282
290
|
catch (error) {
|
|
283
|
-
postInstallSpinner.fail(
|
|
291
|
+
postInstallSpinner.fail("Failed to run post-install commands");
|
|
284
292
|
throw error;
|
|
285
293
|
}
|
|
286
294
|
}
|
|
287
295
|
if (!options?.dryRun && options?.install !== false) {
|
|
288
|
-
const installSpinner = logger_1.logger.startSpinner(
|
|
296
|
+
const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
|
|
289
297
|
try {
|
|
290
298
|
await (0, package_manager_1.installDependencies)(projectRoot, projectInfo.packageManager);
|
|
291
|
-
installSpinner.succeed(
|
|
299
|
+
installSpinner.succeed("Dependencies installed");
|
|
292
300
|
}
|
|
293
301
|
catch (err) {
|
|
294
|
-
installSpinner.fail(
|
|
302
|
+
installSpinner.fail("Failed to install dependencies");
|
|
295
303
|
throw err;
|
|
296
304
|
}
|
|
297
305
|
}
|
|
@@ -314,15 +322,18 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
|
314
322
|
if (moduleMetadata.envVars) {
|
|
315
323
|
const envArray = Array.isArray(moduleMetadata.envVars)
|
|
316
324
|
? moduleMetadata.envVars
|
|
317
|
-
: Object.entries(moduleMetadata.envVars).map(([k, v]) => ({
|
|
325
|
+
: Object.entries(moduleMetadata.envVars).map(([k, v]) => ({
|
|
326
|
+
key: k,
|
|
327
|
+
value: String(v),
|
|
328
|
+
}));
|
|
318
329
|
for (const ev of envArray) {
|
|
319
|
-
if (ev.key && typeof ev.value ===
|
|
330
|
+
if (ev.key && typeof ev.value === "string")
|
|
320
331
|
variables[ev.key] = ev.value;
|
|
321
332
|
}
|
|
322
333
|
for (let pass = 0; pass < 5; pass++) {
|
|
323
334
|
let changed = false;
|
|
324
335
|
for (const ev of envArray) {
|
|
325
|
-
if (!ev.key || typeof ev.value !==
|
|
336
|
+
if (!ev.key || typeof ev.value !== "string")
|
|
326
337
|
continue;
|
|
327
338
|
const resolved = ev.value.replace(/\{\{(\w+)\}\}/g, (_m, k) => variables[k] ?? _m);
|
|
328
339
|
if (variables[ev.key] !== resolved) {
|
package/dist/cli/create.d.ts
CHANGED
|
@@ -10,10 +10,10 @@ interface CliOptions {
|
|
|
10
10
|
packageManager?: string;
|
|
11
11
|
p?: string;
|
|
12
12
|
install?: boolean;
|
|
13
|
-
|
|
13
|
+
"skip-install"?: boolean;
|
|
14
14
|
skipInstall?: boolean;
|
|
15
15
|
git?: boolean;
|
|
16
|
-
|
|
16
|
+
"no-git"?: boolean;
|
|
17
17
|
noGit?: boolean;
|
|
18
18
|
yes?: boolean;
|
|
19
19
|
y?: boolean;
|
package/dist/cli/create.js
CHANGED
|
@@ -18,6 +18,7 @@ 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
20
|
const package_root_1 = require("../lib/utils/package-root");
|
|
21
|
+
const env_editor_1 = require("../lib/env/env-editor");
|
|
21
22
|
async function createProject(projectName, options) {
|
|
22
23
|
logger_1.logger.newLine();
|
|
23
24
|
logger_1.logger.log(chalk_1.default.bold.cyan("📦 Create StackKit App"));
|
|
@@ -30,18 +31,19 @@ async function createProject(projectName, options) {
|
|
|
30
31
|
process.exit(1);
|
|
31
32
|
}
|
|
32
33
|
await generateProject(config, targetDir, options);
|
|
34
|
+
await processGeneratorEnvVars(config, targetDir);
|
|
33
35
|
showNextSteps(config);
|
|
34
36
|
}
|
|
35
37
|
async function getProjectConfig(projectName, options) {
|
|
36
|
-
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(),
|
|
38
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
37
39
|
const discoveredModules = await (0, module_discovery_1.discoverModules)(modulesDir);
|
|
38
40
|
const argv = process.argv.slice(2);
|
|
39
|
-
const createIndex = argv.indexOf(
|
|
41
|
+
const createIndex = argv.indexOf("create");
|
|
40
42
|
const argsAfterCreate = createIndex >= 0 ? argv.slice(createIndex + 1) : [];
|
|
41
|
-
const flagsProvided = argsAfterCreate.some(arg => arg.startsWith(
|
|
43
|
+
const flagsProvided = argsAfterCreate.some((arg) => arg.startsWith("-"));
|
|
42
44
|
const optionsProvided = flagsProvided || !!(options && (options.yes || options.y));
|
|
43
45
|
if (optionsProvided) {
|
|
44
|
-
if (
|
|
46
|
+
if (options && (options.yes || options.y) && !flagsProvided) {
|
|
45
47
|
return {
|
|
46
48
|
projectName: projectName || "my-app",
|
|
47
49
|
framework: "nextjs",
|
|
@@ -54,37 +56,37 @@ async function getProjectConfig(projectName, options) {
|
|
|
54
56
|
}
|
|
55
57
|
const framework = (options && (options.framework || options.f)) || undefined;
|
|
56
58
|
if (discoveredModules.frameworks && discoveredModules.frameworks.length > 0) {
|
|
57
|
-
const validFrameworks = discoveredModules.frameworks.map(f => f.name);
|
|
59
|
+
const validFrameworks = discoveredModules.frameworks.map((f) => f.name);
|
|
58
60
|
if (framework && !validFrameworks.includes(framework)) {
|
|
59
|
-
throw new Error(`Invalid framework: ${framework}. Valid options: ${validFrameworks.join(
|
|
61
|
+
throw new Error(`Invalid framework: ${framework}. Valid options: ${validFrameworks.join(", ")}`);
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
const db = (options && (options.database || options.d)) || undefined;
|
|
63
65
|
let allValidDatabases = [];
|
|
64
66
|
if (discoveredModules.databases && discoveredModules.databases.length > 0) {
|
|
65
67
|
const validDatabases = (0, module_discovery_1.getValidDatabaseOptions)(discoveredModules.databases);
|
|
66
|
-
const validBaseDatabases = discoveredModules.databases.map(db => db.name);
|
|
68
|
+
const validBaseDatabases = discoveredModules.databases.map((db) => db.name);
|
|
67
69
|
allValidDatabases = [...validDatabases, ...validBaseDatabases];
|
|
68
70
|
if (db && !allValidDatabases.includes(db)) {
|
|
69
|
-
throw new Error(`Invalid database: ${db}. Valid options: ${allValidDatabases.filter((v, i, arr) => arr.indexOf(v) === i).join(
|
|
71
|
+
throw new Error(`Invalid database: ${db}. Valid options: ${allValidDatabases.filter((v, i, arr) => arr.indexOf(v) === i).join(", ")}`);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
const authOpt = (options && (options.auth || options.a)) || undefined;
|
|
73
75
|
if (discoveredModules.auth && discoveredModules.auth.length > 0) {
|
|
74
76
|
const validAuth = (0, module_discovery_1.getValidAuthOptions)(discoveredModules.auth);
|
|
75
77
|
if (authOpt && !validAuth.includes(authOpt)) {
|
|
76
|
-
throw new Error(`Invalid auth: ${authOpt}. Valid options: ${validAuth.join(
|
|
78
|
+
throw new Error(`Invalid auth: ${authOpt}. Valid options: ${validAuth.join(", ")}`);
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
|
-
const validLanguages = [
|
|
81
|
+
const validLanguages = ["typescript", "javascript"];
|
|
80
82
|
const language = (options && (options.language || options.l)) || undefined;
|
|
81
83
|
if (language && !validLanguages.includes(language)) {
|
|
82
|
-
throw new Error(`Invalid language: ${language}. Valid options: ${validLanguages.join(
|
|
84
|
+
throw new Error(`Invalid language: ${language}. Valid options: ${validLanguages.join(", ")}`);
|
|
83
85
|
}
|
|
84
|
-
const validPackageManagers = [
|
|
86
|
+
const validPackageManagers = ["pnpm", "npm", "yarn", "bun"];
|
|
85
87
|
const pm = (options && (options.packageManager || options.p)) || undefined;
|
|
86
88
|
if (pm && !validPackageManagers.includes(pm)) {
|
|
87
|
-
throw new Error(`Invalid package manager: ${pm}. Valid options: ${validPackageManagers.join(
|
|
89
|
+
throw new Error(`Invalid package manager: ${pm}. Valid options: ${validPackageManagers.join(", ")}`);
|
|
88
90
|
}
|
|
89
91
|
let database = "none";
|
|
90
92
|
let prismaProvider;
|
|
@@ -136,12 +138,12 @@ async function getProjectConfig(projectName, options) {
|
|
|
136
138
|
type: "list",
|
|
137
139
|
name: "framework",
|
|
138
140
|
message: "Select framework:",
|
|
139
|
-
choices:
|
|
140
|
-
? discoveredModules.frameworks.map(f => ({ name: f.displayName, value: f.name }))
|
|
141
|
+
choices: discoveredModules.frameworks && discoveredModules.frameworks.length > 0
|
|
142
|
+
? discoveredModules.frameworks.map((f) => ({ name: f.displayName, value: f.name }))
|
|
141
143
|
: [
|
|
142
|
-
{ name:
|
|
143
|
-
{ name:
|
|
144
|
-
{ name:
|
|
144
|
+
{ name: "Next.js", value: "nextjs" },
|
|
145
|
+
{ name: "Express.js", value: "express" },
|
|
146
|
+
{ name: "React (Vite)", value: "react" },
|
|
145
147
|
],
|
|
146
148
|
},
|
|
147
149
|
{
|
|
@@ -171,7 +173,7 @@ async function getProjectConfig(projectName, options) {
|
|
|
171
173
|
type: "list",
|
|
172
174
|
name: "auth",
|
|
173
175
|
message: "Select authentication:",
|
|
174
|
-
when: (answers) =>
|
|
176
|
+
when: (answers) => answers.database !== "none" || answers.framework === "react",
|
|
175
177
|
choices: (answers) => (0, module_discovery_1.getCompatibleAuthOptions)(discoveredModules.auth, answers.framework, answers.database || "none"),
|
|
176
178
|
},
|
|
177
179
|
{
|
|
@@ -204,7 +206,7 @@ async function getProjectConfig(projectName, options) {
|
|
|
204
206
|
? "none"
|
|
205
207
|
: answers.database),
|
|
206
208
|
prismaProvider: answers.prismaProvider,
|
|
207
|
-
auth:
|
|
209
|
+
auth: answers.auth || "none",
|
|
208
210
|
language: answers.language,
|
|
209
211
|
packageManager: answers.packageManager,
|
|
210
212
|
};
|
|
@@ -220,7 +222,7 @@ async function generateProject(config, targetDir, options) {
|
|
|
220
222
|
copySpinner.fail("Failed to create project files");
|
|
221
223
|
throw error;
|
|
222
224
|
}
|
|
223
|
-
if (options?.install !== false && !(options?.[
|
|
225
|
+
if (options?.install !== false && !(options?.["skip-install"] || options?.skipInstall)) {
|
|
224
226
|
const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
|
|
225
227
|
try {
|
|
226
228
|
await (0, package_manager_1.installDependencies)(targetDir, config.packageManager);
|
|
@@ -231,7 +233,9 @@ async function generateProject(config, targetDir, options) {
|
|
|
231
233
|
throw error;
|
|
232
234
|
}
|
|
233
235
|
}
|
|
234
|
-
if (postInstallCommands.length > 0 &&
|
|
236
|
+
if (postInstallCommands.length > 0 &&
|
|
237
|
+
options?.install !== false &&
|
|
238
|
+
!(options?.["skip-install"] || options?.skipInstall)) {
|
|
235
239
|
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
236
240
|
try {
|
|
237
241
|
for (const command of postInstallCommands) {
|
|
@@ -244,7 +248,7 @@ async function generateProject(config, targetDir, options) {
|
|
|
244
248
|
throw error;
|
|
245
249
|
}
|
|
246
250
|
}
|
|
247
|
-
if (options?.git !== false && !(options?.[
|
|
251
|
+
if (options?.git !== false && !(options?.["no-git"] || options?.noGit)) {
|
|
248
252
|
const gitSpinner = logger_1.logger.startSpinner("Initializing git repository...");
|
|
249
253
|
try {
|
|
250
254
|
await (0, git_utils_1.initGit)(targetDir);
|
|
@@ -257,8 +261,8 @@ async function generateProject(config, targetDir, options) {
|
|
|
257
261
|
}
|
|
258
262
|
async function composeTemplate(config, targetDir) {
|
|
259
263
|
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
260
|
-
const templatesDir = path_1.default.join(packageRoot,
|
|
261
|
-
const modulesDirForGenerator = path_1.default.join(packageRoot,
|
|
264
|
+
const templatesDir = path_1.default.join(packageRoot, "templates");
|
|
265
|
+
const modulesDirForGenerator = path_1.default.join(packageRoot, "modules");
|
|
262
266
|
await fs_extra_1.default.ensureDir(targetDir);
|
|
263
267
|
const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(config.framework, templatesDir);
|
|
264
268
|
const generator = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
|
|
@@ -266,8 +270,8 @@ async function composeTemplate(config, targetDir) {
|
|
|
266
270
|
const features = [];
|
|
267
271
|
const postInstallCommands = await generator.generate({
|
|
268
272
|
framework: config.framework,
|
|
269
|
-
database: config.database ===
|
|
270
|
-
auth: config.auth ===
|
|
273
|
+
database: config.database === "none" ? undefined : config.database,
|
|
274
|
+
auth: config.auth === "none" ? undefined : config.auth,
|
|
271
275
|
prismaProvider: config.prismaProvider,
|
|
272
276
|
}, features, targetDir);
|
|
273
277
|
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
@@ -285,12 +289,75 @@ async function composeTemplate(config, targetDir) {
|
|
|
285
289
|
}
|
|
286
290
|
}
|
|
287
291
|
catch {
|
|
292
|
+
// env copy failed.
|
|
288
293
|
}
|
|
289
294
|
if (config.language === "javascript") {
|
|
290
295
|
await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
|
|
291
296
|
}
|
|
292
297
|
return postInstallCommands;
|
|
293
298
|
}
|
|
299
|
+
async function processGeneratorEnvVars(config, targetDir) {
|
|
300
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
301
|
+
const envVars = [];
|
|
302
|
+
// Process database generator env vars
|
|
303
|
+
if (config.database && config.database !== "none") {
|
|
304
|
+
const dbGeneratorPath = path_1.default.join(modulesDir, "database", config.database, "generator.json");
|
|
305
|
+
if (await fs_extra_1.default.pathExists(dbGeneratorPath)) {
|
|
306
|
+
const generator = await fs_extra_1.default.readJson(dbGeneratorPath);
|
|
307
|
+
if (generator.operations) {
|
|
308
|
+
for (const operation of generator.operations) {
|
|
309
|
+
if (operation.type === "add-env" && (!operation.condition || checkCondition(operation.condition, config))) {
|
|
310
|
+
for (const [key, value] of Object.entries(operation.envVars)) {
|
|
311
|
+
envVars.push({
|
|
312
|
+
key,
|
|
313
|
+
value: value,
|
|
314
|
+
required: true,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Process auth generator env vars
|
|
323
|
+
if (config.auth && config.auth !== "none") {
|
|
324
|
+
const authGeneratorPath = path_1.default.join(modulesDir, "auth", config.auth, "generator.json");
|
|
325
|
+
if (await fs_extra_1.default.pathExists(authGeneratorPath)) {
|
|
326
|
+
const generator = await fs_extra_1.default.readJson(authGeneratorPath);
|
|
327
|
+
if (generator.operations) {
|
|
328
|
+
for (const operation of generator.operations) {
|
|
329
|
+
if (operation.type === "add-env" && (!operation.condition || checkCondition(operation.condition, config))) {
|
|
330
|
+
for (const [key, value] of Object.entries(operation.envVars)) {
|
|
331
|
+
envVars.push({
|
|
332
|
+
key,
|
|
333
|
+
value: value,
|
|
334
|
+
required: true,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (envVars.length > 0) {
|
|
343
|
+
await (0, env_editor_1.addEnvVariables)(targetDir, envVars, { force: true });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function checkCondition(condition, config) {
|
|
347
|
+
for (const [key, value] of Object.entries(condition)) {
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
if (!value.includes(config[key])) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
if (config[key] !== value) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
294
361
|
function showNextSteps(config) {
|
|
295
362
|
logger_1.logger.newLine();
|
|
296
363
|
logger_1.logger.success(`Created ${config.projectName}`);
|
package/dist/cli/doctor.js
CHANGED
|
@@ -96,7 +96,7 @@ async function runDoctorChecks() {
|
|
|
96
96
|
const gitCheck = await checkGitRepo(projectRoot);
|
|
97
97
|
checks.push(gitCheck);
|
|
98
98
|
const conflicts = checkConflicts(authModules, databaseModules);
|
|
99
|
-
conflicts.forEach(conflict => {
|
|
99
|
+
conflicts.forEach((conflict) => {
|
|
100
100
|
checks.push({
|
|
101
101
|
status: "warning",
|
|
102
102
|
message: conflict,
|
|
@@ -118,8 +118,11 @@ async function runDoctorChecks() {
|
|
|
118
118
|
},
|
|
119
119
|
files: {
|
|
120
120
|
envExample: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example")),
|
|
121
|
-
env: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env")) ||
|
|
122
|
-
|
|
121
|
+
env: (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env"))) ||
|
|
122
|
+
(await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.local"))),
|
|
123
|
+
prismaSchema: databaseModules.includes("prisma")
|
|
124
|
+
? await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"))
|
|
125
|
+
: undefined,
|
|
123
126
|
authRoutes: authModules.length > 0 ? await checkAuthRoutesExist(projectRoot, projectType) : undefined,
|
|
124
127
|
tsconfig: await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "tsconfig.json")),
|
|
125
128
|
eslintConfig: await checkEslintConfigExists(projectRoot),
|
|
@@ -132,8 +135,8 @@ async function runDoctorChecks() {
|
|
|
132
135
|
conflicts,
|
|
133
136
|
checks,
|
|
134
137
|
summary: {
|
|
135
|
-
errors: checks.filter(c => c.status === "error").length,
|
|
136
|
-
warnings: checks.filter(c => c.status === "warning").length,
|
|
138
|
+
errors: checks.filter((c) => c.status === "error").length,
|
|
139
|
+
warnings: checks.filter((c) => c.status === "warning").length,
|
|
137
140
|
suggestions: generateSuggestions(),
|
|
138
141
|
},
|
|
139
142
|
};
|
|
@@ -232,7 +235,9 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
|
|
|
232
235
|
const envExampleExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, ".env.example"));
|
|
233
236
|
checks.push({
|
|
234
237
|
status: envExampleExists ? "success" : "warning",
|
|
235
|
-
message: envExampleExists
|
|
238
|
+
message: envExampleExists
|
|
239
|
+
? ".env.example file found"
|
|
240
|
+
: ".env.example file missing (recommended for documentation)",
|
|
236
241
|
});
|
|
237
242
|
if (databaseModules.includes("prisma")) {
|
|
238
243
|
const schemaExists = await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, "prisma", "schema.prisma"));
|
|
@@ -245,7 +250,9 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
|
|
|
245
250
|
const authRoutesExist = await checkAuthRoutesExist(projectRoot, projectType);
|
|
246
251
|
checks.push({
|
|
247
252
|
status: authRoutesExist ? "success" : "warning",
|
|
248
|
-
message: authRoutesExist
|
|
253
|
+
message: authRoutesExist
|
|
254
|
+
? "Auth routes configured"
|
|
255
|
+
: "Auth routes not found (may need configuration)",
|
|
249
256
|
});
|
|
250
257
|
}
|
|
251
258
|
return checks;
|
|
@@ -300,8 +307,11 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
|
|
|
300
307
|
});
|
|
301
308
|
return { checks, envStatus: { missing: requiredKeys, present: [] } };
|
|
302
309
|
}
|
|
303
|
-
const envLines = envContent
|
|
304
|
-
|
|
310
|
+
const envLines = envContent
|
|
311
|
+
.split("\n")
|
|
312
|
+
.map((line) => line.trim())
|
|
313
|
+
.filter((line) => line && !line.startsWith("#"));
|
|
314
|
+
const envVars = new Set(envLines.map((line) => line.split("=")[0]));
|
|
305
315
|
for (const key of requiredKeys) {
|
|
306
316
|
if (envVars.has(key)) {
|
|
307
317
|
present.push(key);
|
|
@@ -346,7 +356,9 @@ async function checkConfigFiles(projectRoot, projectType, packageJson) {
|
|
|
346
356
|
status: eslintExists ? "success" : "warning",
|
|
347
357
|
message: eslintExists ? MESSAGES.ESLINT_CONFIG_FOUND : MESSAGES.ESLINT_CONFIG_MISSING,
|
|
348
358
|
});
|
|
349
|
-
const hasBuildScript = packageJson.scripts &&
|
|
359
|
+
const hasBuildScript = packageJson.scripts &&
|
|
360
|
+
typeof packageJson.scripts === "object" &&
|
|
361
|
+
"build" in packageJson.scripts;
|
|
350
362
|
checks.push({
|
|
351
363
|
status: hasBuildScript ? "success" : "warning",
|
|
352
364
|
message: hasBuildScript ? MESSAGES.BUILD_SCRIPT_FOUND : MESSAGES.BUILD_SCRIPT_MISSING,
|
|
@@ -521,7 +533,7 @@ function printDoctorReport(report, verbose) {
|
|
|
521
533
|
// Conflicts
|
|
522
534
|
if (report.conflicts.length > 0) {
|
|
523
535
|
logger_1.logger.log(chalk_1.default.bold("Conflicts"));
|
|
524
|
-
report.conflicts.forEach(conflict => {
|
|
536
|
+
report.conflicts.forEach((conflict) => {
|
|
525
537
|
logger_1.logger.warn(conflict);
|
|
526
538
|
});
|
|
527
539
|
logger_1.logger.newLine();
|
|
@@ -529,7 +541,7 @@ function printDoctorReport(report, verbose) {
|
|
|
529
541
|
// Detailed checks if verbose
|
|
530
542
|
if (verbose) {
|
|
531
543
|
logger_1.logger.log(chalk_1.default.bold("Detailed Checks"));
|
|
532
|
-
report.checks.forEach(check => {
|
|
544
|
+
report.checks.forEach((check) => {
|
|
533
545
|
if (check.status === "success") {
|
|
534
546
|
logger_1.logger.success(check.message);
|
|
535
547
|
}
|
|
@@ -552,7 +564,7 @@ function printDoctorReport(report, verbose) {
|
|
|
552
564
|
logger_1.logger.newLine();
|
|
553
565
|
if (report.summary.suggestions.length > 0) {
|
|
554
566
|
logger_1.logger.log(chalk_1.default.bold("Suggestions"));
|
|
555
|
-
report.summary.suggestions.forEach(suggestion => {
|
|
567
|
+
report.summary.suggestions.forEach((suggestion) => {
|
|
556
568
|
logger_1.logger.log(` • ${suggestion}`);
|
|
557
569
|
});
|
|
558
570
|
}
|
package/dist/cli/list.js
CHANGED
|
@@ -52,10 +52,22 @@ async function listCommand(options) {
|
|
|
52
52
|
logger_1.logger.log(` ${chalk_1.default.gray(categoryPrefix)} ${chalk_1.default.yellow(formatCategoryName(category))} ${chalk_1.default.dim(`(${mods.length})`)}`);
|
|
53
53
|
mods.forEach((mod, modIndex) => {
|
|
54
54
|
const isLastMod = modIndex === mods.length - 1;
|
|
55
|
-
const modPrefix = isLastCategory
|
|
55
|
+
const modPrefix = isLastCategory
|
|
56
|
+
? isLastMod
|
|
57
|
+
? " └──"
|
|
58
|
+
: " ├──"
|
|
59
|
+
: isLastMod
|
|
60
|
+
? "│ └──"
|
|
61
|
+
: "│ ├──";
|
|
56
62
|
logger_1.logger.log(` ${chalk_1.default.gray(modPrefix)} ${chalk_1.default.green(mod.displayName)}`);
|
|
57
63
|
if (mod.category === "database" && mod.name === "prisma") {
|
|
58
|
-
const providerPrefix = isLastCategory
|
|
64
|
+
const providerPrefix = isLastCategory
|
|
65
|
+
? isLastMod
|
|
66
|
+
? " └──"
|
|
67
|
+
: " ├──"
|
|
68
|
+
: isLastMod
|
|
69
|
+
? "│ └──"
|
|
70
|
+
: "│ ├──";
|
|
59
71
|
logger_1.logger.log(` ${chalk_1.default.gray(providerPrefix)} ${chalk_1.default.dim("Providers: PostgreSQL, MongoDB, MySQL, SQLite")}`);
|
|
60
72
|
}
|
|
61
73
|
});
|
package/dist/index.js
CHANGED
|
@@ -11,28 +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.5")
|
|
15
15
|
.configureHelp({
|
|
16
16
|
subcommandTerm: (cmd) => {
|
|
17
17
|
const name = cmd.name();
|
|
18
|
-
if (name ===
|
|
19
|
-
return
|
|
20
|
-
if (name ===
|
|
21
|
-
return
|
|
22
|
-
if (name ===
|
|
23
|
-
return
|
|
24
|
-
if (name ===
|
|
25
|
-
return
|
|
26
|
-
if (name ===
|
|
27
|
-
return
|
|
28
|
-
return name +
|
|
29
|
-
}
|
|
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
30
|
});
|
|
31
31
|
// Create command
|
|
32
32
|
program
|
|
33
|
-
.command(
|
|
33
|
+
.command("create [project-name]")
|
|
34
34
|
.description("Create a new StackKit project")
|
|
35
|
-
.usage(
|
|
35
|
+
.usage("[project-name] [options]")
|
|
36
36
|
.option("-f, --framework <framework>", "Framework: nextjs, express, react")
|
|
37
37
|
.option("-d, --database <database>", "Database: prisma, mongoose, none")
|
|
38
38
|
.option("--prisma-provider <provider>", "Prisma provider: postgresql, mongodb, mysql, sqlite")
|
|
@@ -55,7 +55,7 @@ program
|
|
|
55
55
|
program
|
|
56
56
|
.command("add [module]")
|
|
57
57
|
.description("Add a module or category to your existing project")
|
|
58
|
-
.usage(
|
|
58
|
+
.usage("[module] [options]")
|
|
59
59
|
.option("--provider <provider>", "Specific provider/variant to use")
|
|
60
60
|
.option("--force", "Overwrite existing files")
|
|
61
61
|
.option("--dry-run", "Show what would be changed without making changes")
|
|
@@ -10,7 +10,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const package_root_1 = require("../utils/package-root");
|
|
11
11
|
const baseDirs = {
|
|
12
12
|
express: "./src",
|
|
13
|
-
|
|
13
|
+
react: "./src",
|
|
14
14
|
nextjs: ".",
|
|
15
15
|
};
|
|
16
16
|
async function convertToJavaScript(targetDir, framework) {
|