litestar-vite 0.1.1__py3-none-any.whl → 0.15.0rc2__py3-none-any.whl
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.
- litestar_vite/__init__.py +54 -4
- litestar_vite/__metadata__.py +12 -7
- litestar_vite/_codegen/__init__.py +26 -0
- litestar_vite/_codegen/inertia.py +407 -0
- litestar_vite/_codegen/openapi.py +233 -0
- litestar_vite/_codegen/routes.py +653 -0
- litestar_vite/_codegen/ts.py +235 -0
- litestar_vite/_handler/__init__.py +8 -0
- litestar_vite/_handler/app.py +524 -0
- litestar_vite/_handler/routing.py +130 -0
- litestar_vite/cli.py +1147 -10
- litestar_vite/codegen.py +39 -0
- litestar_vite/commands.py +79 -0
- litestar_vite/config.py +1594 -70
- litestar_vite/deploy.py +355 -0
- litestar_vite/doctor.py +1179 -0
- litestar_vite/exceptions.py +78 -0
- litestar_vite/executor.py +316 -0
- litestar_vite/handler.py +9 -0
- litestar_vite/html_transform.py +426 -0
- litestar_vite/inertia/__init__.py +53 -0
- litestar_vite/inertia/_utils.py +114 -0
- litestar_vite/inertia/exception_handler.py +172 -0
- litestar_vite/inertia/helpers.py +1043 -0
- litestar_vite/inertia/middleware.py +54 -0
- litestar_vite/inertia/plugin.py +133 -0
- litestar_vite/inertia/request.py +286 -0
- litestar_vite/inertia/response.py +706 -0
- litestar_vite/inertia/types.py +316 -0
- litestar_vite/loader.py +462 -121
- litestar_vite/plugin.py +2160 -21
- litestar_vite/py.typed +0 -0
- litestar_vite/scaffolding/__init__.py +20 -0
- litestar_vite/scaffolding/generator.py +270 -0
- litestar_vite/scaffolding/templates.py +437 -0
- litestar_vite/templates/__init__.py +0 -0
- litestar_vite/templates/addons/tailwindcss/tailwind.css.j2 +1 -0
- litestar_vite/templates/angular/index.html.j2 +12 -0
- litestar_vite/templates/angular/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular/package.json.j2 +35 -0
- litestar_vite/templates/angular/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular/src/main.ts.j2 +9 -0
- litestar_vite/templates/angular/src/styles.css.j2 +9 -0
- litestar_vite/templates/angular/tsconfig.app.json.j2 +34 -0
- litestar_vite/templates/angular/tsconfig.json.j2 +20 -0
- litestar_vite/templates/angular/vite.config.ts.j2 +21 -0
- litestar_vite/templates/angular-cli/.postcssrc.json.j2 +5 -0
- litestar_vite/templates/angular-cli/angular.json.j2 +36 -0
- litestar_vite/templates/angular-cli/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/angular-cli/package.json.j2 +27 -0
- litestar_vite/templates/angular-cli/proxy.conf.json.j2 +18 -0
- litestar_vite/templates/angular-cli/src/app/app.component.css.j2 +3 -0
- litestar_vite/templates/angular-cli/src/app/app.component.html.j2 +1 -0
- litestar_vite/templates/angular-cli/src/app/app.component.ts.j2 +9 -0
- litestar_vite/templates/angular-cli/src/app/app.config.ts.j2 +5 -0
- litestar_vite/templates/angular-cli/src/index.html.j2 +13 -0
- litestar_vite/templates/angular-cli/src/main.ts.j2 +6 -0
- litestar_vite/templates/angular-cli/src/styles.css.j2 +10 -0
- litestar_vite/templates/angular-cli/tailwind.config.js.j2 +4 -0
- litestar_vite/templates/angular-cli/tsconfig.app.json.j2 +16 -0
- litestar_vite/templates/angular-cli/tsconfig.json.j2 +26 -0
- litestar_vite/templates/angular-cli/tsconfig.spec.json.j2 +9 -0
- litestar_vite/templates/astro/astro.config.mjs.j2 +28 -0
- litestar_vite/templates/astro/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/astro/src/layouts/Layout.astro.j2 +63 -0
- litestar_vite/templates/astro/src/pages/index.astro.j2 +36 -0
- litestar_vite/templates/astro/src/styles/global.css.j2 +1 -0
- litestar_vite/templates/base/.gitignore.j2 +42 -0
- litestar_vite/templates/base/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/base/package.json.j2 +38 -0
- litestar_vite/templates/base/resources/vite-env.d.ts.j2 +1 -0
- litestar_vite/templates/base/tsconfig.json.j2 +37 -0
- litestar_vite/templates/htmx/src/main.js.j2 +8 -0
- litestar_vite/templates/htmx/templates/base.html.j2.j2 +56 -0
- litestar_vite/templates/htmx/templates/index.html.j2.j2 +13 -0
- litestar_vite/templates/htmx/vite.config.ts.j2 +40 -0
- litestar_vite/templates/nuxt/app.vue.j2 +29 -0
- litestar_vite/templates/nuxt/composables/useApi.ts.j2 +33 -0
- litestar_vite/templates/nuxt/nuxt.config.ts.j2 +31 -0
- litestar_vite/templates/nuxt/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/nuxt/pages/index.vue.j2 +54 -0
- litestar_vite/templates/react/index.html.j2 +13 -0
- litestar_vite/templates/react/src/App.css.j2 +56 -0
- litestar_vite/templates/react/src/App.tsx.j2 +19 -0
- litestar_vite/templates/react/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-inertia/index.html.j2 +14 -0
- litestar_vite/templates/react-inertia/package.json.j2 +46 -0
- litestar_vite/templates/react-inertia/resources/App.css.j2 +68 -0
- litestar_vite/templates/react-inertia/resources/main.tsx.j2 +17 -0
- litestar_vite/templates/react-inertia/resources/pages/Home.tsx.j2 +18 -0
- litestar_vite/templates/react-inertia/resources/ssr.tsx.j2 +19 -0
- litestar_vite/templates/react-inertia/vite.config.ts.j2 +59 -0
- litestar_vite/templates/react-router/index.html.j2 +12 -0
- litestar_vite/templates/react-router/src/App.css.j2 +17 -0
- litestar_vite/templates/react-router/src/App.tsx.j2 +7 -0
- litestar_vite/templates/react-router/src/main.tsx.j2 +10 -0
- litestar_vite/templates/react-router/vite.config.ts.j2 +39 -0
- litestar_vite/templates/react-tanstack/index.html.j2 +12 -0
- litestar_vite/templates/react-tanstack/openapi-ts.config.ts.j2 +18 -0
- litestar_vite/templates/react-tanstack/src/App.css.j2 +17 -0
- litestar_vite/templates/react-tanstack/src/main.tsx.j2 +21 -0
- litestar_vite/templates/react-tanstack/src/routeTree.gen.ts.j2 +7 -0
- litestar_vite/templates/react-tanstack/src/routes/__root.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/books.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/src/routes/index.tsx.j2 +9 -0
- litestar_vite/templates/react-tanstack/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte/index.html.j2 +13 -0
- litestar_vite/templates/svelte/src/App.svelte.j2 +30 -0
- litestar_vite/templates/svelte/src/app.css.j2 +45 -0
- litestar_vite/templates/svelte/src/main.ts.j2 +8 -0
- litestar_vite/templates/svelte/src/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte/vite.config.ts.j2 +39 -0
- litestar_vite/templates/svelte-inertia/index.html.j2 +14 -0
- litestar_vite/templates/svelte-inertia/resources/app.css.j2 +21 -0
- litestar_vite/templates/svelte-inertia/resources/main.ts.j2 +11 -0
- litestar_vite/templates/svelte-inertia/resources/pages/Home.svelte.j2 +43 -0
- litestar_vite/templates/svelte-inertia/resources/vite-env.d.ts.j2 +2 -0
- litestar_vite/templates/svelte-inertia/svelte.config.js.j2 +5 -0
- litestar_vite/templates/svelte-inertia/vite.config.ts.j2 +37 -0
- litestar_vite/templates/sveltekit/openapi-ts.config.ts.j2 +15 -0
- litestar_vite/templates/sveltekit/src/app.css.j2 +40 -0
- litestar_vite/templates/sveltekit/src/app.html.j2 +12 -0
- litestar_vite/templates/sveltekit/src/hooks.server.ts.j2 +55 -0
- litestar_vite/templates/sveltekit/src/routes/+layout.svelte.j2 +12 -0
- litestar_vite/templates/sveltekit/src/routes/+page.svelte.j2 +34 -0
- litestar_vite/templates/sveltekit/svelte.config.js.j2 +12 -0
- litestar_vite/templates/sveltekit/tsconfig.json.j2 +14 -0
- litestar_vite/templates/sveltekit/vite.config.ts.j2 +31 -0
- litestar_vite/templates/vue/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue/index.html.j2 +13 -0
- litestar_vite/templates/vue/src/App.vue.j2 +28 -0
- litestar_vite/templates/vue/src/main.ts.j2 +5 -0
- litestar_vite/templates/vue/src/style.css.j2 +45 -0
- litestar_vite/templates/vue/vite.config.ts.j2 +39 -0
- litestar_vite/templates/vue-inertia/env.d.ts.j2 +7 -0
- litestar_vite/templates/vue-inertia/index.html.j2 +14 -0
- litestar_vite/templates/vue-inertia/package.json.j2 +49 -0
- litestar_vite/templates/vue-inertia/resources/main.ts.j2 +18 -0
- litestar_vite/templates/vue-inertia/resources/pages/Home.vue.j2 +22 -0
- litestar_vite/templates/vue-inertia/resources/ssr.ts.j2 +21 -0
- litestar_vite/templates/vue-inertia/resources/style.css.j2 +21 -0
- litestar_vite/templates/vue-inertia/vite.config.ts.j2 +59 -0
- litestar_vite-0.15.0rc2.dist-info/METADATA +230 -0
- litestar_vite-0.15.0rc2.dist-info/RECORD +151 -0
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0rc2.dist-info}/WHEEL +1 -1
- litestar_vite/template_engine.py +0 -103
- litestar_vite-0.1.1.dist-info/METADATA +0 -68
- litestar_vite-0.1.1.dist-info/RECORD +0 -11
- {litestar_vite-0.1.1.dist-info → litestar_vite-0.15.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""TypeScript conversion helpers for code generation."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import PurePosixPath
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
_PATH_PARAM_TYPE_PATTERN = re.compile(r"\{([^:}]+):[^}]+\}")
|
|
8
|
+
|
|
9
|
+
_OPENAPI_STRING_FORMAT_TO_TS_ALIAS: dict[str, str] = {
|
|
10
|
+
"uuid": "UUID",
|
|
11
|
+
"date-time": "DateTime",
|
|
12
|
+
"date": "DateOnly",
|
|
13
|
+
"time": "TimeOnly",
|
|
14
|
+
"duration": "Duration",
|
|
15
|
+
"email": "Email",
|
|
16
|
+
"idn-email": "Email",
|
|
17
|
+
"uri": "URI",
|
|
18
|
+
"url": "URI",
|
|
19
|
+
"iri": "URI",
|
|
20
|
+
"iri-reference": "URI",
|
|
21
|
+
"uri-reference": "URI",
|
|
22
|
+
"uri-template": "URI",
|
|
23
|
+
"ipv4": "IPv4",
|
|
24
|
+
"ipv6": "IPv6",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def normalize_path(path: str) -> str:
|
|
29
|
+
"""Normalize route path to use {param} syntax.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Normalized path string.
|
|
33
|
+
"""
|
|
34
|
+
if not path or path == "/":
|
|
35
|
+
return path
|
|
36
|
+
return _PATH_PARAM_TYPE_PATTERN.sub(r"{\1}", str(PurePosixPath(path)))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def ts_type_from_openapi(schema_dict: dict[str, Any]) -> str:
|
|
40
|
+
"""Convert an OpenAPI schema dict to a TypeScript type string.
|
|
41
|
+
|
|
42
|
+
This function is intentionally lightweight and mirrors the historical
|
|
43
|
+
behavior used in this project's unit tests (OpenAPI 3.1 union types,
|
|
44
|
+
oneOf nullable patterns, etc.). It is not a full OpenAPI-to-TypeScript
|
|
45
|
+
compiler.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A TypeScript type expression string.
|
|
49
|
+
"""
|
|
50
|
+
if not schema_dict:
|
|
51
|
+
return "any"
|
|
52
|
+
|
|
53
|
+
ref = schema_dict.get("$ref")
|
|
54
|
+
if isinstance(ref, str) and ref:
|
|
55
|
+
return ref.split("/")[-1]
|
|
56
|
+
|
|
57
|
+
if "anyOf" in schema_dict and isinstance(schema_dict["anyOf"], list) and schema_dict["anyOf"]:
|
|
58
|
+
schemas = cast("list[Any]", schema_dict["anyOf"])
|
|
59
|
+
union = {_ts_type_from_subschema(s) for s in schemas}
|
|
60
|
+
return _join_union(union)
|
|
61
|
+
|
|
62
|
+
result = "any"
|
|
63
|
+
match schema_dict:
|
|
64
|
+
case {"const": const} if const is not None:
|
|
65
|
+
result = "any" if const is False else _ts_literal(const)
|
|
66
|
+
case {"enum": enum} if isinstance(enum, list) and enum:
|
|
67
|
+
enum_values = cast("list[Any]", enum)
|
|
68
|
+
result = " | ".join(_ts_literal(v) for v in enum_values)
|
|
69
|
+
case {"oneOf": one_of} if isinstance(one_of, list) and one_of:
|
|
70
|
+
schemas = cast("list[Any]", one_of)
|
|
71
|
+
union = {_ts_type_from_subschema(s) for s in schemas}
|
|
72
|
+
result = _join_union(union)
|
|
73
|
+
case {"allOf": all_of} if isinstance(all_of, list) and all_of:
|
|
74
|
+
schemas = cast("list[Any]", all_of)
|
|
75
|
+
parts = [_wrap_union_for_intersection(_ts_type_from_subschema(s)) for s in schemas]
|
|
76
|
+
parts = [p for p in parts if p and p != "any"]
|
|
77
|
+
result = " & ".join(parts) if parts else "any"
|
|
78
|
+
case {"type": list()}:
|
|
79
|
+
type_entries_list: list[Any] = schema_dict["type"]
|
|
80
|
+
parts = [_ts_type_from_openapi_type_entry(t, schema_dict) for t in type_entries_list if isinstance(t, str)]
|
|
81
|
+
result = _join_union(set(parts)) if parts else "any"
|
|
82
|
+
case {"type": str() as schema_type}:
|
|
83
|
+
result = _ts_type_from_openapi_type_entry(schema_type, schema_dict)
|
|
84
|
+
case _:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def python_type_to_typescript(py_type: str, *, fallback: str = "unknown") -> tuple[str, bool]:
|
|
91
|
+
"""Convert a Python typing string representation into a TS type and optionality flag.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
A tuple of the TypeScript type string and a boolean indicating if the type is optional.
|
|
95
|
+
"""
|
|
96
|
+
if not py_type:
|
|
97
|
+
return fallback, False
|
|
98
|
+
|
|
99
|
+
normalized = py_type.replace("typing.", "").replace("types.", "")
|
|
100
|
+
optional = "None" in normalized or "NoneType" in normalized or "Optional[" in normalized
|
|
101
|
+
|
|
102
|
+
normalized = normalized.replace("NoneType", "None").replace("None", "null")
|
|
103
|
+
|
|
104
|
+
mapping: dict[str, str] = {
|
|
105
|
+
"str": "string",
|
|
106
|
+
"int": "number",
|
|
107
|
+
"float": "number",
|
|
108
|
+
"bool": "boolean",
|
|
109
|
+
"dict": "Record<string, unknown>",
|
|
110
|
+
"Dict": "Record<string, unknown>",
|
|
111
|
+
"list": f"{fallback}[]",
|
|
112
|
+
"List": f"{fallback}[]",
|
|
113
|
+
"tuple": f"{fallback}[]",
|
|
114
|
+
"Tuple": f"{fallback}[]",
|
|
115
|
+
"set": f"{fallback}[]",
|
|
116
|
+
"Set": f"{fallback}[]",
|
|
117
|
+
"Any": fallback,
|
|
118
|
+
"unknown": "unknown",
|
|
119
|
+
"null": "null",
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for k, v in mapping.items():
|
|
123
|
+
if normalized == k:
|
|
124
|
+
return v, optional
|
|
125
|
+
|
|
126
|
+
return normalized, optional
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def collect_ref_names(schema_dict: Any) -> set[str]:
|
|
130
|
+
"""Collect referenced component names from an OpenAPI schema dict.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
A set of referenced component names.
|
|
134
|
+
"""
|
|
135
|
+
refs: set[str] = set()
|
|
136
|
+
if isinstance(schema_dict, dict):
|
|
137
|
+
schema_dict_t = cast("dict[str, Any]", schema_dict)
|
|
138
|
+
ref_any = schema_dict_t.get("$ref")
|
|
139
|
+
if isinstance(ref_any, str) and ref_any.startswith("#/components/schemas/"):
|
|
140
|
+
refs.add(ref_any.split("/")[-1])
|
|
141
|
+
for v in schema_dict_t.values():
|
|
142
|
+
refs.update(collect_ref_names(v))
|
|
143
|
+
elif isinstance(schema_dict, list):
|
|
144
|
+
for item in cast("list[Any]", schema_dict):
|
|
145
|
+
refs.update(collect_ref_names(item))
|
|
146
|
+
return refs
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _ts_type_from_subschema(schema: Any) -> str:
|
|
150
|
+
if isinstance(schema, dict):
|
|
151
|
+
return ts_type_from_openapi(cast("dict[str, Any]", schema))
|
|
152
|
+
return "any"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _ts_type_from_openapi_type_entry(type_name: str, schema_dict: dict[str, Any]) -> str:
|
|
156
|
+
primitive_types: dict[str, str] = {
|
|
157
|
+
"string": "string",
|
|
158
|
+
"integer": "number",
|
|
159
|
+
"number": "number",
|
|
160
|
+
"boolean": "boolean",
|
|
161
|
+
"null": "null",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
result = primitive_types.get(type_name, "any")
|
|
165
|
+
if type_name == "string":
|
|
166
|
+
fmt = schema_dict.get("format")
|
|
167
|
+
if isinstance(fmt, str) and fmt:
|
|
168
|
+
result = _OPENAPI_STRING_FORMAT_TO_TS_ALIAS.get(fmt, result)
|
|
169
|
+
if type_name == "array":
|
|
170
|
+
items = schema_dict.get("items")
|
|
171
|
+
item_type = _ts_type_from_subschema(items) if isinstance(items, dict) else "unknown"
|
|
172
|
+
result = f"{_wrap_for_array(item_type)}[]"
|
|
173
|
+
elif type_name == "object":
|
|
174
|
+
properties = schema_dict.get("properties")
|
|
175
|
+
if not isinstance(properties, dict) or not properties:
|
|
176
|
+
result = "{}"
|
|
177
|
+
else:
|
|
178
|
+
required_list = schema_dict.get("required")
|
|
179
|
+
required: set[str] = set()
|
|
180
|
+
if isinstance(required_list, list):
|
|
181
|
+
required = {v for v in cast("list[Any]", required_list) if isinstance(v, str)}
|
|
182
|
+
|
|
183
|
+
lines: list[str] = ["{"]
|
|
184
|
+
for name, prop_schema in cast("dict[str, Any]", properties).items():
|
|
185
|
+
ts_type = _ts_type_from_subschema(prop_schema)
|
|
186
|
+
optional = "" if name in required else "?"
|
|
187
|
+
lines.append(f" {name}{optional}: {ts_type};")
|
|
188
|
+
lines.append("}")
|
|
189
|
+
result = "\n".join(lines)
|
|
190
|
+
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _wrap_for_array(type_expr: str) -> str:
|
|
195
|
+
expr = type_expr.strip()
|
|
196
|
+
if not expr:
|
|
197
|
+
return "unknown"
|
|
198
|
+
if expr.startswith("(") and expr.endswith(")"):
|
|
199
|
+
return expr
|
|
200
|
+
# Parenthesize unions/intersections so `(A | B)[]` / `(A & B)[]` is emitted correctly.
|
|
201
|
+
if " | " in expr or (" & " in expr and not expr.startswith("{")):
|
|
202
|
+
return f"({expr})"
|
|
203
|
+
return expr
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _wrap_union_for_intersection(type_expr: str) -> str:
|
|
207
|
+
expr = type_expr.strip()
|
|
208
|
+
if not expr:
|
|
209
|
+
return "any"
|
|
210
|
+
if expr.startswith("(") and expr.endswith(")"):
|
|
211
|
+
return expr
|
|
212
|
+
if " | " in expr:
|
|
213
|
+
return f"({expr})"
|
|
214
|
+
return expr
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _join_union(types: set[str]) -> str:
|
|
218
|
+
if not types:
|
|
219
|
+
return "any"
|
|
220
|
+
if len(types) == 1:
|
|
221
|
+
return next(iter(types))
|
|
222
|
+
return " | ".join(sorted(types))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _ts_literal(value: Any) -> str:
|
|
226
|
+
if value is None:
|
|
227
|
+
return "null"
|
|
228
|
+
if isinstance(value, bool):
|
|
229
|
+
return "true" if value else "false"
|
|
230
|
+
if isinstance(value, int | float):
|
|
231
|
+
return str(value)
|
|
232
|
+
if isinstance(value, str):
|
|
233
|
+
escaped = value.replace("\\\\", "\\\\\\\\").replace('"', '\\"')
|
|
234
|
+
return f'"{escaped}"'
|
|
235
|
+
return "any"
|