stackkit 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/add.js +113 -83
- package/dist/cli/doctor.js +17 -4
- package/dist/index.js +1 -1
- package/dist/lib/generation/generator-utils.js +14 -9
- package/modules/auth/authjs/generator.json +1 -2
- package/package.json +3 -1
package/dist/cli/add.js
CHANGED
|
@@ -17,7 +17,6 @@ const logger_1 = require("../lib/ui/logger");
|
|
|
17
17
|
const package_manager_1 = require("../lib/pm/package-manager");
|
|
18
18
|
const package_root_1 = require("../lib/utils/package-root");
|
|
19
19
|
const framework_utils_1 = require("../lib/framework/framework-utils");
|
|
20
|
-
const generator_utils_1 = require("../lib/generation/generator-utils");
|
|
21
20
|
async function addCommand(module, options) {
|
|
22
21
|
try {
|
|
23
22
|
const projectRoot = process.cwd();
|
|
@@ -29,10 +28,6 @@ async function addCommand(module, options) {
|
|
|
29
28
|
logger_1.logger.newLine();
|
|
30
29
|
logger_1.logger.success(`Added ${chalk_1.default.bold(config.displayName)}`);
|
|
31
30
|
logger_1.logger.newLine();
|
|
32
|
-
if (config.metadata.envVars && config.metadata.envVars.length > 0) {
|
|
33
|
-
logger_1.logger.log("Next: Fill in environment variables in .env");
|
|
34
|
-
}
|
|
35
|
-
logger_1.logger.newLine();
|
|
36
31
|
}
|
|
37
32
|
catch (error) {
|
|
38
33
|
logger_1.logger.error(`Failed to add module: ${error.message}`);
|
|
@@ -44,88 +39,59 @@ async function addCommand(module, options) {
|
|
|
44
39
|
}
|
|
45
40
|
async function getAddConfig(module, options, projectInfo) {
|
|
46
41
|
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
if (module === "database" || module === "auth") {
|
|
42
|
+
// If no module provided, go interactive
|
|
43
|
+
if (!module) {
|
|
44
|
+
return await getInteractiveConfig(modulesDir, projectInfo);
|
|
45
|
+
}
|
|
46
|
+
// Only allow: no-arg interactive, or explicit category + --provider.
|
|
47
|
+
// Disallow positional provider names like `npx stackkit add better-auth` or
|
|
48
|
+
// `npx stackkit add auth prisma-postgresql` — require `--provider` flag.
|
|
49
|
+
if (module === "database" || module === "auth") {
|
|
50
|
+
if (!options?.provider) {
|
|
58
51
|
if (module === "database") {
|
|
59
|
-
|
|
60
|
-
throw new Error("Provider is required for database. Use --provider <provider>");
|
|
61
|
-
}
|
|
62
|
-
let baseProvider = options.provider;
|
|
63
|
-
let adapterProvider = options.provider;
|
|
64
|
-
if (options.provider.includes("-")) {
|
|
65
|
-
const parts = options.provider.split("-");
|
|
66
|
-
baseProvider = parts[0]; // e.g., "prisma"
|
|
67
|
-
adapterProvider = options.provider; // e.g., "prisma-postgresql"
|
|
68
|
-
}
|
|
69
|
-
const moduleMetadata = await loadModuleMetadata(modulesDir, baseProvider, baseProvider);
|
|
70
|
-
if (!moduleMetadata) {
|
|
71
|
-
throw new Error(`Database provider "${baseProvider}" not found`);
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
module: "database",
|
|
75
|
-
provider: adapterProvider,
|
|
76
|
-
displayName: `${moduleMetadata.displayName} (${adapterProvider.split("-")[1] || adapterProvider})`,
|
|
77
|
-
metadata: moduleMetadata,
|
|
78
|
-
};
|
|
52
|
+
throw new Error('Provider is required for database. Use: `npx stackkit add database --provider <provider>`');
|
|
79
53
|
}
|
|
80
|
-
else
|
|
81
|
-
|
|
82
|
-
const moduleMetadata = await loadModuleMetadata(modulesDir, provider, provider);
|
|
83
|
-
if (!moduleMetadata) {
|
|
84
|
-
throw new Error(`Auth provider "${provider}" not found`);
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
module: "auth",
|
|
88
|
-
provider,
|
|
89
|
-
displayName: moduleMetadata.displayName,
|
|
90
|
-
metadata: moduleMetadata,
|
|
91
|
-
};
|
|
54
|
+
else {
|
|
55
|
+
throw new Error('Provider is required for auth. Use: `npx stackkit add auth --provider <provider>`');
|
|
92
56
|
}
|
|
93
57
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
"
|
|
105
|
-
const providers = Object.keys(moduleMetadata.dependencies.providers || {});
|
|
106
|
-
if (providers.length > 0) {
|
|
107
|
-
const { provider } = await inquirer_1.default.prompt([
|
|
108
|
-
{
|
|
109
|
-
type: "list",
|
|
110
|
-
name: "provider",
|
|
111
|
-
message: "Select database provider:",
|
|
112
|
-
choices: providers.map((p) => ({ name: p, value: p })),
|
|
113
|
-
},
|
|
114
|
-
]);
|
|
115
|
-
selectedProvider = provider;
|
|
116
|
-
}
|
|
58
|
+
if (module === "database") {
|
|
59
|
+
let baseProvider = options.provider;
|
|
60
|
+
let adapterProvider = options.provider;
|
|
61
|
+
if (options.provider.includes("-")) {
|
|
62
|
+
const parts = options.provider.split("-");
|
|
63
|
+
baseProvider = parts[0]; // e.g., "prisma"
|
|
64
|
+
adapterProvider = options.provider; // e.g., "prisma-postgresql"
|
|
65
|
+
}
|
|
66
|
+
const moduleMetadata = await loadModuleMetadata(modulesDir, baseProvider, baseProvider);
|
|
67
|
+
if (!moduleMetadata) {
|
|
68
|
+
throw new Error(`Database provider "${baseProvider}" not found`);
|
|
117
69
|
}
|
|
70
|
+
return {
|
|
71
|
+
module: "database",
|
|
72
|
+
provider: adapterProvider,
|
|
73
|
+
displayName: `${moduleMetadata.displayName} (${adapterProvider.split("-")[1] || adapterProvider})`,
|
|
74
|
+
metadata: moduleMetadata,
|
|
75
|
+
};
|
|
118
76
|
}
|
|
119
|
-
if (
|
|
120
|
-
|
|
77
|
+
else if (module === "auth") {
|
|
78
|
+
const provider = options.provider;
|
|
79
|
+
const moduleMetadata = await loadModuleMetadata(modulesDir, provider, provider);
|
|
80
|
+
if (!moduleMetadata) {
|
|
81
|
+
throw new Error(`Auth provider "${provider}" not found`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
module: "auth",
|
|
85
|
+
provider,
|
|
86
|
+
displayName: moduleMetadata.displayName,
|
|
87
|
+
metadata: moduleMetadata,
|
|
88
|
+
};
|
|
121
89
|
}
|
|
122
|
-
return {
|
|
123
|
-
module,
|
|
124
|
-
provider: selectedProvider,
|
|
125
|
-
displayName: moduleMetadata.displayName,
|
|
126
|
-
metadata: moduleMetadata,
|
|
127
|
-
};
|
|
128
90
|
}
|
|
91
|
+
// Unknown module type
|
|
92
|
+
throw new Error(`Unknown module type "${module}". Use "database" or "auth", or specify a provider directly.`);
|
|
93
|
+
}
|
|
94
|
+
async function getInteractiveConfig(modulesDir, projectInfo) {
|
|
129
95
|
const answers = await inquirer_1.default.prompt([
|
|
130
96
|
{
|
|
131
97
|
type: "list",
|
|
@@ -133,7 +99,7 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
133
99
|
message: "What would you like to add?",
|
|
134
100
|
choices: [
|
|
135
101
|
{ name: "Database", value: "database" },
|
|
136
|
-
{ name: "
|
|
102
|
+
{ name: "Auth", value: "auth" },
|
|
137
103
|
],
|
|
138
104
|
},
|
|
139
105
|
]);
|
|
@@ -167,7 +133,7 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
167
133
|
]);
|
|
168
134
|
return {
|
|
169
135
|
module: "database",
|
|
170
|
-
provider:
|
|
136
|
+
provider: `prisma-${providerAnswers.provider}`,
|
|
171
137
|
displayName: `Prisma (${providerAnswers.provider})`,
|
|
172
138
|
metadata: (await loadModuleMetadata(modulesDir, "prisma", "prisma")),
|
|
173
139
|
};
|
|
@@ -210,6 +176,55 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
210
176
|
}
|
|
211
177
|
throw new Error("Invalid selection");
|
|
212
178
|
}
|
|
179
|
+
async function getProviderConfig(modulesDir, provider, projectInfo) {
|
|
180
|
+
if (provider.includes("-")) {
|
|
181
|
+
const parts = provider.split("-");
|
|
182
|
+
const baseProvider = parts[0];
|
|
183
|
+
const specificProvider = provider;
|
|
184
|
+
if (baseProvider === "prisma") {
|
|
185
|
+
const metadata = await loadModuleMetadata(modulesDir, "database", baseProvider);
|
|
186
|
+
if (!metadata) {
|
|
187
|
+
throw new Error(`Database provider "${baseProvider}" not found`);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
module: "database",
|
|
191
|
+
provider: specificProvider,
|
|
192
|
+
displayName: `Prisma (${parts[1]})`,
|
|
193
|
+
metadata,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (provider === "mongoose") {
|
|
199
|
+
const metadata = await loadModuleMetadata(modulesDir, "database", "mongoose");
|
|
200
|
+
if (!metadata) {
|
|
201
|
+
throw new Error(`Database provider "${provider}" not found`);
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
module: "database",
|
|
205
|
+
provider: "mongoose",
|
|
206
|
+
displayName: "Mongoose",
|
|
207
|
+
metadata,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
else if (provider === "better-auth" || provider === "authjs") {
|
|
211
|
+
const metadata = await loadModuleMetadata(modulesDir, provider, provider);
|
|
212
|
+
if (!metadata) {
|
|
213
|
+
throw new Error(`Auth provider "${provider}" not found`);
|
|
214
|
+
}
|
|
215
|
+
if (projectInfo && !metadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
216
|
+
throw new Error(`Auth provider "${provider}" does not support ${projectInfo.framework}`);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
module: "auth",
|
|
220
|
+
provider,
|
|
221
|
+
displayName: provider === "better-auth" ? "Better Auth" : "Auth.js",
|
|
222
|
+
metadata,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
throw new Error(`Unknown provider "${provider}". Available providers: better-auth, authjs, mongoose, prisma-postgresql, prisma-mongodb, prisma-mysql, prisma-sqlite`);
|
|
227
|
+
}
|
|
213
228
|
async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
214
229
|
const moduleMetadata = config.metadata;
|
|
215
230
|
const selectedProvider = config.provider;
|
|
@@ -228,6 +243,21 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
|
228
243
|
return;
|
|
229
244
|
}
|
|
230
245
|
}
|
|
246
|
+
if (config.module === "database" && projectInfo.hasDatabase && !options?.force) {
|
|
247
|
+
logger_1.logger.warn("Database library already detected in this project");
|
|
248
|
+
const { proceed } = await inquirer_1.default.prompt([
|
|
249
|
+
{
|
|
250
|
+
type: "confirm",
|
|
251
|
+
name: "proceed",
|
|
252
|
+
message: "Continue anyway? (use --force to skip this prompt)",
|
|
253
|
+
default: false,
|
|
254
|
+
},
|
|
255
|
+
]);
|
|
256
|
+
if (!proceed) {
|
|
257
|
+
logger_1.logger.info("Cancelled");
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
231
261
|
if (options?.dryRun) {
|
|
232
262
|
logger_1.logger.warn("Dry run mode - no changes will be made");
|
|
233
263
|
logger_1.logger.newLine();
|
|
@@ -413,10 +443,10 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
|
|
|
413
443
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
414
444
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
415
445
|
if (provider && moduleDir === provider) {
|
|
416
|
-
return
|
|
446
|
+
return metadata;
|
|
417
447
|
}
|
|
418
448
|
if (!provider && (metadata.category === moduleName || moduleDir === moduleName)) {
|
|
419
|
-
return
|
|
449
|
+
return metadata;
|
|
420
450
|
}
|
|
421
451
|
}
|
|
422
452
|
}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -137,7 +137,7 @@ async function runDoctorChecks() {
|
|
|
137
137
|
summary: {
|
|
138
138
|
errors: checks.filter((c) => c.status === "error").length,
|
|
139
139
|
warnings: checks.filter((c) => c.status === "warning").length,
|
|
140
|
-
suggestions: generateSuggestions(),
|
|
140
|
+
suggestions: generateSuggestions(authModules, databaseModules),
|
|
141
141
|
},
|
|
142
142
|
};
|
|
143
143
|
return report;
|
|
@@ -261,6 +261,7 @@ async function checkAuthRoutesExist(projectRoot, projectType) {
|
|
|
261
261
|
if (projectType !== "nextjs")
|
|
262
262
|
return true; // Skip for non-Next.js
|
|
263
263
|
const possiblePaths = [
|
|
264
|
+
// NextAuth routes
|
|
264
265
|
"app/api/auth/[...nextauth]/route.ts",
|
|
265
266
|
"app/api/auth/[...nextauth]/route.js",
|
|
266
267
|
"src/app/api/auth/[...nextauth]/route.ts",
|
|
@@ -269,6 +270,11 @@ async function checkAuthRoutesExist(projectRoot, projectType) {
|
|
|
269
270
|
"pages/api/auth/[...nextauth].js",
|
|
270
271
|
"src/pages/api/auth/[...nextauth].ts",
|
|
271
272
|
"src/pages/api/auth/[...nextauth].js",
|
|
273
|
+
// Better Auth routes
|
|
274
|
+
"app/api/auth/[...all]/route.ts",
|
|
275
|
+
"app/api/auth/[...all]/route.js",
|
|
276
|
+
"src/app/api/auth/[...all]/route.ts",
|
|
277
|
+
"src/app/api/auth/[...all]/route.js",
|
|
272
278
|
];
|
|
273
279
|
for (const routePath of possiblePaths) {
|
|
274
280
|
if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, routePath))) {
|
|
@@ -372,8 +378,10 @@ async function checkDependencies(packageJson) {
|
|
|
372
378
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
373
379
|
for (const [name, version] of Object.entries(deps || {})) {
|
|
374
380
|
if (typeof version === "string" && (version.startsWith("^") || version.startsWith("~"))) {
|
|
381
|
+
// Assume up to date if using flexible versioning
|
|
375
382
|
}
|
|
376
383
|
else {
|
|
384
|
+
// Assume outdated if using exact versions (simplified check)
|
|
377
385
|
outdated.push(name);
|
|
378
386
|
}
|
|
379
387
|
}
|
|
@@ -406,11 +414,16 @@ async function checkEslintConfigExists(projectRoot) {
|
|
|
406
414
|
}
|
|
407
415
|
return false;
|
|
408
416
|
}
|
|
409
|
-
function generateSuggestions() {
|
|
417
|
+
function generateSuggestions(authModules, databaseModules) {
|
|
410
418
|
const suggestions = [];
|
|
419
|
+
// Show suggestions based on what's missing
|
|
420
|
+
if (authModules.length === 0) {
|
|
421
|
+
suggestions.push("stackkit add auth - Add authentication module");
|
|
422
|
+
}
|
|
423
|
+
if (databaseModules.length === 0) {
|
|
424
|
+
suggestions.push("stackkit add db - Add database module");
|
|
425
|
+
}
|
|
411
426
|
// Always show available commands
|
|
412
|
-
suggestions.push("stackkit add auth - Add authentication module");
|
|
413
|
-
suggestions.push("stackkit add db - Add database module");
|
|
414
427
|
suggestions.push("stackkit list - View available modules");
|
|
415
428
|
return suggestions;
|
|
416
429
|
}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ 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.6")
|
|
15
15
|
.configureHelp({
|
|
16
16
|
subcommandTerm: (cmd) => {
|
|
17
17
|
const name = cmd.name();
|
|
@@ -74,15 +74,20 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
|
|
|
74
74
|
if (await fs.pathExists(generatorPath)) {
|
|
75
75
|
try {
|
|
76
76
|
const generator = await fs.readJson(generatorPath);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
for (const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
value
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
// Process add-env operations to extract envVars
|
|
78
|
+
if (generator.operations && Array.isArray(generator.operations)) {
|
|
79
|
+
for (const operation of generator.operations) {
|
|
80
|
+
if (operation.type === "add-env" && operation.envVars) {
|
|
81
|
+
metadata.envVars = metadata.envVars || [];
|
|
82
|
+
for (const [key, value] of Object.entries(operation.envVars)) {
|
|
83
|
+
metadata.envVars.push({
|
|
84
|
+
key,
|
|
85
|
+
value: value,
|
|
86
|
+
description: `Environment variable for ${key}`,
|
|
87
|
+
required: true,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
if (generator.dependencies) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stackkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Production-ready CLI to create and extend JavaScript or TypeScript apps with modular stacks.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"copy-assets": "cp -r ../../templates . && cp -r ../../modules .",
|
|
29
29
|
"clean": "rm -rf dist templates modules",
|
|
30
30
|
"typecheck": "tsc --noEmit",
|
|
31
|
+
"lint": "eslint src --ext .ts",
|
|
32
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
31
33
|
"prepublishOnly": "npm run build"
|
|
32
34
|
},
|
|
33
35
|
"keywords": [
|