boilercraft 1.0.0

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.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/bin/boilercraft.js +110 -0
  4. package/cli/app.js +596 -0
  5. package/cli/ui.js +356 -0
  6. package/docs/STACK_ENGINE.md +93 -0
  7. package/docs/assets/cli-create.svg +52 -0
  8. package/docs/assets/cli-menu.svg +47 -0
  9. package/docs/stacks/dotnet-core.md +69 -0
  10. package/docs/stacks/laravel-vue.md +46 -0
  11. package/docs/stacks/nextjs.md +58 -0
  12. package/docs/stacks/node-express.md +58 -0
  13. package/docs/stacks/raw-php.md +130 -0
  14. package/main.js +82 -0
  15. package/package.json +86 -0
  16. package/public/index.html +721 -0
  17. package/public/logo.png +17 -0
  18. package/public/logo.svg +17 -0
  19. package/server.js +84 -0
  20. package/services/componentGenerator.js +195 -0
  21. package/services/engine/scaffoldEngine.js +517 -0
  22. package/services/engine/toolInstaller.js +232 -0
  23. package/services/engine/versionCatalog.js +115 -0
  24. package/services/generate.js +46 -0
  25. package/services/projectGenerator.js +686 -0
  26. package/services/readmeGenerator.js +221 -0
  27. package/services/themeGenerator.js +150 -0
  28. package/services/versionResolver.js +176 -0
  29. package/stacks/dotnet-core/overlays/api/Controllers/ProductsController.cs +33 -0
  30. package/stacks/dotnet-core/overlays/api/Program.cs +33 -0
  31. package/stacks/dotnet-core/overlays/api/wwwroot/index.html +38 -0
  32. package/stacks/dotnet-core/overlays/api-auth/Controllers/AuthController.cs +87 -0
  33. package/stacks/dotnet-core/overlays/common/Data/ApplicationDbContext.cs +25 -0
  34. package/stacks/dotnet-core/overlays/common/Models/Product.cs +19 -0
  35. package/stacks/dotnet-core/overlays/mvc/Controllers/ProductsController.cs +84 -0
  36. package/stacks/dotnet-core/overlays/mvc/Views/Home/Index.cshtml +23 -0
  37. package/stacks/dotnet-core/overlays/mvc/Views/Products/Create.cshtml +16 -0
  38. package/stacks/dotnet-core/overlays/mvc/Views/Products/Delete.cshtml +16 -0
  39. package/stacks/dotnet-core/overlays/mvc/Views/Products/Edit.cshtml +17 -0
  40. package/stacks/dotnet-core/overlays/mvc/Views/Products/Index.cshtml +46 -0
  41. package/stacks/dotnet-core/overlays/mvc/Views/Products/_Form.cshtml +19 -0
  42. package/stacks/dotnet-core/overlays/mvc-auth/Controllers/AccountController.cs +98 -0
  43. package/stacks/dotnet-core/overlays/mvc-auth/Models/AppUser.cs +48 -0
  44. package/stacks/dotnet-core/overlays/mvc-auth/Views/Account/Login.cshtml +35 -0
  45. package/stacks/dotnet-core/overlays/mvc-auth/Views/Account/Register.cshtml +41 -0
  46. package/stacks/dotnet-core/overlays/mvc-auth/Views/Shared/_AuthNav.cshtml +22 -0
  47. package/stacks/dotnet-core/snippets/packages-auth.xml +4 -0
  48. package/stacks/dotnet-core/snippets/packages-common.xml +5 -0
  49. package/stacks/dotnet-core/snippets/packages-ef.xml +4 -0
  50. package/stacks/dotnet-core/snippets/packages-mssql.xml +4 -0
  51. package/stacks/dotnet-core/snippets/packages-mysql.xml +4 -0
  52. package/stacks/dotnet-core/snippets/packages-swagger.xml +4 -0
  53. package/stacks/dotnet-core/stack.json +426 -0
  54. package/stacks/laravel/overlays/auth/app/Http/Controllers/Api/AuthController.php +67 -0
  55. package/stacks/laravel/overlays/common/app/Http/Controllers/BoilerCraftController.php +14 -0
  56. package/stacks/laravel/overlays/common/resources/views/boilercraft.blade.php +36 -0
  57. package/stacks/laravel/overlays/inertia-common/resources/views/app.blade.php +15 -0
  58. package/stacks/laravel/overlays/inertia-react/resources/js/Pages/Welcome.jsx +18 -0
  59. package/stacks/laravel/overlays/inertia-react/resources/js/app.jsx +13 -0
  60. package/stacks/laravel/overlays/inertia-vue/resources/js/Pages/Welcome.vue +23 -0
  61. package/stacks/laravel/overlays/inertia-vue/resources/js/app.js +13 -0
  62. package/stacks/laravel/snippets/api-routes.php +9 -0
  63. package/stacks/laravel/snippets/web-routes-inertia.php +7 -0
  64. package/stacks/laravel/snippets/web-routes.php +4 -0
  65. package/stacks/laravel/stack.json +477 -0
  66. package/stacks/nextjs/overlays/auth/src/lib/auth.js +36 -0
  67. package/stacks/nextjs/overlays/auth-app/src/app/api/auth/login/route.js +11 -0
  68. package/stacks/nextjs/overlays/auth-app/src/app/api/auth/register/route.js +14 -0
  69. package/stacks/nextjs/overlays/auth-pages/src/pages/api/auth/login.js +14 -0
  70. package/stacks/nextjs/overlays/auth-pages/src/pages/api/auth/register.js +17 -0
  71. package/stacks/nextjs/overlays/common/.env.example +3 -0
  72. package/stacks/nextjs/overlays/common/.env.local +3 -0
  73. package/stacks/nextjs/overlays/db/mongodb/src/lib/db.js +12 -0
  74. package/stacks/nextjs/overlays/db/mssql/src/lib/db.js +17 -0
  75. package/stacks/nextjs/overlays/db/mysql/src/lib/db.js +13 -0
  76. package/stacks/nextjs/overlays/router-app/src/app/api/health/route.js +10 -0
  77. package/stacks/nextjs/overlays/router-app/src/app/layout.js +16 -0
  78. package/stacks/nextjs/overlays/router-app/src/app/page.js +19 -0
  79. package/stacks/nextjs/overlays/router-pages/src/pages/_app.js +7 -0
  80. package/stacks/nextjs/overlays/router-pages/src/pages/_document.js +13 -0
  81. package/stacks/nextjs/overlays/router-pages/src/pages/api/health.js +8 -0
  82. package/stacks/nextjs/overlays/router-pages/src/pages/index.js +27 -0
  83. package/stacks/nextjs/stack.json +257 -0
  84. package/stacks/node-express/overlays/auth/src/controllers/authController.js +53 -0
  85. package/stacks/node-express/overlays/auth/src/middlewares/auth.js +36 -0
  86. package/stacks/node-express/overlays/auth/src/routes/authRoutes.js +16 -0
  87. package/stacks/node-express/overlays/common/.env.example +5 -0
  88. package/stacks/node-express/overlays/common/public/index.html +31 -0
  89. package/stacks/node-express/overlays/common/src/server.js +32 -0
  90. package/stacks/node-express/overlays/db/mongodb/src/db.js +13 -0
  91. package/stacks/node-express/overlays/db/mssql/src/db.js +22 -0
  92. package/stacks/node-express/overlays/db/mysql/src/db.js +22 -0
  93. package/stacks/node-express/stack.json +165 -0
  94. package/templates/dotnet-bootstrap-mssql/Controllers/AuthController.cs +74 -0
  95. package/templates/dotnet-bootstrap-mssql/Controllers/ProductsController.cs +33 -0
  96. package/templates/dotnet-bootstrap-mssql/Data/ApplicationDbContext.cs +24 -0
  97. package/templates/dotnet-bootstrap-mssql/Models/Product.cs +10 -0
  98. package/templates/dotnet-core/Controllers/AuthController.cs +74 -0
  99. package/templates/laravel-full/app/Http/Controllers/AuthController.php +79 -0
  100. package/templates/laravel-full/app/Http/Controllers/HomeController.php +22 -0
  101. package/templates/laravel-full/app/Models/User.php +49 -0
  102. package/templates/laravel-full/artisan +22 -0
  103. package/templates/laravel-full/bootstrap/app.php +21 -0
  104. package/templates/laravel-full/config/database.php +54 -0
  105. package/templates/laravel-full/database/migrations/2024_01_01_000001_create_users_table.php +27 -0
  106. package/templates/laravel-full/public/index.php +17 -0
  107. package/templates/laravel-full/resources/css/app.css +3 -0
  108. package/templates/laravel-full/resources/js/Pages/Home.vue +76 -0
  109. package/templates/laravel-full/resources/js/app.js +21 -0
  110. package/templates/laravel-full/resources/js/bootstrap.js +3 -0
  111. package/templates/laravel-full/resources/views/app.blade.php +15 -0
  112. package/templates/laravel-full/routes/api.php +8 -0
  113. package/templates/laravel-full/routes/console.php +7 -0
  114. package/templates/laravel-full/routes/web.php +24 -0
  115. package/templates/laravel-full/tailwind.config.js +12 -0
  116. package/templates/laravel-full/vite.config.js +20 -0
  117. package/templates/laravel-vue/app/Http/Controllers/Auth/AuthenticatedSessionController.php +43 -0
  118. package/templates/laravel-vue-mysql/app/Http/Controllers/HomeController.php +25 -0
  119. package/templates/laravel-vue-mysql/resources/js/Pages/Home.vue +70 -0
  120. package/templates/nextjs/src/app/api/auth/login/route.js +28 -0
  121. package/templates/nextjs-mongodb/src/app/api/health/route.js +17 -0
  122. package/templates/nextjs-mongodb/src/lib/mongodb.js +18 -0
  123. package/templates/nextjs-mssql/src/app/api/health/route.js +24 -0
  124. package/templates/nextjs-mssql/src/lib/mssql.js +38 -0
  125. package/templates/node-express/src/controllers/authController.js +53 -0
  126. package/templates/node-express/src/middlewares/auth.js +36 -0
  127. package/templates/node-express/src/routes/authRoutes.js +16 -0
  128. package/templates/node-mongodb/src/controllers/userController.js +30 -0
  129. package/templates/node-mongodb/src/middlewares/errorHandler.js +8 -0
  130. package/templates/node-mongodb/src/models/User.js +23 -0
  131. package/templates/node-mongodb/src/routes/api.js +9 -0
  132. package/templates/raw-php-mysql/app/Controllers/AuthController.php +73 -0
  133. package/templates/raw-php-mysql/app/Controllers/HomeController.php +65 -0
  134. package/templates/raw-php-mysql/app/Models/User.php +60 -0
  135. package/templates/raw-php-mysql/app/Views/404.php +15 -0
  136. package/templates/raw-php-mysql/app/Views/auth/login.php +66 -0
  137. package/templates/raw-php-mysql/app/Views/auth/register.php +71 -0
  138. package/templates/raw-php-mysql/app/Views/dashboard.php +50 -0
  139. package/templates/raw-php-mysql/app/Views/home.php +83 -0
  140. package/templates/raw-php-mysql/config/Auth.php +62 -0
  141. package/templates/raw-php-mysql/config/Database.php +100 -0
  142. package/templates/raw-php-mysql/config/Env.php +30 -0
  143. package/templates/raw-php-mysql/config/Middleware.php +53 -0
  144. package/templates/raw-php-mysql/config/QueryBuilder.php +109 -0
  145. package/templates/raw-php-mysql/config/Router.php +33 -0
  146. package/templates/raw-php-mysql/public/index.php +57 -0
