skeleton-framework 0.1.2__tar.gz → 0.1.4__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.
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/PKG-INFO +19 -2
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/README.md +18 -1
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/pyproject.toml +1 -1
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/cli.py +5 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/client.py +3 -1
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/client/init.py +1 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/config.py +151 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/copy.py +393 -0
- {skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src → skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/react}/main.tsx +1 -1
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/react/skeleton-config.json +30 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/react/skeleton-dependencies.json +21 -0
- {skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/templates/app/frontend → skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles}/skeleton-config.json +1 -1
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/main.tsx +20 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/skeleton-config.json +30 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/skeleton-dependencies.json +22 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/tsconfig.json +93 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/App.vue +3 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/main.ts +17 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/skeleton-config.json +30 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/skeleton-dependencies.json +22 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/tsconfig.json +91 -0
- skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/vue.d.ts +11 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/config.py +3 -3
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/main.py +1 -1
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/main.py +1 -1
- skeleton_framework-0.1.4/skeleton/packages/.skeleton/skeleton-cli-1.0.0.tgz +0 -0
- skeleton_framework-0.1.4/skeleton/packages/.skeleton/skeleton-webpack-1.0.0.tgz +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton_framework.egg-info/PKG-INFO +19 -2
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton_framework.egg-info/SOURCES.txt +15 -3
- skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/config.py +0 -54
- skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/copy.py +0 -56
- skeleton_framework-0.1.2/skeleton/packages/.skeleton/skeleton-cli-1.0.0.tgz +0 -0
- skeleton_framework-0.1.2/skeleton/packages/.skeleton/skeleton-webpack-1.0.0.tgz +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/LICENSE +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/setup.cfg +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/client/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/client/install.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/dev/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/dev/build.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/dev/init.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/dev/install.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/commands/dev/pack.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/load.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/backend/requirements.txt +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/backend/routes.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/backend/src/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/backend/src/main.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/package.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/index.html +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/css/styles.css +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/img/skeleton.png +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/js/script.js +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/config/.gitignore +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-dependencies.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-layout.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-schemas.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-servers.json +0 -0
- {skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/templates/app/frontend → skeleton_framework-0.1.4/skeleton/infrastructure/bootstrap/src/templates/profiles/react}/tsconfig.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/config.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/constants.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/context.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/env.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/layout.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/loader.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/repository.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/common/src/utils.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/settings/proxy.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/settings/services.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/app.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/client.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/config.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/context.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/headers.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/lifespan.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/loader.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/logging.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/parser.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/registry.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/reverseproxy.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/router.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/schemas.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/service.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/utils.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/gateway/src/validation.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/layout.json +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/__init__.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/actions.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/browser.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/cmd.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/command.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/frontend.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/gateway.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/hook.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/packages.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/parser.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/process.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/services.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/utils.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/infrastructure/runners/src/validation.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton/root.py +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton_framework.egg-info/dependency_links.txt +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton_framework.egg-info/entry_points.txt +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton_framework.egg-info/requires.txt +0 -0
- {skeleton_framework-0.1.2 → skeleton_framework-0.1.4}/skeleton_framework.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: skeleton-framework
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A full-stack app framework: Python orchestration + FastAPI + WebPack frontend.
|
|
5
5
|
Author-email: lambdadotjoburg <lambdadotjoburg@gmail.com>
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -107,6 +107,23 @@ Skeleton also exposes the individual stages when you want to configure the appli
|
|
|
107
107
|
|
|
108
108
|
### 1. Generate configuration
|
|
109
109
|
|
|
110
|
+
Generate the project configuration files before initialization.
|
|
111
|
+
Skeleton supports React and SolidJS frontend profiles.
|
|
112
|
+
|
|
113
|
+
Select the desired frontend framework using the `--config` command:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
skeleton --config react
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or select SolidJS:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
skeleton --config solid
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
If no framework is specified, React is selected by default:
|
|
126
|
+
|
|
110
127
|
```bash
|
|
111
128
|
skeleton --config
|
|
112
129
|
```
|
|
@@ -400,7 +417,7 @@ source .venv/bin/activate
|
|
|
400
417
|
From the test project directory:
|
|
401
418
|
|
|
402
419
|
```bash
|
|
403
|
-
python -m pip install --force-reinstall ../framework/dist/skeleton_framework-0.1.
|
|
420
|
+
python -m pip install --force-reinstall ../framework/dist/skeleton_framework-0.1.3-py3-none-any.whl
|
|
404
421
|
```
|
|
405
422
|
|
|
406
423
|
Adjust the relative path, wheel filename & version to match your framework checkout and build output.
|
|
@@ -89,6 +89,23 @@ Skeleton also exposes the individual stages when you want to configure the appli
|
|
|
89
89
|
|
|
90
90
|
### 1. Generate configuration
|
|
91
91
|
|
|
92
|
+
Generate the project configuration files before initialization.
|
|
93
|
+
Skeleton supports React and SolidJS frontend profiles.
|
|
94
|
+
|
|
95
|
+
Select the desired frontend framework using the `--config` command:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
skeleton --config react
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or select SolidJS:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
skeleton --config solid
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If no framework is specified, React is selected by default:
|
|
108
|
+
|
|
92
109
|
```bash
|
|
93
110
|
skeleton --config
|
|
94
111
|
```
|
|
@@ -382,7 +399,7 @@ source .venv/bin/activate
|
|
|
382
399
|
From the test project directory:
|
|
383
400
|
|
|
384
401
|
```bash
|
|
385
|
-
python -m pip install --force-reinstall ../framework/dist/skeleton_framework-0.1.
|
|
402
|
+
python -m pip install --force-reinstall ../framework/dist/skeleton_framework-0.1.3-py3-none-any.whl
|
|
386
403
|
```
|
|
387
404
|
|
|
388
405
|
Adjust the relative path, wheel filename & version to match your framework checkout and build output.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "skeleton-framework"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "A full-stack app framework: Python orchestration + FastAPI + WebPack frontend."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -27,10 +27,15 @@ from pathlib import Path
|
|
|
27
27
|
# ============================================================
|
|
28
28
|
|
|
29
29
|
# skeleton --config
|
|
30
|
+
# skeleton --config react
|
|
31
|
+
# skeleton --config solid
|
|
32
|
+
# skeleton --config vue
|
|
33
|
+
|
|
30
34
|
# skeleton --init
|
|
31
35
|
# skeleton --install
|
|
32
36
|
# skeleton --start
|
|
33
37
|
# skeleton --start frontend gateway --open
|
|
38
|
+
|
|
34
39
|
# skeleton --quickstart
|
|
35
40
|
|
|
36
41
|
# skeleton --serve
|
|
@@ -176,11 +176,13 @@ def main(
|
|
|
176
176
|
|
|
177
177
|
from skeleton.infrastructure.bootstrap.src.config import config
|
|
178
178
|
|
|
179
|
+
framework = args[1] if len(args) > 1 else None
|
|
180
|
+
|
|
179
181
|
config(
|
|
180
182
|
framework_root=framework_root,
|
|
181
183
|
project_root=project_root,
|
|
184
|
+
framework=framework,
|
|
182
185
|
)
|
|
183
|
-
|
|
184
186
|
return 0
|
|
185
187
|
|
|
186
188
|
if command == "--init":
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import json, shutil
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
SUPPORTED_FRAMEWORKS = {"react", "solid", "vue"}
|
|
5
|
+
|
|
6
|
+
def read_json(path: Path) -> dict:
|
|
7
|
+
with path.open("r", encoding="utf-8") as file:
|
|
8
|
+
data = json.load(file)
|
|
9
|
+
|
|
10
|
+
if not isinstance(data, dict):
|
|
11
|
+
raise ValueError(f"Expected a JSON object: {path}")
|
|
12
|
+
|
|
13
|
+
return data
|
|
14
|
+
|
|
15
|
+
def write_json(path: Path, data: dict) -> None:
|
|
16
|
+
with path.open("w", encoding="utf-8") as file:
|
|
17
|
+
json.dump(data, file, indent=4)
|
|
18
|
+
file.write("\n")
|
|
19
|
+
|
|
20
|
+
def config(
|
|
21
|
+
framework_root: Path,
|
|
22
|
+
project_root: Path,
|
|
23
|
+
framework: str | None = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
|
|
26
|
+
templates_root = (
|
|
27
|
+
framework_root
|
|
28
|
+
/ "skeleton"
|
|
29
|
+
/ "infrastructure"
|
|
30
|
+
/ "bootstrap"
|
|
31
|
+
/ "src"
|
|
32
|
+
/ "templates"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
config_templates = templates_root / "config"
|
|
36
|
+
profiles_root = templates_root / "profiles"
|
|
37
|
+
|
|
38
|
+
if not config_templates.is_dir():
|
|
39
|
+
raise FileNotFoundError(
|
|
40
|
+
f"Configuration templates not found: {config_templates}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if not profiles_root.is_dir():
|
|
44
|
+
raise FileNotFoundError(
|
|
45
|
+
f"Framework profiles not found: {profiles_root}"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if framework is not None:
|
|
49
|
+
framework = framework.strip().lower()
|
|
50
|
+
|
|
51
|
+
if framework not in SUPPORTED_FRAMEWORKS:
|
|
52
|
+
supported = ", ".join(sorted(SUPPORTED_FRAMEWORKS))
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Unsupported frontend framework: {framework!r}. "
|
|
55
|
+
f"Choose one of: {supported}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
project_root = project_root.resolve()
|
|
59
|
+
project_root.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------
|
|
62
|
+
# Copy shared project configuration.
|
|
63
|
+
# ---------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
for template in config_templates.iterdir():
|
|
66
|
+
destination = project_root / template.name
|
|
67
|
+
|
|
68
|
+
if destination.exists():
|
|
69
|
+
print(f"INFO: Preserving existing file: {destination}")
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
if template.is_dir():
|
|
73
|
+
shutil.copytree(template, destination)
|
|
74
|
+
else:
|
|
75
|
+
shutil.copy2(template, destination)
|
|
76
|
+
|
|
77
|
+
print(f"INFO: Created: {destination}")
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------
|
|
80
|
+
# Determine the selected framework.
|
|
81
|
+
# ---------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
frontend_config_path = project_root / "skeleton-config.json"
|
|
84
|
+
dependencies_path = project_root / "skeleton-dependencies.json"
|
|
85
|
+
|
|
86
|
+
if framework is not None:
|
|
87
|
+
selected_framework = framework
|
|
88
|
+
|
|
89
|
+
elif frontend_config_path.is_file():
|
|
90
|
+
existing_config = read_json(frontend_config_path)
|
|
91
|
+
selected_framework = existing_config.get("framework", "react")
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
not isinstance(selected_framework, str)
|
|
95
|
+
or selected_framework not in SUPPORTED_FRAMEWORKS
|
|
96
|
+
):
|
|
97
|
+
raise ValueError(
|
|
98
|
+
f"Invalid framework in {frontend_config_path}: "
|
|
99
|
+
f"{selected_framework!r}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
else:
|
|
103
|
+
selected_framework = "react"
|
|
104
|
+
|
|
105
|
+
profile = profiles_root / selected_framework
|
|
106
|
+
|
|
107
|
+
if not profile.is_dir():
|
|
108
|
+
raise FileNotFoundError(
|
|
109
|
+
f"Framework profile not found: {profile}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
profile_config_path = profile / "skeleton-config.json"
|
|
113
|
+
profile_dependencies_path = profile / "skeleton-dependencies.json"
|
|
114
|
+
|
|
115
|
+
for path in (profile_config_path, profile_dependencies_path):
|
|
116
|
+
if not path.is_file():
|
|
117
|
+
raise FileNotFoundError(
|
|
118
|
+
f"Required framework profile file not found: {path}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Read and validate both profile files before writing either.
|
|
122
|
+
frontend_config = read_json(profile_config_path)
|
|
123
|
+
dependencies = read_json(profile_dependencies_path)
|
|
124
|
+
|
|
125
|
+
# Ensure the selected profile identifies itself correctly.
|
|
126
|
+
frontend_config["framework"] = selected_framework
|
|
127
|
+
|
|
128
|
+
# ---------------------------------------------------------
|
|
129
|
+
# Generate the root-level framework configuration.
|
|
130
|
+
# ---------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
# Explicit selection resets both framework-managed files
|
|
133
|
+
# to the chosen profile defaults.
|
|
134
|
+
#
|
|
135
|
+
# Without an explicit selection, preserve existing files
|
|
136
|
+
# and create only files that are missing.
|
|
137
|
+
|
|
138
|
+
if framework is not None or not frontend_config_path.exists():
|
|
139
|
+
write_json(frontend_config_path, frontend_config)
|
|
140
|
+
print(f"INFO: Created/updated: {frontend_config_path}")
|
|
141
|
+
else:
|
|
142
|
+
print(f"INFO: Preserving existing file: {frontend_config_path}")
|
|
143
|
+
|
|
144
|
+
if framework is not None or not dependencies_path.exists():
|
|
145
|
+
write_json(dependencies_path, dependencies)
|
|
146
|
+
print(f"INFO: Created/updated: {dependencies_path}")
|
|
147
|
+
else:
|
|
148
|
+
print(f"INFO: Preserving existing file: {dependencies_path}")
|
|
149
|
+
|
|
150
|
+
print(f"INFO: Selected frontend framework: {selected_framework}")
|
|
151
|
+
print("INFO: Configuration complete")
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
def copy_template(source: Path, target: Path) -> None:
|
|
7
|
+
|
|
8
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
9
|
+
|
|
10
|
+
for item in source.iterdir():
|
|
11
|
+
|
|
12
|
+
destination = target / item.name
|
|
13
|
+
|
|
14
|
+
if destination.exists():
|
|
15
|
+
|
|
16
|
+
if item.is_dir() and destination.is_dir():
|
|
17
|
+
copy_template(item, destination)
|
|
18
|
+
|
|
19
|
+
continue
|
|
20
|
+
|
|
21
|
+
if item.is_dir():
|
|
22
|
+
shutil.copytree(item, destination)
|
|
23
|
+
|
|
24
|
+
else:
|
|
25
|
+
shutil.copy2(item, destination)
|
|
26
|
+
|
|
27
|
+
def _copy_profile_entry_point(
|
|
28
|
+
framework_root: Path,
|
|
29
|
+
project_root: Path,
|
|
30
|
+
frontend: str,
|
|
31
|
+
config: dict,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Copy the selected framework's entry point into the client."""
|
|
34
|
+
|
|
35
|
+
framework = config.get("framework")
|
|
36
|
+
|
|
37
|
+
profile_filenames = {
|
|
38
|
+
"react": "main.tsx",
|
|
39
|
+
"solid": "main.tsx",
|
|
40
|
+
"vue": "main.ts",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if framework not in profile_filenames:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"Unsupported frontend framework: {framework!r}"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
entry = config.get("entry")
|
|
49
|
+
if not isinstance(entry, dict):
|
|
50
|
+
raise ValueError(
|
|
51
|
+
"skeleton-config.json: 'entry' must be an object."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
entry_name = entry.get("main")
|
|
55
|
+
if not isinstance(entry_name, str) or not entry_name.strip():
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"skeleton-config.json: 'entry.main' must be "
|
|
58
|
+
"a non-empty string."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
source_name = profile_filenames[framework]
|
|
62
|
+
|
|
63
|
+
source = (
|
|
64
|
+
framework_root
|
|
65
|
+
/ "skeleton"
|
|
66
|
+
/ "infrastructure"
|
|
67
|
+
/ "bootstrap"
|
|
68
|
+
/ "src"
|
|
69
|
+
/ "templates"
|
|
70
|
+
/ "profiles"
|
|
71
|
+
/ framework
|
|
72
|
+
/ source_name
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if not source.is_file():
|
|
76
|
+
raise FileNotFoundError(
|
|
77
|
+
f"Frontend entry-point template not found: {source}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# entry.main is relative to the frontend source directory.
|
|
81
|
+
entry_path = Path(entry_name)
|
|
82
|
+
|
|
83
|
+
if entry_path.is_absolute() or ".." in entry_path.parts:
|
|
84
|
+
raise ValueError(
|
|
85
|
+
"skeleton-config.json: 'entry.main' must be a "
|
|
86
|
+
"relative path within the frontend source directory."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# If an extension is supplied, it must match the selected profile.
|
|
90
|
+
if entry_path.suffix and entry_path.suffix != source.suffix:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Entry point {entry_name!r} must use the "
|
|
93
|
+
f"{source.suffix} extension for {framework}."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if not entry_path.suffix:
|
|
97
|
+
entry_path = entry_path.with_suffix(source.suffix)
|
|
98
|
+
|
|
99
|
+
config_lib = config.get("lib")
|
|
100
|
+
if not isinstance(config_lib, dict):
|
|
101
|
+
raise ValueError(
|
|
102
|
+
"skeleton-config.json: 'lib' must be an object."
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
source_directory = config_lib.get("src", "src")
|
|
106
|
+
if (
|
|
107
|
+
not isinstance(source_directory, str)
|
|
108
|
+
or not source_directory.strip()
|
|
109
|
+
):
|
|
110
|
+
raise ValueError(
|
|
111
|
+
"skeleton-config.json: 'lib.src' must be "
|
|
112
|
+
"a non-empty relative path."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
source_path = Path(source_directory)
|
|
116
|
+
if source_path.is_absolute() or ".." in source_path.parts:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
"skeleton-config.json: 'lib.src' must be a "
|
|
119
|
+
"relative path within the frontend directory."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
frontend_root = project_root / frontend
|
|
123
|
+
if not frontend_root.is_dir():
|
|
124
|
+
raise FileNotFoundError(
|
|
125
|
+
f"Frontend directory not found: {frontend_root}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
destination_directory = frontend_root / source_path
|
|
129
|
+
destination = destination_directory / entry_path
|
|
130
|
+
|
|
131
|
+
# Preserve existing client code on repeated initialization.
|
|
132
|
+
if destination.exists():
|
|
133
|
+
print(
|
|
134
|
+
f"INFO: Preserving existing frontend entry point: "
|
|
135
|
+
f"{destination}"
|
|
136
|
+
)
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
destination_directory.mkdir(
|
|
140
|
+
parents=True,
|
|
141
|
+
exist_ok=True,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
shutil.copy2(source, destination)
|
|
145
|
+
|
|
146
|
+
print(
|
|
147
|
+
f"INFO: Copied {framework} entry point: {destination}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def _copy_profile_files(
|
|
151
|
+
framework_root: Path,
|
|
152
|
+
project_root: Path,
|
|
153
|
+
frontend: str,
|
|
154
|
+
config: dict,
|
|
155
|
+
) -> None:
|
|
156
|
+
"""Copy additional source files required by the selected framework."""
|
|
157
|
+
|
|
158
|
+
framework = config.get("framework")
|
|
159
|
+
|
|
160
|
+
profile_files = {
|
|
161
|
+
"react": (),
|
|
162
|
+
"solid": (),
|
|
163
|
+
"vue": (
|
|
164
|
+
"App.vue",
|
|
165
|
+
"vue.d.ts",
|
|
166
|
+
),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if framework not in profile_files:
|
|
170
|
+
raise ValueError(
|
|
171
|
+
f"Unsupported frontend framework: {framework!r}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if not profile_files[framework]:
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
config_lib = config.get("lib")
|
|
178
|
+
|
|
179
|
+
if not isinstance(config_lib, dict):
|
|
180
|
+
raise ValueError(
|
|
181
|
+
"skeleton-config.json: 'lib' must be an object."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
source_directory = config_lib.get("src", "src")
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
not isinstance(source_directory, str)
|
|
188
|
+
or not source_directory.strip()
|
|
189
|
+
):
|
|
190
|
+
raise ValueError(
|
|
191
|
+
"skeleton-config.json: 'lib.src' must be "
|
|
192
|
+
"a non-empty relative path."
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
source_path = Path(source_directory)
|
|
196
|
+
|
|
197
|
+
if source_path.is_absolute() or ".." in source_path.parts:
|
|
198
|
+
raise ValueError(
|
|
199
|
+
"skeleton-config.json: 'lib.src' must be a "
|
|
200
|
+
"relative path within the frontend directory."
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
profile = (
|
|
204
|
+
framework_root
|
|
205
|
+
/ "skeleton"
|
|
206
|
+
/ "infrastructure"
|
|
207
|
+
/ "bootstrap"
|
|
208
|
+
/ "src"
|
|
209
|
+
/ "templates"
|
|
210
|
+
/ "profiles"
|
|
211
|
+
/ framework
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
destination_directory = (
|
|
215
|
+
project_root
|
|
216
|
+
/ frontend
|
|
217
|
+
/ source_path
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
destination_directory.mkdir(
|
|
221
|
+
parents=True,
|
|
222
|
+
exist_ok=True,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
for filename in profile_files[framework]:
|
|
226
|
+
|
|
227
|
+
source = profile / filename
|
|
228
|
+
destination = destination_directory / filename
|
|
229
|
+
|
|
230
|
+
if not source.is_file():
|
|
231
|
+
raise FileNotFoundError(
|
|
232
|
+
f"Frontend profile template not found: {source}"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Preserve client files on repeated initialization.
|
|
236
|
+
if destination.exists():
|
|
237
|
+
print(
|
|
238
|
+
f"INFO: Preserving existing frontend file: "
|
|
239
|
+
f"{destination}"
|
|
240
|
+
)
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
shutil.copy2(
|
|
244
|
+
source,
|
|
245
|
+
destination,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
print(
|
|
249
|
+
f"INFO: Copied {framework} profile file: "
|
|
250
|
+
f"{destination}"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def copy_app_template(
|
|
254
|
+
source: Path,
|
|
255
|
+
target: Path,
|
|
256
|
+
frontend: str,
|
|
257
|
+
backend: str,
|
|
258
|
+
framework_root: Path,
|
|
259
|
+
) -> None:
|
|
260
|
+
|
|
261
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
262
|
+
|
|
263
|
+
directories = {
|
|
264
|
+
"frontend": frontend,
|
|
265
|
+
"backend": backend,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# ---------------------------------------------------------
|
|
269
|
+
# Copy the application templates.
|
|
270
|
+
# ---------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
for item in source.iterdir():
|
|
273
|
+
|
|
274
|
+
name = directories.get(item.name, item.name)
|
|
275
|
+
destination = target / name
|
|
276
|
+
|
|
277
|
+
if destination.exists():
|
|
278
|
+
|
|
279
|
+
if item.is_dir() and destination.is_dir():
|
|
280
|
+
copy_template(item, destination)
|
|
281
|
+
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
if item.is_dir():
|
|
285
|
+
shutil.copytree(item, destination)
|
|
286
|
+
|
|
287
|
+
else:
|
|
288
|
+
shutil.copy2(item, destination)
|
|
289
|
+
|
|
290
|
+
# ---------------------------------------------------------
|
|
291
|
+
# Read the selected frontend framework.
|
|
292
|
+
# ---------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
config_path = target / "skeleton-config.json"
|
|
295
|
+
|
|
296
|
+
if not config_path.is_file():
|
|
297
|
+
raise FileNotFoundError(
|
|
298
|
+
f"Skeleton configuration not found: {config_path}. "
|
|
299
|
+
"Run 'skeleton --config' first."
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
with config_path.open(
|
|
303
|
+
"r",
|
|
304
|
+
encoding="utf-8",
|
|
305
|
+
) as file:
|
|
306
|
+
config = json.load(file)
|
|
307
|
+
|
|
308
|
+
if not isinstance(config, dict):
|
|
309
|
+
raise ValueError(
|
|
310
|
+
f"Expected a JSON object: {config_path}"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
framework = config.get("framework")
|
|
314
|
+
|
|
315
|
+
supported_frameworks = {
|
|
316
|
+
"react",
|
|
317
|
+
"solid",
|
|
318
|
+
"vue",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if framework not in supported_frameworks:
|
|
322
|
+
raise ValueError(
|
|
323
|
+
f"Unsupported frontend framework in {config_path}: "
|
|
324
|
+
f"{framework!r}. Choose one of: "
|
|
325
|
+
f"{', '.join(sorted(supported_frameworks))}"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# ---------------------------------------------------------
|
|
329
|
+
# Locate the selected profile's TypeScript configuration.
|
|
330
|
+
# ---------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
profile_tsconfig = (
|
|
333
|
+
framework_root
|
|
334
|
+
/ "skeleton"
|
|
335
|
+
/ "infrastructure"
|
|
336
|
+
/ "bootstrap"
|
|
337
|
+
/ "src"
|
|
338
|
+
/ "templates"
|
|
339
|
+
/ "profiles"
|
|
340
|
+
/ framework
|
|
341
|
+
/ "tsconfig.json"
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if not profile_tsconfig.is_file():
|
|
345
|
+
raise FileNotFoundError(
|
|
346
|
+
f"TypeScript configuration not found for "
|
|
347
|
+
f"framework {framework!r}: {profile_tsconfig}"
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# ---------------------------------------------------------
|
|
351
|
+
# Copy tsconfig.json into the configured frontend directory.
|
|
352
|
+
# ---------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
frontend_root = target / frontend
|
|
355
|
+
|
|
356
|
+
if not frontend_root.is_dir():
|
|
357
|
+
raise FileNotFoundError(
|
|
358
|
+
f"Frontend directory not found: {frontend_root}"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
destination = frontend_root / "tsconfig.json"
|
|
362
|
+
|
|
363
|
+
shutil.copy2(
|
|
364
|
+
profile_tsconfig,
|
|
365
|
+
destination,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
print(
|
|
369
|
+
f"INFO: Copied {framework} TypeScript configuration: "
|
|
370
|
+
f"{destination}"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# ---------------------------------------------------------
|
|
374
|
+
# Copy the selected profile's entry point.
|
|
375
|
+
# ---------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
_copy_profile_entry_point(
|
|
378
|
+
framework_root=framework_root,
|
|
379
|
+
project_root=target,
|
|
380
|
+
frontend=frontend,
|
|
381
|
+
config=config,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# ---------------------------------------------------------
|
|
385
|
+
# Copy additional framework-specific source files.
|
|
386
|
+
# ---------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
_copy_profile_files(
|
|
389
|
+
framework_root=framework_root,
|
|
390
|
+
project_root=target,
|
|
391
|
+
frontend=frontend,
|
|
392
|
+
config=config,
|
|
393
|
+
)
|