stackkit 0.1.9 → 0.2.1
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 +15 -24
- package/dist/cli/add.js +42 -107
- package/dist/cli/create.js +28 -15
- package/dist/cli/doctor.js +203 -21
- package/dist/cli/list.js +37 -1
- package/dist/index.js +104 -6
- package/dist/lib/discovery/module-discovery.d.ts +0 -7
- package/dist/lib/discovery/module-discovery.js +26 -23
- package/dist/lib/discovery/shared.d.ts +6 -0
- package/dist/lib/discovery/shared.js +50 -0
- package/dist/lib/framework/framework-utils.js +43 -3
- package/dist/lib/pm/package-manager.js +58 -31
- package/modules/auth/authjs/generator.json +1 -1
- package/modules/auth/better-auth/files/lib/auth.ts +5 -1
- package/package.json +1 -1
- package/templates/express/README.md +43 -0
- package/templates/express/template.json +9 -1
- package/templates/nextjs/.env.example +1 -0
- package/templates/nextjs/README.md +24 -1
- package/templates/nextjs/app/page.tsx +3 -3
- package/templates/nextjs/template.json +2 -0
- package/templates/react/README.md +35 -2
- package/templates/react/src/lib/queryClient.ts +2 -2
- package/templates/react/src/pages/Home.tsx +3 -3
- package/templates/react/src/utils/utils.ts +3 -0
- package/templates/react/template.json +2 -0
- package/templates/react/src/config/constants.ts +0 -5
- package/templates/react/src/hooks/index.ts +0 -64
- package/templates/react/src/utils/helpers.ts +0 -51
package/README.md
CHANGED
|
@@ -1,43 +1,34 @@
|
|
|
1
1
|
# StackKit CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI for scaffolding and composing modular JavaScript applications.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm install -g stackkit
|
|
9
|
-
# or
|
|
10
|
-
npx stackkit
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Create New Project
|
|
7
|
+
Create a new project without installing globally:
|
|
14
8
|
|
|
15
9
|
```bash
|
|
16
|
-
npx stackkit create my-app
|
|
10
|
+
npx stackkit@latest create my-app
|
|
17
11
|
```
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
Add features to an existing project:
|
|
20
14
|
|
|
21
15
|
```bash
|
|
22
|
-
npx stackkit add
|
|
23
|
-
# or non-interactive
|
|
24
|
-
npx stackkit add auth
|
|
25
|
-
npx stackkit add database
|
|
16
|
+
npx stackkit@latest add <module>
|
|
26
17
|
```
|
|
27
18
|
|
|
28
|
-
|
|
19
|
+
Check project health:
|
|
29
20
|
|
|
30
21
|
```bash
|
|
31
|
-
npx stackkit doctor
|
|
22
|
+
npx stackkit@latest doctor
|
|
32
23
|
```
|
|
33
24
|
|
|
34
|
-
## Supported
|
|
25
|
+
## Supported technologies
|
|
35
26
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
27
|
+
- Frameworks: Next.js, React, Express
|
|
28
|
+
- Databases: Prisma, Mongoose
|
|
29
|
+
- Auth: Auth.js / Better Auth
|
|
39
30
|
|
|
40
|
-
##
|
|
31
|
+
## Links
|
|
41
32
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
33
|
+
- Website: https://stackkit.tariqul.dev
|
|
34
|
+
- Repo: https://github.com/tariqul420/stackkit
|
package/dist/cli/add.js
CHANGED
|
@@ -9,14 +9,16 @@ const child_process_1 = require("child_process");
|
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
10
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const module_discovery_1 = require("../lib/discovery/module-discovery");
|
|
13
|
+
const shared_1 = require("../lib/discovery/shared");
|
|
14
14
|
const env_editor_1 = require("../lib/env/env-editor");
|
|
15
|
+
const framework_utils_1 = require("../lib/framework/framework-utils");
|
|
15
16
|
const files_1 = require("../lib/fs/files");
|
|
16
|
-
const
|
|
17
|
+
const code_generator_1 = require("../lib/generation/code-generator");
|
|
17
18
|
const package_manager_1 = require("../lib/pm/package-manager");
|
|
19
|
+
const detect_1 = require("../lib/project/detect");
|
|
20
|
+
const logger_1 = require("../lib/ui/logger");
|
|
18
21
|
const package_root_1 = require("../lib/utils/package-root");
|
|
19
|
-
const framework_utils_1 = require("../lib/framework/framework-utils");
|
|
20
22
|
async function addCommand(module, options) {
|
|
21
23
|
try {
|
|
22
24
|
const projectRoot = process.cwd();
|
|
@@ -39,30 +41,24 @@ async function addCommand(module, options) {
|
|
|
39
41
|
}
|
|
40
42
|
async function getAddConfig(module, options, projectInfo) {
|
|
41
43
|
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
42
|
-
// If no module provided, go interactive
|
|
43
44
|
if (!module) {
|
|
44
45
|
return await getInteractiveConfig(modulesDir, projectInfo);
|
|
45
46
|
}
|
|
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
47
|
if (module === "database" || module === "auth") {
|
|
50
48
|
if (!options?.provider) {
|
|
51
49
|
if (module === "database") {
|
|
52
|
-
throw new Error("Provider is required for database. Use: `npx stackkit add database --provider <provider>`");
|
|
50
|
+
throw new Error("Provider is required for database. Use: `npx stackkit@latest add database --provider <provider>`");
|
|
53
51
|
}
|
|
54
52
|
else {
|
|
55
|
-
throw new Error("Provider is required for auth. Use: `npx stackkit add auth --provider <provider>`");
|
|
53
|
+
throw new Error("Provider is required for auth. Use: `npx stackkit@latest add auth --provider <provider>`");
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
56
|
if (module === "database") {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
adapterProvider = options.provider; // e.g., "prisma-postgresql"
|
|
65
|
-
}
|
|
57
|
+
const parsed = (0, shared_1.parseDatabaseOption)(options.provider || "");
|
|
58
|
+
const baseProvider = parsed.database;
|
|
59
|
+
const adapterProvider = parsed.provider
|
|
60
|
+
? `${parsed.database}-${parsed.provider}`
|
|
61
|
+
: parsed.database;
|
|
66
62
|
const moduleMetadata = await loadModuleMetadata(modulesDir, baseProvider, baseProvider);
|
|
67
63
|
if (!moduleMetadata) {
|
|
68
64
|
throw new Error(`Database provider "${baseProvider}" not found`);
|
|
@@ -70,7 +66,9 @@ async function getAddConfig(module, options, projectInfo) {
|
|
|
70
66
|
return {
|
|
71
67
|
module: "database",
|
|
72
68
|
provider: adapterProvider,
|
|
73
|
-
displayName:
|
|
69
|
+
displayName: parsed.database === "prisma" && parsed.provider
|
|
70
|
+
? `${moduleMetadata.displayName} (${parsed.provider})`
|
|
71
|
+
: moduleMetadata.displayName,
|
|
74
72
|
metadata: moduleMetadata,
|
|
75
73
|
};
|
|
76
74
|
}
|
|
@@ -104,59 +102,46 @@ async function getInteractiveConfig(modulesDir, projectInfo) {
|
|
|
104
102
|
},
|
|
105
103
|
]);
|
|
106
104
|
const category = answers.category;
|
|
105
|
+
const discovered = await (0, module_discovery_1.discoverModules)(modulesDir);
|
|
107
106
|
if (category === "database") {
|
|
107
|
+
const dbChoices = (0, module_discovery_1.getDatabaseChoices)(discovered.databases || [], projectInfo?.framework || "nextjs");
|
|
108
108
|
const dbAnswers = await inquirer_1.default.prompt([
|
|
109
109
|
{
|
|
110
110
|
type: "list",
|
|
111
111
|
name: "database",
|
|
112
112
|
message: "Select database:",
|
|
113
|
-
choices:
|
|
114
|
-
{ name: "Prisma", value: "prisma" },
|
|
115
|
-
{ name: "Mongoose", value: "mongoose" },
|
|
116
|
-
],
|
|
113
|
+
choices: dbChoices,
|
|
117
114
|
},
|
|
118
115
|
]);
|
|
119
116
|
const selectedDb = dbAnswers.database;
|
|
120
|
-
if (selectedDb
|
|
121
|
-
const
|
|
122
|
-
{
|
|
123
|
-
type: "list",
|
|
124
|
-
name: "provider",
|
|
125
|
-
message: "Select Prisma provider:",
|
|
126
|
-
choices: [
|
|
127
|
-
{ name: "PostgreSQL", value: "postgresql" },
|
|
128
|
-
{ name: "MongoDB", value: "mongodb" },
|
|
129
|
-
{ name: "MySQL", value: "mysql" },
|
|
130
|
-
{ name: "SQLite", value: "sqlite" },
|
|
131
|
-
],
|
|
132
|
-
},
|
|
133
|
-
]);
|
|
117
|
+
if (selectedDb.startsWith("prisma-")) {
|
|
118
|
+
const provider = selectedDb.split("-")[1];
|
|
134
119
|
return {
|
|
135
120
|
module: "database",
|
|
136
|
-
provider: `prisma-${
|
|
137
|
-
displayName: `Prisma (${
|
|
121
|
+
provider: `prisma-${provider}`,
|
|
122
|
+
displayName: `Prisma (${provider})`,
|
|
138
123
|
metadata: (await loadModuleMetadata(modulesDir, "prisma", "prisma")),
|
|
139
124
|
};
|
|
140
125
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
126
|
+
// Other databases (mongoose, etc.)
|
|
127
|
+
const meta = (await loadModuleMetadata(modulesDir, selectedDb, selectedDb));
|
|
128
|
+
if (!meta)
|
|
129
|
+
throw new Error(`Database provider "${selectedDb}" not found`);
|
|
130
|
+
return {
|
|
131
|
+
module: "database",
|
|
132
|
+
provider: selectedDb,
|
|
133
|
+
displayName: meta.displayName || selectedDb,
|
|
134
|
+
metadata: meta,
|
|
135
|
+
};
|
|
149
136
|
}
|
|
150
137
|
else if (category === "auth") {
|
|
138
|
+
const authChoices = (discovered.auth || []).map((a) => ({ name: a.displayName, value: a.name }));
|
|
151
139
|
const authAnswers = await inquirer_1.default.prompt([
|
|
152
140
|
{
|
|
153
141
|
type: "list",
|
|
154
142
|
name: "auth",
|
|
155
143
|
message: "Select authentication:",
|
|
156
|
-
choices:
|
|
157
|
-
{ name: "Better Auth", value: "better-auth" },
|
|
158
|
-
{ name: "Auth.js", value: "authjs" },
|
|
159
|
-
],
|
|
144
|
+
choices: authChoices,
|
|
160
145
|
},
|
|
161
146
|
]);
|
|
162
147
|
const selectedAuth = authAnswers.auth;
|
|
@@ -164,67 +149,19 @@ async function getInteractiveConfig(modulesDir, projectInfo) {
|
|
|
164
149
|
if (!metadata) {
|
|
165
150
|
throw new Error(`Auth provider "${selectedAuth}" not found`);
|
|
166
151
|
}
|
|
167
|
-
if (projectInfo && !metadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
152
|
+
if (projectInfo && metadata.supportedFrameworks && !metadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
168
153
|
throw new Error(`Auth provider "${selectedAuth}" does not support ${projectInfo.framework}`);
|
|
169
154
|
}
|
|
170
155
|
return {
|
|
171
156
|
module: "auth",
|
|
172
157
|
provider: selectedAuth,
|
|
173
|
-
displayName:
|
|
158
|
+
displayName: metadata.displayName || selectedAuth,
|
|
174
159
|
metadata,
|
|
175
160
|
};
|
|
176
161
|
}
|
|
177
162
|
throw new Error("Invalid selection");
|
|
178
163
|
}
|
|
179
|
-
|
|
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
|
-
}
|
|
164
|
+
/* removed unused getProviderConfig — discovery-based flows handle providers */
|
|
228
165
|
async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
229
166
|
const moduleMetadata = config.metadata;
|
|
230
167
|
const selectedProvider = config.provider;
|
|
@@ -297,12 +234,10 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
|
297
234
|
}
|
|
298
235
|
const selectedModules = { framework: projectInfo.framework };
|
|
299
236
|
if (config.module === "database" && config.provider) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
else {
|
|
305
|
-
selectedModules.database = config.provider;
|
|
237
|
+
const parsed = (0, shared_1.parseDatabaseOption)(config.provider);
|
|
238
|
+
selectedModules.database = parsed.database;
|
|
239
|
+
if (parsed.database === "prisma" && parsed.provider) {
|
|
240
|
+
selectedModules.prismaProvider = parsed.provider;
|
|
306
241
|
}
|
|
307
242
|
}
|
|
308
243
|
if (config.module === "auth" && config.provider) {
|
package/dist/cli/create.js
CHANGED
|
@@ -10,15 +10,16 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
10
10
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
|
|
13
|
-
const git_utils_1 = require("../lib/git-utils");
|
|
14
13
|
const js_conversion_1 = require("../lib/conversion/js-conversion");
|
|
15
|
-
const package_manager_1 = require("../lib/pm/package-manager");
|
|
16
|
-
const logger_1 = require("../lib/ui/logger");
|
|
17
|
-
const framework_utils_1 = require("../lib/framework/framework-utils");
|
|
18
14
|
const module_discovery_1 = require("../lib/discovery/module-discovery");
|
|
15
|
+
const shared_1 = require("../lib/discovery/shared");
|
|
16
|
+
const env_editor_1 = require("../lib/env/env-editor");
|
|
17
|
+
const framework_utils_1 = require("../lib/framework/framework-utils");
|
|
19
18
|
const code_generator_1 = require("../lib/generation/code-generator");
|
|
19
|
+
const git_utils_1 = require("../lib/git-utils");
|
|
20
|
+
const package_manager_1 = require("../lib/pm/package-manager");
|
|
21
|
+
const logger_1 = require("../lib/ui/logger");
|
|
20
22
|
const package_root_1 = require("../lib/utils/package-root");
|
|
21
|
-
const env_editor_1 = require("../lib/env/env-editor");
|
|
22
23
|
async function createProject(projectName, options) {
|
|
23
24
|
logger_1.logger.newLine();
|
|
24
25
|
logger_1.logger.log(chalk_1.default.bold.cyan("📦 Create StackKit App"));
|
|
@@ -91,7 +92,7 @@ async function getProjectConfig(projectName, options) {
|
|
|
91
92
|
let database = "none";
|
|
92
93
|
let prismaProvider;
|
|
93
94
|
if (db && db !== "none") {
|
|
94
|
-
const parsed = (0,
|
|
95
|
+
const parsed = (0, shared_1.parseDatabaseOption)(db);
|
|
95
96
|
database = parsed.database;
|
|
96
97
|
prismaProvider = parsed.provider;
|
|
97
98
|
}
|
|
@@ -151,12 +152,16 @@ async function getProjectConfig(projectName, options) {
|
|
|
151
152
|
name: "database",
|
|
152
153
|
message: "Select database/ORM:",
|
|
153
154
|
when: (answers) => answers.framework !== "react",
|
|
154
|
-
choices:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
choices: (answers) => discoveredModules.databases && discoveredModules.databases.length > 0
|
|
156
|
+
? (0, module_discovery_1.getDatabaseChoices)(discoveredModules.databases, answers.framework)
|
|
157
|
+
: [
|
|
158
|
+
{ name: "Prisma", value: "prisma" },
|
|
159
|
+
{ name: "Mongoose", value: "mongoose" },
|
|
160
|
+
{ name: "None", value: "none" },
|
|
161
|
+
],
|
|
159
162
|
},
|
|
163
|
+
// If a prisma-* choice is selected above, `prismaProvider` will be derived from it,
|
|
164
|
+
// otherwise prompt for provider when `prisma` is selected directly.
|
|
160
165
|
{
|
|
161
166
|
type: "list",
|
|
162
167
|
name: "prismaProvider",
|
|
@@ -199,13 +204,21 @@ async function getProjectConfig(projectName, options) {
|
|
|
199
204
|
default: "pnpm",
|
|
200
205
|
},
|
|
201
206
|
]));
|
|
207
|
+
// Normalize database answer (interactive flow): handle values like `prisma-postgresql`
|
|
208
|
+
let databaseAnswer = answers.framework === "react" ? "none" : answers.database;
|
|
209
|
+
let prismaProviderAnswer = answers.prismaProvider;
|
|
210
|
+
if (typeof databaseAnswer === "string" && databaseAnswer.startsWith("prisma-")) {
|
|
211
|
+
const parts = databaseAnswer.split("-");
|
|
212
|
+
if (parts.length >= 2) {
|
|
213
|
+
prismaProviderAnswer = parts[1];
|
|
214
|
+
databaseAnswer = "prisma";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
202
217
|
return {
|
|
203
218
|
projectName: (projectName || answers.projectName),
|
|
204
219
|
framework: answers.framework,
|
|
205
|
-
database:
|
|
206
|
-
|
|
207
|
-
: answers.database),
|
|
208
|
-
prismaProvider: answers.prismaProvider,
|
|
220
|
+
database: databaseAnswer,
|
|
221
|
+
prismaProvider: prismaProviderAnswer,
|
|
209
222
|
auth: answers.auth || "none",
|
|
210
223
|
language: answers.language,
|
|
211
224
|
packageManager: answers.packageManager,
|
package/dist/cli/doctor.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
|
// Constants for consistent messaging
|
|
12
13
|
const MESSAGES = {
|
|
13
14
|
NO_PACKAGE_JSON: "No package.json found in current directory or any parent directory.",
|
|
@@ -211,24 +212,125 @@ function checkNodeVersion() {
|
|
|
211
212
|
function detectAuthModules(packageJson) {
|
|
212
213
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
213
214
|
const modules = [];
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
try {
|
|
216
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "auth");
|
|
217
|
+
if (fs_extra_1.default.existsSync(modulesDir)) {
|
|
218
|
+
const authDirs = fs_extra_1.default.readdirSync(modulesDir);
|
|
219
|
+
for (const authDir of authDirs) {
|
|
220
|
+
try {
|
|
221
|
+
const genPath = path_1.default.join(modulesDir, authDir, "generator.json");
|
|
222
|
+
const modJson = path_1.default.join(modulesDir, authDir, "module.json");
|
|
223
|
+
let pkgNames = [];
|
|
224
|
+
if (fs_extra_1.default.existsSync(genPath)) {
|
|
225
|
+
const gen = JSON.parse(fs_extra_1.default.readFileSync(genPath, "utf-8"));
|
|
226
|
+
if (Array.isArray(gen.operations)) {
|
|
227
|
+
for (const op of gen.operations) {
|
|
228
|
+
if (op.dependencies && typeof op.dependencies === "object") {
|
|
229
|
+
pkgNames.push(...Object.keys(op.dependencies));
|
|
230
|
+
}
|
|
231
|
+
if (op.devDependencies && typeof op.devDependencies === "object") {
|
|
232
|
+
pkgNames.push(...Object.keys(op.devDependencies));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Fallback: check module.json provider/name
|
|
238
|
+
let moduleName = authDir;
|
|
239
|
+
if (fs_extra_1.default.existsSync(modJson)) {
|
|
240
|
+
try {
|
|
241
|
+
const m = JSON.parse(fs_extra_1.default.readFileSync(modJson, "utf-8"));
|
|
242
|
+
if (m && m.name)
|
|
243
|
+
moduleName = m.name;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
/* ignore */
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
for (const pkg of pkgNames) {
|
|
250
|
+
if (deps[pkg]) {
|
|
251
|
+
modules.push(moduleName);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// ignore per-module errors
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
216
261
|
}
|
|
217
|
-
|
|
218
|
-
|
|
262
|
+
catch {
|
|
263
|
+
// ignore discovery errors
|
|
219
264
|
}
|
|
220
|
-
|
|
265
|
+
// Fallback to original simple checks if nothing found
|
|
266
|
+
if (modules.length === 0) {
|
|
267
|
+
if (deps["better-auth"])
|
|
268
|
+
modules.push("better-auth");
|
|
269
|
+
if (deps["next-auth"])
|
|
270
|
+
modules.push("authjs");
|
|
271
|
+
}
|
|
272
|
+
return Array.from(new Set(modules));
|
|
221
273
|
}
|
|
222
274
|
function detectDatabaseModules(packageJson) {
|
|
223
275
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
224
276
|
const modules = [];
|
|
225
|
-
|
|
226
|
-
|
|
277
|
+
try {
|
|
278
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "database");
|
|
279
|
+
if (fs_extra_1.default.existsSync(modulesDir)) {
|
|
280
|
+
const dbDirs = fs_extra_1.default.readdirSync(modulesDir);
|
|
281
|
+
for (const dbDir of dbDirs) {
|
|
282
|
+
try {
|
|
283
|
+
const genPath = path_1.default.join(modulesDir, dbDir, "generator.json");
|
|
284
|
+
const modJson = path_1.default.join(modulesDir, dbDir, "module.json");
|
|
285
|
+
let pkgNames = [];
|
|
286
|
+
if (fs_extra_1.default.existsSync(genPath)) {
|
|
287
|
+
const gen = JSON.parse(fs_extra_1.default.readFileSync(genPath, "utf-8"));
|
|
288
|
+
if (Array.isArray(gen.operations)) {
|
|
289
|
+
for (const op of gen.operations) {
|
|
290
|
+
if (op.dependencies && typeof op.dependencies === "object") {
|
|
291
|
+
pkgNames.push(...Object.keys(op.dependencies));
|
|
292
|
+
}
|
|
293
|
+
if (op.devDependencies && typeof op.devDependencies === "object") {
|
|
294
|
+
pkgNames.push(...Object.keys(op.devDependencies));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
let moduleName = dbDir;
|
|
300
|
+
if (fs_extra_1.default.existsSync(modJson)) {
|
|
301
|
+
try {
|
|
302
|
+
const m = JSON.parse(fs_extra_1.default.readFileSync(modJson, "utf-8"));
|
|
303
|
+
if (m && m.name)
|
|
304
|
+
moduleName = m.name;
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
/* ignore */
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
for (const pkg of pkgNames) {
|
|
311
|
+
if (deps[pkg]) {
|
|
312
|
+
modules.push(moduleName);
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// ignore per-module errors
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// ignore discovery errors
|
|
227
325
|
}
|
|
228
|
-
if
|
|
229
|
-
|
|
326
|
+
// Fallback to original checks if nothing found
|
|
327
|
+
if (modules.length === 0) {
|
|
328
|
+
if (deps["@prisma/client"] || deps["prisma"])
|
|
329
|
+
modules.push("prisma");
|
|
330
|
+
if (deps["mongoose"])
|
|
331
|
+
modules.push("mongoose");
|
|
230
332
|
}
|
|
231
|
-
return modules;
|
|
333
|
+
return Array.from(new Set(modules));
|
|
232
334
|
}
|
|
233
335
|
async function checkKeyFiles(projectRoot, projectType, authModules, databaseModules) {
|
|
234
336
|
const checks = [];
|
|
@@ -260,8 +362,42 @@ async function checkKeyFiles(projectRoot, projectType, authModules, databaseModu
|
|
|
260
362
|
async function checkAuthRoutesExist(projectRoot, projectType) {
|
|
261
363
|
if (projectType !== "nextjs")
|
|
262
364
|
return true; // Skip for non-Next.js
|
|
263
|
-
|
|
264
|
-
|
|
365
|
+
// Build candidate auth route paths from generator.json files in modules/auth
|
|
366
|
+
const candidates = new Set();
|
|
367
|
+
try {
|
|
368
|
+
const authModulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules", "auth");
|
|
369
|
+
if (await fs_extra_1.default.pathExists(authModulesDir)) {
|
|
370
|
+
const authDirs = await fs_extra_1.default.readdir(authModulesDir);
|
|
371
|
+
for (const dir of authDirs) {
|
|
372
|
+
const genPath = path_1.default.join(authModulesDir, dir, "generator.json");
|
|
373
|
+
if (!(await fs_extra_1.default.pathExists(genPath)))
|
|
374
|
+
continue;
|
|
375
|
+
try {
|
|
376
|
+
const gen = await fs_extra_1.default.readJson(genPath);
|
|
377
|
+
if (Array.isArray(gen.operations)) {
|
|
378
|
+
for (const op of gen.operations) {
|
|
379
|
+
if (typeof op.destination === "string")
|
|
380
|
+
candidates.add(op.destination);
|
|
381
|
+
if (Array.isArray(op.operations)) {
|
|
382
|
+
for (const sub of op.operations) {
|
|
383
|
+
if (typeof sub.destination === "string")
|
|
384
|
+
candidates.add(sub.destination);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
// ignore malformed generator
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
// ignore discovery errors
|
|
398
|
+
}
|
|
399
|
+
// Fallback to known common paths if generators don't provide any
|
|
400
|
+
const fallback = [
|
|
265
401
|
"app/api/auth/[...nextauth]/route.ts",
|
|
266
402
|
"app/api/auth/[...nextauth]/route.js",
|
|
267
403
|
"src/app/api/auth/[...nextauth]/route.ts",
|
|
@@ -270,13 +406,14 @@ async function checkAuthRoutesExist(projectRoot, projectType) {
|
|
|
270
406
|
"pages/api/auth/[...nextauth].js",
|
|
271
407
|
"src/pages/api/auth/[...nextauth].ts",
|
|
272
408
|
"src/pages/api/auth/[...nextauth].js",
|
|
273
|
-
// Better Auth routes
|
|
274
409
|
"app/api/auth/[...all]/route.ts",
|
|
275
410
|
"app/api/auth/[...all]/route.js",
|
|
276
411
|
"src/app/api/auth/[...all]/route.ts",
|
|
277
412
|
"src/app/api/auth/[...all]/route.js",
|
|
278
413
|
];
|
|
279
|
-
for (const
|
|
414
|
+
for (const p of fallback)
|
|
415
|
+
candidates.add(p);
|
|
416
|
+
for (const routePath of candidates) {
|
|
280
417
|
if (await fs_extra_1.default.pathExists(path_1.default.join(projectRoot, routePath))) {
|
|
281
418
|
return true;
|
|
282
419
|
}
|
|
@@ -288,14 +425,59 @@ async function checkEnvFiles(projectRoot, authModules, databaseModules) {
|
|
|
288
425
|
const requiredKeys = [];
|
|
289
426
|
const missing = [];
|
|
290
427
|
const present = [];
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
428
|
+
// Dynamically collect required env keys from generator.json for detected modules
|
|
429
|
+
try {
|
|
430
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
431
|
+
async function collectEnvKeys(category, name) {
|
|
432
|
+
const genPath = path_1.default.join(modulesDir, category, name, "generator.json");
|
|
433
|
+
if (!(await fs_extra_1.default.pathExists(genPath)))
|
|
434
|
+
return;
|
|
435
|
+
try {
|
|
436
|
+
const gen = await fs_extra_1.default.readJson(genPath);
|
|
437
|
+
if (Array.isArray(gen.operations)) {
|
|
438
|
+
for (const op of gen.operations) {
|
|
439
|
+
if (op.type === "add-env" && op.envVars && typeof op.envVars === "object") {
|
|
440
|
+
for (const k of Object.keys(op.envVars)) {
|
|
441
|
+
if (!requiredKeys.includes(k))
|
|
442
|
+
requiredKeys.push(k);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
// Also check nested operations (e.g., patch-file -> operations)
|
|
446
|
+
if (Array.isArray(op.operations)) {
|
|
447
|
+
for (const sub of op.operations) {
|
|
448
|
+
if (sub.type === "add-env" && sub.envVars && typeof sub.envVars === "object") {
|
|
449
|
+
for (const k of Object.keys(sub.envVars)) {
|
|
450
|
+
if (!requiredKeys.includes(k))
|
|
451
|
+
requiredKeys.push(k);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
// ignore malformed generator
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
for (const db of databaseModules) {
|
|
464
|
+
await collectEnvKeys("database", db);
|
|
465
|
+
}
|
|
466
|
+
for (const auth of authModules) {
|
|
467
|
+
await collectEnvKeys("auth", auth);
|
|
468
|
+
}
|
|
296
469
|
}
|
|
297
|
-
|
|
298
|
-
|
|
470
|
+
catch {
|
|
471
|
+
// fallback to previous minimal checks if discovery fails
|
|
472
|
+
if (databaseModules.includes("prisma")) {
|
|
473
|
+
requiredKeys.push("DATABASE_URL");
|
|
474
|
+
}
|
|
475
|
+
if (authModules.includes("authjs")) {
|
|
476
|
+
requiredKeys.push("NEXTAUTH_SECRET", "NEXTAUTH_URL");
|
|
477
|
+
}
|
|
478
|
+
if (authModules.includes("better-auth")) {
|
|
479
|
+
requiredKeys.push("BETTER_AUTH_SECRET", "BETTER_AUTH_URL");
|
|
480
|
+
}
|
|
299
481
|
}
|
|
300
482
|
const envPaths = [".env", ".env.local"];
|
|
301
483
|
let envContent = "";
|