@@ -0,0 +1,686 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const crypto = require('crypto');
4
+ const { resolveDependencies } = require('./versionResolver');
5
+ const { generateThemeCSS, getThemeSwitcherJS } = require('./themeGenerator');
6
+ const { generateDynamicStackReadme } = require('./readmeGenerator');
7
+ const {
8
+ getSkeletonComponent,
9
+ getSpinnerComponent,
10
+ getModalComponent,
11
+ getOffcanvasComponent,
12
+ getSwalFireComponent,
13
+ getToastrSonnerComponent
14
+ } = require('./componentGenerator');
15
+
16
+ function ensureDir(p) {
17
+ if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true });
18
+ }
19
+
20
+ // Escapes user-supplied text before it is interpolated into generated
21
+ // HTML/JSX templates, so a malicious appTitle/description/author/logoUrl
22
+ // in the generator config can't inject markup into the scaffolded output.
23
+ function escapeHtml(value) {
24
+ return String(value ?? '')
25
+ .replace(/&/g, '&')
26
+ .replace(/</g, '&lt;')
27
+ .replace(/>/g, '&gt;')
28
+ .replace(/"/g, '&quot;')
29
+ .replace(/'/g, '&#39;');
30
+ }
31
+
32
+ function copyFolderRecursive(source, target, replaceMap = {}) {
33
+ ensureDir(target);
34
+ const files = fs.readdirSync(source);
35
+
36
+ for (const file of files) {
37
+ const srcPath = path.join(source, file);
38
+ const destPath = path.join(target, file);
39
+ const stat = fs.statSync(srcPath);
40
+
41
+ if (stat.isDirectory()) {
42
+ copyFolderRecursive(srcPath, destPath, replaceMap);
43
+ } else {
44
+ let content = fs.readFileSync(srcPath, 'utf8');
45
+ for (const [key, val] of Object.entries(replaceMap)) {
46
+ content = content.split(key).join(val);
47
+ }
48
+ fs.writeFileSync(destPath, content, 'utf8');
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Universal Database Connection Generator for any backend
55
+ */
56
+ function getUniversalDbHelper(backend, database, projectName) {
57
+ if (backend === 'node' || backend === 'nextjs') {
58
+ if (database === 'mongodb') {
59
+ return `
60
+ const mongoose = require('mongoose');
61
+ async function connectDB() {
62
+ const uri = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/${projectName}';
63
+ try {
64
+ await mongoose.connect(uri);
65
+ console.log('⚡ Connected to MongoDB (Mongoose)');
66
+ } catch (err) {
67
+ console.error('❌ MongoDB Connection Error:', err.message);
68
+ }
69
+ }
70
+ module.exports = { connectDB };
71
+ `;
72
+ } else if (database === 'mysql') {
73
+ return `
74
+ const mysql = require('mysql2/promise');
75
+ async function connectDB() {
76
+ try {
77
+ const pool = mysql.createPool({
78
+ host: process.env.DB_HOST || '127.0.0.1',
79
+ user: process.env.DB_USER || 'root',
80
+ password: process.env.DB_PASSWORD || '',
81
+ database: process.env.DB_NAME || '${projectName}_db',
82
+ port: parseInt(process.env.DB_PORT || '3306')
83
+ });
84
+ console.log('⚡ Connected to MySQL');
85
+ return pool;
86
+ } catch (err) {
87
+ console.error('❌ MySQL Connection Error:', err.message);
88
+ }
89
+ }
90
+ module.exports = { connectDB };
91
+ `;
92
+ } else if (database === 'mssql') {
93
+ return `
94
+ const sql = require('mssql');
95
+ const config = {
96
+ user: process.env.DB_USER || 'sa',
97
+ password: process.env.DB_PASSWORD || 'YourStrong@Password',
98
+ server: process.env.DB_HOST || 'localhost',
99
+ database: process.env.DB_NAME || '${projectName}_db',
100
+ port: parseInt(process.env.DB_PORT || '1433'),
101
+ options: { encrypt: false, trustServerCertificate: true }
102
+ };
103
+ async function connectDB() {
104
+ try {
105
+ const pool = await sql.connect(config);
106
+ console.log('⚡ Connected to MSSQL (SQL Server)');
107
+ return pool;
108
+ } catch (err) {
109
+ console.error('❌ MSSQL Connection Error:', err.message);
110
+ }
111
+ }
112
+ module.exports = { connectDB, sql };
113
+ `;
114
+ }
115
+ }
116
+ return '';
117
+ }
118
+
119
+ /**
120
+ * Universal Project Generator supporting ANY COMBINATION of:
121
+ * - Backend: Raw PHP, Laravel, Node.js Express, Next.js, .NET Core
122
+ * - Frontend / UI: Vue, React, Blade, Vanilla
123
+ * - Database: MySQL, MongoDB, MSSQL
124
+ * - Styling: Tailwind CSS, Bootstrap 5, Vanilla CSS
125
+ * - UI Components: Skeleton, Spinner, Modal, Offcanvas
126
+ * - Themes: Midnight, Emerald, Royal, Crimson & Dark/Light/Device
127
+ */
128
+ async function generateProject(config, outputBasePath = null) {
129
+ const projectName = (config.projectName || 'my-app').trim().toLowerCase().replace(/[^a-z0-9_-]/g, '-');
130
+ const targetDir = outputBasePath
131
+ ? path.join(outputBasePath, projectName)
132
+ : path.join(__dirname, '..', 'generated', projectName);
133
+
134
+ ensureDir(targetDir);
135
+
136
+ const backend = config.backend || config.stack || 'node-express';
137
+ const database = config.database || 'mysql';
138
+ const styling = config.styling || 'tailwind';
139
+ const themeKey = config.theme || 'midnight';
140
+ const colorMode = config.colorMode || 'all';
141
+
142
+ const themeCSS = generateThemeCSS(themeKey, colorMode);
143
+ const themeJS = getThemeSwitcherJS();
144
+
145
+ // Sanitized copies of user-supplied text, safe to interpolate into HTML/JSX.
146
+ const safeAppTitle = escapeHtml(config.appTitle || projectName);
147
+ const safeDescription = escapeHtml(config.description || 'Enterprise grade multi-combination application');
148
+ const safeAuthor = escapeHtml(config.author || 'ARG RABBI');
149
+ const safeLogoUrl = config.logoUrl ? escapeHtml(config.logoUrl) : '';
150
+
151
+ let uiComponents = '';
152
+ if (config.components?.skeleton) uiComponents += getSkeletonComponent(styling) + '\n';
153
+ if (config.components?.spinner) uiComponents += getSpinnerComponent(styling) + '\n';
154
+ if (config.components?.modal) uiComponents += getModalComponent(styling) + '\n';
155
+ if (config.components?.offcanvas) uiComponents += getOffcanvasComponent(styling) + '\n';
156
+ if (config.components?.swalfire) uiComponents += getSwalFireComponent() + '\n';
157
+ if (config.components?.toastr) uiComponents += getToastrSonnerComponent() + '\n';
158
+
159
+ const logoHtml = safeLogoUrl
160
+ ? `<img src="${safeLogoUrl}" alt="${safeAppTitle}" style="max-height: 60px; margin: 0 auto 1.5rem auto; display: block;" />`
161
+ : `<div style="width: 56px; height: 56px; border-radius: 14px; background: var(--color-primary); color: white; display: flex; align-items: center; justify-content: center; font-size: 26px; font-weight: bold; margin: 0 auto 1.5rem auto;">${escapeHtml((config.projectName || 'B')[0].toUpperCase())}</div>`;
162
+
163
+ const stylesheetLink = styling === 'bootstrap'
164
+ ? '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">'
165
+ : styling === 'tailwind'
166
+ ? '<script src="https://cdn.tailwindcss.com"></script>'
167
+ : '';
168
+
169
+ const replaceMap = {
170
+ '{{PROJECT_NAME}}': projectName,
171
+ '{{APP_TITLE}}': safeAppTitle,
172
+ '{{APP_DESC}}': safeDescription,
173
+ '{{AUTHOR}}': safeAuthor,
174
+ '{{DB_NAME}}': `${projectName}_db`,
175
+ '{{DB_TYPE}}': database,
176
+ '{{COLOR_MODE}}': colorMode === 'all' || colorMode === 'device' ? 'dark' : colorMode,
177
+ '{{LOGO_HTML}}': logoHtml,
178
+ '{{STYLESHEET_LINK}}': stylesheetLink,
179
+ '{{UI_COMPONENTS}}': uiComponents
180
+ };
181
+
182
+ // ==========================================
183
+ // 1. .NET CORE (ANY STYLING: Tailwind / Bootstrap + MSSQL / MySQL)
184
+ // ==========================================
185
+ if (backend === 'dotnet-core' || backend.includes('dotnet')) {
186
+ const srcDir = path.join(__dirname, '..', 'templates', 'dotnet-bootstrap-mssql');
187
+ copyFolderRecursive(srcDir, targetDir, replaceMap);
188
+
189
+ const dotnetVer = config.versions?.dotnet || 'net8.0';
190
+ const efCoreVer = dotnetVer === 'net9.0' ? '9.0.*' : '8.0.*';
191
+ const csproj = `<Project Sdk="Microsoft.NET.Sdk.Web">
192
+ <PropertyGroup>
193
+ <TargetFramework>${dotnetVer}</TargetFramework>
194
+ <Nullable>enable</Nullable>
195
+ <ImplicitUsings>enable</ImplicitUsings>
196
+ <RootNamespace>${projectName.replace(/-/g, '_')}</RootNamespace>
197
+ </PropertyGroup>
198
+
199
+ <ItemGroup>
200
+ ${database === 'mysql' ? `<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="${efCoreVer}" />` : `<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="${efCoreVer}" />`}
201
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${efCoreVer}" />
202
+ <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
203
+ </ItemGroup>
204
+ </Project>
205
+ `;
206
+ fs.writeFileSync(path.join(targetDir, `${projectName}.csproj`), csproj);
207
+
208
+ // Visual Studio Solution (.sln) file
209
+ const projectGuid = crypto.randomUUID().toUpperCase();
210
+ const solutionGuid = crypto.randomUUID().toUpperCase();
211
+ const sln = `Microsoft Visual Studio Solution File, Format Version 12.00
212
+ # Visual Studio Version 17
213
+ VisualStudioVersion = 17.0.31903.59
214
+ MinimumVisualStudioVersion = 10.0.40219.1
215
+ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "${projectName}", "${projectName}.csproj", "{${projectGuid}}"
216
+ EndProject
217
+ Global
218
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
219
+ Debug|Any CPU = Debug|Any CPU
220
+ Release|Any CPU = Release|Any CPU
221
+ EndGlobalSection
222
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
223
+ {${projectGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
224
+ {${projectGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU
225
+ {${projectGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU
226
+ {${projectGuid}}.Release|Any CPU.Build.0 = Release|Any CPU
227
+ EndGlobalSection
228
+ GlobalSection(SolutionProperties) = preSolution
229
+ HideSolutionNode = FALSE
230
+ EndGlobalSection
231
+ GlobalSection(ExtensibilityGlobals) = postSolution
232
+ SolutionGuid = {${solutionGuid}}
233
+ EndGlobalSection
234
+ EndGlobal
235
+ `;
236
+ fs.writeFileSync(path.join(targetDir, `${projectName}.sln`), sln);
237
+
238
+ // Dynamic Program.cs with user's selected styling (Tailwind OR Bootstrap)
239
+ const programCs = `
240
+ using Microsoft.EntityFrameworkCore;
241
+ using DotNetBootstrapMssql.Data;
242
+
243
+ var builder = WebApplication.CreateBuilder(args);
244
+
245
+ builder.Services.AddControllers();
246
+ builder.Services.AddEndpointsApiExplorer();
247
+ builder.Services.AddSwaggerGen();
248
+
249
+ // Configure Database: ${database.toUpperCase()}
250
+ builder.Services.AddDbContext<ApplicationDbContext>(options =>
251
+ {
252
+ ${database === 'mysql'
253
+ ? `var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? "Server=localhost;Port=3306;Database=${projectName};Uid=root;Pwd=;";
254
+ options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));`
255
+ : `options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")
256
+ ?? "Server=localhost,1433;Database=${projectName};User Id=sa;Password=YourStrong@Password;TrustServerCertificate=True;");`
257
+ }
258
+ });
259
+
260
+ var app = builder.Build();
261
+
262
+ if (app.Environment.IsDevelopment())
263
+ {
264
+ app.UseSwagger();
265
+ app.UseSwaggerUI();
266
+ }
267
+
268
+ app.UseHttpsRedirection();
269
+ app.UseStaticFiles();
270
+ app.UseAuthorization();
271
+ app.MapControllers();
272
+
273
+ app.MapGet("/", () => Results.Content(@"
274
+ <!DOCTYPE html>
275
+ <html lang=""en"" data-theme=""${colorMode}"">
276
+ <head>
277
+ <meta charset=""utf-8"" />
278
+ <meta name=""viewport"" content=""width=device-width, initial-scale=1.0"" />
279
+ <title>${safeAppTitle}</title>
280
+ ${stylesheetLink}
281
+ <style>${themeCSS}</style>
282
+ </head>
283
+ <body class=""p-8"">
284
+ <div style=""max-width: 900px; margin: 0 auto; text-align: center;"">
285
+ ${logoHtml}
286
+ <h1 style=""font-size: 2.5rem; font-weight: 800;"">${safeAppTitle}</h1>
287
+ <p style=""color: var(--color-text-muted);"">${config.description ? safeDescription : '.NET Core 8 Web API with ' + styling.toUpperCase() + ' and ' + database.toUpperCase()}</p>
288
+ <p style=""font-size: 0.85rem; color: var(--color-primary); font-weight: bold;"">Developer: ${safeAuthor}</p>
289
+
290
+ <div style=""margin: 1.5rem 0;"">
291
+ <a href=""/swagger"" style=""padding: 10px 20px; background: var(--color-primary); color: white; border-radius: 8px; text-decoration: none; font-weight: bold; display: inline-block;"">
292
+ Explore Swagger API ↗
293
+ </a>
294
+ </div>
295
+
296
+ <div style=""text-align: left; padding: 2rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-base); margin-top: 2rem; box-shadow: 0 10px 30px rgba(0,0,0,0.15);"">
297
+ <h3 style=""margin-top: 0;"">⚡ Active Architecture:</h3>
298
+ <ul>
299
+ <li><strong>Backend:</strong> .NET Core 8 Web API</li>
300
+ <li><strong>Database Engine:</strong> ${database.toUpperCase()} (EF Core)</li>
301
+ <li><strong>Styling Engine:</strong> ${styling.toUpperCase()}</li>
302
+ <li><strong>Theme:</strong> ${themeKey} (${colorMode})</li>
303
+ </ul>
304
+
305
+ <hr style=""border: none; border-top: 1px solid var(--color-border); margin: 1.5rem 0;"" />
306
+ <h4>Dynamic Selected UI Components:</h4>
307
+ ${uiComponents}
308
+ </div>
309
+ </div>
310
+ <script>${themeJS}</script>
311
+ </body>
312
+ </html>", "text/html"));
313
+
314
+ app.Run();
315
+ `;
316
+ fs.writeFileSync(path.join(targetDir, 'Program.cs'), programCs);
317
+ }
318
+
319
+ // ==========================================
320
+ // 2. RAW PHP (ANY DATABASE + ANY STYLING)
321
+ // ==========================================
322
+ else if (backend === 'raw-php') {
323
+ const srcDir = path.join(__dirname, '..', 'templates', 'raw-php-mysql');
324
+ copyFolderRecursive(srcDir, targetDir, replaceMap);
325
+ fs.writeFileSync(path.join(targetDir, 'public', 'css', 'theme.css'), themeCSS);
326
+ fs.writeFileSync(path.join(targetDir, 'public', 'js', 'theme.js'), themeJS);
327
+ }
328
+
329
+ // ==========================================
330
+ // 3. LARAVEL (Genuine Full Laravel 11 Structure + Vue 3 / Blade + Artisan + Migrations)
331
+ // ==========================================
332
+ else if (backend === 'laravel' || backend === 'laravel-vue-mysql') {
333
+ const srcDir = path.join(__dirname, '..', 'templates', 'laravel-full');
334
+ copyFolderRecursive(srcDir, targetDir, replaceMap);
335
+
336
+ const chosenLaravelVer = config.versions?.laravel || '^11.0';
337
+ const isAuth = config.auth ? config.auth.enabled !== false : true;
338
+
339
+ const laravelDeps = {
340
+ "php": "^8.2",
341
+ "laravel/framework": chosenLaravelVer,
342
+ "laravel/tinker": "^2.9",
343
+ "inertiajs/inertia-laravel": "^1.0"
344
+ };
345
+
346
+ if (isAuth) {
347
+ laravelDeps["laravel/sanctum"] = "^4.0";
348
+ }
349
+
350
+ const composerJson = {
351
+ name: `developer/${projectName}`,
352
+ type: "project",
353
+ description: config.description || `Laravel ${chosenLaravelVer.replace(/[^0-9.]/g, '')} Full Stack Project`,
354
+ keywords: ["framework", "laravel"],
355
+ license: "MIT",
356
+ require: laravelDeps,
357
+ "require-dev": {
358
+ "fakerphp/faker": "^1.23",
359
+ "mockery/mockery": "^1.6",
360
+ "nunomaduro/collision": "^8.0",
361
+ "phpunit/phpunit": "^11.0"
362
+ },
363
+ "autoload": {
364
+ "psr-4": {
365
+ "App\\\\": "app/",
366
+ "Database\\\\Factories\\\\": "database/factories/",
367
+ "Database\\\\Seeders\\\\": "database/seeders/"
368
+ }
369
+ },
370
+ "scripts": {
371
+ "post-autoload-dump": [
372
+ "Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump",
373
+ "@php artisan package:discover --ansi"
374
+ ]
375
+ },
376
+ "config": {
377
+ "optimize-autoloader": true,
378
+ "preferred-install": "dist",
379
+ "sort-packages": true
380
+ }
381
+ };
382
+ fs.writeFileSync(path.join(targetDir, 'composer.json'), JSON.stringify(composerJson, null, 2));
383
+
384
+ const vueDeps = await resolveDependencies([
385
+ 'vue',
386
+ '@vitejs/plugin-vue',
387
+ '@inertiajs/vue3',
388
+ 'laravel-vite-plugin',
389
+ 'vite',
390
+ 'axios'
391
+ ], 'npm');
392
+
393
+ if (styling === 'tailwind') {
394
+ Object.assign(vueDeps, await resolveDependencies(['tailwindcss', 'postcss', 'autoprefixer'], 'npm'));
395
+ }
396
+
397
+ const pkgJson = {
398
+ private: true,
399
+ type: "module",
400
+ scripts: {
401
+ "dev": "vite",
402
+ "build": "vite build"
403
+ },
404
+ devDependencies: vueDeps
405
+ };
406
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
407
+ }
408
+
409
+ // ==========================================
410
+ // 4. NEXT.JS (ANY DATABASE: MongoDB / MSSQL / MySQL + ANY STYLING)
411
+ // ==========================================
412
+ else if (backend === 'nextjs' || backend.includes('nextjs')) {
413
+ const reqPkgs = ['next', 'react', 'react-dom'];
414
+ if (database === 'mongodb') reqPkgs.push('mongoose');
415
+ if (database === 'mysql') reqPkgs.push('mysql2');
416
+ if (database === 'mssql') reqPkgs.push('mssql');
417
+ if (styling === 'tailwind') reqPkgs.push('tailwindcss', 'postcss', 'autoprefixer');
418
+ const chosenNextVer = config.versions?.nextjs || '^14.2.0';
419
+ const isAuth = config.auth ? config.auth.enabled !== false : true;
420
+
421
+ if (isAuth) {
422
+ reqPkgs.push('jsonwebtoken', 'bcryptjs');
423
+ }
424
+
425
+ const deps = await resolveDependencies(reqPkgs, 'npm');
426
+ deps['next'] = chosenNextVer;
427
+ if (chosenNextVer.startsWith('^15')) {
428
+ deps['react'] = '^19.0.0';
429
+ deps['react-dom'] = '^19.0.0';
430
+ } else {
431
+ deps['react'] = '^18.3.1';
432
+ deps['react-dom'] = '^18.3.1';
433
+ }
434
+
435
+ const pkg = {
436
+ name: projectName,
437
+ version: '1.0.0',
438
+ scripts: {
439
+ dev: `next dev -p ${config.port || 3000}`,
440
+ build: "next build",
441
+ start: `next start -p ${config.port || 3000}`
442
+ },
443
+ dependencies: deps
444
+ };
445
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
446
+
447
+ ensureDir(path.join(targetDir, 'src', 'app', 'api', 'health'));
448
+ ensureDir(path.join(targetDir, 'src', 'styles'));
449
+ ensureDir(path.join(targetDir, 'src', 'lib'));
450
+
451
+ const nextTplDir = path.join(__dirname, '..', 'templates', 'nextjs', 'src');
452
+ if (fs.existsSync(nextTplDir)) {
453
+ copyFolderRecursive(nextTplDir, path.join(targetDir, 'src'), replaceMap);
454
+ }
455
+
456
+ fs.writeFileSync(path.join(targetDir, 'src', 'styles', 'theme.css'), themeCSS);
457
+ fs.writeFileSync(path.join(targetDir, 'src', 'lib', 'db.js'), getUniversalDbHelper('nextjs', database, projectName));
458
+
459
+ // Next.js Route Handler
460
+ fs.writeFileSync(path.join(targetDir, 'src', 'app', 'api', 'health', 'route.js'), `
461
+ import { NextResponse } from 'next/server';
462
+ export async function GET() {
463
+ return NextResponse.json({
464
+ status: 'online',
465
+ stack: 'Next.js App Router',
466
+ database: '${database.toUpperCase()}',
467
+ styling: '${styling}',
468
+ appName: '${config.appTitle || projectName}',
469
+ author: '${config.author || 'ARG RABBI'}'
470
+ });
471
+ }
472
+ `);
473
+
474
+ // Next.js Root Layout
475
+ fs.writeFileSync(path.join(targetDir, 'src', 'app', 'layout.jsx'), `
476
+ import '../styles/theme.css';
477
+ ${styling === 'bootstrap' ? "import 'bootstrap/dist/css/bootstrap.min.css';" : ''}
478
+
479
+ export const metadata = {
480
+ title: "${config.appTitle || projectName}",
481
+ description: "${config.description || 'Next.js Multi-Stack Application'}"
482
+ };
483
+
484
+ export default function RootLayout({ children }) {
485
+ return (
486
+ <html lang="en" data-theme="${colorMode === 'all' || colorMode === 'device' ? 'dark' : colorMode}">
487
+ <body>{children}</body>
488
+ </html>
489
+ );
490
+ }
491
+ `);
492
+
493
+ // Next.js Home Page
494
+ fs.writeFileSync(path.join(targetDir, 'src', 'app', 'page.jsx'), `
495
+ export default function Home() {
496
+ return (
497
+ <div style={{ maxWidth: '900px', margin: '3rem auto', padding: '1rem', textAlign: 'center' }}>
498
+ ${logoHtml}
499
+ <h1 style={{ fontSize: '2.5rem', fontWeight: 800 }}>${safeAppTitle}</h1>
500
+ <p style={{ color: 'var(--color-text-muted)' }}>${config.description ? safeDescription : 'Next.js + ' + database.toUpperCase() + ' + ' + styling.toUpperCase()}</p>
501
+
502
+ <div style={{ marginTop: '2rem', padding: '1.5rem', background: 'var(--color-surface)', borderRadius: '1rem', textAlign: 'left', border: '1px solid var(--color-border)' }}>
503
+ <h3>🚀 Configured Architecture:</h3>
504
+ <ul>
505
+ <li><strong>Framework:</strong> Next.js (App Router)</li>
506
+ <li><strong>Database Engine:</strong> ${database.toUpperCase()}</li>
507
+ <li><strong>Styling System:</strong> ${styling.toUpperCase()}</li>
508
+ <li><strong>Theme:</strong> ${themeKey}</li>
509
+ </ul>
510
+ <p>Health API: <a href="/api/health" style={{ color: 'var(--color-primary)' }}>/api/health</a></p>
511
+ <hr style={{ borderColor: 'var(--color-border)', margin: '1.5rem 0' }} />
512
+ <h4>Included UI Components:</h4>
513
+ ${uiComponents}
514
+ </div>
515
+ </div>
516
+ );
517
+ }
518
+ `);
519
+ }
520
+
521
+ // ==========================================
522
+ // 5. NODE.JS + EXPRESS (ANY DATABASE + ANY STYLING)
523
+ // ==========================================
524
+ else {
525
+ const reqPkgs = ['express', 'dotenv', 'cors'];
526
+ if (database === 'mongodb') reqPkgs.push('mongoose');
527
+ if (database === 'mysql') reqPkgs.push('mysql2');
528
+ if (database === 'mssql') reqPkgs.push('mssql');
529
+
530
+ const chosenExpressVer = config.versions?.nodeExpress || '^5.0.0';
531
+ const isAuth = config.auth ? config.auth.enabled !== false : true;
532
+
533
+ if (isAuth) {
534
+ reqPkgs.push('jsonwebtoken', 'bcryptjs');
535
+ }
536
+
537
+ const deps = await resolveDependencies(reqPkgs, 'npm');
538
+ deps['express'] = chosenExpressVer;
539
+
540
+ const pkg = {
541
+ name: projectName,
542
+ version: '1.0.0',
543
+ description: config.description,
544
+ author: config.author || 'ARG RABBI',
545
+ main: 'src/server.js',
546
+ scripts: {
547
+ start: 'node src/server.js',
548
+ dev: 'node --watch src/server.js'
549
+ },
550
+ dependencies: deps
551
+ };
552
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
553
+
554
+ ensureDir(path.join(targetDir, 'src'));
555
+ ensureDir(path.join(targetDir, 'public'));
556
+
557
+ fs.writeFileSync(path.join(targetDir, 'public', 'theme.css'), themeCSS);
558
+ fs.writeFileSync(path.join(targetDir, 'public', 'theme.js'), themeJS);
559
+ fs.writeFileSync(path.join(targetDir, 'src', 'db.js'), getUniversalDbHelper('node', database, projectName));
560
+
561
+ const nodeTplDir = path.join(__dirname, '..', 'templates', 'node-express', 'src');
562
+ if (fs.existsSync(nodeTplDir)) {
563
+ copyFolderRecursive(nodeTplDir, path.join(targetDir, 'src'), replaceMap);
564
+ }
565
+
566
+ const srv = `
567
+ const express = require('express');
568
+ const cors = require('cors');
569
+ const path = require('path');
570
+ require('dotenv').config();
571
+ const { connectDB } = require('./db');
572
+ const authRoutes = require('./routes/authRoutes');
573
+
574
+ const app = express();
575
+ const PORT = process.env.PORT || ${config.port || 5000};
576
+
577
+ app.use(cors());
578
+ app.use(express.json());
579
+ app.use(express.static(path.join(__dirname, '../public')));
580
+
581
+ connectDB();
582
+
583
+ // API Endpoints
584
+ app.use('/api/auth', authRoutes);
585
+
586
+ app.get('/api/health', (req, res) => {
587
+ res.json({
588
+ status: 'online',
589
+ app: '${config.appTitle || projectName}',
590
+ database: '${database}',
591
+ styling: '${styling}',
592
+ auth: ${isAuth ? 'true' : 'false'},
593
+ author: '${config.author || 'ARG RABBI'}'
594
+ });
595
+ });
596
+
597
+ app.listen(PORT, () => {
598
+ console.log(\`⚡ Server listening at http://localhost:\${PORT}\`);
599
+ });
600
+ `;
601
+ fs.writeFileSync(path.join(targetDir, 'src', 'server.js'), srv);
602
+
603
+ const indexHtml = `
604
+ <!DOCTYPE html>
605
+ <html lang="en" data-theme="${colorMode}">
606
+ <head>
607
+ <meta charset="UTF-8">
608
+ <title>${safeAppTitle}</title>
609
+ <link rel="stylesheet" href="/theme.css">
610
+ ${stylesheetLink}
611
+ </head>
612
+ <body class="p-6">
613
+ <div style="max-width: 850px; margin: 2rem auto; text-align: center;">
614
+ ${logoHtml}
615
+ <h1>${safeAppTitle}</h1>
616
+ <p style="color: var(--color-text-muted);">${config.description ? safeDescription : 'Node.js Express + ' + database.toUpperCase() + ' + ' + styling.toUpperCase()}</p>
617
+
618
+ <div style="text-align: left; padding: 2rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-base); margin-top: 2rem;">
619
+ <h3>Active Architecture:</h3>
620
+ <ul>
621
+ <li><strong>Backend:</strong> Node.js Express</li>
622
+ <li><strong>Database:</strong> ${database.toUpperCase()}</li>
623
+ <li><strong>Styling:</strong> ${styling.toUpperCase()}</li>
624
+ </ul>
625
+ <hr style="border: none; border-top: 1px solid var(--color-border); margin: 1.5rem 0;" />
626
+ <h4>Components:</h4>
627
+ ${uiComponents}
628
+ </div>
629
+ </div>
630
+ <script src="/theme.js"></script>
631
+ </body>
632
+ </html>
633
+ `;
634
+ fs.writeFileSync(path.join(targetDir, 'public', 'index.html'), indexHtml);
635
+ }
636
+
637
+ // Common environment and documentation
638
+ fs.writeFileSync(path.join(targetDir, '.env.example'), getEnvExample(config, database));
639
+ fs.writeFileSync(path.join(targetDir, '.env'), getEnvLocal(config, database));
640
+ fs.writeFileSync(path.join(targetDir, 'README.md'), generateDynamicStackReadme(config, backend, database, styling, themeKey, colorMode));
641
+
642
+ return {
643
+ success: true,
644
+ projectName,
645
+ path: targetDir,
646
+ combination: `${backend} + ${database} + ${styling}`
647
+ };
648
+ }
649
+
650
+ // .env.example: placeholder values only, safe to commit, never real secrets.
651
+ function getEnvExample(config, dbType) {
652
+ return `
653
+ PORT=${config.port || 3000}
654
+ APP_NAME="${config.appTitle || config.projectName}"
655
+ APP_ENV=development
656
+
657
+ # Database Settings (${dbType.toUpperCase()})
658
+ ${dbType === 'mongodb' ? `MONGODB_URI=mongodb://127.0.0.1:27017/${config.projectName}` : ''}
659
+ ${dbType === 'mysql' ? `DB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=${config.projectName}\nDB_USERNAME=root\nDB_PASSWORD=change-me` : ''}
660
+ ${dbType === 'mssql' ? `DB_HOST=127.0.0.1\nDB_PORT=1433\nDB_DATABASE=${config.projectName}\nDB_USERNAME=sa\nDB_PASSWORD=change-me` : ''}
661
+ `;
662
+ }
663
+
664
+ // .env: the actual local dev file. Generates a fresh random password/secret per
665
+ // project instead of baking in a shared default credential (e.g. MSSQL's
666
+ // "YourStrong@Password"), which would otherwise ship identically in every
667
+ // scaffolded project.
668
+ function getEnvLocal(config, dbType) {
669
+ const randomSecret = () => crypto.randomBytes(16).toString('hex');
670
+
671
+ return `
672
+ PORT=${config.port || 3000}
673
+ APP_NAME="${config.appTitle || config.projectName}"
674
+ APP_ENV=development
675
+ APP_KEY=${randomSecret()}
676
+
677
+ # Database Settings (${dbType.toUpperCase()})
678
+ ${dbType === 'mongodb' ? `MONGODB_URI=mongodb://127.0.0.1:27017/${config.projectName}` : ''}
679
+ ${dbType === 'mysql' ? `DB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=${config.projectName}\nDB_USERNAME=root\nDB_PASSWORD=` : ''}
680
+ ${dbType === 'mssql' ? `DB_HOST=127.0.0.1\nDB_PORT=1433\nDB_DATABASE=${config.projectName}\nDB_USERNAME=sa\nDB_PASSWORD=${randomSecret()}` : ''}
681
+ `;
682
+ }
683
+
684
+ module.exports = {
685
+ generateProject
686
+ };