skeleton-framework 0.1.2__tar.gz → 0.1.3__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.
Files changed (112) hide show
  1. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/PKG-INFO +19 -2
  2. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/README.md +18 -1
  3. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/pyproject.toml +1 -1
  4. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/cli.py +5 -0
  5. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/client.py +3 -1
  6. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/client/init.py +1 -0
  7. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/config.py +153 -0
  8. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/copy.py +279 -0
  9. {skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src → skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/react}/main.tsx +1 -1
  10. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/react/skeleton-config.json +30 -0
  11. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/react/skeleton-dependencies.json +21 -0
  12. {skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/templates/app/frontend → skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles}/skeleton-config.json +1 -1
  13. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/main.tsx +20 -0
  14. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/skeleton-config.json +30 -0
  15. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/skeleton-dependencies.json +22 -0
  16. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/solid/tsconfig.json +93 -0
  17. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/main.ts +20 -0
  18. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/skeleton-config.json +30 -0
  19. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/skeleton-dependencies.json +21 -0
  20. skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/vue/tsconfig.json +91 -0
  21. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/config.py +3 -3
  22. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/main.py +1 -1
  23. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/main.py +1 -1
  24. skeleton_framework-0.1.3/skeleton/packages/.skeleton/skeleton-cli-1.0.0.tgz +0 -0
  25. skeleton_framework-0.1.3/skeleton/packages/.skeleton/skeleton-webpack-1.0.0.tgz +0 -0
  26. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton_framework.egg-info/PKG-INFO +19 -2
  27. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton_framework.egg-info/SOURCES.txt +13 -3
  28. skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/config.py +0 -54
  29. skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/copy.py +0 -56
  30. skeleton_framework-0.1.2/skeleton/packages/.skeleton/skeleton-cli-1.0.0.tgz +0 -0
  31. skeleton_framework-0.1.2/skeleton/packages/.skeleton/skeleton-webpack-1.0.0.tgz +0 -0
  32. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/LICENSE +0 -0
  33. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/setup.cfg +0 -0
  34. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/__init__.py +0 -0
  35. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/__init__.py +0 -0
  36. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/__init__.py +0 -0
  37. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/__init__.py +0 -0
  38. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/__init__.py +0 -0
  39. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/client/__init__.py +0 -0
  40. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/client/install.py +0 -0
  41. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/dev/__init__.py +0 -0
  42. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/dev/build.py +0 -0
  43. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/dev/init.py +0 -0
  44. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/dev/install.py +0 -0
  45. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/commands/dev/pack.py +0 -0
  46. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/load.py +0 -0
  47. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/backend/requirements.txt +0 -0
  48. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/backend/routes.json +0 -0
  49. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/backend/src/__init__.py +0 -0
  50. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/backend/src/main.py +0 -0
  51. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/package.json +0 -0
  52. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/index.html +0 -0
  53. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/css/styles.css +0 -0
  54. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/img/skeleton.png +0 -0
  55. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/app/frontend/src/static/js/script.js +0 -0
  56. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/config/.gitignore +0 -0
  57. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-dependencies.json +0 -0
  58. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-layout.json +0 -0
  59. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-schemas.json +0 -0
  60. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/bootstrap/src/templates/config/skeleton-servers.json +0 -0
  61. {skeleton_framework-0.1.2/skeleton/infrastructure/bootstrap/src/templates/app/frontend → skeleton_framework-0.1.3/skeleton/infrastructure/bootstrap/src/templates/profiles/react}/tsconfig.json +0 -0
  62. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/__init__.py +0 -0
  63. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/config.json +0 -0
  64. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/__init__.py +0 -0
  65. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/constants.py +0 -0
  66. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/context.py +0 -0
  67. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/env.py +0 -0
  68. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/layout.py +0 -0
  69. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/loader.py +0 -0
  70. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/repository.py +0 -0
  71. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/common/src/utils.py +0 -0
  72. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/__init__.py +0 -0
  73. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/settings/proxy.json +0 -0
  74. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/settings/services.json +0 -0
  75. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/__init__.py +0 -0
  76. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/app.py +0 -0
  77. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/client.py +0 -0
  78. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/config.py +0 -0
  79. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/context.py +0 -0
  80. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/headers.py +0 -0
  81. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/lifespan.py +0 -0
  82. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/loader.py +0 -0
  83. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/logging.py +0 -0
  84. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/parser.py +0 -0
  85. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/registry.py +0 -0
  86. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/reverseproxy.py +0 -0
  87. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/router.py +0 -0
  88. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/schemas.py +0 -0
  89. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/service.py +0 -0
  90. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/utils.py +0 -0
  91. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/gateway/src/validation.py +0 -0
  92. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/layout.json +0 -0
  93. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/__init__.py +0 -0
  94. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/__init__.py +0 -0
  95. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/actions.py +0 -0
  96. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/browser.py +0 -0
  97. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/cmd.py +0 -0
  98. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/command.py +0 -0
  99. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/frontend.py +0 -0
  100. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/gateway.py +0 -0
  101. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/hook.py +0 -0
  102. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/packages.py +0 -0
  103. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/parser.py +0 -0
  104. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/process.py +0 -0
  105. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/services.py +0 -0
  106. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/utils.py +0 -0
  107. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/infrastructure/runners/src/validation.py +0 -0
  108. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton/root.py +0 -0
  109. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton_framework.egg-info/dependency_links.txt +0 -0
  110. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton_framework.egg-info/entry_points.txt +0 -0
  111. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/skeleton_framework.egg-info/requires.txt +0 -0
  112. {skeleton_framework-0.1.2 → skeleton_framework-0.1.3}/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.2
