create-fstack-app 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/bin/index.js +8 -0
- package/package.json +43 -0
- package/src/cli.js +46 -0
- package/src/generators/createProject.js +55 -0
- package/src/generators/templateEngine.js +118 -0
- package/src/generators/templates/addons.js +119 -0
- package/src/generators/templates/backend.js +265 -0
- package/src/generators/templates/database.js +45 -0
- package/src/generators/templates/frontend.js +271 -0
- package/src/generators/templates/root.js +170 -0
- package/src/install/installDependencies.js +30 -0
- package/src/install/packageMap.js +3 -0
- package/src/prompts/index.js +106 -0
- package/src/stacks.js +94 -0
- package/src/utils/names.js +15 -0
- package/templates/README.md +10 -0
- package/templates/addons/auth/.gitkeep +1 -0
- package/templates/addons/docker/.gitkeep +1 -0
- package/templates/addons/eslint/.gitkeep +1 -0
- package/templates/addons/testing/.gitkeep +1 -0
- package/templates/backend/django/.gitkeep +1 -0
- package/templates/backend/express/.gitkeep +1 -0
- package/templates/backend/laravel/.gitkeep +1 -0
- package/templates/backend/nest/.gitkeep +1 -0
- package/templates/databases/mongodb/.gitkeep +1 -0
- package/templates/databases/mysql/.gitkeep +1 -0
- package/templates/databases/postgres/.gitkeep +1 -0
- package/templates/frontend/angular/.gitkeep +1 -0
- package/templates/frontend/next/.gitkeep +1 -0
- package/templates/frontend/react/.gitkeep +1 -0
- package/templates/frontend/vue/.gitkeep +1 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
export function createBackendFiles(config) {
|
|
2
|
+
if (config.backend === "None" || config.structure === "t3") return t3Files(config);
|
|
3
|
+
if (config.backend === "Express.js" || config.backend === "Fastify") return nodeApiFiles(config);
|
|
4
|
+
if (config.backend === "Firebase Functions") return firebaseFiles(config);
|
|
5
|
+
if (config.backend === "Django") return djangoFiles(config);
|
|
6
|
+
if (config.backend === "Laravel") return laravelFiles(config);
|
|
7
|
+
return genericBackendFiles(config);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function nodeApiFiles(config) {
|
|
11
|
+
const isFastify = config.backend === "Fastify";
|
|
12
|
+
return {
|
|
13
|
+
"backend/package.json": `${JSON.stringify({
|
|
14
|
+
name: "backend",
|
|
15
|
+
version: "0.1.0",
|
|
16
|
+
private: true,
|
|
17
|
+
type: "module",
|
|
18
|
+
scripts: {
|
|
19
|
+
dev: "nodemon src/server.js",
|
|
20
|
+
start: "node src/server.js"
|
|
21
|
+
},
|
|
22
|
+
dependencies: backendDependencies(config),
|
|
23
|
+
devDependencies: { nodemon: "^3.1.4" }
|
|
24
|
+
}, null, 2)}\n`,
|
|
25
|
+
"backend/.env": `PORT=5000
|
|
26
|
+
${databaseEnv(config)}
|
|
27
|
+
`,
|
|
28
|
+
"backend/src/server.js": isFastify ? fastifyServer() : expressServer(),
|
|
29
|
+
"backend/src/app.js": isFastify ? fastifyApp(config) : expressApp(config),
|
|
30
|
+
"backend/src/routes/health.routes.js": isFastify ? fastifyRoutes() : expressRoutes(),
|
|
31
|
+
"backend/src/controllers/health.controller.js": `export function healthCheck(req, res) {
|
|
32
|
+
res.json({ message: "API is healthy" });
|
|
33
|
+
}
|
|
34
|
+
`,
|
|
35
|
+
"backend/src/models/.gitkeep": "",
|
|
36
|
+
"backend/src/middleware/.gitkeep": "",
|
|
37
|
+
"backend/src/config/database.js": databaseConfig(config),
|
|
38
|
+
"backend/src/utils/.gitkeep": "",
|
|
39
|
+
"backend/src/services/.gitkeep": ""
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function expressServer() {
|
|
44
|
+
return `import app from "./app.js";
|
|
45
|
+
|
|
46
|
+
const port = process.env.PORT || 5000;
|
|
47
|
+
|
|
48
|
+
app.listen(port, () => {
|
|
49
|
+
console.log(\`API running on http://localhost:\${port}\`);
|
|
50
|
+
});
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function expressApp(config) {
|
|
55
|
+
const swagger = config.features.includes("Swagger Docs")
|
|
56
|
+
? "\napp.get('/api/docs', (_req, res) => res.json({ message: 'Add Swagger UI here.' }));"
|
|
57
|
+
: "";
|
|
58
|
+
return `import express from "express";
|
|
59
|
+
import cors from "cors";
|
|
60
|
+
import dotenv from "dotenv";
|
|
61
|
+
import healthRoutes from "./routes/health.routes.js";
|
|
62
|
+
|
|
63
|
+
dotenv.config();
|
|
64
|
+
|
|
65
|
+
const app = express();
|
|
66
|
+
|
|
67
|
+
app.use(cors());
|
|
68
|
+
app.use(express.json());
|
|
69
|
+
|
|
70
|
+
app.use("/api/health", healthRoutes);${swagger}
|
|
71
|
+
|
|
72
|
+
export default app;
|
|
73
|
+
`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function expressRoutes() {
|
|
77
|
+
return `import { Router } from "express";
|
|
78
|
+
import { healthCheck } from "../controllers/health.controller.js";
|
|
79
|
+
|
|
80
|
+
const router = Router();
|
|
81
|
+
|
|
82
|
+
router.get("/", healthCheck);
|
|
83
|
+
|
|
84
|
+
export default router;
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function fastifyServer() {
|
|
89
|
+
return `import { buildApp } from "./app.js";
|
|
90
|
+
|
|
91
|
+
const app = buildApp();
|
|
92
|
+
const port = Number(process.env.PORT || 5000);
|
|
93
|
+
|
|
94
|
+
await app.listen({ port, host: "0.0.0.0" });
|
|
95
|
+
console.log(\`API running on http://localhost:\${port}\`);
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function fastifyApp() {
|
|
100
|
+
return `import Fastify from "fastify";
|
|
101
|
+
import cors from "@fastify/cors";
|
|
102
|
+
import { healthRoutes } from "./routes/health.routes.js";
|
|
103
|
+
|
|
104
|
+
export function buildApp() {
|
|
105
|
+
const app = Fastify({ logger: true });
|
|
106
|
+
app.register(cors);
|
|
107
|
+
app.register(healthRoutes, { prefix: "/api/health" });
|
|
108
|
+
return app;
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function fastifyRoutes() {
|
|
114
|
+
return `export async function healthRoutes(app) {
|
|
115
|
+
app.get("/", async () => ({ message: "API is healthy" }));
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function firebaseFiles(config) {
|
|
121
|
+
return {
|
|
122
|
+
"backend/package.json": `${JSON.stringify({
|
|
123
|
+
name: "functions",
|
|
124
|
+
version: "0.1.0",
|
|
125
|
+
private: true,
|
|
126
|
+
type: "module",
|
|
127
|
+
scripts: { dev: "firebase emulators:start --only functions", deploy: "firebase deploy --only functions" },
|
|
128
|
+
dependencies: { "firebase-admin": "^12.2.0", "firebase-functions": "^5.0.1" },
|
|
129
|
+
devDependencies: { "firebase-tools": "^13.13.1" }
|
|
130
|
+
}, null, 2)}\n`,
|
|
131
|
+
"backend/src/index.js": `import { onRequest } from "firebase-functions/v2/https";
|
|
132
|
+
|
|
133
|
+
export const health = onRequest((_request, response) => {
|
|
134
|
+
response.json({ message: "${config.stackLabel} functions are healthy" });
|
|
135
|
+
});
|
|
136
|
+
`,
|
|
137
|
+
"firebase.json": `${JSON.stringify({ functions: { source: "backend" }, emulators: { functions: { port: 5001 } } }, null, 2)}\n`
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function djangoFiles(config) {
|
|
142
|
+
return {
|
|
143
|
+
"backend/README.md": `# Django Backend
|
|
144
|
+
|
|
145
|
+
Create a virtual environment, install Django and djangorestframework, then run migrations.
|
|
146
|
+
`,
|
|
147
|
+
"backend/manage.py": `#!/usr/bin/env python
|
|
148
|
+
import os
|
|
149
|
+
import sys
|
|
150
|
+
|
|
151
|
+
def main():
|
|
152
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
|
|
153
|
+
from django.core.management import execute_from_command_line
|
|
154
|
+
execute_from_command_line(sys.argv)
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
main()
|
|
158
|
+
`,
|
|
159
|
+
"backend/app/__init__.py": "",
|
|
160
|
+
"backend/app/settings.py": `SECRET_KEY = "change-me"
|
|
161
|
+
DEBUG = True
|
|
162
|
+
ROOT_URLCONF = "app.urls"
|
|
163
|
+
INSTALLED_APPS = ["django.contrib.contenttypes", "django.contrib.auth", "rest_framework"]
|
|
164
|
+
DATABASES = {"default": {"ENGINE": "django.db.backends.postgresql", "NAME": "my_fullstack_app"}}
|
|
165
|
+
`,
|
|
166
|
+
"backend/app/urls.py": `from django.http import JsonResponse
|
|
167
|
+
from django.urls import path
|
|
168
|
+
|
|
169
|
+
def health(_request):
|
|
170
|
+
return JsonResponse({"message": "${config.stackLabel} API is healthy"})
|
|
171
|
+
|
|
172
|
+
urlpatterns = [path("api/health/", health)]
|
|
173
|
+
`,
|
|
174
|
+
"backend/requirements.txt": "django>=5.0\ndjangorestframework>=3.15\npsycopg[binary]>=3.2\n"
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function laravelFiles(config) {
|
|
179
|
+
return {
|
|
180
|
+
"backend/README.md": `# Laravel Backend
|
|
181
|
+
|
|
182
|
+
Run \`composer create-project laravel/laravel .\` inside this folder to expand the Laravel starter.
|
|
183
|
+
`,
|
|
184
|
+
"backend/routes/api.php": `<?php
|
|
185
|
+
|
|
186
|
+
use Illuminate\\Support\\Facades\\Route;
|
|
187
|
+
|
|
188
|
+
Route::get('/health', fn () => ['message' => '${config.stackLabel} API is healthy']);
|
|
189
|
+
`,
|
|
190
|
+
"backend/.env.example": "APP_NAME=MyFullstackApp\nAPP_ENV=local\nDB_CONNECTION=mysql\n"
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function genericBackendFiles(config) {
|
|
195
|
+
return {
|
|
196
|
+
"backend/README.md": `# ${config.backend} Backend
|
|
197
|
+
|
|
198
|
+
This folder is reserved for your selected backend framework.
|
|
199
|
+
`
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function t3Files(config) {
|
|
204
|
+
if (config.structure !== "t3") return {};
|
|
205
|
+
return {
|
|
206
|
+
"src/pages/index.tsx": `export default function Home() {
|
|
207
|
+
return <main><h1>${config.stackLabel}</h1><p>Your T3-style starter is ready.</p></main>;
|
|
208
|
+
}
|
|
209
|
+
`,
|
|
210
|
+
"server/api/root.ts": `export const appRouter = {};
|
|
211
|
+
export type AppRouter = typeof appRouter;
|
|
212
|
+
`,
|
|
213
|
+
"prisma/schema.prisma": `generator client {
|
|
214
|
+
provider = "prisma-client-js"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
datasource db {
|
|
218
|
+
provider = "postgresql"
|
|
219
|
+
url = env("DATABASE_URL")
|
|
220
|
+
}
|
|
221
|
+
`,
|
|
222
|
+
"app/.gitkeep": "",
|
|
223
|
+
"next.config.js": `const nextConfig = {};
|
|
224
|
+
export default nextConfig;
|
|
225
|
+
`,
|
|
226
|
+
"tsconfig.json": `${JSON.stringify({ compilerOptions: { strict: true, jsx: "preserve", moduleResolution: "bundler" } }, null, 2)}\n`
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function backendDependencies(config) {
|
|
231
|
+
if (config.backend === "Fastify") {
|
|
232
|
+
return { "@fastify/cors": "^9.0.1", dotenv: "^16.4.5", fastify: "^4.28.1", ...databaseDeps(config) };
|
|
233
|
+
}
|
|
234
|
+
return { cors: "^2.8.5", dotenv: "^16.4.5", express: "^4.19.2", ...databaseDeps(config), ...authDeps(config) };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function databaseDeps(config) {
|
|
238
|
+
if (config.database === "MongoDB") return { mongoose: "^8.4.4" };
|
|
239
|
+
if (config.database === "PostgreSQL") return { pg: "^8.12.0" };
|
|
240
|
+
if (config.database === "MySQL") return { mysql2: "^3.10.2" };
|
|
241
|
+
if (config.database === "SQLite") return { sqlite3: "^5.1.7" };
|
|
242
|
+
if (config.features.includes("Prisma")) return { "@prisma/client": "^5.16.1" };
|
|
243
|
+
return {};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function authDeps(config) {
|
|
247
|
+
if (config.auth === "JWT") return { bcryptjs: "^2.4.3", jsonwebtoken: "^9.0.2" };
|
|
248
|
+
return {};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function databaseEnv(config) {
|
|
252
|
+
if (config.database === "MongoDB") return "MONGODB_URI=mongodb://localhost:27017/my_fullstack_app";
|
|
253
|
+
if (config.database === "PostgreSQL") return "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/my_fullstack_app";
|
|
254
|
+
if (config.database === "MySQL") return "DATABASE_URL=mysql://root:password@localhost:3306/my_fullstack_app";
|
|
255
|
+
if (config.database === "SQLite") return "DATABASE_URL=file:./dev.db";
|
|
256
|
+
return "";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function databaseConfig(config) {
|
|
260
|
+
return `export const database = {
|
|
261
|
+
provider: "${config.database}",
|
|
262
|
+
url: process.env.DATABASE_URL || process.env.MONGODB_URI || ""
|
|
263
|
+
};
|
|
264
|
+
`;
|
|
265
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function createDatabaseFiles(config) {
|
|
2
|
+
if (config.database === "None" || config.structure === "t3") return {};
|
|
3
|
+
|
|
4
|
+
const files = {
|
|
5
|
+
"database/README.md": `# Database
|
|
6
|
+
|
|
7
|
+
Provider: ${config.database}
|
|
8
|
+
|
|
9
|
+
Add schema, migrations, seed data, and local setup notes here.
|
|
10
|
+
`
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (config.database === "MongoDB") {
|
|
14
|
+
files["database/mongodb.md"] = "Default local URI: mongodb://localhost:27017/my_fullstack_app\n";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (config.database === "PostgreSQL") {
|
|
18
|
+
files["database/schema.sql"] = "CREATE TABLE IF NOT EXISTS health_checks (id SERIAL PRIMARY KEY, created_at TIMESTAMP DEFAULT NOW());\n";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (config.database === "MySQL") {
|
|
22
|
+
files["database/schema.sql"] = "CREATE TABLE IF NOT EXISTS health_checks (id INT AUTO_INCREMENT PRIMARY KEY, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);\n";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (config.features.includes("Prisma")) {
|
|
26
|
+
files["database/prisma/schema.prisma"] = `generator client {
|
|
27
|
+
provider = "prisma-client-js"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
datasource db {
|
|
31
|
+
provider = "${prismaProvider(config.database)}"
|
|
32
|
+
url = env("DATABASE_URL")
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return files;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function prismaProvider(database) {
|
|
41
|
+
if (database === "PostgreSQL") return "postgresql";
|
|
42
|
+
if (database === "MySQL") return "mysql";
|
|
43
|
+
if (database === "SQLite") return "sqlite";
|
|
44
|
+
return "mongodb";
|
|
45
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
export function createFrontendFiles(config) {
|
|
2
|
+
if (config.frontend === "None" || config.structure === "t3") return {};
|
|
3
|
+
if (config.frontend === "React") return reactFiles(config);
|
|
4
|
+
if (config.frontend === "Next.js") return nextFiles(config);
|
|
5
|
+
if (config.frontend === "Angular") return angularFiles(config);
|
|
6
|
+
return genericFrontendFiles(config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function reactFiles(config) {
|
|
10
|
+
const ext = config.language === "TypeScript" ? "tsx" : "jsx";
|
|
11
|
+
const mainExt = config.language === "TypeScript" ? "tsx" : "jsx";
|
|
12
|
+
const useTailwind = config.styling === "Tailwind CSS";
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
"frontend/package.json": `${JSON.stringify({
|
|
16
|
+
name: "frontend",
|
|
17
|
+
version: "0.1.0",
|
|
18
|
+
private: true,
|
|
19
|
+
type: "module",
|
|
20
|
+
scripts: {
|
|
21
|
+
dev: "vite --host 0.0.0.0",
|
|
22
|
+
build: "vite build",
|
|
23
|
+
preview: "vite preview"
|
|
24
|
+
},
|
|
25
|
+
dependencies: frontendDependencies(config),
|
|
26
|
+
devDependencies: frontendDevDependencies(config)
|
|
27
|
+
}, null, 2)}\n`,
|
|
28
|
+
"frontend/index.html": `<div id="root"></div><script type="module" src="/src/main.${mainExt}"></script>\n`,
|
|
29
|
+
[`frontend/src/main.${mainExt}`]: `import React from "react";
|
|
30
|
+
import ReactDOM from "react-dom/client";
|
|
31
|
+
import { BrowserRouter } from "react-router-dom";
|
|
32
|
+
import App from "./App.${ext}";
|
|
33
|
+
import "./styles.css";
|
|
34
|
+
|
|
35
|
+
ReactDOM.createRoot(document.getElementById("root")).render(
|
|
36
|
+
<React.StrictMode>
|
|
37
|
+
<BrowserRouter>
|
|
38
|
+
<App />
|
|
39
|
+
</BrowserRouter>
|
|
40
|
+
</React.StrictMode>
|
|
41
|
+
);
|
|
42
|
+
`,
|
|
43
|
+
[`frontend/src/App.${ext}`]: `import { Route, Routes } from "react-router-dom";
|
|
44
|
+
import { Home } from "./pages/Home";
|
|
45
|
+
|
|
46
|
+
export default function App() {
|
|
47
|
+
return (
|
|
48
|
+
<Routes>
|
|
49
|
+
<Route path="/" element={<Home />} />
|
|
50
|
+
</Routes>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
`,
|
|
54
|
+
[`frontend/src/pages/Home.${ext}`]: `import { api } from "../services/api";
|
|
55
|
+
|
|
56
|
+
export function Home() {
|
|
57
|
+
async function checkApi() {
|
|
58
|
+
const { data } = await api.get("/health");
|
|
59
|
+
alert(data.message);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<main className="min-h-screen bg-slate-950 px-6 py-16 text-white">
|
|
64
|
+
<section className="mx-auto max-w-4xl">
|
|
65
|
+
<p className="text-sm uppercase tracking-widest text-cyan-300">${config.stackLabel}</p>
|
|
66
|
+
<h1 className="mt-4 text-4xl font-bold">Full-stack app ready.</h1>
|
|
67
|
+
<p className="mt-4 max-w-2xl text-slate-300">
|
|
68
|
+
Frontend, backend, routing, services, and project folders are already wired for your next build.
|
|
69
|
+
</p>
|
|
70
|
+
<button
|
|
71
|
+
className="mt-8 rounded-md bg-cyan-400 px-4 py-2 font-semibold text-slate-950"
|
|
72
|
+
onClick={checkApi}
|
|
73
|
+
>
|
|
74
|
+
Check API
|
|
75
|
+
</button>
|
|
76
|
+
</section>
|
|
77
|
+
</main>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
`,
|
|
81
|
+
"frontend/src/services/api.js": `import axios from "axios";
|
|
82
|
+
|
|
83
|
+
export const api = axios.create({
|
|
84
|
+
baseURL: import.meta.env.VITE_API_URL || "http://localhost:5000/api"
|
|
85
|
+
});
|
|
86
|
+
`,
|
|
87
|
+
"frontend/src/styles.css": useTailwind ? `@tailwind base;
|
|
88
|
+
@tailwind components;
|
|
89
|
+
@tailwind utilities;
|
|
90
|
+
` : `body {
|
|
91
|
+
margin: 0;
|
|
92
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
93
|
+
}
|
|
94
|
+
`,
|
|
95
|
+
"frontend/src/components/.gitkeep": "",
|
|
96
|
+
"frontend/src/hooks/.gitkeep": "",
|
|
97
|
+
"frontend/src/layouts/.gitkeep": "",
|
|
98
|
+
"frontend/src/utils/.gitkeep": "",
|
|
99
|
+
"frontend/src/assets/.gitkeep": "",
|
|
100
|
+
"frontend/src/context/.gitkeep": "",
|
|
101
|
+
"frontend/src/routes/.gitkeep": "",
|
|
102
|
+
"frontend/public/.gitkeep": "",
|
|
103
|
+
"frontend/vite.config.js": viteConfig(config),
|
|
104
|
+
...(useTailwind ? tailwindFiles() : {}),
|
|
105
|
+
...(config.language === "TypeScript" ? tsConfigFiles() : {})
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function nextFiles(config) {
|
|
110
|
+
return {
|
|
111
|
+
"frontend/package.json": `${JSON.stringify({
|
|
112
|
+
name: "frontend",
|
|
113
|
+
version: "0.1.0",
|
|
114
|
+
private: true,
|
|
115
|
+
scripts: { dev: "next dev", build: "next build", start: "next start", lint: "next lint" },
|
|
116
|
+
dependencies: { next: "^14.2.5", react: "^18.3.1", "react-dom": "^18.3.1" },
|
|
117
|
+
devDependencies: config.styling === "Tailwind CSS" ? tailwindDevDeps() : {}
|
|
118
|
+
}, null, 2)}\n`,
|
|
119
|
+
"frontend/app/page.tsx": `export default function Home() {
|
|
120
|
+
return <main><h1>${config.stackLabel}</h1><p>Your Next.js app is ready.</p></main>;
|
|
121
|
+
}
|
|
122
|
+
`,
|
|
123
|
+
"frontend/app/layout.tsx": `import "./globals.css";
|
|
124
|
+
|
|
125
|
+
export default function RootLayout({ children }) {
|
|
126
|
+
return <html lang="en"><body>{children}</body></html>;
|
|
127
|
+
}
|
|
128
|
+
`,
|
|
129
|
+
"frontend/app/globals.css": config.styling === "Tailwind CSS" ? "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n" : "body { margin: 0; font-family: sans-serif; }\n",
|
|
130
|
+
...(config.styling === "Tailwind CSS" ? tailwindFiles("frontend/") : {})
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function angularFiles(config) {
|
|
135
|
+
return {
|
|
136
|
+
"frontend/package.json": `${JSON.stringify({
|
|
137
|
+
name: "frontend",
|
|
138
|
+
version: "0.1.0",
|
|
139
|
+
private: true,
|
|
140
|
+
scripts: { dev: "ng serve --host 0.0.0.0", build: "ng build" },
|
|
141
|
+
dependencies: { "@angular/core": "^18.0.0", "@angular/common": "^18.0.0", "@angular/router": "^18.0.0", rxjs: "^7.8.1", "zone.js": "^0.14.7" },
|
|
142
|
+
devDependencies: { "@angular/cli": "^18.0.0", typescript: "^5.4.5" }
|
|
143
|
+
}, null, 2)}\n`,
|
|
144
|
+
"frontend/src/app/app.component.ts": `export class AppComponent {
|
|
145
|
+
title = "${config.stackLabel}";
|
|
146
|
+
}
|
|
147
|
+
`,
|
|
148
|
+
"frontend/src/main.ts": `console.log("Angular starter generated. Run ng generate application to expand this shell.");\n`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function genericFrontendFiles(config) {
|
|
153
|
+
return {
|
|
154
|
+
"frontend/package.json": `${JSON.stringify({
|
|
155
|
+
name: "frontend",
|
|
156
|
+
version: "0.1.0",
|
|
157
|
+
private: true,
|
|
158
|
+
scripts: { dev: "vite --host 0.0.0.0", build: "vite build", preview: "vite preview" },
|
|
159
|
+
dependencies: {},
|
|
160
|
+
devDependencies: { vite: "^5.3.5" }
|
|
161
|
+
}, null, 2)}\n`,
|
|
162
|
+
"frontend/src/main.js": `console.log("${config.frontend} starter selected.");\n`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function frontendDependencies(config) {
|
|
167
|
+
const deps = {
|
|
168
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
169
|
+
axios: "^1.7.2",
|
|
170
|
+
react: "^18.3.1",
|
|
171
|
+
"react-dom": "^18.3.1",
|
|
172
|
+
"react-router-dom": "^6.24.1"
|
|
173
|
+
};
|
|
174
|
+
if (config.auth === "Firebase Auth" || config.database === "Firebase") {
|
|
175
|
+
deps.firebase = "^10.12.3";
|
|
176
|
+
deps["react-firebase-hooks"] = "^5.1.1";
|
|
177
|
+
}
|
|
178
|
+
if (config.features.includes("Zustand")) deps.zustand = "^4.5.4";
|
|
179
|
+
if (config.features.includes("Redux Toolkit")) {
|
|
180
|
+
deps["@reduxjs/toolkit"] = "^2.2.6";
|
|
181
|
+
deps["react-redux"] = "^9.1.2";
|
|
182
|
+
}
|
|
183
|
+
if (config.styling === "Chakra UI") {
|
|
184
|
+
deps["@chakra-ui/react"] = "^2.8.2";
|
|
185
|
+
deps["@emotion/react"] = "^11.11.4";
|
|
186
|
+
deps["@emotion/styled"] = "^11.11.5";
|
|
187
|
+
deps["framer-motion"] = "^11.2.12";
|
|
188
|
+
}
|
|
189
|
+
if (config.styling === "Material UI") {
|
|
190
|
+
deps["@mui/material"] = "^5.16.4";
|
|
191
|
+
deps["@emotion/react"] = "^11.11.4";
|
|
192
|
+
deps["@emotion/styled"] = "^11.11.5";
|
|
193
|
+
}
|
|
194
|
+
if (config.styling === "Styled Components") deps["styled-components"] = "^6.1.11";
|
|
195
|
+
return deps;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function frontendDevDependencies(config) {
|
|
199
|
+
const deps = { vite: "^5.3.5" };
|
|
200
|
+
if (config.language === "TypeScript") {
|
|
201
|
+
deps.typescript = "^5.5.3";
|
|
202
|
+
deps["@types/react"] = "^18.3.3";
|
|
203
|
+
deps["@types/react-dom"] = "^18.3.0";
|
|
204
|
+
}
|
|
205
|
+
if (config.styling === "Tailwind CSS") Object.assign(deps, tailwindDevDeps());
|
|
206
|
+
if (config.styling === "SCSS") deps.sass = "^1.77.8";
|
|
207
|
+
return deps;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function viteConfig(config) {
|
|
211
|
+
return `import { defineConfig } from "vite";
|
|
212
|
+
import react from "@vitejs/plugin-react";
|
|
213
|
+
|
|
214
|
+
export default defineConfig({
|
|
215
|
+
plugins: [react()],
|
|
216
|
+
server: {
|
|
217
|
+
port: 5173
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function tailwindDevDeps() {
|
|
224
|
+
return { tailwindcss: "^3.4.4", postcss: "^8.4.39", autoprefixer: "^10.4.19" };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function tailwindFiles(prefix = "frontend/") {
|
|
228
|
+
return {
|
|
229
|
+
[`${prefix}tailwind.config.js`]: `export default {
|
|
230
|
+
content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}", "./app/**/*.{js,jsx,ts,tsx}"],
|
|
231
|
+
theme: {
|
|
232
|
+
extend: {}
|
|
233
|
+
},
|
|
234
|
+
plugins: []
|
|
235
|
+
};
|
|
236
|
+
`,
|
|
237
|
+
[`${prefix}postcss.config.js`]: `export default {
|
|
238
|
+
plugins: {
|
|
239
|
+
tailwindcss: {},
|
|
240
|
+
autoprefixer: {}
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
`
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function tsConfigFiles() {
|
|
248
|
+
return {
|
|
249
|
+
"frontend/tsconfig.json": `${JSON.stringify({
|
|
250
|
+
compilerOptions: {
|
|
251
|
+
target: "ES2020",
|
|
252
|
+
useDefineForClassFields: true,
|
|
253
|
+
lib: ["DOM", "DOM.Iterable", "ES2020"],
|
|
254
|
+
allowJs: false,
|
|
255
|
+
skipLibCheck: true,
|
|
256
|
+
esModuleInterop: true,
|
|
257
|
+
allowSyntheticDefaultImports: true,
|
|
258
|
+
strict: true,
|
|
259
|
+
forceConsistentCasingInFileNames: true,
|
|
260
|
+
module: "ESNext",
|
|
261
|
+
moduleResolution: "Node",
|
|
262
|
+
resolveJsonModule: true,
|
|
263
|
+
isolatedModules: true,
|
|
264
|
+
noEmit: true,
|
|
265
|
+
jsx: "react-jsx"
|
|
266
|
+
},
|
|
267
|
+
include: ["src"],
|
|
268
|
+
references: []
|
|
269
|
+
}, null, 2)}\n`
|
|
270
|
+
};
|
|
271
|
+
}
|