stackkit 0.1.3 → 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 +5 -3
- package/dist/cli/add.d.ts +2 -1
- package/dist/cli/add.js +325 -102
- package/dist/cli/create.d.ts +2 -2
- package/dist/cli/create.js +96 -30
- package/dist/cli/doctor.js +25 -17
- package/dist/cli/list.js +14 -2
- package/dist/index.js +22 -3
- package/dist/lib/conversion/js-conversion.js +2 -2
- package/dist/lib/discovery/module-discovery.d.ts +0 -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 +18 -4
- package/dist/lib/generation/code-generator.js +212 -147
- package/dist/lib/generation/generator-utils.d.ts +11 -0
- package/dist/lib/generation/generator-utils.js +124 -0
- package/dist/lib/git-utils.js +20 -0
- package/dist/lib/project/detect.js +2 -4
- package/dist/lib/utils/package-root.js +2 -2
- package/dist/types/index.d.ts +0 -10
- package/modules/auth/authjs/generator.json +10 -0
- package/modules/auth/authjs/module.json +0 -9
- package/modules/auth/better-auth/files/lib/auth.ts +38 -31
- package/modules/auth/better-auth/files/prisma/schema.prisma +3 -3
- package/modules/auth/better-auth/generator.json +58 -28
- package/modules/auth/better-auth/module.json +0 -24
- package/modules/database/mongoose/files/models/health.ts +34 -0
- package/modules/database/mongoose/generator.json +27 -8
- package/modules/database/mongoose/module.json +1 -2
- package/modules/database/prisma/files/lib/prisma.ts +27 -21
- package/modules/database/prisma/files/prisma.config.ts +17 -0
- package/modules/database/prisma/generator.json +79 -15
- package/modules/database/prisma/module.json +1 -4
- package/package.json +1 -1
- package/templates/express/src/server.ts +9 -3
- package/templates/express/tsconfig.json +2 -23
- package/templates/nextjs/lib/env.ts +1 -1
- package/dist/lib/database/database-config.d.ts +0 -6
- package/dist/lib/database/database-config.js +0 -9
- package/modules/database/mongoose/files/models/User.ts +0 -34
- /package/modules/database/mongoose/files/lib/{db.ts → mongoose.ts} +0 -0
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ npx stackkit create my-app
|
|
|
19
19
|
## Add Features to Existing Project
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
npx stackkit add
|
|
23
|
+
# or non-interactive
|
|
22
24
|
npx stackkit add auth
|
|
23
25
|
npx stackkit add database
|
|
24
26
|
```
|
|
@@ -32,10 +34,10 @@ npx stackkit doctor
|
|
|
32
34
|
## Supported Technologies
|
|
33
35
|
|
|
34
36
|
- **Frameworks**: Next.js, React, Express
|
|
35
|
-
- **Databases**: Prisma (PostgreSQL, MySQL, SQLite), Mongoose (MongoDB)
|
|
37
|
+
- **Databases**: Prisma (PostgreSQL, MongoDB, MySQL, SQLite), Mongoose (MongoDB)
|
|
36
38
|
- **Auth**: Better Auth, Auth.js
|
|
37
39
|
|
|
38
40
|
## Documentation
|
|
39
41
|
|
|
40
|
-
- [StackKit Docs](https://
|
|
41
|
-
- [GitHub Repository](https://github.com/tariqul420/stackkit)
|
|
42
|
+
- [StackKit Docs](https://stackkit.tariqul.dev)
|
|
43
|
+
- [GitHub Repository](https://github.com/tariqul420/stackkit)
|
package/dist/cli/add.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ interface AddOptions {
|
|
|
3
3
|
force?: boolean;
|
|
4
4
|
dryRun?: boolean;
|
|
5
5
|
install?: boolean;
|
|
6
|
+
yes?: boolean;
|
|
6
7
|
}
|
|
7
|
-
export declare function addCommand(module
|
|
8
|
+
export declare function addCommand(module?: string, options?: AddOptions): Promise<void>;
|
|
8
9
|
export {};
|
package/dist/cli/add.js
CHANGED
|
@@ -9,27 +9,99 @@ 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 code_generator_1 = require("../lib/generation/code-generator");
|
|
12
13
|
const detect_1 = require("../lib/project/detect");
|
|
13
14
|
const env_editor_1 = require("../lib/env/env-editor");
|
|
14
15
|
const files_1 = require("../lib/fs/files");
|
|
15
16
|
const logger_1 = require("../lib/ui/logger");
|
|
16
17
|
const package_manager_1 = require("../lib/pm/package-manager");
|
|
17
|
-
const database_config_1 = require("../lib/database/database-config");
|
|
18
18
|
const package_root_1 = require("../lib/utils/package-root");
|
|
19
|
+
const framework_utils_1 = require("../lib/framework/framework-utils");
|
|
20
|
+
const generator_utils_1 = require("../lib/generation/generator-utils");
|
|
19
21
|
async function addCommand(module, options) {
|
|
20
22
|
try {
|
|
21
23
|
const projectRoot = process.cwd();
|
|
22
24
|
const spinner = logger_1.logger.startSpinner("Detecting project...");
|
|
23
25
|
const projectInfo = await (0, detect_1.detectProjectInfo)(projectRoot);
|
|
24
26
|
spinner.succeed(`Detected ${projectInfo.framework} (${projectInfo.router} router, ${projectInfo.language})`);
|
|
25
|
-
const
|
|
27
|
+
const config = await getAddConfig(module, options, projectInfo);
|
|
28
|
+
await addModuleToProject(projectRoot, projectInfo, config, options);
|
|
29
|
+
logger_1.logger.newLine();
|
|
30
|
+
logger_1.logger.success(`Added ${chalk_1.default.bold(config.displayName)}`);
|
|
31
|
+
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
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
logger_1.logger.error(`Failed to add module: ${error.message}`);
|
|
39
|
+
if (error instanceof Error && error.stack) {
|
|
40
|
+
logger_1.logger.log(chalk_1.default.gray(error.stack));
|
|
41
|
+
}
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function getAddConfig(module, options, projectInfo) {
|
|
46
|
+
const modulesDir = path_1.default.join((0, package_root_1.getPackageRoot)(), "modules");
|
|
47
|
+
const argv = process.argv.slice(2);
|
|
48
|
+
const addIndex = argv.indexOf("add");
|
|
49
|
+
const argsAfterAdd = addIndex >= 0 ? argv.slice(addIndex + 1) : [];
|
|
50
|
+
const flagsProvided = argsAfterAdd.some((arg) => arg.startsWith("-"));
|
|
51
|
+
const optionsProvided = flagsProvided ||
|
|
52
|
+
!!(options && (options.yes || options.provider || options.force || options.dryRun));
|
|
53
|
+
if (optionsProvided) {
|
|
54
|
+
if (!module) {
|
|
55
|
+
throw new Error("Module name is required when using flags");
|
|
56
|
+
}
|
|
57
|
+
if (module === "database" || module === "auth") {
|
|
58
|
+
if (module === "database") {
|
|
59
|
+
if (!options?.provider) {
|
|
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
|
+
};
|
|
79
|
+
}
|
|
80
|
+
else if (module === "auth") {
|
|
81
|
+
const provider = options?.provider || "better-auth";
|
|
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
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const moduleMetadata = await loadModuleMetadata(modulesDir, module, options?.provider);
|
|
26
95
|
if (!moduleMetadata) {
|
|
27
|
-
|
|
28
|
-
|
|
96
|
+
throw new Error(`Module "${module}" not found`);
|
|
97
|
+
}
|
|
98
|
+
let selectedProvider = options?.provider;
|
|
99
|
+
if (!selectedProvider && moduleMetadata.category !== module) {
|
|
100
|
+
selectedProvider = module;
|
|
29
101
|
}
|
|
30
|
-
let selectedProvider = options.provider;
|
|
31
102
|
if (moduleMetadata.category === "database" && !selectedProvider) {
|
|
32
|
-
if (typeof moduleMetadata.dependencies === "object" &&
|
|
103
|
+
if (typeof moduleMetadata.dependencies === "object" &&
|
|
104
|
+
"providers" in moduleMetadata.dependencies) {
|
|
33
105
|
const providers = Object.keys(moduleMetadata.dependencies.providers || {});
|
|
34
106
|
if (providers.length > 0) {
|
|
35
107
|
const { provider } = await inquirer_1.default.prompt([
|
|
@@ -44,58 +116,173 @@ async function addCommand(module, options) {
|
|
|
44
116
|
}
|
|
45
117
|
}
|
|
46
118
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (moduleMetadata.frameworkConfigs?.shared?.dependencies) {
|
|
50
|
-
Object.assign(mergedDeps, moduleMetadata.frameworkConfigs.shared.dependencies);
|
|
119
|
+
if (projectInfo && !moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
120
|
+
throw new Error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(", ")}`);
|
|
51
121
|
}
|
|
52
|
-
|
|
53
|
-
|
|
122
|
+
return {
|
|
123
|
+
module,
|
|
124
|
+
provider: selectedProvider,
|
|
125
|
+
displayName: moduleMetadata.displayName,
|
|
126
|
+
metadata: moduleMetadata,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const answers = await inquirer_1.default.prompt([
|
|
130
|
+
{
|
|
131
|
+
type: "list",
|
|
132
|
+
name: "category",
|
|
133
|
+
message: "What would you like to add?",
|
|
134
|
+
choices: [
|
|
135
|
+
{ name: "Database", value: "database" },
|
|
136
|
+
{ name: "Authentication", value: "auth" },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
const category = answers.category;
|
|
141
|
+
if (category === "database") {
|
|
142
|
+
const dbAnswers = await inquirer_1.default.prompt([
|
|
143
|
+
{
|
|
144
|
+
type: "list",
|
|
145
|
+
name: "database",
|
|
146
|
+
message: "Select database:",
|
|
147
|
+
choices: [
|
|
148
|
+
{ name: "Prisma", value: "prisma" },
|
|
149
|
+
{ name: "Mongoose", value: "mongoose" },
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
]);
|
|
153
|
+
const selectedDb = dbAnswers.database;
|
|
154
|
+
if (selectedDb === "prisma") {
|
|
155
|
+
const providerAnswers = await inquirer_1.default.prompt([
|
|
156
|
+
{
|
|
157
|
+
type: "list",
|
|
158
|
+
name: "provider",
|
|
159
|
+
message: "Select Prisma provider:",
|
|
160
|
+
choices: [
|
|
161
|
+
{ name: "PostgreSQL", value: "postgresql" },
|
|
162
|
+
{ name: "MongoDB", value: "mongodb" },
|
|
163
|
+
{ name: "MySQL", value: "mysql" },
|
|
164
|
+
{ name: "SQLite", value: "sqlite" },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
]);
|
|
168
|
+
return {
|
|
169
|
+
module: "database",
|
|
170
|
+
provider: "prisma",
|
|
171
|
+
displayName: `Prisma (${providerAnswers.provider})`,
|
|
172
|
+
metadata: (await loadModuleMetadata(modulesDir, "prisma", "prisma")),
|
|
173
|
+
};
|
|
54
174
|
}
|
|
55
|
-
|
|
56
|
-
|
|
175
|
+
else {
|
|
176
|
+
return {
|
|
177
|
+
module: "database",
|
|
178
|
+
provider: "mongoose",
|
|
179
|
+
displayName: "Mongoose",
|
|
180
|
+
metadata: (await loadModuleMetadata(modulesDir, "mongoose", "mongoose")),
|
|
181
|
+
};
|
|
57
182
|
}
|
|
58
|
-
|
|
59
|
-
|
|
183
|
+
}
|
|
184
|
+
else if (category === "auth") {
|
|
185
|
+
const authAnswers = await inquirer_1.default.prompt([
|
|
186
|
+
{
|
|
187
|
+
type: "list",
|
|
188
|
+
name: "auth",
|
|
189
|
+
message: "Select authentication:",
|
|
190
|
+
choices: [
|
|
191
|
+
{ name: "Better Auth", value: "better-auth" },
|
|
192
|
+
{ name: "Auth.js", value: "authjs" },
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
]);
|
|
196
|
+
const selectedAuth = authAnswers.auth;
|
|
197
|
+
const metadata = await loadModuleMetadata(modulesDir, selectedAuth, selectedAuth);
|
|
198
|
+
if (!metadata) {
|
|
199
|
+
throw new Error(`Auth provider "${selectedAuth}" not found`);
|
|
60
200
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const variables = {};
|
|
64
|
-
if (selectedProvider) {
|
|
65
|
-
variables.provider = selectedProvider;
|
|
66
|
-
variables.connectionString = database_config_1.DATABASE_CONNECTION_STRINGS[selectedProvider] || "";
|
|
201
|
+
if (projectInfo && !metadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
202
|
+
throw new Error(`Auth provider "${selectedAuth}" does not support ${projectInfo.framework}`);
|
|
67
203
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
204
|
+
return {
|
|
205
|
+
module: "auth",
|
|
206
|
+
provider: selectedAuth,
|
|
207
|
+
displayName: selectedAuth === "better-auth" ? "Better Auth" : "Auth.js",
|
|
208
|
+
metadata,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
throw new Error("Invalid selection");
|
|
212
|
+
}
|
|
213
|
+
async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
214
|
+
const moduleMetadata = config.metadata;
|
|
215
|
+
const selectedProvider = config.provider;
|
|
216
|
+
if (config.module === "auth" && projectInfo.hasAuth && !options?.force) {
|
|
217
|
+
logger_1.logger.warn("Auth library already detected in this project");
|
|
218
|
+
const { proceed } = await inquirer_1.default.prompt([
|
|
219
|
+
{
|
|
220
|
+
type: "confirm",
|
|
221
|
+
name: "proceed",
|
|
222
|
+
message: "Continue anyway? (use --force to skip this prompt)",
|
|
223
|
+
default: false,
|
|
224
|
+
},
|
|
225
|
+
]);
|
|
226
|
+
if (!proceed) {
|
|
227
|
+
logger_1.logger.info("Cancelled");
|
|
228
|
+
return;
|
|
71
229
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
230
|
+
}
|
|
231
|
+
if (options?.dryRun) {
|
|
232
|
+
logger_1.logger.warn("Dry run mode - no changes will be made");
|
|
233
|
+
logger_1.logger.newLine();
|
|
234
|
+
}
|
|
235
|
+
const moduleBasePath = await findModulePath(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"), config.module, config.provider);
|
|
236
|
+
if (moduleBasePath) {
|
|
237
|
+
const frameworkConfig = await framework_utils_1.FrameworkUtils.loadFrameworkConfig(projectInfo.framework, path_1.default.join((0, package_root_1.getPackageRoot)(), "templates"));
|
|
238
|
+
const gen = new code_generator_1.AdvancedCodeGenerator(frameworkConfig);
|
|
239
|
+
await gen.loadGenerators(path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"));
|
|
240
|
+
const moduleName = path_1.default.basename(moduleBasePath);
|
|
241
|
+
const available = gen.getAvailableGenerators();
|
|
242
|
+
const alreadyRegistered = (config.module === "database" && available.databases.includes(moduleName)) ||
|
|
243
|
+
(config.module === "auth" && available.auths.includes(moduleName));
|
|
244
|
+
if (!alreadyRegistered) {
|
|
245
|
+
const ops = [];
|
|
246
|
+
if (Array.isArray(moduleMetadata.patches)) {
|
|
247
|
+
for (const p of moduleMetadata.patches) {
|
|
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
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (ops.length > 0) {
|
|
259
|
+
gen.registerGenerator(config.module, moduleName, {
|
|
260
|
+
name: moduleName,
|
|
261
|
+
type: config.module,
|
|
262
|
+
priority: 0,
|
|
263
|
+
operations: ops,
|
|
264
|
+
dependencies: {},
|
|
265
|
+
});
|
|
85
266
|
}
|
|
86
267
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
268
|
+
const selectedModules = { framework: projectInfo.framework };
|
|
269
|
+
if (config.module === "database" && config.provider) {
|
|
270
|
+
if (config.provider.startsWith("prisma-")) {
|
|
271
|
+
selectedModules.database = "prisma";
|
|
272
|
+
selectedModules.prismaProvider = config.provider.split("-")[1];
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
selectedModules.database = config.provider;
|
|
276
|
+
}
|
|
90
277
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
await applyFrameworkPatches(projectRoot, moduleMetadata.frameworkPatches, projectInfo.framework);
|
|
278
|
+
if (config.module === "auth" && config.provider) {
|
|
279
|
+
selectedModules.auth = config.provider;
|
|
94
280
|
}
|
|
95
|
-
|
|
281
|
+
const postInstall = await gen.applyToProject(selectedModules, [], projectRoot);
|
|
282
|
+
if (postInstall && postInstall.length > 0 && !options?.dryRun) {
|
|
96
283
|
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
97
284
|
try {
|
|
98
|
-
for (const command of
|
|
285
|
+
for (const command of postInstall) {
|
|
99
286
|
(0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
|
|
100
287
|
}
|
|
101
288
|
postInstallSpinner.succeed("Post-install commands completed");
|
|
@@ -105,50 +292,105 @@ async function addCommand(module, options) {
|
|
|
105
292
|
throw error;
|
|
106
293
|
}
|
|
107
294
|
}
|
|
108
|
-
if (
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
await (0, package_manager_1.
|
|
112
|
-
|
|
113
|
-
else {
|
|
114
|
-
logger_1.logger.info(`Would add dependencies: ${deps.join(", ")}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (Object.keys(mergedDevDeps).length > 0 && options.install !== false) {
|
|
118
|
-
const devDeps = Object.entries(mergedDevDeps).map(([name, version]) => `${name}@${version}`);
|
|
119
|
-
if (!options.dryRun) {
|
|
120
|
-
await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, devDeps, true);
|
|
295
|
+
if (!options?.dryRun && options?.install !== false) {
|
|
296
|
+
const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
|
|
297
|
+
try {
|
|
298
|
+
await (0, package_manager_1.installDependencies)(projectRoot, projectInfo.packageManager);
|
|
299
|
+
installSpinner.succeed("Dependencies installed");
|
|
121
300
|
}
|
|
122
|
-
|
|
123
|
-
|
|
301
|
+
catch (err) {
|
|
302
|
+
installSpinner.fail("Failed to install dependencies");
|
|
303
|
+
throw err;
|
|
124
304
|
}
|
|
125
305
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const mergedDeps = {};
|
|
309
|
+
const mergedDevDeps = {};
|
|
310
|
+
if (moduleMetadata.frameworkConfigs?.shared?.dependencies) {
|
|
311
|
+
Object.assign(mergedDeps, moduleMetadata.frameworkConfigs.shared.dependencies);
|
|
312
|
+
}
|
|
313
|
+
if (moduleMetadata.frameworkConfigs?.shared?.devDependencies) {
|
|
314
|
+
Object.assign(mergedDevDeps, moduleMetadata.frameworkConfigs.shared.devDependencies);
|
|
315
|
+
}
|
|
316
|
+
// Adapter-specific dependencies are applied via generator metadata; frameworkConfigs still merge above.
|
|
317
|
+
// Do not mutate the loaded module metadata here; use mergedDeps/mergedDevDeps for installation.
|
|
318
|
+
const variables = {};
|
|
319
|
+
if (selectedProvider) {
|
|
320
|
+
variables.provider = selectedProvider;
|
|
321
|
+
}
|
|
322
|
+
if (moduleMetadata.envVars) {
|
|
323
|
+
const envArray = Array.isArray(moduleMetadata.envVars)
|
|
324
|
+
? moduleMetadata.envVars
|
|
325
|
+
: Object.entries(moduleMetadata.envVars).map(([k, v]) => ({
|
|
326
|
+
key: k,
|
|
327
|
+
value: String(v),
|
|
130
328
|
}));
|
|
131
|
-
|
|
132
|
-
|
|
329
|
+
for (const ev of envArray) {
|
|
330
|
+
if (ev.key && typeof ev.value === "string")
|
|
331
|
+
variables[ev.key] = ev.value;
|
|
332
|
+
}
|
|
333
|
+
for (let pass = 0; pass < 5; pass++) {
|
|
334
|
+
let changed = false;
|
|
335
|
+
for (const ev of envArray) {
|
|
336
|
+
if (!ev.key || typeof ev.value !== "string")
|
|
337
|
+
continue;
|
|
338
|
+
const resolved = ev.value.replace(/\{\{(\w+)\}\}/g, (_m, k) => variables[k] ?? _m);
|
|
339
|
+
if (variables[ev.key] !== resolved) {
|
|
340
|
+
variables[ev.key] = resolved;
|
|
341
|
+
changed = true;
|
|
342
|
+
}
|
|
133
343
|
}
|
|
134
|
-
|
|
135
|
-
|
|
344
|
+
if (!changed)
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
await applyModulePatches(projectRoot, projectInfo, moduleMetadata, path_1.default.join((0, package_root_1.getPackageRoot)(), "modules"), config.module, options || {});
|
|
349
|
+
if (moduleMetadata.frameworkPatches && !options?.dryRun) {
|
|
350
|
+
await applyFrameworkPatches(projectRoot, moduleMetadata.frameworkPatches, projectInfo.framework);
|
|
351
|
+
}
|
|
352
|
+
if (moduleMetadata.postInstall && moduleMetadata.postInstall.length > 0 && !options?.dryRun) {
|
|
353
|
+
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
354
|
+
try {
|
|
355
|
+
for (const command of moduleMetadata.postInstall) {
|
|
356
|
+
(0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
|
|
136
357
|
}
|
|
358
|
+
postInstallSpinner.succeed("Post-install commands completed");
|
|
137
359
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (moduleMetadata.envVars && moduleMetadata.envVars.some((v) => v.required)) {
|
|
142
|
-
logger_1.logger.log("Next: Fill in environment variables in .env");
|
|
360
|
+
catch (error) {
|
|
361
|
+
postInstallSpinner.fail("Failed to run post-install commands");
|
|
362
|
+
throw error;
|
|
143
363
|
}
|
|
144
|
-
logger_1.logger.newLine();
|
|
145
364
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
|
|
365
|
+
if (Object.keys(mergedDeps).length > 0 && options?.install !== false) {
|
|
366
|
+
const deps = Object.entries(mergedDeps).map(([name, version]) => `${name}@${version}`);
|
|
367
|
+
if (!options?.dryRun) {
|
|
368
|
+
await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, deps, false);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
logger_1.logger.info(`Would add dependencies: ${deps.join(", ")}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (Object.keys(mergedDevDeps).length > 0 && options?.install !== false) {
|
|
375
|
+
const devDeps = Object.entries(mergedDevDeps).map(([name, version]) => `${name}@${version}`);
|
|
376
|
+
if (!options?.dryRun) {
|
|
377
|
+
await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, devDeps, true);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
logger_1.logger.info(`Would add dev dependencies: ${devDeps.join(", ")}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (moduleMetadata.envVars && moduleMetadata.envVars.length > 0) {
|
|
384
|
+
const processedEnvVars = moduleMetadata.envVars.map((envVar) => ({
|
|
385
|
+
...envVar,
|
|
386
|
+
value: envVar.value?.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] || match),
|
|
387
|
+
}));
|
|
388
|
+
if (!options?.dryRun) {
|
|
389
|
+
await (0, env_editor_1.addEnvVariables)(projectRoot, processedEnvVars, { force: options?.force });
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
logger_1.logger.log(` ${chalk_1.default.dim("~")} .env.example`);
|
|
150
393
|
}
|
|
151
|
-
process.exit(1);
|
|
152
394
|
}
|
|
153
395
|
}
|
|
154
396
|
async function loadModuleMetadata(modulesDir, moduleName, provider) {
|
|
@@ -171,35 +413,16 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
|
|
|
171
413
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
172
414
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
173
415
|
if (provider && moduleDir === provider) {
|
|
174
|
-
return await
|
|
416
|
+
return await (0, generator_utils_1.mergeGeneratorIntoModuleMetadata)(metadata, modulePath);
|
|
175
417
|
}
|
|
176
|
-
if (!provider && metadata.category === moduleName) {
|
|
177
|
-
return await
|
|
418
|
+
if (!provider && (metadata.category === moduleName || moduleDir === moduleName)) {
|
|
419
|
+
return await (0, generator_utils_1.mergeGeneratorIntoModuleMetadata)(metadata, modulePath);
|
|
178
420
|
}
|
|
179
421
|
}
|
|
180
422
|
}
|
|
181
423
|
}
|
|
182
424
|
return null;
|
|
183
425
|
}
|
|
184
|
-
async function loadGeneratorAndMerge(metadata, modulePath) {
|
|
185
|
-
const generatorPath = path_1.default.join(modulePath, "generator.json");
|
|
186
|
-
if (await fs_extra_1.default.pathExists(generatorPath)) {
|
|
187
|
-
const generator = await fs_extra_1.default.readJSON(generatorPath);
|
|
188
|
-
if (generator.envVars) {
|
|
189
|
-
metadata.envVars = metadata.envVars || [];
|
|
190
|
-
for (const [key, value] of Object.entries(generator.envVars)) {
|
|
191
|
-
metadata.envVars.push({ key, value: value, description: `Environment variable for ${key}`, required: true });
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (generator.dependencies) {
|
|
195
|
-
metadata.dependencies = { ...metadata.dependencies, ...generator.dependencies };
|
|
196
|
-
}
|
|
197
|
-
if (generator.devDependencies) {
|
|
198
|
-
metadata.devDependencies = { ...metadata.devDependencies, ...generator.devDependencies };
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return metadata;
|
|
202
|
-
}
|
|
203
426
|
async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, moduleName, options) {
|
|
204
427
|
if (!moduleMetadata.patches || !Array.isArray(moduleMetadata.patches)) {
|
|
205
428
|
return;
|
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;
|