create-jemo-app 0.1.0__tar.gz
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.
- create_jemo_app-0.1.0/PKG-INFO +57 -0
- create_jemo_app-0.1.0/README.md +47 -0
- create_jemo_app-0.1.0/pyproject.toml +21 -0
- create_jemo_app-0.1.0/setup.cfg +4 -0
- create_jemo_app-0.1.0/src/create_jemo_app.egg-info/PKG-INFO +57 -0
- create_jemo_app-0.1.0/src/create_jemo_app.egg-info/SOURCES.txt +12 -0
- create_jemo_app-0.1.0/src/create_jemo_app.egg-info/dependency_links.txt +1 -0
- create_jemo_app-0.1.0/src/create_jemo_app.egg-info/entry_points.txt +2 -0
- create_jemo_app-0.1.0/src/create_jemo_app.egg-info/requires.txt +3 -0
- create_jemo_app-0.1.0/src/create_jemo_app.egg-info/top_level.txt +1 -0
- create_jemo_app-0.1.0/src/jemo_admin/generators/backend.py +163 -0
- create_jemo_app-0.1.0/src/jemo_admin/generators/frontend.py +186 -0
- create_jemo_app-0.1.0/src/jemo_admin/generators/vcs.py +42 -0
- create_jemo_app-0.1.0/src/jemo_admin/main.py +194 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: create-jemo-app
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool to scaffold modern web projects with FastAPI, Django, Convex, Next.js, SvelteKit, or TanStack Start.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: questionary>=2.1.1
|
|
8
|
+
Requires-Dist: rich>=14.3.2
|
|
9
|
+
Requires-Dist: typer>=0.23.0
|
|
10
|
+
|
|
11
|
+
# Jemo Admin CLI
|
|
12
|
+
|
|
13
|
+
A powerful CLI tool to scaffold modern web projects with your favorite stack.
|
|
14
|
+
|
|
15
|
+
## Supported Stacks
|
|
16
|
+
|
|
17
|
+
**Backend:**
|
|
18
|
+
- **FastAPI** (with Tortoise ORM & Aerich)
|
|
19
|
+
- **Django**
|
|
20
|
+
- **Convex** (Serverless)
|
|
21
|
+
|
|
22
|
+
**Frontend:**
|
|
23
|
+
- **Next.js**
|
|
24
|
+
- **SvelteKit**
|
|
25
|
+
- **TanStack Start**
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
You can install `jemo-admin` using `pipx`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pipx install .
|
|
33
|
+
# Or from a git repo:
|
|
34
|
+
# pipx install git+https://github.com/yourusername/create-jemo-app.git
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Create a new project interactively:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
jemo-admin create
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or specify the project name directly:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
jemo-admin create my-awesome-app
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Follow the interactive prompts to select your backend and frontend framework.
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- **Python 3.12+**
|
|
56
|
+
- **Bun** (for frontend package management)
|
|
57
|
+
- **UV** (for Python package management)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Jemo Admin CLI
|
|
2
|
+
|
|
3
|
+
A powerful CLI tool to scaffold modern web projects with your favorite stack.
|
|
4
|
+
|
|
5
|
+
## Supported Stacks
|
|
6
|
+
|
|
7
|
+
**Backend:**
|
|
8
|
+
- **FastAPI** (with Tortoise ORM & Aerich)
|
|
9
|
+
- **Django**
|
|
10
|
+
- **Convex** (Serverless)
|
|
11
|
+
|
|
12
|
+
**Frontend:**
|
|
13
|
+
- **Next.js**
|
|
14
|
+
- **SvelteKit**
|
|
15
|
+
- **TanStack Start**
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
You can install `jemo-admin` using `pipx`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pipx install .
|
|
23
|
+
# Or from a git repo:
|
|
24
|
+
# pipx install git+https://github.com/yourusername/create-jemo-app.git
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Create a new project interactively:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
jemo-admin create
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or specify the project name directly:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
jemo-admin create my-awesome-app
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Follow the interactive prompts to select your backend and frontend framework.
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- **Python 3.12+**
|
|
46
|
+
- **Bun** (for frontend package management)
|
|
47
|
+
- **UV** (for Python package management)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "create-jemo-app"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A CLI tool to scaffold modern web projects with FastAPI, Django, Convex, Next.js, SvelteKit, or TanStack Start."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"questionary>=2.1.1",
|
|
9
|
+
"rich>=14.3.2",
|
|
10
|
+
"typer>=0.23.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
jemo-admin = "jemo_admin.main:app"
|
|
15
|
+
|
|
16
|
+
[tool.uv.workspace]
|
|
17
|
+
members = [
|
|
18
|
+
"test/test-app/backend",
|
|
19
|
+
"test/my-jemo-app/backend",
|
|
20
|
+
"test/reproduce_error/backend",
|
|
21
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: create-jemo-app
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool to scaffold modern web projects with FastAPI, Django, Convex, Next.js, SvelteKit, or TanStack Start.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: questionary>=2.1.1
|
|
8
|
+
Requires-Dist: rich>=14.3.2
|
|
9
|
+
Requires-Dist: typer>=0.23.0
|
|
10
|
+
|
|
11
|
+
# Jemo Admin CLI
|
|
12
|
+
|
|
13
|
+
A powerful CLI tool to scaffold modern web projects with your favorite stack.
|
|
14
|
+
|
|
15
|
+
## Supported Stacks
|
|
16
|
+
|
|
17
|
+
**Backend:**
|
|
18
|
+
- **FastAPI** (with Tortoise ORM & Aerich)
|
|
19
|
+
- **Django**
|
|
20
|
+
- **Convex** (Serverless)
|
|
21
|
+
|
|
22
|
+
**Frontend:**
|
|
23
|
+
- **Next.js**
|
|
24
|
+
- **SvelteKit**
|
|
25
|
+
- **TanStack Start**
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
You can install `jemo-admin` using `pipx`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pipx install .
|
|
33
|
+
# Or from a git repo:
|
|
34
|
+
# pipx install git+https://github.com/yourusername/create-jemo-app.git
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Create a new project interactively:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
jemo-admin create
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or specify the project name directly:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
jemo-admin create my-awesome-app
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Follow the interactive prompts to select your backend and frontend framework.
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- **Python 3.12+**
|
|
56
|
+
- **Bun** (for frontend package management)
|
|
57
|
+
- **UV** (for Python package management)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/create_jemo_app.egg-info/PKG-INFO
|
|
4
|
+
src/create_jemo_app.egg-info/SOURCES.txt
|
|
5
|
+
src/create_jemo_app.egg-info/dependency_links.txt
|
|
6
|
+
src/create_jemo_app.egg-info/entry_points.txt
|
|
7
|
+
src/create_jemo_app.egg-info/requires.txt
|
|
8
|
+
src/create_jemo_app.egg-info/top_level.txt
|
|
9
|
+
src/jemo_admin/main.py
|
|
10
|
+
src/jemo_admin/generators/backend.py
|
|
11
|
+
src/jemo_admin/generators/frontend.py
|
|
12
|
+
src/jemo_admin/generators/vcs.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jemo_admin
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import subprocess
|
|
3
|
+
import shutil
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
console = Console()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run_command(command, cwd=None, shell=False):
|
|
10
|
+
"""Run a shell command."""
|
|
11
|
+
try:
|
|
12
|
+
# Capture output to show on error
|
|
13
|
+
subprocess.run(command, cwd=cwd, shell=shell, check=True, capture_output=False)
|
|
14
|
+
except subprocess.CalledProcessError as e:
|
|
15
|
+
console.print(f"[red]Error executing command: {command}[/red]")
|
|
16
|
+
console.print(f"[red]Exit Code: {e.returncode}[/red]")
|
|
17
|
+
if e.stdout:
|
|
18
|
+
console.print(f"[red]Stdout: {e.stdout.decode()}[/red]")
|
|
19
|
+
if e.stderr:
|
|
20
|
+
console.print(f"[red]Stderr: {e.stderr.decode()}[/red]")
|
|
21
|
+
raise e
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def create_fastapi(project_dir: Path, separate_folders: bool = True):
|
|
25
|
+
"""Scaffold a FastAPI project with Tortoise ORM and Aerich."""
|
|
26
|
+
|
|
27
|
+
backend_dir = project_dir / "backend"
|
|
28
|
+
if separate_folders:
|
|
29
|
+
backend_dir.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
else:
|
|
31
|
+
backend_dir = project_dir # If not separate folders, use project dir directly (uncommon for backend+frontend combo)
|
|
32
|
+
|
|
33
|
+
console.print(
|
|
34
|
+
f"[bold cyan]Initializing FastAPI backend in {backend_dir}...[/bold cyan]"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Initialize UV project
|
|
38
|
+
# Use --no-workspace to ensure we create a standalone project
|
|
39
|
+
run_command(["uv", "init", "--app", "--no-workspace"], cwd=backend_dir)
|
|
40
|
+
|
|
41
|
+
# Add dependencies
|
|
42
|
+
console.print(
|
|
43
|
+
"[bold]Adding dependencies: fastapi, uvicorn, tortoise-orm, aerich...[/bold]"
|
|
44
|
+
)
|
|
45
|
+
run_command(
|
|
46
|
+
["uv", "add", "fastapi", "uvicorn", "tortoise-orm", "aerich"], cwd=backend_dir
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Create main.py
|
|
50
|
+
main_py_content = """
|
|
51
|
+
from fastapi import FastAPI
|
|
52
|
+
from tortoise import Tortoise
|
|
53
|
+
from tortoise.contrib.fastapi import register_tortoise
|
|
54
|
+
|
|
55
|
+
app = FastAPI(title="Jemo Admin API")
|
|
56
|
+
|
|
57
|
+
@app.get("/")
|
|
58
|
+
async def read_root():
|
|
59
|
+
return {"message": "Welcome to Jemo Admin FastAPI Backend with Tortoise ORM!"}
|
|
60
|
+
|
|
61
|
+
# Database Configuration
|
|
62
|
+
TORTOISE_ORM = {
|
|
63
|
+
"connections": {"default": "sqlite://db.sqlite3"},
|
|
64
|
+
"apps": {
|
|
65
|
+
"models": {
|
|
66
|
+
"models": ["aerich.models", "models"], # Add your models here
|
|
67
|
+
"default_connection": "default",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
register_tortoise(
|
|
73
|
+
app,
|
|
74
|
+
config=TORTOISE_ORM,
|
|
75
|
+
generate_schemas=True,
|
|
76
|
+
add_exception_handlers=True,
|
|
77
|
+
)
|
|
78
|
+
"""
|
|
79
|
+
(backend_dir / "main.py").write_text(main_py_content)
|
|
80
|
+
|
|
81
|
+
# Create models.py
|
|
82
|
+
models_py_content = """
|
|
83
|
+
from tortoise import fields, models
|
|
84
|
+
|
|
85
|
+
class User(models.Model):
|
|
86
|
+
id = fields.IntField(pk=True)
|
|
87
|
+
username = fields.CharField(max_length=50, unique=True)
|
|
88
|
+
email = fields.CharField(max_length=255, unique=True)
|
|
89
|
+
created_at = fields.DatetimeField(auto_now_add=True)
|
|
90
|
+
|
|
91
|
+
def __str__(self):
|
|
92
|
+
return self.username
|
|
93
|
+
"""
|
|
94
|
+
(backend_dir / "models.py").write_text(models_py_content)
|
|
95
|
+
|
|
96
|
+
# Create tortoise_conf.py for Aerich
|
|
97
|
+
tortoise_conf_content = """
|
|
98
|
+
TORTOISE_ORM = {
|
|
99
|
+
"connections": {"default": "sqlite://db.sqlite3"},
|
|
100
|
+
"apps": {
|
|
101
|
+
"models": {
|
|
102
|
+
"models": ["aerich.models", "models"],
|
|
103
|
+
"default_connection": "default",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
"""
|
|
108
|
+
(backend_dir / "tortoise_conf.py").write_text(tortoise_conf_content)
|
|
109
|
+
|
|
110
|
+
console.print("[green]FastAPI backend setup complete![/green]")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def create_django(project_dir: Path, separate_folders: bool = True):
|
|
114
|
+
"""Scaffold a Django project."""
|
|
115
|
+
|
|
116
|
+
backend_dir = project_dir / "backend"
|
|
117
|
+
if separate_folders:
|
|
118
|
+
backend_dir.mkdir(parents=True, exist_ok=True)
|
|
119
|
+
else:
|
|
120
|
+
backend_dir = project_dir
|
|
121
|
+
|
|
122
|
+
console.print(
|
|
123
|
+
f"[bold cyan]Initializing Django backend in {backend_dir}...[/bold cyan]"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Initialize UV project
|
|
127
|
+
# Use --no-workspace to ensure we create a standalone project
|
|
128
|
+
run_command(["uv", "init", "--app", "--no-workspace"], cwd=backend_dir)
|
|
129
|
+
|
|
130
|
+
# Add Django
|
|
131
|
+
console.print("[bold]Adding dependencies: django...[/bold]")
|
|
132
|
+
run_command(["uv", "add", "django"], cwd=backend_dir)
|
|
133
|
+
|
|
134
|
+
# Create Django Project
|
|
135
|
+
# We use 'config' as the project name usually, or 'core'. Let's use 'config'.
|
|
136
|
+
console.print("[bold]Running django-admin startproject...[/bold]")
|
|
137
|
+
run_command(
|
|
138
|
+
["uv", "run", "django-admin", "startproject", "config", "."], cwd=backend_dir
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
console.print("[green]Django backend setup complete![/green]")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def create_convex_standalone(project_dir: Path):
|
|
145
|
+
"""Initialize Convex in a standalone directory (rarely used without frontend)."""
|
|
146
|
+
console.print(f"[bold cyan]Initializing Convex in {project_dir}...[/bold cyan]")
|
|
147
|
+
|
|
148
|
+
project_dir.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
|
|
150
|
+
# Check if npm/bun init is needed first
|
|
151
|
+
if not (project_dir / "package.json").exists():
|
|
152
|
+
run_command(["bun", "init", "-y"], cwd=project_dir)
|
|
153
|
+
|
|
154
|
+
run_command(["bun", "add", "convex"], cwd=project_dir)
|
|
155
|
+
run_command(
|
|
156
|
+
["npx", "convex", "dev"], cwd=project_dir
|
|
157
|
+
) # This is interactive usually!
|
|
158
|
+
# Wait, 'npx convex dev' is interactive and blocking. We probably shouldn't run it here.
|
|
159
|
+
# We should just install it and tell user to run it.
|
|
160
|
+
|
|
161
|
+
console.print(
|
|
162
|
+
"[green]Convex setup complete! Run 'npx convex dev' to start.[/green]"
|
|
163
|
+
)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import subprocess
|
|
3
|
+
import shutil
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
console = Console()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run_command(command, cwd=None, capture_output=False):
|
|
10
|
+
"""Run a shell command."""
|
|
11
|
+
try:
|
|
12
|
+
result = subprocess.run(
|
|
13
|
+
command, cwd=cwd, check=True, capture_output=capture_output, text=True
|
|
14
|
+
)
|
|
15
|
+
return result
|
|
16
|
+
except subprocess.CalledProcessError as e:
|
|
17
|
+
console.print(f"[red]Error executing command: {' '.join(command)}[/red]")
|
|
18
|
+
if e.stderr:
|
|
19
|
+
console.print(f"[red]{e.stderr}[/red]")
|
|
20
|
+
raise e
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def add_dependency(package_manager, package, cwd, dev=False):
|
|
24
|
+
"""Add a dependency."""
|
|
25
|
+
cmd = [package_manager, "add", "-d" if dev else "", package]
|
|
26
|
+
cmd = [c for c in cmd if c]
|
|
27
|
+
subprocess.run(cmd, cwd=cwd, check=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def install_tailwind(project_dir: Path, framework: str):
|
|
31
|
+
"""Install and configure Tailwind CSS."""
|
|
32
|
+
console.print(f"[bold]Setting up Tailwind CSS for {framework}...[/bold]")
|
|
33
|
+
|
|
34
|
+
if framework == "nextjs":
|
|
35
|
+
# Next.js with --yes already includes Tailwind by default
|
|
36
|
+
console.print(
|
|
37
|
+
"[green]Tailwind CSS already included with Next.js defaults[/green]"
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
# For other frameworks, install Tailwind v4 (zero config)
|
|
42
|
+
try:
|
|
43
|
+
# Install tailwindcss v4
|
|
44
|
+
run_command(
|
|
45
|
+
["bun", "add", "-d", "tailwindcss"],
|
|
46
|
+
cwd=project_dir,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
console.print("[green]Tailwind CSS v4 installed (zero config mode)[/green]")
|
|
50
|
+
console.print("[yellow]Note: Import Tailwind in your CSS file:[/yellow]")
|
|
51
|
+
console.print(' @import "tailwindcss";')
|
|
52
|
+
except Exception as e:
|
|
53
|
+
console.print(
|
|
54
|
+
f"[yellow]Warning: Could not auto-configure Tailwind. You may need to set it up manually.[/yellow]"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def create_nextjs(
|
|
59
|
+
project_dir: Path, subfolder: str = "frontend", use_convex: bool = False
|
|
60
|
+
):
|
|
61
|
+
"""Scaffold a Next.js project with Tailwind CSS."""
|
|
62
|
+
target_dir = project_dir / subfolder if subfolder != "." else project_dir
|
|
63
|
+
console.print(f"[bold cyan]Initializing Next.js in {target_dir}...[/bold cyan]")
|
|
64
|
+
|
|
65
|
+
if not project_dir.exists():
|
|
66
|
+
project_dir.mkdir(parents=True)
|
|
67
|
+
|
|
68
|
+
# Use --yes for non-interactive mode with defaults (includes Tailwind)
|
|
69
|
+
cmd = ["bun", "create", "next-app", subfolder if subfolder != "." else ".", "--yes"]
|
|
70
|
+
try:
|
|
71
|
+
run_command(cmd, cwd=project_dir)
|
|
72
|
+
except Exception:
|
|
73
|
+
console.print("[red]Failed to create Next.js project[/red]")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
if not target_dir.exists():
|
|
77
|
+
console.print(f"[red]Error: Directory {target_dir} was not created.[/red]")
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# Tailwind is already included with --yes flag (default Next.js setup includes it)
|
|
81
|
+
console.print("[green]Next.js with Tailwind CSS created successfully![/green]")
|
|
82
|
+
|
|
83
|
+
if use_convex:
|
|
84
|
+
console.print("[bold]Adding Convex to Next.js...[/bold]")
|
|
85
|
+
add_dependency("bun", "convex", cwd=target_dir)
|
|
86
|
+
console.print(
|
|
87
|
+
"[yellow]Note: Run 'npx convex dev' in the frontend directory to configure Convex.[/yellow]"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def create_sveltekit(
|
|
92
|
+
project_dir: Path, subfolder: str = "frontend", use_convex: bool = False
|
|
93
|
+
):
|
|
94
|
+
"""Scaffold a SvelteKit project with Tailwind CSS."""
|
|
95
|
+
target_dir = project_dir / subfolder if subfolder != "." else project_dir
|
|
96
|
+
console.print(f"[bold cyan]Initializing SvelteKit in {target_dir}...[/bold cyan]")
|
|
97
|
+
|
|
98
|
+
if not project_dir.exists():
|
|
99
|
+
project_dir.mkdir(parents=True)
|
|
100
|
+
|
|
101
|
+
# Use npx sv create with flags for non-interactive mode
|
|
102
|
+
folder_arg = subfolder if subfolder != "." else "."
|
|
103
|
+
cmd = [
|
|
104
|
+
"npx",
|
|
105
|
+
"sv",
|
|
106
|
+
"create",
|
|
107
|
+
folder_arg,
|
|
108
|
+
"--template",
|
|
109
|
+
"demo", # Use demo template (includes more features)
|
|
110
|
+
"--types",
|
|
111
|
+
"ts", # TypeScript
|
|
112
|
+
"--no-add-ons", # Don't prompt for add-ons
|
|
113
|
+
"--install",
|
|
114
|
+
"bun", # Use bun to install
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
run_command(cmd, cwd=project_dir)
|
|
119
|
+
except Exception:
|
|
120
|
+
console.print("[red]Failed to create SvelteKit project[/red]")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
if not target_dir.exists():
|
|
124
|
+
console.print(
|
|
125
|
+
f"[red]Error: Directory {target_dir} was not created. Skipping subsequent steps.[/red]"
|
|
126
|
+
)
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
console.print("[bold]Installing dependencies...[/bold]")
|
|
130
|
+
run_command(["bun", "install"], cwd=target_dir)
|
|
131
|
+
|
|
132
|
+
# Add Tailwind CSS
|
|
133
|
+
install_tailwind(target_dir, "sveltekit")
|
|
134
|
+
|
|
135
|
+
if use_convex:
|
|
136
|
+
console.print("[bold]Adding Convex to SvelteKit...[/bold]")
|
|
137
|
+
add_dependency("bun", "convex", cwd=target_dir)
|
|
138
|
+
console.print(
|
|
139
|
+
"[yellow]Note: Run 'npx convex dev' in the frontend directory to configure Convex.[/yellow]"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def create_tanstack(
|
|
144
|
+
project_dir: Path, subfolder: str = "frontend", use_convex: bool = False
|
|
145
|
+
):
|
|
146
|
+
"""Scaffold a TanStack Start project with Tailwind CSS."""
|
|
147
|
+
target_dir = project_dir / subfolder if subfolder != "." else project_dir
|
|
148
|
+
console.print(
|
|
149
|
+
f"[bold cyan]Initializing TanStack Start in {target_dir}...[/bold cyan]"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if not project_dir.exists():
|
|
153
|
+
project_dir.mkdir(parents=True)
|
|
154
|
+
|
|
155
|
+
# Use create subcommand (required for @tanstack/start v1)
|
|
156
|
+
folder_arg = subfolder if subfolder != "." else "."
|
|
157
|
+
cmd = [
|
|
158
|
+
"bun",
|
|
159
|
+
"create",
|
|
160
|
+
"@tanstack/start",
|
|
161
|
+
"create", # Subcommand required
|
|
162
|
+
folder_arg,
|
|
163
|
+
"--package-manager",
|
|
164
|
+
"bun",
|
|
165
|
+
"--no-git", # We'll handle git separately
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
run_command(cmd, cwd=project_dir)
|
|
170
|
+
except Exception:
|
|
171
|
+
console.print("[red]Failed to create TanStack Start project[/red]")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
if not target_dir.exists():
|
|
175
|
+
console.print(f"[red]Error: Directory {target_dir} was not created.[/red]")
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
# Add Tailwind CSS
|
|
179
|
+
install_tailwind(target_dir, "tanstack")
|
|
180
|
+
|
|
181
|
+
if use_convex:
|
|
182
|
+
console.print("[bold]Adding Convex to TanStack Start...[/bold]")
|
|
183
|
+
add_dependency("bun", "convex", cwd=target_dir)
|
|
184
|
+
console.print(
|
|
185
|
+
"[yellow]Note: Run 'npx convex dev' in the frontend directory to configure Convex.[/yellow]"
|
|
186
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import subprocess
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
console = Console()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run_command(command, cwd=None):
|
|
9
|
+
"""Run a shell command."""
|
|
10
|
+
try:
|
|
11
|
+
subprocess.run(command, cwd=cwd, check=True, stdout=subprocess.DEVNULL)
|
|
12
|
+
except subprocess.CalledProcessError as e:
|
|
13
|
+
console.print(f"[red]Error executing command: {' '.join(command)}[/red]")
|
|
14
|
+
# Don't raise, just warn for VCS
|
|
15
|
+
console.print(
|
|
16
|
+
f"[yellow]VCS initialization failed. You may need to install the tool or init manually.[/yellow]"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def init_git(project_dir: Path):
|
|
21
|
+
"""Initialize a Git repository."""
|
|
22
|
+
console.print(
|
|
23
|
+
f"[bold cyan]Initializing Git repository in {project_dir}...[/bold cyan]"
|
|
24
|
+
)
|
|
25
|
+
run_command(["git", "init"], cwd=project_dir)
|
|
26
|
+
# create .gitignore if it doesn't exist?
|
|
27
|
+
# Usually frameworks create their own .gitignores.
|
|
28
|
+
# We might want to stage files.
|
|
29
|
+
# run_command(["git", "add", "."], cwd=project_dir)
|
|
30
|
+
# console.print("[green]Git initialized.[/green]")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def init_jj(project_dir: Path):
|
|
34
|
+
"""Initialize a Jujutsu repository (git-backed)."""
|
|
35
|
+
console.print(
|
|
36
|
+
f"[bold cyan]Initializing Jujutsu (jj) repository in {project_dir}...[/bold cyan]"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# 'jj git init' creates a repo backed by git, which is most common for interop.
|
|
40
|
+
run_command(["jj", "git", "init"], cwd=project_dir)
|
|
41
|
+
|
|
42
|
+
console.print("[green]Jujutsu initialized.[/green]")
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import questionary
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import subprocess
|
|
7
|
+
import shutil
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(
|
|
12
|
+
help="CLI tool to scaffold modern web projects.", add_completion=False
|
|
13
|
+
)
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.callback()
|
|
18
|
+
def main():
|
|
19
|
+
"""
|
|
20
|
+
Jemo Admin CLI to scaffold modern web projects.
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.command()
|
|
26
|
+
def info():
|
|
27
|
+
"""
|
|
28
|
+
Show information about the CLI.
|
|
29
|
+
"""
|
|
30
|
+
console.print(Panel.fit("Jemo Admin CLI v0.1.0", border_style="cyan"))
|
|
31
|
+
console.print("Powered by [bold]Typer[/bold] and [bold]Rich[/bold].")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.command()
|
|
35
|
+
def create(
|
|
36
|
+
project_name: str = typer.Argument(
|
|
37
|
+
None, help="The name of the project directory (optional)."
|
|
38
|
+
),
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Create a new project with your choice of backend and frontend.
|
|
42
|
+
"""
|
|
43
|
+
console.print(
|
|
44
|
+
Panel.fit(
|
|
45
|
+
"Welcome to [bold cyan]jemo-admin[/bold cyan]! Let's build something great.",
|
|
46
|
+
border_style="cyan",
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# 1. Project Name / Directory
|
|
51
|
+
if not project_name:
|
|
52
|
+
project_name = questionary.text(
|
|
53
|
+
"What is your project name?", default="my-jemo-app"
|
|
54
|
+
).ask()
|
|
55
|
+
|
|
56
|
+
if not project_name:
|
|
57
|
+
console.print("[red]Project name is required![/red]")
|
|
58
|
+
raise typer.Exit(code=1)
|
|
59
|
+
|
|
60
|
+
# Resolve project directory
|
|
61
|
+
project_dir = Path.cwd() / project_name
|
|
62
|
+
|
|
63
|
+
if project_dir.exists() and any(project_dir.iterdir()):
|
|
64
|
+
overwrite = questionary.confirm(
|
|
65
|
+
f"Directory '{project_name}' is not empty. Do you want to continue (files may be overwritten)?"
|
|
66
|
+
).ask()
|
|
67
|
+
if not overwrite:
|
|
68
|
+
console.print("[yellow]Aborted.[/yellow]")
|
|
69
|
+
raise typer.Exit(code=0)
|
|
70
|
+
|
|
71
|
+
# 2. Select Backend
|
|
72
|
+
backend_choice = questionary.select(
|
|
73
|
+
"Select your backend framework:",
|
|
74
|
+
choices=[
|
|
75
|
+
"FastAPI (Tortoise ORM + Aerich)",
|
|
76
|
+
"Django",
|
|
77
|
+
"Convex (Serverless)",
|
|
78
|
+
"None (Frontend Only)",
|
|
79
|
+
],
|
|
80
|
+
).ask()
|
|
81
|
+
|
|
82
|
+
# 3. Select Frontend
|
|
83
|
+
frontend_choice = questionary.select(
|
|
84
|
+
"Select your frontend framework:",
|
|
85
|
+
choices=["Next.js", "SvelteKit", "TanStack Start", "None (Backend Only)"],
|
|
86
|
+
).ask()
|
|
87
|
+
|
|
88
|
+
# 4. Select Version Control
|
|
89
|
+
vcs_choice = questionary.select(
|
|
90
|
+
"Select your Version Control System:",
|
|
91
|
+
choices=["Git", "Jujutsu (jj)", "None"],
|
|
92
|
+
).ask()
|
|
93
|
+
|
|
94
|
+
if not backend_choice or not frontend_choice or not vcs_choice:
|
|
95
|
+
# Handle cancellation (Ctrl+C)
|
|
96
|
+
console.print("[yellow]Operation cancelled.[/yellow]")
|
|
97
|
+
raise typer.Exit(code=0)
|
|
98
|
+
|
|
99
|
+
if "None" in backend_choice and "None" in frontend_choice:
|
|
100
|
+
console.print("[red]You must select at least a backend or a frontend![/red]")
|
|
101
|
+
raise typer.Exit(code=1)
|
|
102
|
+
|
|
103
|
+
# Confirm Plan
|
|
104
|
+
console.print(f"\n[bold green]Plan:[/bold green]")
|
|
105
|
+
console.print(f" Project: [cyan]{project_name}[/cyan]")
|
|
106
|
+
console.print(f" Backend: [cyan]{backend_choice}[/cyan]")
|
|
107
|
+
console.print(f" Frontend: [cyan]{frontend_choice}[/cyan]")
|
|
108
|
+
console.print(f" VCS: [cyan]{vcs_choice}[/cyan]")
|
|
109
|
+
|
|
110
|
+
if not questionary.confirm("Does this look correct?").ask():
|
|
111
|
+
console.print("[yellow]Aborted.[/yellow]")
|
|
112
|
+
raise typer.Exit(code=0)
|
|
113
|
+
|
|
114
|
+
# Create Project Directory
|
|
115
|
+
if not project_dir.exists():
|
|
116
|
+
project_dir.mkdir(parents=True)
|
|
117
|
+
|
|
118
|
+
console.print("\n[bold]Scaffolding project...[/bold]")
|
|
119
|
+
|
|
120
|
+
# Import generators here to avoid circular imports or issues before structure is ready
|
|
121
|
+
from jemo_admin.generators import backend as backend_gen
|
|
122
|
+
from jemo_admin.generators import frontend as frontend_gen
|
|
123
|
+
from jemo_admin.generators import vcs as vcs_gen
|
|
124
|
+
|
|
125
|
+
# Determine types
|
|
126
|
+
is_convex = "Convex" in backend_choice
|
|
127
|
+
is_fastapi = "FastAPI" in backend_choice
|
|
128
|
+
is_django = "Django" in backend_choice
|
|
129
|
+
|
|
130
|
+
# Backend Generation
|
|
131
|
+
if is_fastapi:
|
|
132
|
+
backend_gen.create_fastapi(project_dir)
|
|
133
|
+
elif is_django:
|
|
134
|
+
backend_gen.create_django(project_dir)
|
|
135
|
+
elif is_convex and "None" in frontend_choice:
|
|
136
|
+
# Standalone Convex (rare but possible)
|
|
137
|
+
backend_gen.create_convex_standalone(project_dir)
|
|
138
|
+
|
|
139
|
+
# Frontend Generation
|
|
140
|
+
frontend_subfolder = "frontend" if (is_fastapi or is_django) else "."
|
|
141
|
+
|
|
142
|
+
if "Next.js" in frontend_choice:
|
|
143
|
+
frontend_gen.create_nextjs(
|
|
144
|
+
project_dir, subfolder=frontend_subfolder, use_convex=is_convex
|
|
145
|
+
)
|
|
146
|
+
elif "SvelteKit" in frontend_choice:
|
|
147
|
+
frontend_gen.create_sveltekit(
|
|
148
|
+
project_dir, subfolder=frontend_subfolder, use_convex=is_convex
|
|
149
|
+
)
|
|
150
|
+
elif "TanStack" in frontend_choice:
|
|
151
|
+
frontend_gen.create_tanstack(
|
|
152
|
+
project_dir, subfolder=frontend_subfolder, use_convex=is_convex
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# VCS Initialization
|
|
156
|
+
if "Git" in vcs_choice:
|
|
157
|
+
vcs_gen.init_git(project_dir)
|
|
158
|
+
elif "Jujutsu" in vcs_choice:
|
|
159
|
+
vcs_gen.init_jj(project_dir)
|
|
160
|
+
|
|
161
|
+
console.print(f"\n[bold green]Successfully created {project_name}![/bold green]")
|
|
162
|
+
console.print("\n[bold]To get started:[/bold]")
|
|
163
|
+
console.print(f" cd {project_name}")
|
|
164
|
+
|
|
165
|
+
if is_fastapi:
|
|
166
|
+
console.print("\n [bold cyan]Backend (FastAPI):[/bold cyan]")
|
|
167
|
+
console.print(" cd backend")
|
|
168
|
+
console.print(" uv run aerich init -t tortoise_conf.TORTOISE_ORM")
|
|
169
|
+
console.print(" uv run aerich init-db")
|
|
170
|
+
console.print(" uv run uvicorn main:app --reload")
|
|
171
|
+
elif is_django:
|
|
172
|
+
console.print("\n [bold cyan]Backend (Django):[/bold cyan]")
|
|
173
|
+
console.print(" cd backend")
|
|
174
|
+
console.print(" uv run python manage.py migrate")
|
|
175
|
+
console.print(" uv run python manage.py runserver")
|
|
176
|
+
|
|
177
|
+
if "None" not in frontend_choice:
|
|
178
|
+
console.print("\n [bold cyan]Frontend:[/bold cyan]")
|
|
179
|
+
if is_fastapi or is_django:
|
|
180
|
+
console.print(" cd ../frontend")
|
|
181
|
+
# If single folder (Convex/None backend), we are already in root (or just cd into it initially)
|
|
182
|
+
|
|
183
|
+
console.print(" bun install")
|
|
184
|
+
if is_convex:
|
|
185
|
+
console.print(" npx convex dev")
|
|
186
|
+
console.print(" bun dev")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if __name__ == "__main__":
|
|
190
|
+
try:
|
|
191
|
+
app()
|
|
192
|
+
except KeyboardInterrupt:
|
|
193
|
+
console.print("\n[yellow]Operation cancelled by user.[/yellow]")
|
|
194
|
+
sys.exit(0)
|