3
+ Version: 0.1.3
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.2-py3-none-any.whl
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.2-py3-none-any.whl
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.2"
7
+ version = "0.1.3"
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":
@@ -362,6 +362,7 @@ def init(
362
362
  target=project_root,
363
363
  frontend=layout["frontend"],
364
364
  backend=layout["backend"],
365
+ framework_root=framework_root,
365
366
  )
366
367
 
367
368
  # ---------------------------------------------------------
@@ -0,0 +1,153 @@
1
+ import json, shutil
2
+ from pathlib import Path
3
+
4
+ SUPPORTED_FRAMEWORKS = {"react", "solid"} # add 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
+
16
+ def write_json(path: Path, data: dict) -> None:
17
+ with path.open("w", encoding="utf-8") as file:
18
+ json.dump(data, file, indent=4)
19
+ file.write("\n")
20
+
21
+
22
+ def config(
23
+ framework_root: Path,
24
+ project_root: Path,
25
+ framework: str | None = None,
26
+ ) -> None:
27
+
28
+ templates_root = (
29
+ framework_root
30
+ / "skeleton"
31
+ / "infrastructure"
32
+ / "bootstrap"
33
+ / "src"
34
+ / "templates"
35
+ )
36
+
37
+ config_templates = templates_root / "config"
38
+ profiles_root = templates_root / "profiles"
39
+
40
+ if not config_templates.is_dir():
41
+ raise FileNotFoundError(
42
+ f"Configuration templates not found: {config_templates}"
43
+ )
44
+
45
+ if not profiles_root.is_dir():
46
+ raise FileNotFoundError(
47
+ f"Framework profiles not found: {profiles_root}"
48
+ )
49
+
50
+ if framework is not None:
51
+ framework = framework.strip().lower()
52
+
53
+ if framework not in SUPPORTED_FRAMEWORKS:
54
+ supported = ", ".join(sorted(SUPPORTED_FRAMEWORKS))
55
+ raise ValueError(
56
+ f"Unsupported frontend framework: {framework!r}. "
57
+ f"Choose one of: {supported}"
58
+ )
59
+
60
+ project_root = project_root.resolve()
61
+ project_root.mkdir(parents=True, exist_ok=True)
62
+
63
+ # ---------------------------------------------------------
64
+ # Copy shared project configuration.
65
+ # ---------------------------------------------------------
66
+
67
+ for template in config_templates.iterdir():
68
+ destination = project_root / template.name
69
+
70
+ if destination.exists():
71
+ print(f"INFO: Preserving existing file: {destination}")
72
+ continue
73
+
74
+ if template.is_dir():
75
+ shutil.copytree(template, destination)
76
+ else:
77
+ shutil.copy2(template, destination)
78
+
79
+ print(f"INFO: Created: {destination}")
80
+
81
+ # ---------------------------------------------------------
82
+ # Determine the selected framework.
83
+ # ---------------------------------------------------------
84
+
85
+ frontend_config_path = project_root / "skeleton-config.json"
86
+ dependencies_path = project_root / "skeleton-dependencies.json"
87
+
88
+ if framework is not None:
89
+ selected_framework = framework
90
+
91
+ elif frontend_config_path.is_file():
92
+ existing_config = read_json(frontend_config_path)
93
+ selected_framework = existing_config.get("framework", "react")
94
+
95
+ if (
96
+ not isinstance(selected_framework, str)
97
+ or selected_framework not in SUPPORTED_FRAMEWORKS
98
+ ):
99
+ raise ValueError(
100
+ f"Invalid framework in {frontend_config_path}: "
101
+ f"{selected_framework!r}"
102
+ )
103
+
104
+ else:
105
+ selected_framework = "react"
106
+
107
+ profile = profiles_root / selected_framework
108
+
109
+ if not profile.is_dir():
110
+ raise FileNotFoundError(
111
+ f"Framework profile not found: {profile}"
112
+ )
113
+
114
+ profile_config_path = profile / "skeleton-config.json"
115
+ profile_dependencies_path = profile / "skeleton-dependencies.json"
116
+
117
+ for path in (profile_config_path, profile_dependencies_path):
118
+ if not path.is_file():
119
+ raise FileNotFoundError(
120
+ f"Required framework profile file not found: {path}"
121
+ )
122
+
123
+ # Read and validate both profile files before writing either.
124
+ frontend_config = read_json(profile_config_path)
125
+ dependencies = read_json(profile_dependencies_path)
126
+
127
+ # Ensure the selected profile identifies itself correctly.
128
+ frontend_config["framework"] = selected_framework
129
+
130
+ # ---------------------------------------------------------
131
+ # Generate the root-level framework configuration.
132
+ # ---------------------------------------------------------
133
+
134
+ # Explicit selection resets both framework-managed files
135
+ # to the chosen profile defaults.
136
+ #
137
+ # Without an explicit selection, preserve existing files
138
+ # and create only files that are missing.
139
+
140
+ if framework is not None or not frontend_config_path.exists():
141
+ write_json(frontend_config_path, frontend_config)
142
+ print(f"INFO: Created/updated: {frontend_config_path}")
143
+ else:
144
+ print(f"INFO: Preserving existing file: {frontend_config_path}")
145
+
146
+ if framework is not None or not dependencies_path.exists():
147
+ write_json(dependencies_path, dependencies)
148
+ print(f"INFO: Created/updated: {dependencies_path}")
149
+ else:
150
+ print(f"INFO: Preserving existing file: {dependencies_path}")
151
+
152
+ print(f"INFO: Selected frontend framework: {selected_framework}")
153
+ print("INFO: Configuration complete")
@@ -0,0 +1,279 @@
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_app_template(
151
+ source: Path,
152
+ target: Path,
153
+ frontend: str,
154
+ backend: str,
155
+ framework_root: Path,
156
+ ) -> None:
157
+
158
+ target.mkdir(parents=True, exist_ok=True)
159
+
160
+ directories = {
161
+ "frontend": frontend,
162
+ "backend": backend,
163
+ }
164
+
165
+ # ---------------------------------------------------------
166
+ # Copy the application templates.
167
+ # ---------------------------------------------------------
168
+
169
+ for item in source.iterdir():
170
+
171
+ name = directories.get(item.name, item.name)
172
+ destination = target / name
173
+
174
+ if destination.exists():
175
+
176
+ if item.is_dir() and destination.is_dir():
177
+ copy_template(item, destination)
178
+
179
+ continue
180
+
181
+ if item.is_dir():
182
+ shutil.copytree(item, destination)
183
+
184
+ else:
185
+ shutil.copy2(item, destination)
186
+
187
+ # ---------------------------------------------------------
188
+ # Read the selected frontend framework.
189
+ # ---------------------------------------------------------
190
+
191
+ config_path = target / "skeleton-config.json"
192
+
193
+ if not config_path.is_file():
194
+ raise FileNotFoundError(
195
+ f"Skeleton configuration not found: {config_path}. "
196
+ "Run 'skeleton --config' first."
197
+ )
198
+
199
+ with config_path.open(
200
+ "r",
201
+ encoding="utf-8",
202
+ ) as file:
203
+ config = json.load(file)
204
+
205
+ if not isinstance(config, dict):
206
+ raise ValueError(
207
+ f"Expected a JSON object: {config_path}"
208
+ )
209
+
210
+ framework = config.get("framework")
211
+
212
+ supported_frameworks = {
213
+ "react",
214
+ "solid",
215
+ "vue",
216
+ }
217
+
218
+ if framework not in supported_frameworks:
219
+ raise ValueError(
220
+ f"Unsupported frontend framework in {config_path}: "
221
+ f"{framework!r}. Choose one of: "
222
+ f"{', '.join(sorted(supported_frameworks))}"
223
+ )
224
+
225
+ # ---------------------------------------------------------
226
+ # Locate the selected profile's TypeScript configuration.
227
+ # ---------------------------------------------------------
228
+
229
+ profile_tsconfig = (
230
+ framework_root
231
+ / "skeleton"
232
+ / "infrastructure"
233
+ / "bootstrap"
234
+ / "src"
235
+ / "templates"
236
+ / "profiles"
237
+ / framework
238
+ / "tsconfig.json"
239
+ )
240
+
241
+ if not profile_tsconfig.is_file():
242
+ raise FileNotFoundError(
243
+ f"TypeScript configuration not found for "
244
+ f"framework {framework!r}: {profile_tsconfig}"
245
+ )
246
+
247
+ # ---------------------------------------------------------
248
+ # Copy tsconfig.json into the configured frontend directory.
249
+ # ---------------------------------------------------------
250
+
251
+ frontend_root = target / frontend
252
+
253
+ if not frontend_root.is_dir():
254
+ raise FileNotFoundError(
255
+ f"Frontend directory not found: {frontend_root}"
256
+ )
257
+
258
+ destination = frontend_root / "tsconfig.json"
259
+
260
+ shutil.copy2(
261
+ profile_tsconfig,
262
+ destination,
263
+ )
264
+
265
+ print(
266
+ f"INFO: Copied {framework} TypeScript configuration: "
267
+ f"{destination}"
268
+ )
269
+
270
+ # ---------------------------------------------------------
271
+ # Copy the selected profile's entry point.
272
+ # ---------------------------------------------------------
273
+
274
+ _copy_profile_entry_point(
275
+ framework_root=framework_root,
276
+ project_root=target,
277
+ frontend=frontend,
278
+ config=config,
279
+ )
@@ -3,7 +3,7 @@ import { createRoot } from "react-dom/client";
3
3
  const body = document.querySelector("body");
