create-stackkit-app 0.4.3 → 0.4.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/bin/create-stackkit.js +10 -1
- package/dist/index.js +62 -3
- package/dist/lib/create-project.d.ts +20 -1
- package/dist/lib/create-project.js +193 -568
- package/dist/lib/utils/config-utils.d.ts +2 -0
- package/dist/lib/utils/config-utils.js +88 -0
- package/dist/lib/utils/file-utils.d.ts +8 -0
- package/dist/lib/utils/file-utils.js +75 -0
- package/dist/lib/utils/git-utils.d.ts +1 -0
- package/dist/lib/utils/git-utils.js +9 -0
- package/dist/lib/utils/js-conversion.d.ts +1 -0
- package/dist/lib/utils/js-conversion.js +244 -0
- package/dist/lib/utils/logger.d.ts +16 -0
- package/dist/lib/utils/logger.js +59 -0
- package/dist/lib/utils/module-utils.d.ts +2 -0
- package/dist/lib/utils/module-utils.js +461 -0
- package/dist/lib/utils/package-utils.d.ts +1 -0
- package/dist/lib/utils/package-utils.js +39 -0
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
- package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
- package/modules/auth/authjs/files/lib/auth.ts +41 -0
- package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
- package/modules/auth/authjs/module.json +95 -0
- package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
- package/modules/auth/better-auth/files/lib/auth.ts +75 -0
- package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
- package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
- package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
- package/modules/auth/better-auth/module.json +191 -0
- package/modules/database/mongoose/files/lib/db.ts +68 -0
- package/modules/database/mongoose/files/models/User.ts +34 -0
- package/modules/database/mongoose/module.json +55 -0
- package/modules/database/prisma/files/lib/prisma.ts +4 -0
- package/modules/database/prisma/files/prisma/schema.prisma +8 -0
- package/modules/database/prisma/files/prisma.config.ts +12 -0
- package/modules/database/prisma/module.json +122 -0
- package/package.json +7 -3
- package/templates/express/.env.example +1 -10
- package/templates/express/package.json +15 -21
- package/templates/express/src/app.ts +9 -29
- package/templates/express/src/config/env.ts +3 -14
- package/templates/express/src/features/health/health.controller.ts +18 -0
- package/templates/express/src/features/health/health.route.ts +9 -0
- package/templates/express/src/features/health/health.service.ts +6 -0
- package/templates/express/src/middlewares/error.middleware.ts +2 -2
- package/templates/express/src/server.ts +1 -1
- package/templates/express/template.json +1 -5
- package/templates/express/tsconfig.json +0 -1
- package/templates/nextjs/lib/env.ts +8 -0
- package/templates/nextjs/package.json +7 -7
- package/templates/react-vite/.env.example +1 -2
- package/templates/react-vite/.prettierignore +4 -0
- package/templates/react-vite/.prettierrc +9 -0
- package/templates/react-vite/README.md +22 -0
- package/templates/react-vite/package.json +16 -16
- package/templates/react-vite/src/router.tsx +0 -12
- package/templates/react-vite/vite.config.ts +0 -6
- package/dist/lib/template-composer.d.ts +0 -16
- package/dist/lib/template-composer.js +0 -197
- package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
- package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
- package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
- package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
- package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
- package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-express/module.json +0 -61
- package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
- package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
- package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/module.json +0 -62
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
- package/modules/auth/better-auth-react/module.json +0 -28
- package/modules/auth/clerk-express/files/lib/auth.ts +0 -7
- package/modules/auth/clerk-express/module.json +0 -34
- package/modules/auth/clerk-nextjs/files/lib/auth-provider.tsx +0 -5
- package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
- package/modules/auth/clerk-nextjs/module.json +0 -64
- package/modules/auth/clerk-react/files/lib/auth-provider.tsx +0 -15
- package/modules/auth/clerk-react/module.json +0 -28
- package/modules/database/mongoose-mongodb/files/lib/db.ts +0 -40
- package/modules/database/mongoose-mongodb/module.json +0 -55
- package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
- package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-mongodb/module.json +0 -60
- package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
- package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-postgresql/module.json +0 -60
- package/templates/react-vite/src/api/services/user.service.ts +0 -18
- package/templates/react-vite/src/pages/UserProfile.tsx +0 -40
- package/templates/react-vite/src/types/user.d.ts +0 -6
- /package/modules/auth/{better-auth-nextjs → better-auth}/files/api/auth/[...all]/route.ts +0 -0
|
@@ -8,27 +8,112 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
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
|
-
const ora_1 = __importDefault(require("ora"));
|
|
12
11
|
const path_1 = __importDefault(require("path"));
|
|
13
12
|
const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const file_utils_1 = require("./utils/file-utils");
|
|
14
|
+
const git_utils_1 = require("./utils/git-utils");
|
|
15
|
+
const js_conversion_1 = require("./utils/js-conversion");
|
|
16
|
+
const logger_1 = require("./utils/logger");
|
|
17
|
+
const module_utils_1 = require("./utils/module-utils");
|
|
18
|
+
const package_utils_1 = require("./utils/package-utils");
|
|
19
|
+
async function createProject(projectName, options) {
|
|
20
|
+
logger_1.logger.newLine();
|
|
21
|
+
logger_1.logger.log(chalk_1.default.bold.cyan("Create StackKit App"));
|
|
22
|
+
logger_1.logger.newLine();
|
|
23
|
+
const config = await getProjectConfig(projectName, options);
|
|
19
24
|
const targetDir = path_1.default.join(process.cwd(), config.projectName);
|
|
20
25
|
if (await fs_extra_1.default.pathExists(targetDir)) {
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
logger_1.logger.error(`Directory "${config.projectName}" already exists`);
|
|
27
|
+
logger_1.logger.log(chalk_1.default.gray("Please choose a different name or remove the existing directory.\n"));
|
|
23
28
|
process.exit(1);
|
|
24
29
|
}
|
|
25
|
-
|
|
26
|
-
await generateProject(config, targetDir);
|
|
27
|
-
// Show next steps
|
|
30
|
+
await generateProject(config, targetDir, options);
|
|
28
31
|
showNextSteps(config);
|
|
29
32
|
}
|
|
30
|
-
async function getProjectConfig(projectName) {
|
|
31
|
-
|
|
33
|
+
async function getProjectConfig(projectName, options) {
|
|
34
|
+
if (options && Object.keys(options).length > 0) {
|
|
35
|
+
if (options.yes || options.y) {
|
|
36
|
+
return {
|
|
37
|
+
projectName: projectName || "my-app",
|
|
38
|
+
framework: "nextjs",
|
|
39
|
+
database: "prisma",
|
|
40
|
+
dbProvider: "postgresql",
|
|
41
|
+
auth: "better-auth",
|
|
42
|
+
language: "typescript",
|
|
43
|
+
packageManager: "pnpm",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Validate options
|
|
47
|
+
const validFrameworks = ['nextjs', 'express', 'react-vite'];
|
|
48
|
+
const framework = options.framework || options.f;
|
|
49
|
+
if (framework && !validFrameworks.includes(framework)) {
|
|
50
|
+
throw new Error(`Invalid framework: ${framework}. Valid options: ${validFrameworks.join(', ')}`);
|
|
51
|
+
}
|
|
52
|
+
const validDatabases = ['prisma-postgresql', 'prisma-mongodb', 'prisma-mysql', 'prisma-sqlite', 'mongoose-mongodb', 'none'];
|
|
53
|
+
const db = options.database || options.d;
|
|
54
|
+
if (db && !validDatabases.includes(db)) {
|
|
55
|
+
throw new Error(`Invalid database: ${db}. Valid options: ${validDatabases.join(', ')}`);
|
|
56
|
+
}
|
|
57
|
+
const validAuth = ['better-auth', 'authjs', 'none'];
|
|
58
|
+
const authOpt = options.auth || options.a;
|
|
59
|
+
if (authOpt && !validAuth.includes(authOpt)) {
|
|
60
|
+
throw new Error(`Invalid auth: ${authOpt}. Valid options: ${validAuth.join(', ')}`);
|
|
61
|
+
}
|
|
62
|
+
const validLanguages = ['typescript', 'javascript'];
|
|
63
|
+
const language = options.language || options.l;
|
|
64
|
+
if (language && !validLanguages.includes(language)) {
|
|
65
|
+
throw new Error(`Invalid language: ${language}. Valid options: ${validLanguages.join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
const validPackageManagers = ['pnpm', 'npm', 'yarn', 'bun'];
|
|
68
|
+
const pm = options.packageManager || options.p;
|
|
69
|
+
if (pm && !validPackageManagers.includes(pm)) {
|
|
70
|
+
throw new Error(`Invalid package manager: ${pm}. Valid options: ${validPackageManagers.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
let database = "none";
|
|
73
|
+
let dbProvider;
|
|
74
|
+
if (db && db !== "none") {
|
|
75
|
+
if (db.startsWith("prisma-")) {
|
|
76
|
+
database = "prisma";
|
|
77
|
+
const provider = db.split("-")[1];
|
|
78
|
+
if (!["postgresql", "mongodb", "mysql", "sqlite"].includes(provider)) {
|
|
79
|
+
throw new Error(`Invalid Prisma provider: ${provider}`);
|
|
80
|
+
}
|
|
81
|
+
dbProvider = provider;
|
|
82
|
+
}
|
|
83
|
+
else if (db === "mongoose-mongodb") {
|
|
84
|
+
database = "mongoose";
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
throw new Error(`Unsupported database: ${db}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
let auth = "none";
|
|
91
|
+
if (authOpt && authOpt !== "none") {
|
|
92
|
+
auth = authOpt;
|
|
93
|
+
}
|
|
94
|
+
const finalFramework = (framework || "nextjs");
|
|
95
|
+
if (finalFramework === "react-vite") {
|
|
96
|
+
database = "none";
|
|
97
|
+
dbProvider = undefined;
|
|
98
|
+
}
|
|
99
|
+
// Validate auth compatibility
|
|
100
|
+
if (auth === "authjs" && (database !== "prisma" || finalFramework !== "nextjs")) {
|
|
101
|
+
throw new Error("Auth.js is only supported with Next.js and Prisma database");
|
|
102
|
+
}
|
|
103
|
+
if (auth === "better-auth" && database === "none" && finalFramework !== "react-vite") {
|
|
104
|
+
throw new Error("Better Auth requires a database for server frameworks");
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
projectName: projectName || "my-app",
|
|
108
|
+
framework: finalFramework,
|
|
109
|
+
database,
|
|
110
|
+
dbProvider,
|
|
111
|
+
auth,
|
|
112
|
+
language: (language || "typescript"),
|
|
113
|
+
packageManager: (pm || "pnpm"),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const answers = (await inquirer_1.default.prompt([
|
|
32
117
|
{
|
|
33
118
|
type: "input",
|
|
34
119
|
name: "projectName",
|
|
@@ -62,41 +147,54 @@ async function getProjectConfig(projectName) {
|
|
|
62
147
|
message: "Select database/ORM:",
|
|
63
148
|
when: (answers) => answers.framework !== "react-vite",
|
|
64
149
|
choices: [
|
|
65
|
-
{ name: "Prisma
|
|
66
|
-
{ name: "
|
|
67
|
-
{ name: "Mongoose + MongoDB", value: "mongoose-mongodb" },
|
|
150
|
+
{ name: "Prisma", value: "prisma" },
|
|
151
|
+
{ name: "Mongoose (MongoDB)", value: "mongoose" },
|
|
68
152
|
{ name: "None", value: "none" },
|
|
69
153
|
],
|
|
70
154
|
},
|
|
155
|
+
{
|
|
156
|
+
type: "list",
|
|
157
|
+
name: "dbProvider",
|
|
158
|
+
message: "Select database provider for Prisma:",
|
|
159
|
+
when: (answers) => answers.database === "prisma",
|
|
160
|
+
choices: [
|
|
161
|
+
{ name: "PostgreSQL", value: "postgresql" },
|
|
162
|
+
{ name: "MongoDB", value: "mongodb" },
|
|
163
|
+
{ name: "MySQL", value: "mysql" },
|
|
164
|
+
{ name: "SQLite", value: "sqlite" },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
71
167
|
{
|
|
72
168
|
type: "list",
|
|
73
169
|
name: "auth",
|
|
74
170
|
message: "Select authentication:",
|
|
171
|
+
when: (answers) => (answers.database !== "none" || answers.framework === "react-vite") && answers.database !== "mongoose",
|
|
75
172
|
choices: (answers) => {
|
|
76
173
|
if (answers.framework === "react-vite") {
|
|
77
174
|
return [
|
|
78
|
-
{ name: "Better Auth", value: "better-auth
|
|
79
|
-
{ name: "
|
|
175
|
+
{ name: "Better Auth", value: "better-auth" },
|
|
176
|
+
{ name: "None", value: "none" },
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
if (answers.database === "mongoose") {
|
|
180
|
+
return [
|
|
181
|
+
{ name: "Better Auth", value: "better-auth" },
|
|
80
182
|
{ name: "None", value: "none" },
|
|
81
183
|
];
|
|
82
184
|
}
|
|
83
|
-
|
|
84
|
-
if (answers.framework === "nextjs") {
|
|
185
|
+
if (answers.framework === "nextjs" && answers.database === "prisma") {
|
|
85
186
|
return [
|
|
86
|
-
{ name: "Better Auth", value: "better-auth
|
|
87
|
-
{ name: "
|
|
187
|
+
{ name: "Better Auth", value: "better-auth" },
|
|
188
|
+
{ name: "Auth.js", value: "authjs" },
|
|
88
189
|
{ name: "None", value: "none" },
|
|
89
190
|
];
|
|
90
191
|
}
|
|
91
|
-
|
|
92
|
-
if (answers.framework === "express") {
|
|
192
|
+
if (answers.framework === "express" && answers.database === "prisma") {
|
|
93
193
|
return [
|
|
94
|
-
{ name: "Better Auth", value: "better-auth
|
|
95
|
-
{ name: "Clerk", value: "clerk-express" },
|
|
194
|
+
{ name: "Better Auth", value: "better-auth" },
|
|
96
195
|
{ name: "None", value: "none" },
|
|
97
196
|
];
|
|
98
197
|
}
|
|
99
|
-
// Default - no auth
|
|
100
198
|
return [{ name: "None", value: "none" }];
|
|
101
199
|
},
|
|
102
200
|
},
|
|
@@ -122,22 +220,24 @@ async function getProjectConfig(projectName) {
|
|
|
122
220
|
],
|
|
123
221
|
default: "pnpm",
|
|
124
222
|
},
|
|
125
|
-
]);
|
|
223
|
+
]));
|
|
126
224
|
return {
|
|
127
|
-
projectName: projectName || answers.projectName,
|
|
225
|
+
projectName: (projectName || answers.projectName),
|
|
128
226
|
framework: answers.framework,
|
|
129
|
-
database: answers.framework === "react-vite"
|
|
130
|
-
|
|
227
|
+
database: (answers.framework === "react-vite"
|
|
228
|
+
? "none"
|
|
229
|
+
: answers.database),
|
|
230
|
+
dbProvider: answers.dbProvider,
|
|
231
|
+
auth: answers.database === "mongoose" ? "better-auth" : (answers.auth || "none"),
|
|
131
232
|
language: answers.language,
|
|
132
233
|
packageManager: answers.packageManager,
|
|
133
234
|
};
|
|
134
235
|
}
|
|
135
|
-
async function generateProject(config, targetDir) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const copySpinner = (0, ora_1.default)("Creating project files...").start();
|
|
236
|
+
async function generateProject(config, targetDir, options) {
|
|
237
|
+
const copySpinner = logger_1.logger.startSpinner("Creating project files...");
|
|
238
|
+
let postInstallCommands = [];
|
|
139
239
|
try {
|
|
140
|
-
await composeTemplate(config, targetDir);
|
|
240
|
+
postInstallCommands = await composeTemplate(config, targetDir);
|
|
141
241
|
copySpinner.succeed("Project files created");
|
|
142
242
|
}
|
|
143
243
|
catch (error) {
|
|
@@ -145,559 +245,84 @@ async function generateProject(config, targetDir) {
|
|
|
145
245
|
throw error;
|
|
146
246
|
}
|
|
147
247
|
// Install dependencies
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
catch (error) {
|
|
154
|
-
installSpinner.fail("Failed to install dependencies");
|
|
155
|
-
throw error;
|
|
156
|
-
}
|
|
157
|
-
// Initialize git
|
|
158
|
-
const gitSpinner = (0, ora_1.default)("Initializing git repository...").start();
|
|
159
|
-
try {
|
|
160
|
-
await initGit(targetDir);
|
|
161
|
-
gitSpinner.succeed("Git repository initialized");
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
gitSpinner.warn("Failed to initialize git repository");
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
async function composeTemplate(config, targetDir) {
|
|
168
|
-
const templatesDir = path_1.default.join(__dirname, "..", "..", "templates");
|
|
169
|
-
await fs_extra_1.default.ensureDir(targetDir);
|
|
170
|
-
// 1. Copy base framework template
|
|
171
|
-
await copyBaseFramework(templatesDir, targetDir, config.framework);
|
|
172
|
-
// 2. Merge database configuration
|
|
173
|
-
if (config.database !== "none") {
|
|
174
|
-
await mergeDatabaseConfig(templatesDir, targetDir, config.database, config.framework);
|
|
175
|
-
}
|
|
176
|
-
// 3. Merge auth configuration
|
|
177
|
-
if (config.auth !== "none") {
|
|
178
|
-
await mergeAuthConfig(templatesDir, targetDir, config.framework, config.auth, config.database);
|
|
179
|
-
}
|
|
180
|
-
// 4. Update package.json with project name
|
|
181
|
-
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
182
|
-
if (await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
183
|
-
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
184
|
-
packageJson.name = config.projectName;
|
|
185
|
-
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
186
|
-
}
|
|
187
|
-
// 5. Convert to JavaScript if selected
|
|
188
|
-
if (config.language === "javascript") {
|
|
189
|
-
await convertToJavaScript(targetDir, config.framework);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
async function copyBaseFramework(templatesDir, targetDir, framework) {
|
|
193
|
-
const baseDir = path_1.default.join(templatesDir, framework);
|
|
194
|
-
if (!(await fs_extra_1.default.pathExists(baseDir))) {
|
|
195
|
-
throw new Error(`Base template not found for framework: ${framework}\n` + `Expected at: ${baseDir}`);
|
|
196
|
-
}
|
|
197
|
-
await fs_extra_1.default.copy(baseDir, targetDir, {
|
|
198
|
-
filter: (src) => {
|
|
199
|
-
const basename = path_1.default.basename(src);
|
|
200
|
-
return !["template.json", "config.json", "node_modules", ".git"].includes(basename);
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
async function mergeDatabaseConfig(templatesDir, targetDir, database, framework) {
|
|
205
|
-
// Use modules directory (sibling to templates)
|
|
206
|
-
const modulesDir = path_1.default.join(templatesDir, "..", "modules");
|
|
207
|
-
const dbModulePath = path_1.default.join(modulesDir, "database", database);
|
|
208
|
-
if (!(await fs_extra_1.default.pathExists(dbModulePath))) {
|
|
209
|
-
console.warn(`Database module not found: ${database}`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
// Read module.json
|
|
213
|
-
const moduleJsonPath = path_1.default.join(dbModulePath, "module.json");
|
|
214
|
-
if (!(await fs_extra_1.default.pathExists(moduleJsonPath))) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const moduleData = await fs_extra_1.default.readJson(moduleJsonPath);
|
|
218
|
-
// Copy files from module
|
|
219
|
-
const filesDir = path_1.default.join(dbModulePath, "files");
|
|
220
|
-
if (await fs_extra_1.default.pathExists(filesDir)) {
|
|
221
|
-
// Copy files based on patches in module.json
|
|
222
|
-
for (const patch of moduleData.patches || []) {
|
|
223
|
-
if (patch.type === "create-file") {
|
|
224
|
-
const sourceFile = path_1.default.join(filesDir, patch.source);
|
|
225
|
-
let destFile = path_1.default.join(targetDir, patch.destination);
|
|
226
|
-
// Simple placeholder replacement for lib
|
|
227
|
-
destFile = destFile.replace("{{lib}}", "lib").replace("{{src}}", "src");
|
|
228
|
-
if (await fs_extra_1.default.pathExists(sourceFile)) {
|
|
229
|
-
await fs_extra_1.default.ensureDir(path_1.default.dirname(destFile));
|
|
230
|
-
await fs_extra_1.default.copy(sourceFile, destFile, { overwrite: false });
|
|
231
|
-
}
|
|
232
|
-
}
|
|
248
|
+
if (options?.install !== false && !options?.['skip-install']) {
|
|
249
|
+
const installSpinner = logger_1.logger.startSpinner("Installing dependencies...");
|
|
250
|
+
try {
|
|
251
|
+
await (0, package_utils_1.installDependencies)(targetDir, config.packageManager);
|
|
252
|
+
installSpinner.succeed("Dependencies installed");
|
|
233
253
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
dependencies: moduleData.dependencies,
|
|
238
|
-
devDependencies: moduleData.devDependencies,
|
|
239
|
-
});
|
|
240
|
-
// Merge .env with module envVars
|
|
241
|
-
const envVars = {};
|
|
242
|
-
for (const envVar of moduleData.envVars || []) {
|
|
243
|
-
envVars[envVar.key] = envVar.value;
|
|
244
|
-
}
|
|
245
|
-
await mergeEnvFile(targetDir, envVars);
|
|
246
|
-
// Apply framework-specific patches from database module
|
|
247
|
-
if (moduleData.frameworkPatches) {
|
|
248
|
-
const frameworkKey = framework === "react-vite" ? "react" : framework;
|
|
249
|
-
const patches = moduleData.frameworkPatches[frameworkKey];
|
|
250
|
-
if (patches) {
|
|
251
|
-
await applyFrameworkPatches(targetDir, patches);
|
|
254
|
+
catch (error) {
|
|
255
|
+
installSpinner.fail("Failed to install dependencies");
|
|
256
|
+
throw error;
|
|
252
257
|
}
|
|
253
258
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// If auth already has framework suffix, use it directly
|
|
261
|
-
// Otherwise, map old names to new ones
|
|
262
|
-
const authMap = {
|
|
263
|
-
nextauth: "nextauth",
|
|
264
|
-
"better-auth": framework === "nextjs" ? "better-auth-nextjs" : "better-auth-express",
|
|
265
|
-
clerk: framework === "nextjs"
|
|
266
|
-
? "clerk-nextjs"
|
|
267
|
-
: framework === "react-vite"
|
|
268
|
-
? "clerk-react"
|
|
269
|
-
: "clerk-express",
|
|
270
|
-
};
|
|
271
|
-
const authKey = auth.includes("-") ? auth : authMap[auth] || auth;
|
|
272
|
-
const authModulePath = path_1.default.join(modulesDir, "auth", authKey);
|
|
273
|
-
if (!(await fs_extra_1.default.pathExists(authModulePath))) {
|
|
274
|
-
console.warn(`Auth module not found: ${authKey}`);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
// Read module.json
|
|
278
|
-
const moduleJsonPath = path_1.default.join(authModulePath, "module.json");
|
|
279
|
-
if (!(await fs_extra_1.default.pathExists(moduleJsonPath))) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const moduleData = await fs_extra_1.default.readJson(moduleJsonPath);
|
|
283
|
-
// Copy files from module
|
|
284
|
-
const filesDir = path_1.default.join(authModulePath, "files");
|
|
285
|
-
if (await fs_extra_1.default.pathExists(filesDir)) {
|
|
286
|
-
// Determine path replacements based on framework
|
|
287
|
-
const getReplacements = () => {
|
|
288
|
-
if (framework === "nextjs") {
|
|
289
|
-
return { lib: "lib", router: "app" };
|
|
290
|
-
}
|
|
291
|
-
else if (framework === "express") {
|
|
292
|
-
return { lib: "src", router: "src" };
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
return { lib: "src", router: "src" };
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
const replacements = getReplacements();
|
|
299
|
-
// Copy files based on patches in module.json
|
|
300
|
-
for (const patch of moduleData.patches || []) {
|
|
301
|
-
if (patch.type === "create-file") {
|
|
302
|
-
const sourceFile = path_1.default.join(filesDir, patch.source);
|
|
303
|
-
let destFile = path_1.default.join(targetDir, patch.destination);
|
|
304
|
-
// Replace placeholders
|
|
305
|
-
destFile = destFile
|
|
306
|
-
.replace("{{lib}}", replacements.lib)
|
|
307
|
-
.replace("{{router}}", replacements.router);
|
|
308
|
-
if (await fs_extra_1.default.pathExists(sourceFile)) {
|
|
309
|
-
await fs_extra_1.default.ensureDir(path_1.default.dirname(destFile));
|
|
310
|
-
await fs_extra_1.default.copy(sourceFile, destFile, { overwrite: false });
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
// Handle database-specific adapters and schemas
|
|
316
|
-
if (database !== "none" && moduleData.databaseAdapters) {
|
|
317
|
-
const adapterConfig = moduleData.databaseAdapters[database];
|
|
318
|
-
if (adapterConfig) {
|
|
319
|
-
// Copy adapter file
|
|
320
|
-
if (adapterConfig.adapter) {
|
|
321
|
-
const adapterSource = path_1.default.join(authModulePath, adapterConfig.adapter);
|
|
322
|
-
const adapterFileName = path_1.default.basename(adapterConfig.adapter);
|
|
323
|
-
// Determine destination based on framework
|
|
324
|
-
let adapterDest;
|
|
325
|
-
if (framework === "nextjs") {
|
|
326
|
-
adapterDest = path_1.default.join(targetDir, "lib", "auth.ts");
|
|
327
|
-
}
|
|
328
|
-
else if (framework === "express") {
|
|
329
|
-
adapterDest = path_1.default.join(targetDir, "src", "auth.ts");
|
|
330
|
-
}
|
|
331
|
-
else {
|
|
332
|
-
adapterDest = path_1.default.join(targetDir, "src", "lib", "auth.ts");
|
|
333
|
-
}
|
|
334
|
-
if (await fs_extra_1.default.pathExists(adapterSource)) {
|
|
335
|
-
await fs_extra_1.default.ensureDir(path_1.default.dirname(adapterDest));
|
|
336
|
-
await fs_extra_1.default.copy(adapterSource, adapterDest, { overwrite: true });
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
// Copy schema file if it exists
|
|
340
|
-
if (adapterConfig.schema && adapterConfig.schemaDestination) {
|
|
341
|
-
const schemaSource = path_1.default.join(authModulePath, adapterConfig.schema);
|
|
342
|
-
const schemaDest = path_1.default.join(targetDir, adapterConfig.schemaDestination);
|
|
343
|
-
if (await fs_extra_1.default.pathExists(schemaSource)) {
|
|
344
|
-
await fs_extra_1.default.ensureDir(path_1.default.dirname(schemaDest));
|
|
345
|
-
await fs_extra_1.default.copy(schemaSource, schemaDest, { overwrite: true });
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
// Merge adapter-specific dependencies
|
|
349
|
-
if (adapterConfig.dependencies) {
|
|
350
|
-
await mergePackageJson(targetDir, {
|
|
351
|
-
dependencies: adapterConfig.dependencies,
|
|
352
|
-
});
|
|
259
|
+
// Run post-install commands
|
|
260
|
+
if (postInstallCommands.length > 0) {
|
|
261
|
+
const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
|
|
262
|
+
try {
|
|
263
|
+
for (const command of postInstallCommands) {
|
|
264
|
+
(0, child_process_1.execSync)(command, { cwd: targetDir, stdio: "pipe" });
|
|
353
265
|
}
|
|
266
|
+
postInstallSpinner.succeed("Post-install commands completed");
|
|
354
267
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
dependencies: moduleData.dependencies,
|
|
359
|
-
devDependencies: moduleData.devDependencies,
|
|
360
|
-
});
|
|
361
|
-
// Merge .env with module envVars
|
|
362
|
-
const envVars = {};
|
|
363
|
-
for (const envVar of moduleData.envVars || []) {
|
|
364
|
-
envVars[envVar.key] = envVar.value;
|
|
365
|
-
}
|
|
366
|
-
await mergeEnvFile(targetDir, envVars);
|
|
367
|
-
}
|
|
368
|
-
async function mergePackageJson(targetDir, config) {
|
|
369
|
-
const pkgPath = path_1.default.join(targetDir, "package.json");
|
|
370
|
-
if (!(await fs_extra_1.default.pathExists(pkgPath))) {
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
374
|
-
if (config.dependencies) {
|
|
375
|
-
pkg.dependencies = { ...pkg.dependencies, ...config.dependencies };
|
|
376
|
-
}
|
|
377
|
-
if (config.devDependencies) {
|
|
378
|
-
pkg.devDependencies = { ...pkg.devDependencies, ...config.devDependencies };
|
|
379
|
-
}
|
|
380
|
-
if (config.scripts) {
|
|
381
|
-
pkg.scripts = { ...pkg.scripts, ...config.scripts };
|
|
382
|
-
}
|
|
383
|
-
await fs_extra_1.default.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
384
|
-
}
|
|
385
|
-
async function mergeEnvFile(targetDir, envVars) {
|
|
386
|
-
if (Object.keys(envVars).length === 0) {
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
const envExamplePath = path_1.default.join(targetDir, ".env.example");
|
|
390
|
-
const envPath = path_1.default.join(targetDir, ".env");
|
|
391
|
-
const envContent = Object.entries(envVars)
|
|
392
|
-
.map(([key, value]) => `${key}="${value}"`)
|
|
393
|
-
.join("\n") + "\n";
|
|
394
|
-
// Update .env.example
|
|
395
|
-
if (await fs_extra_1.default.pathExists(envExamplePath)) {
|
|
396
|
-
const existing = await fs_extra_1.default.readFile(envExamplePath, "utf-8");
|
|
397
|
-
const existingKeys = existing.split("\n").map((line) => line.split("=")[0]);
|
|
398
|
-
const newVars = Object.keys(envVars).filter((key) => !existingKeys.includes(key));
|
|
399
|
-
if (newVars.length > 0) {
|
|
400
|
-
const newContent = newVars.map((key) => `${key}="${envVars[key]}"`).join("\n");
|
|
401
|
-
await fs_extra_1.default.appendFile(envExamplePath, "\n" + newContent + "\n");
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
await fs_extra_1.default.writeFile(envExamplePath, envContent);
|
|
406
|
-
}
|
|
407
|
-
// Create .env if doesn't exist
|
|
408
|
-
if (!(await fs_extra_1.default.pathExists(envPath))) {
|
|
409
|
-
await fs_extra_1.default.writeFile(envPath, envContent);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
async function convertToJavaScript(targetDir, framework) {
|
|
413
|
-
// Remove TS config and declaration files
|
|
414
|
-
const tsFiles = [
|
|
415
|
-
"tsconfig.json",
|
|
416
|
-
"tsconfig.app.json",
|
|
417
|
-
"tsconfig.node.json",
|
|
418
|
-
"next-env.d.ts",
|
|
419
|
-
"vite-env.d.ts",
|
|
420
|
-
];
|
|
421
|
-
for (const file of tsFiles) {
|
|
422
|
-
const filePath = path_1.default.join(targetDir, file);
|
|
423
|
-
if (await fs_extra_1.default.pathExists(filePath)) {
|
|
424
|
-
await fs_extra_1.default.remove(filePath);
|
|
268
|
+
catch (error) {
|
|
269
|
+
postInstallSpinner.fail("Failed to run post-install commands");
|
|
270
|
+
throw error;
|
|
425
271
|
}
|
|
426
272
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
else if (entry.isFile() && entry.name.endsWith(".d.ts")) {
|
|
435
|
-
await fs_extra_1.default.remove(fullPath);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
await removeDtsFiles(targetDir);
|
|
440
|
-
// Use Babel to strip types only, preserving exact formatting/comments/blank lines, producing clean production-ready code
|
|
441
|
-
const babel = require("@babel/core");
|
|
442
|
-
const transpileAllTsFiles = async (dir) => {
|
|
443
|
-
const entries = await fs_extra_1.default.readdir(dir, { withFileTypes: true });
|
|
444
|
-
for (const entry of entries) {
|
|
445
|
-
const fullPath = path_1.default.join(dir, entry.name);
|
|
446
|
-
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
447
|
-
await transpileAllTsFiles(fullPath);
|
|
448
|
-
}
|
|
449
|
-
else if (entry.isFile()) {
|
|
450
|
-
if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
451
|
-
const code = await fs_extra_1.default.readFile(fullPath, "utf8");
|
|
452
|
-
const isTsx = entry.name.endsWith(".tsx");
|
|
453
|
-
const outFile = fullPath.replace(/\.tsx$/, ".jsx").replace(/\.ts$/, ".js");
|
|
454
|
-
const presets = [
|
|
455
|
-
[
|
|
456
|
-
require.resolve("@babel/preset-typescript"),
|
|
457
|
-
{
|
|
458
|
-
onlyRemoveTypeImports: true,
|
|
459
|
-
allowDeclareFields: true,
|
|
460
|
-
allowNamespaces: true,
|
|
461
|
-
optimizeForSpeed: true,
|
|
462
|
-
allExtensions: true,
|
|
463
|
-
isTSX: isTsx,
|
|
464
|
-
},
|
|
465
|
-
],
|
|
466
|
-
[
|
|
467
|
-
require.resolve("@babel/preset-env"),
|
|
468
|
-
{
|
|
469
|
-
targets: { node: "18" },
|
|
470
|
-
modules: false,
|
|
471
|
-
},
|
|
472
|
-
],
|
|
473
|
-
];
|
|
474
|
-
if (isTsx) {
|
|
475
|
-
presets.push([
|
|
476
|
-
require.resolve("@babel/preset-react"),
|
|
477
|
-
{
|
|
478
|
-
runtime: "automatic",
|
|
479
|
-
},
|
|
480
|
-
]);
|
|
481
|
-
}
|
|
482
|
-
// Use recast + Babel AST transform (same approach as transform.tools)
|
|
483
|
-
try {
|
|
484
|
-
const recast = require("recast");
|
|
485
|
-
const { transformFromAstSync } = require("@babel/core");
|
|
486
|
-
const transformTypescript = require("@babel/plugin-transform-typescript");
|
|
487
|
-
// getBabelOptions may be exported as default or directly
|
|
488
|
-
let getBabelOptions = require("recast/parsers/_babel_options");
|
|
489
|
-
if (getBabelOptions && getBabelOptions.default)
|
|
490
|
-
getBabelOptions = getBabelOptions.default;
|
|
491
|
-
const babelParser = require("recast/parsers/babel").parser;
|
|
492
|
-
const ast = recast.parse(code, {
|
|
493
|
-
parser: {
|
|
494
|
-
parse: (source, options) => {
|
|
495
|
-
const babelOptions = getBabelOptions(options || {});
|
|
496
|
-
// ensure typescript and jsx handling
|
|
497
|
-
if (isTsx) {
|
|
498
|
-
babelOptions.plugins.push("typescript", "jsx");
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
babelOptions.plugins.push("typescript");
|
|
502
|
-
}
|
|
503
|
-
return babelParser.parse(source, babelOptions);
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
|
-
});
|
|
507
|
-
const opts = {
|
|
508
|
-
cloneInputAst: false,
|
|
509
|
-
code: false,
|
|
510
|
-
ast: true,
|
|
511
|
-
plugins: [transformTypescript],
|
|
512
|
-
configFile: false,
|
|
513
|
-
};
|
|
514
|
-
const { ast: transformedAST } = transformFromAstSync(ast, code, opts);
|
|
515
|
-
const resultCode = recast.print(transformedAST).code;
|
|
516
|
-
await fs_extra_1.default.writeFile(outFile, resultCode, "utf8");
|
|
517
|
-
await fs_extra_1.default.remove(fullPath);
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
catch (e) {
|
|
521
|
-
// fallback to previous Babel pipeline if anything fails
|
|
522
|
-
}
|
|
523
|
-
const result = await babel.transformAsync(code, {
|
|
524
|
-
filename: entry.name,
|
|
525
|
-
presets,
|
|
526
|
-
comments: true,
|
|
527
|
-
retainLines: true,
|
|
528
|
-
compact: false,
|
|
529
|
-
babelrc: false,
|
|
530
|
-
configFile: false,
|
|
531
|
-
});
|
|
532
|
-
await fs_extra_1.default.writeFile(outFile, result.code, "utf8");
|
|
533
|
-
await fs_extra_1.default.remove(fullPath);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
273
|
+
// Initialize git
|
|
274
|
+
if (options?.git !== false && !options?.['no-git']) {
|
|
275
|
+
const gitSpinner = logger_1.logger.startSpinner("Initializing git repository...");
|
|
276
|
+
try {
|
|
277
|
+
await (0, git_utils_1.initGit)(targetDir);
|
|
278
|
+
gitSpinner.succeed("Git repository initialized");
|
|
536
279
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const templatesRoot = path_1.default.join(__dirname, "..", "..", "templates");
|
|
540
|
-
const templateName = framework;
|
|
541
|
-
let fileReplacements = [];
|
|
542
|
-
let jsScripts = null;
|
|
543
|
-
if (templateName) {
|
|
544
|
-
const templateJsonPath = path_1.default.join(templatesRoot, templateName, "template.json");
|
|
545
|
-
if (await fs_extra_1.default.pathExists(templateJsonPath)) {
|
|
546
|
-
try {
|
|
547
|
-
const templateJson = await fs_extra_1.default.readJson(templateJsonPath);
|
|
548
|
-
if (Array.isArray(templateJson.fileReplacements)) {
|
|
549
|
-
fileReplacements = templateJson.fileReplacements;
|
|
550
|
-
}
|
|
551
|
-
if (templateJson.jsScripts) {
|
|
552
|
-
jsScripts = templateJson.jsScripts;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
catch { }
|
|
280
|
+
catch {
|
|
281
|
+
gitSpinner.warn("Failed to initialize git repository");
|
|
556
282
|
}
|
|
557
283
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
284
|
+
}
|
|
285
|
+
async function composeTemplate(config, targetDir) {
|
|
286
|
+
const templatesDir = path_1.default.join(__dirname, "..", "..", "templates");
|
|
287
|
+
await fs_extra_1.default.ensureDir(targetDir);
|
|
288
|
+
await (0, file_utils_1.copyBaseFramework)(templatesDir, targetDir, config.framework);
|
|
289
|
+
// Ensure .env exists: if .env.example was copied from the template, create .env from it
|
|
290
|
+
try {
|
|
291
|
+
const envExamplePath = path_1.default.join(targetDir, ".env.example");
|
|
292
|
+
const envPath = path_1.default.join(targetDir, ".env");
|
|
293
|
+
if ((await fs_extra_1.default.pathExists(envExamplePath)) && !(await fs_extra_1.default.pathExists(envPath))) {
|
|
294
|
+
const envContent = await fs_extra_1.default.readFile(envExamplePath, "utf-8");
|
|
295
|
+
await fs_extra_1.default.writeFile(envPath, envContent);
|
|
566
296
|
}
|
|
567
297
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if (await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
571
|
-
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
572
|
-
packageJson.scripts = { ...packageJson.scripts, ...jsScripts };
|
|
573
|
-
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
574
|
-
}
|
|
298
|
+
catch {
|
|
299
|
+
// non-fatal
|
|
575
300
|
}
|
|
576
|
-
const
|
|
577
|
-
if (
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const templateJsconfig = path_1.default.join(templatesRoot, tmpl.name, "jsconfig.json");
|
|
581
|
-
if (await fs_extra_1.default.pathExists(templateJsconfig)) {
|
|
582
|
-
await fs_extra_1.default.copy(templateJsconfig, jsconfig);
|
|
583
|
-
break;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
301
|
+
const postInstallCommands = [];
|
|
302
|
+
if (config.database !== "none") {
|
|
303
|
+
const dbPostInstall = await (0, module_utils_1.mergeDatabaseConfig)(templatesDir, targetDir, config.database, config.framework, config.dbProvider);
|
|
304
|
+
postInstallCommands.push(...dbPostInstall);
|
|
587
305
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const srcFiles = await fs_extra_1.default.readdir(srcDir);
|
|
591
|
-
for (const file of srcFiles) {
|
|
592
|
-
if ((file.endsWith(".js") || file.endsWith(".jsx")) &&
|
|
593
|
-
file.replace(/\.(js|jsx)$/, ".ts") &&
|
|
594
|
-
srcFiles.includes(file.replace(/\.(js|jsx)$/, ".ts"))) {
|
|
595
|
-
await fs_extra_1.default.remove(path_1.default.join(srcDir, file.replace(/\.(js|jsx)$/, ".ts")));
|
|
596
|
-
}
|
|
597
|
-
if (file.endsWith(".jsx") && srcFiles.includes(file.replace(/\.jsx$/, ".tsx"))) {
|
|
598
|
-
await fs_extra_1.default.remove(path_1.default.join(srcDir, file.replace(/\.jsx$/, ".tsx")));
|
|
599
|
-
}
|
|
600
|
-
}
|
|
306
|
+
if (config.auth !== "none") {
|
|
307
|
+
await (0, module_utils_1.mergeAuthConfig)(templatesDir, targetDir, config.framework, config.auth, config.database, config.dbProvider);
|
|
601
308
|
}
|
|
602
309
|
const packageJsonPath = path_1.default.join(targetDir, "package.json");
|
|
603
310
|
if (await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
604
311
|
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
605
|
-
|
|
606
|
-
delete packageJson.devDependencies["typescript"];
|
|
607
|
-
delete packageJson.devDependencies["@types/node"];
|
|
608
|
-
delete packageJson.devDependencies["@types/react"];
|
|
609
|
-
delete packageJson.devDependencies["@types/react-dom"];
|
|
610
|
-
}
|
|
312
|
+
packageJson.name = config.projectName;
|
|
611
313
|
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
612
314
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const commands = {
|
|
616
|
-
npm: "npm install",
|
|
617
|
-
yarn: "yarn install",
|
|
618
|
-
pnpm: "pnpm install",
|
|
619
|
-
bun: "bun install",
|
|
620
|
-
};
|
|
621
|
-
const isAvailable = (cmd) => {
|
|
622
|
-
try {
|
|
623
|
-
(0, child_process_1.execSync)(`command -v ${cmd}`, { stdio: "ignore" });
|
|
624
|
-
return true;
|
|
625
|
-
}
|
|
626
|
-
catch {
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
let chosen = packageManager;
|
|
631
|
-
// If requested package manager is not available, try to fall back to a common one
|
|
632
|
-
if (!isAvailable(chosen)) {
|
|
633
|
-
const fallbacks = ["pnpm", "npm", "yarn", "bun"];
|
|
634
|
-
const found = fallbacks.find((p) => isAvailable(p));
|
|
635
|
-
if (found) {
|
|
636
|
-
console.warn(`Selected package manager '${chosen}' was not found. Falling back to '${found}'.`);
|
|
637
|
-
chosen = found;
|
|
638
|
-
}
|
|
639
|
-
else {
|
|
640
|
-
throw new Error(`Selected package manager '${packageManager}' was not found and no fallback package manager is available. Please install '${packageManager}' or use a different package manager.`);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
const command = commands[chosen];
|
|
644
|
-
(0, child_process_1.execSync)(command, {
|
|
645
|
-
cwd,
|
|
646
|
-
stdio: "pipe",
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
async function initGit(cwd) {
|
|
650
|
-
try {
|
|
651
|
-
(0, child_process_1.execSync)("git --version", { stdio: "pipe" });
|
|
652
|
-
(0, child_process_1.execSync)("git init", { cwd, stdio: "pipe" });
|
|
653
|
-
(0, child_process_1.execSync)("git add -A", { cwd, stdio: "pipe" });
|
|
654
|
-
(0, child_process_1.execSync)('git commit -m "Initial commit from create-stackkit-app"', {
|
|
655
|
-
cwd,
|
|
656
|
-
stdio: "pipe",
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
catch (error) {
|
|
660
|
-
throw new Error("Git initialization failed");
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
async function applyFrameworkPatches(targetDir, patches) {
|
|
664
|
-
for (const [filename, patchConfig] of Object.entries(patches)) {
|
|
665
|
-
const filePath = path_1.default.join(targetDir, filename);
|
|
666
|
-
if (await fs_extra_1.default.pathExists(filePath)) {
|
|
667
|
-
const fileContent = await fs_extra_1.default.readJson(filePath);
|
|
668
|
-
if (patchConfig.merge) {
|
|
669
|
-
// Deep merge configuration
|
|
670
|
-
const merged = deepMerge(fileContent, patchConfig.merge);
|
|
671
|
-
await fs_extra_1.default.writeJson(filePath, merged, { spaces: 2 });
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
function deepMerge(target, source) {
|
|
677
|
-
const output = { ...target };
|
|
678
|
-
for (const key in source) {
|
|
679
|
-
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
|
|
680
|
-
if (target[key]) {
|
|
681
|
-
output[key] = deepMerge(target[key], source[key]);
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
684
|
-
output[key] = source[key];
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
else if (Array.isArray(source[key])) {
|
|
688
|
-
// For arrays, merge uniquely
|
|
689
|
-
output[key] = Array.from(new Set([...(target[key] || []), ...source[key]]));
|
|
690
|
-
}
|
|
691
|
-
else {
|
|
692
|
-
output[key] = source[key];
|
|
693
|
-
}
|
|
315
|
+
if (config.language === "javascript") {
|
|
316
|
+
await (0, js_conversion_1.convertToJavaScript)(targetDir, config.framework);
|
|
694
317
|
}
|
|
695
|
-
return
|
|
318
|
+
return postInstallCommands;
|
|
696
319
|
}
|
|
697
320
|
function showNextSteps(config) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
321
|
+
logger_1.logger.newLine();
|
|
322
|
+
logger_1.logger.success(`Created ${config.projectName}`);
|
|
323
|
+
logger_1.logger.newLine();
|
|
324
|
+
logger_1.logger.log("Next steps:");
|
|
325
|
+
logger_1.logger.log(` cd ${config.projectName}`);
|
|
326
|
+
logger_1.logger.log(` ${config.packageManager} run dev`);
|
|
327
|
+
logger_1.logger.newLine();
|
|
703
328
|
}
|