instaui 0.1.0__tar.gz → 0.1.2__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.
- {instaui-0.1.0 → instaui-0.1.2}/PKG-INFO +1 -1
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/component.py +1 -1
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/element.py +37 -17
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/checkbox.py +0 -7
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/slot.py +2 -3
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/vfor.py +1 -1
- instaui-0.1.2/instaui/dependencies/component_dependency.py +16 -0
- instaui-0.1.2/instaui/dependencies/plugin_dependency.py +28 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/debug_mode_router.py +0 -1
- instaui-0.1.2/instaui/fastapi_server/dependency_router.py +21 -0
- instaui-0.1.2/instaui/fastapi_server/resource.py +35 -0
- instaui-0.1.2/instaui/fastapi_server/server.py +270 -0
- instaui-0.1.2/instaui/html_tools.py +45 -0
- instaui-0.1.2/instaui/page_info.py +13 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/_app.py +9 -20
- instaui-0.1.2/instaui/runtime/resource.py +52 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_file_base_utils.py +47 -42
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_route_model.py +3 -25
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/templates/page_routes +1 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.esm-browser.prod.js +3663 -3663
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.iife.js +29 -29
- instaui-0.1.2/instaui/static/templates/web.html +74 -0
- instaui-0.1.2/instaui/static/templates/zero.html +71 -0
- instaui-0.1.2/instaui/systems/file_system.py +8 -0
- instaui-0.1.2/instaui/template/web_template.py +49 -0
- instaui-0.1.2/instaui/template/zero_template.py +109 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui/__init__.py +2 -5
- instaui-0.1.2/instaui/ui_functions/ui_page.py +16 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/path_var.py +2 -1
- instaui-0.1.2/instaui/zero/func.py +111 -0
- instaui-0.1.2/instaui/zero/scope.py +20 -0
- {instaui-0.1.0 → instaui-0.1.2}/pyproject.toml +1 -1
- instaui-0.1.0/instaui/dependencies/__init__.py +0 -15
- instaui-0.1.0/instaui/dependencies/component_registrar.py +0 -82
- instaui-0.1.0/instaui/dependencies/installer.py +0 -5
- instaui-0.1.0/instaui/fastapi_server/config_router.py +0 -60
- instaui-0.1.0/instaui/fastapi_server/server.py +0 -246
- instaui-0.1.0/instaui/html_tools.py +0 -139
- instaui-0.1.0/instaui/page_info.py +0 -23
- instaui-0.1.0/instaui/runtime/resource.py +0 -87
- instaui-0.1.0/instaui/static/templates/web.html +0 -118
- instaui-0.1.0/instaui/static/templates/zero.html +0 -55
- instaui-0.1.0/instaui/systems/file_system.py +0 -17
- instaui-0.1.0/instaui/template/web_template.py +0 -55
- instaui-0.1.0/instaui/template/zero_template.py +0 -24
- instaui-0.1.0/instaui/ui_functions/ui_page.py +0 -31
- instaui-0.1.0/instaui/zero/scope.py +0 -9
- {instaui-0.1.0 → instaui-0.1.2}/LICENSE +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/README.md +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/_helper/observable_helper.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/boot_info.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/common/jsonable.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/column.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/content.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/directive.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/grid.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/_mixins.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/button.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/date.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/div.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/form.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/input.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/label.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/li.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/link.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/number.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/paragraph.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/range.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/select.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/span.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/ul.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/match.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/row.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/transition_group.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/value_element.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/components/vif.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/consts.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/event/event_mixin.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/event/js_event.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/event/web_event.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/experimental/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/experimental/debug.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/_utils.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/_uvicorn.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/event_router.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/middlewares.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/request_context.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/watch_router.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/_utils.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/computed_handler.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/config_handler.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/event_handler.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/watch_handler.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/inject.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/js/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/js/js_output.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/js/lambda_func.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/launch_collector.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/_inner_helper.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/context.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/dataclass.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/scope.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/ui_state_scope.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/settings/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/settings/__settings.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/skip.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_components.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_functions.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_install.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_router_box.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_router_output.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_router_param_var.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_types.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.css +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.iife.js.map +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.js.map +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/tailwindcss.min.js +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/templates/debug/sse.html +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/vue.esm-browser.prod.js +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/vue.global.prod.js +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/static/vue.runtime.esm-browser.prod.js +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/func_system.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/js_system.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/pydantic_system.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/string_system.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/template/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/template/env.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui/events.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/input_slient_data.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/server.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/str_format.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/ui_types.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/url_location.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/__init__.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/_types.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/_utils.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/data.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/element_ref.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/event_context.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/event_extend.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/js_computed.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/common_type.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/element_binding.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/observable.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/pathable.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/py_binding.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/str_format_binding.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/var_type.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/ref.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/slot_prop.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/state.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/types.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/vfor_item.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/vue_computed.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/web_computed.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/web_view_computed.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/version.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/_types.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/_utils.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/js_watch.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/vue_watch.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/web_watch.py +0 -0
- {instaui-0.1.0 → instaui-0.1.2}/instaui/zero/__init__.py +0 -0
@@ -13,6 +13,7 @@ from typing import (
|
|
13
13
|
Optional,
|
14
14
|
Tuple,
|
15
15
|
Union,
|
16
|
+
cast,
|
16
17
|
overload,
|
17
18
|
TYPE_CHECKING,
|
18
19
|
)
|
@@ -21,11 +22,11 @@ from collections import defaultdict
|
|
21
22
|
from instaui.runtime._app import get_app_slot
|
22
23
|
from instaui.runtime._app import get_current_scope
|
23
24
|
from instaui.vars.element_ref import ElementRef
|
24
|
-
|
25
|
-
|
26
25
|
from instaui.vars.vfor_item import VForItem
|
27
26
|
from instaui.components.directive import Directive
|
28
|
-
from instaui.dependencies import
|
27
|
+
from instaui.dependencies.component_dependency import (
|
28
|
+
ComponentDependencyInfo,
|
29
|
+
)
|
29
30
|
from .slot import SlotManager, Slot
|
30
31
|
from instaui import consts
|
31
32
|
from instaui.components.component import Component
|
@@ -69,14 +70,14 @@ PROPS_PATTERN = re.compile(
|
|
69
70
|
|
70
71
|
|
71
72
|
class Element(Component):
|
72
|
-
|
73
|
+
dependency: ClassVar[Optional[ComponentDependencyInfo]] = None
|
73
74
|
_default_props: ClassVar[Dict[str, Any]] = {}
|
74
75
|
_default_classes: ClassVar[List[str]] = []
|
75
76
|
_default_style: ClassVar[Dict[str, str]] = {}
|
76
77
|
|
77
78
|
def __init__(self, tag: Optional[Union[str, ElementBindingMixin]] = None):
|
78
|
-
if self.
|
79
|
-
tag = self.
|
79
|
+
if self.dependency:
|
80
|
+
tag = self.dependency.tag_name or ""
|
80
81
|
|
81
82
|
super().__init__(tag)
|
82
83
|
|
@@ -101,19 +102,28 @@ class Element(Component):
|
|
101
102
|
def __init_subclass__(
|
102
103
|
cls,
|
103
104
|
*,
|
104
|
-
|
105
|
+
esm: Union[str, Path, None] = None,
|
106
|
+
externals: Optional[List[Union[str, Path]]] = None,
|
107
|
+
css: Union[List[Union[str, Path]], None] = None,
|
105
108
|
) -> None:
|
106
109
|
super().__init_subclass__()
|
107
110
|
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
111
|
+
if esm:
|
112
|
+
esm = _make_dependency_path(esm, cls)
|
113
|
+
|
114
|
+
if externals:
|
115
|
+
externals = [_make_dependency_path(e, cls) for e in externals]
|
116
|
+
|
117
|
+
if css:
|
118
|
+
css = [_make_dependency_path(c, cls) for c in css]
|
113
119
|
|
114
|
-
|
115
|
-
|
116
|
-
|
120
|
+
tag_name = f"instaui-{esm.stem}"
|
121
|
+
|
122
|
+
cls.dependency = ComponentDependencyInfo(
|
123
|
+
tag_name=tag_name,
|
124
|
+
esm=esm,
|
125
|
+
externals=cast(List[Path], externals or []),
|
126
|
+
css=cast(List[Path], css or []),
|
117
127
|
)
|
118
128
|
|
119
129
|
cls._default_props = copy(cls._default_props)
|
@@ -365,8 +375,8 @@ class Element(Component):
|
|
365
375
|
if self._directives:
|
366
376
|
data["dir"] = list(self._directives.keys())
|
367
377
|
|
368
|
-
if self.
|
369
|
-
get_app_slot().
|
378
|
+
if self.dependency:
|
379
|
+
get_app_slot().use_component_dependency(self.dependency)
|
370
380
|
|
371
381
|
if self._element_ref:
|
372
382
|
scope = get_current_scope()
|
@@ -460,3 +470,13 @@ def _classifyBindableDict(
|
|
460
470
|
value_data[key] = value
|
461
471
|
|
462
472
|
return value_data, bind_data
|
473
|
+
|
474
|
+
|
475
|
+
def _make_dependency_path(path: Union[str, Path], cls: type):
|
476
|
+
if isinstance(path, str):
|
477
|
+
path = Path(path)
|
478
|
+
|
479
|
+
if not path.is_absolute():
|
480
|
+
path = Path(inspect.getfile(cls)).parent / path
|
481
|
+
|
482
|
+
return path
|
@@ -31,12 +31,5 @@ class Checkbox(InputEventMixin, ValueElement[Union[bool, str]]):
|
|
31
31
|
if model_value is not None:
|
32
32
|
self.props({"value": model_value})
|
33
33
|
|
34
|
-
# def vmodel(
|
35
|
-
# self,
|
36
|
-
# value: Any,
|
37
|
-
# *modifiers: Literal["trim"] | Literal["number"] | Literal["lazy"],
|
38
|
-
# ):
|
39
|
-
# return super().vmodel(value, *modifiers) # type: ignore
|
40
|
-
|
41
34
|
def _input_event_mixin_element(self) -> Element:
|
42
35
|
return self
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import TYPE_CHECKING, Dict, List, Optional, Set
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Set
|
4
4
|
from instaui.common.jsonable import Jsonable
|
5
5
|
from instaui.runtime import get_slot_stacks, pop_slot
|
6
6
|
from instaui.runtime._app import get_app_slot
|
@@ -8,7 +8,6 @@ from instaui.vars.slot_prop import BindingSlotPropItem
|
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
from instaui.components.component import Component
|
11
|
-
from instaui.components.vfor import VFor
|
12
11
|
|
13
12
|
_DEFAULT_SLOT_NAME = ":"
|
14
13
|
|
@@ -50,7 +49,7 @@ class Slot(Jsonable):
|
|
50
49
|
|
51
50
|
self._id: Optional[str] = None
|
52
51
|
self._name = name
|
53
|
-
self._children: List[
|
52
|
+
self._children: List[Component] = []
|
54
53
|
self._props_use_name: Set[str] = set()
|
55
54
|
|
56
55
|
def _has_props_use(self):
|
@@ -60,7 +60,7 @@ class VFor(Component, Generic[_T]):
|
|
60
60
|
def __enter__(self) -> _T:
|
61
61
|
self.__scope = self.__scope_manager.__enter__()
|
62
62
|
super().__enter__()
|
63
|
-
return VForItem(self).proxy
|
63
|
+
return VForItem(self).proxy # type: ignore
|
64
64
|
|
65
65
|
def __exit__(self, *_) -> None:
|
66
66
|
self.__scope_manager.__exit__(*_)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import List, Optional
|
5
|
+
|
6
|
+
_TTagName = str
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass(frozen=True)
|
10
|
+
class ComponentDependencyInfo:
|
11
|
+
tag_name: _TTagName = field(hash=True)
|
12
|
+
esm: Path = field(hash=False)
|
13
|
+
externals: Optional[List[Path]] = field(
|
14
|
+
default_factory=list, compare=False, hash=False
|
15
|
+
)
|
16
|
+
css: List[Path] = field(default_factory=list, compare=False, hash=False)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import List, Optional
|
5
|
+
from instaui.runtime._app import get_app_slot
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass(frozen=True)
|
9
|
+
class PluginDependencyInfo:
|
10
|
+
name: str = field(hash=True)
|
11
|
+
esm: Path = field(hash=False)
|
12
|
+
externals: Optional[List[Path]] = field(
|
13
|
+
default_factory=list, compare=False, hash=False
|
14
|
+
)
|
15
|
+
css: List[Path] = field(default_factory=list, compare=False, hash=False)
|
16
|
+
|
17
|
+
|
18
|
+
def register_plugin(
|
19
|
+
name: str,
|
20
|
+
esm: Path,
|
21
|
+
*,
|
22
|
+
externals: Optional[List[Path]] = None,
|
23
|
+
css: Optional[List[Path]] = None,
|
24
|
+
):
|
25
|
+
info = PluginDependencyInfo(f"plugin/{name}", esm, externals or [], css or [])
|
26
|
+
|
27
|
+
get_app_slot().use_plugin_dependency(info)
|
28
|
+
return info
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from fastapi import FastAPI
|
2
|
+
from fastapi.responses import FileResponse
|
3
|
+
|
4
|
+
from instaui.fastapi_server import resource
|
5
|
+
|
6
|
+
|
7
|
+
URL = f"{resource.URL}/{{hash_part:path}}"
|
8
|
+
|
9
|
+
|
10
|
+
def create_router(app: FastAPI):
|
11
|
+
_dependency_handler(app)
|
12
|
+
|
13
|
+
|
14
|
+
def _dependency_handler(app: FastAPI):
|
15
|
+
@app.get(URL)
|
16
|
+
def _(hash_part: str) -> FileResponse:
|
17
|
+
local_file = resource.get_by_hash(hash_part)
|
18
|
+
|
19
|
+
return FileResponse(
|
20
|
+
local_file, headers={"Cache-Control": "public, max-age=3600"}
|
21
|
+
)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Dict
|
3
|
+
from urllib.parse import quote as urllib_quote
|
4
|
+
from instaui.systems import file_system
|
5
|
+
from instaui.version import __version__ as _INSTA_VERSION
|
6
|
+
|
7
|
+
URL = f"/_instaui_{_INSTA_VERSION}/resource"
|
8
|
+
_THashPart = str
|
9
|
+
_HASH_PART_MAP: Dict[_THashPart, Path] = {}
|
10
|
+
_FILE_URL_MAP: Dict[Path, _THashPart] = {}
|
11
|
+
|
12
|
+
|
13
|
+
def get_by_hash(hash_part: str) -> Path:
|
14
|
+
return _HASH_PART_MAP[hash_part]
|
15
|
+
|
16
|
+
|
17
|
+
def record_resource(path: Path):
|
18
|
+
if path in _FILE_URL_MAP:
|
19
|
+
return _FILE_URL_MAP[path]
|
20
|
+
|
21
|
+
hash_part = _generate_hash_part(path)
|
22
|
+
_HASH_PART_MAP[hash_part] = path
|
23
|
+
url = f"{URL}/{hash_part}"
|
24
|
+
_FILE_URL_MAP[path] = url
|
25
|
+
return url
|
26
|
+
|
27
|
+
|
28
|
+
def generate_static_url(file_path: Path):
|
29
|
+
return urllib_quote(f"{URL}/{_generate_hash_part(file_path)}")
|
30
|
+
|
31
|
+
|
32
|
+
def _generate_hash_part(file_path: Path):
|
33
|
+
file_path = Path(file_path).resolve()
|
34
|
+
hash_name = file_system.generate_hash_name_from_path(file_path)
|
35
|
+
return f"{hash_name}/{file_path.name}"
|
@@ -0,0 +1,270 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from contextlib import contextmanager
|
3
|
+
import inspect
|
4
|
+
import os
|
5
|
+
import multiprocessing
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Any, Optional, Set
|
8
|
+
import __main__
|
9
|
+
|
10
|
+
from fastapi import FastAPI
|
11
|
+
from fastapi import Request
|
12
|
+
from fastapi.responses import HTMLResponse
|
13
|
+
import uvicorn
|
14
|
+
from uvicorn.supervisors import ChangeReload
|
15
|
+
import itertools
|
16
|
+
|
17
|
+
from instaui.html_tools import to_config_data
|
18
|
+
from instaui.launch_collector import get_launch_collector
|
19
|
+
from instaui.page_info import PageInfo
|
20
|
+
|
21
|
+
from instaui import consts
|
22
|
+
from instaui.runtime._app import get_app_slot, get_default_app_slot
|
23
|
+
from instaui.runtime.dataclass import JsLink, VueAppComponent
|
24
|
+
from instaui.template import web_template
|
25
|
+
|
26
|
+
|
27
|
+
from . import dependency_router
|
28
|
+
from . import event_router
|
29
|
+
from . import watch_router
|
30
|
+
from . import debug_mode_router
|
31
|
+
from .middlewares import RequestContextMiddleware
|
32
|
+
from ._uvicorn import UvicornServer
|
33
|
+
from . import resource
|
34
|
+
|
35
|
+
APP_IMPORT_STRING = "instaui.fastapi_server.server:Server._instance.app"
|
36
|
+
|
37
|
+
VUE_JS_HASH_LINK = resource.record_resource(consts.VUE_ES_JS_PATH)
|
38
|
+
INSTAUI_JS_HASH_LINK = resource.record_resource(consts.APP_ES_JS_PATH)
|
39
|
+
APP_CSS_LINK = resource.record_resource(consts.APP_CSS_PATH)
|
40
|
+
TAILWIND_JS_HASH_LINK = resource.record_resource(consts.TAILWIND_JS_PATH)
|
41
|
+
|
42
|
+
|
43
|
+
class Server:
|
44
|
+
_instance: Optional[Server] = None
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def get_instance(cls):
|
48
|
+
if cls._instance is None:
|
49
|
+
cls._instance = cls()
|
50
|
+
return cls._instance
|
51
|
+
|
52
|
+
def __init__(self):
|
53
|
+
self.app = FastAPI()
|
54
|
+
self.app.add_middleware(RequestContextMiddleware)
|
55
|
+
dependency_router.create_router(self.app)
|
56
|
+
event_router.create_router(self.app)
|
57
|
+
watch_router.create_router(self.app)
|
58
|
+
debug_mode_router.create_router(self.app)
|
59
|
+
|
60
|
+
for page_info in get_launch_collector()._page_router.values():
|
61
|
+
self.register_page(page_info)
|
62
|
+
|
63
|
+
self._registered_static_routes: Set[str] = set()
|
64
|
+
|
65
|
+
def register_page(self, info: PageInfo):
|
66
|
+
is_async = inspect.iscoroutinefunction(info.func)
|
67
|
+
|
68
|
+
self._remove_route(info.path)
|
69
|
+
|
70
|
+
if is_async:
|
71
|
+
|
72
|
+
@self.app.get(info.path)
|
73
|
+
async def _(request: Request):
|
74
|
+
self._update_page_info(request, info)
|
75
|
+
with _execute_request_lifespans():
|
76
|
+
await info.func()
|
77
|
+
html = self._to_web_html(
|
78
|
+
page_info=info,
|
79
|
+
request=request,
|
80
|
+
)
|
81
|
+
|
82
|
+
return HTMLResponse(html)
|
83
|
+
|
84
|
+
else:
|
85
|
+
|
86
|
+
@self.app.get(info.path)
|
87
|
+
def _(request: Request):
|
88
|
+
self._update_page_info(request, info)
|
89
|
+
with _execute_request_lifespans():
|
90
|
+
info.func()
|
91
|
+
html = self._to_web_html(
|
92
|
+
page_info=info,
|
93
|
+
request=request,
|
94
|
+
)
|
95
|
+
|
96
|
+
return HTMLResponse(html)
|
97
|
+
|
98
|
+
def _to_web_html(
|
99
|
+
self,
|
100
|
+
*,
|
101
|
+
page_info: PageInfo,
|
102
|
+
request: Request,
|
103
|
+
):
|
104
|
+
config_data = to_config_data()
|
105
|
+
|
106
|
+
model = web_template.WebTemplateModel(
|
107
|
+
vue_js_link=VUE_JS_HASH_LINK,
|
108
|
+
instaui_js_link=INSTAUI_JS_HASH_LINK,
|
109
|
+
css_links=[
|
110
|
+
APP_CSS_LINK,
|
111
|
+
],
|
112
|
+
config_dict=config_data,
|
113
|
+
)
|
114
|
+
|
115
|
+
system_slot = get_app_slot()
|
116
|
+
default_app_slot = get_default_app_slot()
|
117
|
+
html_resource = system_slot._html_resource
|
118
|
+
default_html_resource = default_app_slot._html_resource
|
119
|
+
|
120
|
+
if html_resource.use_tailwind is None:
|
121
|
+
if default_html_resource.use_tailwind:
|
122
|
+
model.js_links.append(JsLink(TAILWIND_JS_HASH_LINK))
|
123
|
+
else:
|
124
|
+
if html_resource.use_tailwind:
|
125
|
+
model.js_links.append(JsLink(TAILWIND_JS_HASH_LINK))
|
126
|
+
|
127
|
+
# register custom components
|
128
|
+
for component in system_slot._component_dependencies:
|
129
|
+
if not component.esm:
|
130
|
+
continue
|
131
|
+
|
132
|
+
model.vue_app_component.append(
|
133
|
+
VueAppComponent(
|
134
|
+
name=component.tag_name,
|
135
|
+
url=resource.record_resource(component.esm),
|
136
|
+
)
|
137
|
+
)
|
138
|
+
|
139
|
+
if component.css:
|
140
|
+
for css_link in component.css:
|
141
|
+
model.css_links.append(resource.record_resource(css_link))
|
142
|
+
|
143
|
+
# register custom plugins
|
144
|
+
for plugin in set(
|
145
|
+
itertools.chain(
|
146
|
+
system_slot._plugin_dependencies, default_app_slot._plugin_dependencies
|
147
|
+
)
|
148
|
+
):
|
149
|
+
if not plugin.esm:
|
150
|
+
continue
|
151
|
+
|
152
|
+
model.vue_app_use.append(plugin.name)
|
153
|
+
|
154
|
+
model.add_extra_import_map(
|
155
|
+
plugin.name, resource.record_resource(plugin.esm)
|
156
|
+
)
|
157
|
+
|
158
|
+
if plugin.css:
|
159
|
+
for css_link in plugin.css:
|
160
|
+
model.css_links.append(resource.record_resource(css_link))
|
161
|
+
|
162
|
+
# css file link to web static link
|
163
|
+
for link, attrs in itertools.chain(
|
164
|
+
html_resource._css_links.items(), default_html_resource._css_links.items()
|
165
|
+
):
|
166
|
+
if isinstance(link, Path):
|
167
|
+
model.css_links.append(resource.record_resource(link))
|
168
|
+
|
169
|
+
# js file link to web static link
|
170
|
+
for info in itertools.chain(
|
171
|
+
html_resource._js_links, default_html_resource._js_links
|
172
|
+
):
|
173
|
+
if isinstance(info.link, Path):
|
174
|
+
model.js_links.append(JsLink((resource.record_resource(info.link))))
|
175
|
+
|
176
|
+
for js_code in itertools.chain(
|
177
|
+
html_resource._script_tags, default_html_resource._script_tags
|
178
|
+
):
|
179
|
+
model.script_tags.append(js_code)
|
180
|
+
|
181
|
+
for sylte_code in itertools.chain(
|
182
|
+
html_resource._style_tags, default_html_resource._style_tags
|
183
|
+
):
|
184
|
+
model.style_tags.append(sylte_code)
|
185
|
+
|
186
|
+
model.prefix = request.headers.get(
|
187
|
+
"X-Forwarded-Prefix", request.scope.get("root_path", "")
|
188
|
+
)
|
189
|
+
|
190
|
+
return web_template.render_web_html(model)
|
191
|
+
|
192
|
+
def _update_page_info(self, request: Request, page_info: PageInfo):
|
193
|
+
app = get_app_slot()
|
194
|
+
|
195
|
+
app._page_path = page_info.path
|
196
|
+
app._page_params = request.path_params
|
197
|
+
app._query_params = dict(request.query_params)
|
198
|
+
|
199
|
+
def _remove_route(self, path: str) -> None:
|
200
|
+
self.app.routes[:] = [
|
201
|
+
r for r in self.app.routes if getattr(r, "path", None) != path
|
202
|
+
]
|
203
|
+
|
204
|
+
def try_close_server(self):
|
205
|
+
UvicornServer.instance.should_exit = True
|
206
|
+
|
207
|
+
def run(
|
208
|
+
self,
|
209
|
+
host="0.0.0.0",
|
210
|
+
port=8080,
|
211
|
+
reload: bool = True,
|
212
|
+
reload_dirs: str = ".",
|
213
|
+
reload_includes: str = "*.py",
|
214
|
+
reload_excludes: str = ".*, .py[cod], .sw.*, ~*",
|
215
|
+
log_level="info",
|
216
|
+
workers: int | None = None,
|
217
|
+
uds: str | None = None,
|
218
|
+
**kwargs: Any,
|
219
|
+
):
|
220
|
+
if multiprocessing.current_process().name != "MainProcess":
|
221
|
+
return
|
222
|
+
|
223
|
+
if reload and not hasattr(__main__, "__file__"):
|
224
|
+
reload = False
|
225
|
+
|
226
|
+
config = uvicorn.Config(
|
227
|
+
APP_IMPORT_STRING if reload else self.app,
|
228
|
+
host=host,
|
229
|
+
port=port,
|
230
|
+
reload=reload,
|
231
|
+
log_level=log_level,
|
232
|
+
workers=workers,
|
233
|
+
uds=uds,
|
234
|
+
reload_includes=_split_args(reload_includes) if reload else None,
|
235
|
+
reload_excludes=_split_args(reload_excludes) if reload else None,
|
236
|
+
reload_dirs=_split_args(reload_dirs) if reload else None,
|
237
|
+
**kwargs,
|
238
|
+
)
|
239
|
+
|
240
|
+
UvicornServer.create_singleton(config, [debug_mode_router.when_server_reload])
|
241
|
+
|
242
|
+
if config.should_reload:
|
243
|
+
ChangeReload(config, target=UvicornServer.instance.run, sockets=[]).run()
|
244
|
+
else:
|
245
|
+
UvicornServer.instance.run()
|
246
|
+
|
247
|
+
if config.uds:
|
248
|
+
os.remove(config.uds) # pragma: py-win32
|
249
|
+
|
250
|
+
def run_with(self, app):
|
251
|
+
assert isinstance(app, FastAPI), "app must be a FastAPI instance"
|
252
|
+
|
253
|
+
|
254
|
+
def _split_args(args: str):
|
255
|
+
return [a.strip() for a in args.split(",")]
|
256
|
+
|
257
|
+
|
258
|
+
@contextmanager
|
259
|
+
def _execute_request_lifespans():
|
260
|
+
events = [iter(event()) for event in get_launch_collector().page_request_lifespans]
|
261
|
+
for event in events:
|
262
|
+
next(event)
|
263
|
+
|
264
|
+
yield
|
265
|
+
|
266
|
+
for event in events:
|
267
|
+
try:
|
268
|
+
next(event)
|
269
|
+
except StopIteration:
|
270
|
+
pass
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Any, Dict, Literal, Optional, Union
|
4
|
+
from instaui.common.jsonable import dumps, dumps2dict
|
5
|
+
from instaui.runtime._app import get_app_slot
|
6
|
+
|
7
|
+
|
8
|
+
def add_css_link(href: Union[str, Path]):
|
9
|
+
get_app_slot()._html_resource.add_css_link(href)
|
10
|
+
|
11
|
+
|
12
|
+
def add_js_link(
|
13
|
+
link: Union[str, Path],
|
14
|
+
*,
|
15
|
+
type: Optional[Literal["module"]] = None,
|
16
|
+
):
|
17
|
+
attrs = {
|
18
|
+
"type": type,
|
19
|
+
}
|
20
|
+
|
21
|
+
get_app_slot()._html_resource.add_js_link(link, attrs=attrs)
|
22
|
+
|
23
|
+
|
24
|
+
def add_style(content: str):
|
25
|
+
get_app_slot()._html_resource.add_style_tag(content)
|
26
|
+
|
27
|
+
|
28
|
+
def use_tailwind(value=True):
|
29
|
+
get_app_slot()._html_resource.use_tailwind = value
|
30
|
+
|
31
|
+
|
32
|
+
def add_js_code(code: str, *, script_attrs: Optional[Dict[str, Any]] = None):
|
33
|
+
get_app_slot()._html_resource.add_script_tag(code, script_attrs=script_attrs)
|
34
|
+
|
35
|
+
|
36
|
+
def add_vue_app_use(name: str):
|
37
|
+
get_app_slot()._html_resource.add_vue_app_use(name)
|
38
|
+
|
39
|
+
|
40
|
+
def to_config_data() -> Dict:
|
41
|
+
return dumps2dict(get_app_slot())
|
42
|
+
|
43
|
+
|
44
|
+
def to_json(indent=False):
|
45
|
+
return dumps(get_app_slot(), indent=indent)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Callable
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from urllib.parse import quote
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class PageInfo:
|
9
|
+
path: str
|
10
|
+
func: Callable
|
11
|
+
|
12
|
+
def create_key(self) -> str:
|
13
|
+
return quote(self.path)
|
@@ -5,7 +5,6 @@ from instaui.common.jsonable import Jsonable
|
|
5
5
|
from .resource import HtmlResource
|
6
6
|
from instaui.consts import _T_App_Mode
|
7
7
|
from contextvars import ContextVar, Token
|
8
|
-
from copy import copy
|
9
8
|
from contextlib import contextmanager
|
10
9
|
from instaui.runtime.scope import Scope, GlobalScope
|
11
10
|
from types import MappingProxyType
|
@@ -13,14 +12,14 @@ from types import MappingProxyType
|
|
13
12
|
if TYPE_CHECKING:
|
14
13
|
from instaui.components.component import Component
|
15
14
|
from instaui.components.slot import Slot
|
16
|
-
|
15
|
+
|
16
|
+
from instaui.dependencies.component_dependency import ComponentDependencyInfo
|
17
|
+
from instaui.dependencies.plugin_dependency import PluginDependencyInfo
|
17
18
|
from instaui.spa_router._route_model import RouteCollector
|
18
19
|
|
19
20
|
|
20
21
|
class App(Jsonable):
|
21
22
|
_default_app_slot: ClassVar[Optional[App]] = None
|
22
|
-
_default_js_components: ClassVar[Set[ComponentRegistrationInfo]] = set()
|
23
|
-
_default_plugins: ClassVar[Set[PluginRegistrationInfo]] = set()
|
24
23
|
|
25
24
|
def __init__(self, *, mode: _T_App_Mode) -> None:
|
26
25
|
super().__init__()
|
@@ -35,11 +34,9 @@ class App(Jsonable):
|
|
35
34
|
self._scope_stack: List[Scope] = [defalut_scope]
|
36
35
|
self._scopes: List[Scope] = [defalut_scope]
|
37
36
|
self._html_resource = HtmlResource()
|
38
|
-
self.
|
39
|
-
|
40
|
-
)
|
37
|
+
self._component_dependencies: Set[ComponentDependencyInfo] = set()
|
38
|
+
self._plugin_dependencies: Set[PluginDependencyInfo] = set()
|
41
39
|
|
42
|
-
self._plugins: Set[PluginRegistrationInfo] = copy(self._default_plugins)
|
43
40
|
self._page_path: Optional[str] = None
|
44
41
|
self._page_params: Dict[str, Any] = {}
|
45
42
|
self._query_params: Dict[str, Any] = {}
|
@@ -74,11 +71,11 @@ class App(Jsonable):
|
|
74
71
|
def reset_html_resource(self):
|
75
72
|
self._html_resource = HtmlResource()
|
76
73
|
|
77
|
-
def
|
78
|
-
self.
|
74
|
+
def use_component_dependency(self, dependency: ComponentDependencyInfo) -> None:
|
75
|
+
self._component_dependencies.add(dependency)
|
79
76
|
|
80
|
-
def
|
81
|
-
self.
|
77
|
+
def use_plugin_dependency(self, dependency: PluginDependencyInfo) -> None:
|
78
|
+
self._plugin_dependencies.add(dependency)
|
82
79
|
|
83
80
|
def register_router(self, collector: RouteCollector) -> None:
|
84
81
|
self._route_collector = collector
|
@@ -115,14 +112,6 @@ class App(Jsonable):
|
|
115
112
|
cls._default_app_slot = DefaultApp(mode="web")
|
116
113
|
return cls._default_app_slot
|
117
114
|
|
118
|
-
@classmethod
|
119
|
-
def default_js_components(cls, component: ComponentRegistrationInfo):
|
120
|
-
cls._default_js_components.add(component)
|
121
|
-
|
122
|
-
@classmethod
|
123
|
-
def default_plugins(cls, plugin: PluginRegistrationInfo):
|
124
|
-
cls._default_plugins.add(plugin)
|
125
|
-
|
126
115
|
|
127
116
|
class DefaultApp(App):
|
128
117
|
_instance = None
|