4
4
 
5
5
  if (!body) {
6
- throw new Error("Body element not found");
6
+ throw new Error("<body> element not found");
7
7
  }
8
8
 
9
9
  const root = document.createElement("div");
@@ -0,0 +1,30 @@
1
+ {
2
+ "framework": "react",
3
+ "entry": {
4
+ "main": "main",
5
+ "index": "index"
6
+ },
7
+ "lib": {
8
+ "src": "src",
9
+ "dist": "dist",
10
+ "static": "static"
11
+ },
12
+ "devServer": {
13
+ "open": false,
14
+ "historyApiFallback": true,
15
+ "host": "127.0.0.1",
16
+ "port": 80,
17
+ "hot": true,
18
+ "overlay": true
19
+ },
20
+ "proxy": {
21
+ "on": false,
22
+ "host": "127.0.0.1",
23
+ "port": 8888,
24
+ "changeOrigin": true,
25
+ "secure": false
26
+ },
27
+ "tsconfig": {
28
+ "configFile": "tsconfig.json"
29
+ }
30
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "frontend": {
3
+ "packages": {
4
+ "dependencies": {
5
+ "react": "^19.3.1",
6
+ "react-dom": "^19.3.1"
7
+ },
8
+ "devDependencies": {
9
+ "@types/react": "^19.3.0",
10
+ "@types/react-dom": "^19.3.0"
11
+ }
12
+ }
13
+ },
14
+ "backend": {
15
+ "requirements": {
16
+ "uvicorn": ">=0.34,<1.0",
17
+ "pydantic": ">=2.10,<3.0",
18
+ "sqlalchemy": ">=2.0,<2.1"
19
+ }
20
+ }
21
+ }
@@ -24,6 +24,6 @@
24
24
  "secure": false
