@adriankulik/create-fullstack-app 1.9.5 → 1.11.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.
- package/README.md +9 -8
- package/cli/build-date.js +1 -1
- package/cli/index.js +25 -9
- package/package.json +7 -5
- package/templates/backend/dotnet/Program.cs +26 -4
- package/templates/backend/dotnet/Tests/Tests.csproj +5 -5
- package/templates/backend/dotnet/dotnet.csproj +4 -0
- package/templates/backend/fastapi/main.py +22 -5
- package/templates/backend/fastapi/requirements.txt +6 -5
- package/templates/backend/flask/main.py +21 -3
- package/templates/backend/flask/requirements.txt +5 -4
- package/templates/backend/nodejs/package-lock.json +824 -653
- package/templates/backend/nodejs/package.json +12 -10
- package/templates/backend/nodejs/src/index.ts +13 -2
- package/templates/base/README.md +5 -3
- package/templates/base/database/README +1 -0
- package/templates/base/database/alembic.ini +147 -0
- package/templates/base/database/env.py +78 -0
- package/templates/base/database/requirements.txt +3 -0
- package/templates/base/database/script.py.mako +28 -0
- package/templates/base/database/versions/a6bb6e434c98_create_calculations_table.py +31 -0
- package/templates/base/docker-compose.yml +10 -0
- package/templates/base/start.sh +23 -0
- package/templates/frontend/nextjs/package-lock.json +1816 -1441
- package/templates/frontend/nextjs/package.json +13 -13
- package/templates/frontend/svelte/package-lock.json +496 -962
- package/templates/frontend/svelte/package.json +14 -14
- package/templates/frontend/vue/package-lock.json +473 -542
- package/templates/frontend/vue/package.json +10 -10
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Before running the CLI or the scaffolded applications, ensure you have the follo
|
|
|
12
12
|
|
|
13
13
|
- **Node.js**: v20 or newer
|
|
14
14
|
- **npm**: v10 or newer
|
|
15
|
-
- **Python**:
|
|
15
|
+
- **Python**: The newest stable release _(only required for FastAPI / Flask backends. We frequently update frameworks to their latest versions, so using older Pythons may cause pip install errors)_
|
|
16
16
|
- **[.NET 9 SDK](https://aka.ms/dotnet/download)** _(only required for the .NET backend — the CLI will offer to install it automatically on macOS/Linux if not found)_
|
|
17
17
|
|
|
18
18
|
## Quickstart
|
|
@@ -30,15 +30,16 @@ You will be prompted to choose a project name, your preferred frontend, and back
|
|
|
30
30
|
## Features
|
|
31
31
|
|
|
32
32
|
- **Frontend Options**:
|
|
33
|
-
- Next.js (v16.2
|
|
34
|
-
- Angular (
|
|
35
|
-
- Vue (v3.5.
|
|
36
|
-
- Svelte (v5.
|
|
33
|
+
- Next.js (v16.3.2)
|
|
34
|
+
- Angular (v22.1.3) - _Configured with Zone.js_
|
|
35
|
+
- Vue (v3.5.41)
|
|
36
|
+
- Svelte (v5.56.10)
|
|
37
37
|
- **Backend Options**:
|
|
38
|
-
- FastAPI (v0.
|
|
38
|
+
- FastAPI (v0.141.1)
|
|
39
39
|
- Flask (v3.1.3)
|
|
40
|
-
- .NET (
|
|
41
|
-
- Node.js Express (
|
|
40
|
+
- .NET (v10.0.11)
|
|
41
|
+
- Node.js Express (v5.2.1)
|
|
42
|
+
- **Database**: PostgreSQL (v15) with Alembic (v1.16.5), SQLAlchemy (v2.0.52), and Psycopg2 (v2.9.12).
|
|
42
43
|
- **Opinionated Defaults**: We bake in sensible, predefined choices (e.g., Angular uses `zone.js`, testing is unified under Playwright and framework-native test runners).
|
|
43
44
|
- **Unified Scripts**: `start.sh`, `test.sh`, and `lint.sh` available out of the box to manage both frontend and backend seamlessly.
|
|
44
45
|
- **CI/CD Ready**: Includes a pre-configured `.github/workflows/cli-e2e.yml` that tests both ends. _Tip: To enforce this, enable branch protection in your GitHub repository settings and require the "test" status check to pass._
|
package/cli/build-date.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = '2026-08-
|
|
1
|
+
module.exports = '2026-08-22 22:04:01';
|
package/cli/index.js
CHANGED
|
@@ -47,19 +47,19 @@ async function main() {
|
|
|
47
47
|
name: "frontend",
|
|
48
48
|
message: "Which frontend framework would you like to use?",
|
|
49
49
|
choices: [
|
|
50
|
-
{ title: "Next.js (v16.2
|
|
50
|
+
{ title: "Next.js (v16.3.2)", value: "nextjs", description: "React framework" },
|
|
51
51
|
{
|
|
52
|
-
title: "Angular (
|
|
52
|
+
title: "Angular (v22.1.3)",
|
|
53
53
|
value: "angular",
|
|
54
54
|
description: "Enterprise-grade platform",
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
|
-
title: "Vue (v3.5.
|
|
57
|
+
title: "Vue (v3.5.41)",
|
|
58
58
|
value: "vue",
|
|
59
59
|
description: "Progressive JavaScript framework",
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
|
-
title: "Svelte (v5.
|
|
62
|
+
title: "Svelte (v5.56.10)",
|
|
63
63
|
value: "svelte",
|
|
64
64
|
description: "Cybernetically enhanced web apps",
|
|
65
65
|
},
|
|
@@ -75,7 +75,7 @@ async function main() {
|
|
|
75
75
|
message: "Which backend framework would you like to use?",
|
|
76
76
|
choices: [
|
|
77
77
|
{
|
|
78
|
-
title: "FastAPI (v0.
|
|
78
|
+
title: "FastAPI (v0.141.1)",
|
|
79
79
|
value: "fastapi",
|
|
80
80
|
description: "Modern, fast Python web framework",
|
|
81
81
|
},
|
|
@@ -85,12 +85,12 @@ async function main() {
|
|
|
85
85
|
description: "Lightweight Python WSGI web application framework",
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
title: ".NET (
|
|
88
|
+
title: ".NET (v10.0.11)",
|
|
89
89
|
value: "dotnet",
|
|
90
90
|
description: "Robust, C#, high-performance framework by Microsoft",
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
|
-
title: "Node.js Express (
|
|
93
|
+
title: "Node.js Express (v5.2.1)",
|
|
94
94
|
value: "nodejs",
|
|
95
95
|
description: "Minimalist web framework for Node.js",
|
|
96
96
|
},
|
|
@@ -117,6 +117,7 @@ async function main() {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
console.log(pc.blue(`\nScaffolding project in ${targetDir}...`));
|
|
120
|
+
console.log(pc.blue(`Configuring PostgreSQL (v15) database with Alembic, SQLAlchemy, and Psycopg2...`));
|
|
120
121
|
|
|
121
122
|
// Determine paths to templates relative to the CLI script
|
|
122
123
|
const templatesDir = path.resolve(__dirname, "../templates");
|
|
@@ -195,8 +196,23 @@ async function main() {
|
|
|
195
196
|
} catch (e) {
|
|
196
197
|
console.error(
|
|
197
198
|
pc.red(
|
|
198
|
-
" Failed to set up Python virtual environment or install dependencies."
|
|
199
|
-
)
|
|
199
|
+
"\n Failed to set up Python virtual environment or install dependencies."
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
console.error(
|
|
203
|
+
pc.yellow(
|
|
204
|
+
" TIP: We frequently update templates to use the latest framework versions."
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
console.error(
|
|
208
|
+
pc.yellow(
|
|
209
|
+
" If pip failed to find a matching version, your default 'python3' version might be too old."
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
console.error(
|
|
213
|
+
pc.yellow(
|
|
214
|
+
" Please try updating to the newest version of Python and run this again."
|
|
215
|
+
)
|
|
200
216
|
);
|
|
201
217
|
process.exit(1);
|
|
202
218
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.11.0",
|
|
7
7
|
"description": "A CLI tool for scaffolding a full-stack web application with your choice of frontend and backend technologies.",
|
|
8
8
|
"main": "cli/index.js",
|
|
9
9
|
"bin": {
|
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
"test": "tests"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"fs-extra": "^11.
|
|
20
|
+
"fs-extra": "^11.4.0",
|
|
21
21
|
"picocolors": "^1.1.1",
|
|
22
22
|
"prompts": "^2.4.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@playwright/test": "^1.
|
|
26
|
-
"
|
|
27
|
-
"
|
|
25
|
+
"@playwright/test": "^1.62.1",
|
|
26
|
+
"@types/pg": "^8.23.1",
|
|
27
|
+
"pg": "^8.23.0",
|
|
28
|
+
"vitest": "^4.1.11",
|
|
29
|
+
"wait-on": "^9.1.0"
|
|
28
30
|
},
|
|
29
31
|
"scripts": {
|
|
30
32
|
"prepublishOnly": "echo \"module.exports = '$(date -u +\"%Y-%m-%d %H:%M:%S\")';\" > cli/build-date.js",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
using Microsoft.AspNetCore.Mvc;
|
|
2
|
+
using Npgsql;
|
|
2
3
|
|
|
3
4
|
var builder = WebApplication.CreateBuilder(args);
|
|
4
5
|
|
|
@@ -15,12 +16,33 @@ var app = builder.Build();
|
|
|
15
16
|
|
|
16
17
|
app.UseCors();
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
var dbUrl = Environment.GetEnvironmentVariable("DATABASE_URL") ?? "postgresql://user:password@localhost:5432/appdb";
|
|
20
|
+
var connStr = dbUrl;
|
|
21
|
+
if (dbUrl.StartsWith("postgres://") || dbUrl.StartsWith("postgresql://"))
|
|
19
22
|
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
var uri = new Uri(dbUrl);
|
|
24
|
+
var userInfo = uri.UserInfo.Split(':');
|
|
25
|
+
connStr = $"Host={uri.Host};Port={uri.Port};Username={userInfo[0]};Password={userInfo[1]};Database={uri.LocalPath.Substring(1)}";
|
|
26
|
+
}
|
|
23
27
|
|
|
28
|
+
app.MapPost("/api/multiply", async ([FromBody] MultiplyRequest request) =>
|
|
29
|
+
{
|
|
30
|
+
var result = request.Number * 2;
|
|
31
|
+
try
|
|
32
|
+
{
|
|
33
|
+
await using var dataSource = NpgsqlDataSource.Create(connStr);
|
|
34
|
+
await using var cmd = dataSource.CreateCommand("INSERT INTO calculations (input_number, result) VALUES (@input_number, @result)");
|
|
35
|
+
cmd.Parameters.AddWithValue("input_number", request.Number);
|
|
36
|
+
cmd.Parameters.AddWithValue("result", result);
|
|
37
|
+
await cmd.ExecuteNonQueryAsync();
|
|
38
|
+
}
|
|
39
|
+
catch (Exception e)
|
|
40
|
+
{
|
|
41
|
+
Console.WriteLine($"Error saving to db: {e.Message}");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new { result };
|
|
45
|
+
});
|
|
24
46
|
|
|
25
47
|
app.Run("http://localhost:8000");
|
|
26
48
|
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
</PropertyGroup>
|
|
11
11
|
|
|
12
12
|
<ItemGroup>
|
|
13
|
-
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="
|
|
14
|
-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="
|
|
15
|
-
<PackageReference Include="xunit" Version="2.
|
|
16
|
-
<PackageReference Include="xunit.runner.visualstudio" Version="
|
|
13
|
+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.11" />
|
|
14
|
+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
|
|
15
|
+
<PackageReference Include="xunit" Version="2.9.3" />
|
|
16
|
+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
|
|
17
17
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
18
18
|
<PrivateAssets>all</PrivateAssets>
|
|
19
19
|
</PackageReference>
|
|
20
|
-
<PackageReference Include="coverlet.collector" Version="
|
|
20
|
+
<PackageReference Include="coverlet.collector" Version="10.0.1">
|
|
21
21
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
22
22
|
<PrivateAssets>all</PrivateAssets>
|
|
23
23
|
</PackageReference>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from fastapi import FastAPI
|
|
2
3
|
from fastapi.middleware.cors import CORSMiddleware
|
|
3
4
|
from pydantic import BaseModel
|
|
5
|
+
import psycopg2
|
|
4
6
|
|
|
5
7
|
app = FastAPI()
|
|
6
8
|
|
|
7
|
-
# Allow CORS for the frontend
|
|
8
9
|
app.add_middleware(
|
|
9
10
|
CORSMiddleware,
|
|
10
|
-
allow_origins=["*"],
|
|
11
|
+
allow_origins=["*"],
|
|
11
12
|
allow_credentials=True,
|
|
12
13
|
allow_methods=["*"],
|
|
13
14
|
allow_headers=["*"],
|
|
@@ -19,8 +20,24 @@ class MultiplyRequest(BaseModel):
|
|
|
19
20
|
class MultiplyResponse(BaseModel):
|
|
20
21
|
result: float
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
def multiply_number(request: MultiplyRequest):
|
|
24
|
-
return MultiplyResponse(result=request.number * 2)
|
|
23
|
+
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@localhost:5432/appdb")
|
|
25
24
|
|
|
25
|
+
def save_calculation(number: float, result: float):
|
|
26
|
+
conn = psycopg2.connect(DATABASE_URL)
|
|
27
|
+
cursor = conn.cursor()
|
|
28
|
+
cursor.execute(
|
|
29
|
+
"INSERT INTO calculations (input_number, result) VALUES (%s, %s)",
|
|
30
|
+
(number, result)
|
|
31
|
+
)
|
|
32
|
+
conn.commit()
|
|
33
|
+
cursor.close()
|
|
34
|
+
conn.close()
|
|
26
35
|
|
|
36
|
+
@app.post("/api/multiply", response_model=MultiplyResponse)
|
|
37
|
+
def multiply_number(request: MultiplyRequest):
|
|
38
|
+
result = request.number * 2
|
|
39
|
+
try:
|
|
40
|
+
save_calculation(request.number, result)
|
|
41
|
+
except psycopg2.Error as e:
|
|
42
|
+
print(f"Error saving to db: {e}")
|
|
43
|
+
return MultiplyResponse(result=result)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
fastapi
|
|
2
|
-
uvicorn
|
|
3
|
-
pytest
|
|
4
|
-
httpx
|
|
5
|
-
ruff
|
|
1
|
+
fastapi
|
|
2
|
+
uvicorn
|
|
3
|
+
pytest
|
|
4
|
+
httpx
|
|
5
|
+
ruff
|
|
6
|
+
psycopg2-binary
|
|
@@ -1,9 +1,24 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from flask import Flask, request, jsonify
|
|
2
3
|
from flask_cors import CORS
|
|
4
|
+
import psycopg2
|
|
3
5
|
|
|
4
6
|
app = Flask(__name__)
|
|
5
7
|
CORS(app)
|
|
6
8
|
|
|
9
|
+
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@localhost:5432/appdb")
|
|
10
|
+
|
|
11
|
+
def save_calculation(number: float, result: float):
|
|
12
|
+
conn = psycopg2.connect(DATABASE_URL)
|
|
13
|
+
cursor = conn.cursor()
|
|
14
|
+
cursor.execute(
|
|
15
|
+
"INSERT INTO calculations (input_number, result) VALUES (%s, %s)",
|
|
16
|
+
(number, result)
|
|
17
|
+
)
|
|
18
|
+
conn.commit()
|
|
19
|
+
cursor.close()
|
|
20
|
+
conn.close()
|
|
21
|
+
|
|
7
22
|
@app.route("/api/multiply", methods=["POST"])
|
|
8
23
|
def multiply_number():
|
|
9
24
|
data = request.get_json()
|
|
@@ -12,11 +27,14 @@ def multiply_number():
|
|
|
12
27
|
|
|
13
28
|
try:
|
|
14
29
|
number = float(data["number"])
|
|
15
|
-
|
|
30
|
+
result = number * 2
|
|
31
|
+
try:
|
|
32
|
+
save_calculation(number, result)
|
|
33
|
+
except psycopg2.Error as e:
|
|
34
|
+
print(f"Error saving to db: {e}")
|
|
35
|
+
return jsonify({"result": result})
|
|
16
36
|
except ValueError:
|
|
17
37
|
return jsonify({"error": "Invalid number"}), 400
|
|
18
38
|
|
|
19
|
-
|
|
20
|
-
|
|
21
39
|
if __name__ == "__main__":
|
|
22
40
|
app.run(port=8000)
|