25
25
  },
26
26
  "tsconfig": {
27
- "configFile": "./tsconfig.json"
27
+ "configFile": "tsconfig.json"
28
28
  }
29
29
  }
@@ -0,0 +1,20 @@
1
+ import { render } from "solid-js/web";
2
+
3
+ const body = document.querySelector("body");
4
+
5
+ if (!body) {
6
+ throw new Error("<body> element not found");
7
+ }
8
+
9
+ const root = document.createElement("div");
10
+
11
+ body.appendChild(root);
12
+
13
+ const App = () => (
14
+ <h1>Skeleton + FastAPI + SolidJS + Webpack</h1>
15
+ );
16
+
17
+ render(
18
+ () => <App />,
19
+ root
20
+ );
@@ -0,0 +1,30 @@
1
+ {
2
+ "framework": "solid",
3
+ "entry": {
4
+ "main": "main",
5
+ "index": "index"
6
+ },
7
+ "lib": {
8
+ "src": "src",
9
+ "dist": "dist",
10
+ "static": "static"
11
+ },
12
+ "devServer": {
13
+ "open": false,
14
+ "historyApiFallback": true,
15
+ "host": "127.0.0.1",
16
+ "port": 80,
17
+ "hot": true,
18
+ "overlay": true
19
+ },
20
+ "proxy": {
21
+ "on": false,
22
+ "host": "127.0.0.1",
23
+ "port": 8888,
24
+ "changeOrigin": true,
25
+ "secure": false
26
+ },
27
+ "tsconfig": {
28
+ "configFile": "tsconfig.json"
29
+ }
30
+ }