1
1
from __future__ import annotations
2
2
3
- import re
4
3
from dataclasses import dataclass
5
4
from pathlib import Path
6
5
from typing import Any , Callable , Iterator , Sequence
9
8
from idom import component , create_context , use_context , use_memo , use_state
10
9
from idom .core .types import VdomAttributesAndChildren , VdomDict
11
10
from idom .core .vdom import coalesce_attributes_and_children
12
- from idom .types import BackendImplementation , ComponentType , Context , Location
11
+ from idom .types import ComponentType , Context , Location
13
12
from idom .web .module import export , module_from_file
14
- from starlette .routing import compile_path
13
+ from starlette .routing import compile_path as _compile_starlette_path
14
+
15
+ from idom_router .types import RoutePattern , RouteCompiler , Route
15
16
16
17
try :
17
18
from typing import Protocol
18
19
except ImportError : # pragma: no cover
19
20
from typing_extensions import Protocol # type: ignore
20
21
21
22
22
- class RouterConstructor (Protocol ):
23
- def __call__ (self , * routes : Route ) -> ComponentType :
24
- ...
25
-
26
-
27
- def create_router (
28
- implementation : BackendImplementation [Any ] | Callable [[], Location ]
29
- ) -> RouterConstructor :
30
- if isinstance (implementation , BackendImplementation ):
31
- use_location = implementation .use_location
32
- elif callable (implementation ):
33
- use_location = implementation
34
- else :
35
- raise TypeError (
36
- "Expected a 'BackendImplementation' or "
37
- f"'use_location' hook, not { implementation } "
38
- )
39
-
40
- @component
41
- def router (* routes : Route ) -> ComponentType | None :
42
- initial_location = use_location ()
43
- location , set_location = use_state (initial_location )
44
- compiled_routes = use_memo (
45
- lambda : _iter_compile_routes (routes ), dependencies = routes
46
- )
47
- for r in compiled_routes :
48
- match = r .pattern .match (location .pathname )
49
- if match :
50
- return _LocationStateContext (
51
- r .element ,
52
- value = _LocationState (
53
- location ,
54
- set_location ,
55
- {k : r .converters [k ](v ) for k , v in match .groupdict ().items ()},
56
- ),
57
- key = r .pattern .pattern ,
58
- )
59
- return None
60
-
61
- return router
62
-
23
+ def compile_starlette_route (route : str ) -> RoutePattern :
24
+ pattern , _ , converters = _compile_starlette_path (route )
25
+ return RoutePattern (pattern , {k : v .convert for k , v in converters .items ()})
63
26
64
- @dataclass
65
- class Route :
66
- path : str
67
- element : Any
68
- routes : Sequence [Route ]
69
27
70
- def __init__ (self , path : str , element : Any | None , * routes : Route ) -> None :
71
- self .path = path
72
- self .element = element
73
- self .routes = routes
28
+ @component
29
+ def router (
30
+ * routes : Route ,
31
+ compiler : RouteCompiler = compile_starlette_route ,
32
+ ) -> ComponentType | None :
33
+ initial_location = use_location ()
34
+ location , set_location = use_state (initial_location )
35
+ compiled_routes = use_memo (
36
+ lambda : [(compiler (r ), e ) for r , e in _iter_routes (routes )],
37
+ dependencies = routes ,
38
+ )
39
+ for compiled_route , element in compiled_routes :
40
+ match = compiled_route .pattern .match (location .pathname )
41
+ if match :
42
+ return _LocationStateContext (
43
+ element ,
44
+ value = _LocationState (
45
+ location ,
46
+ set_location ,
47
+ {
48
+ k : compiled_route .converters [k ](v )
49
+ for k , v in match .groupdict ().items ()
50
+ },
51
+ ),
52
+ key = compiled_route .pattern .pattern ,
53
+ )
54
+ return None
74
55
75
56
76
57
@component
@@ -113,28 +94,13 @@ def use_query(
113
94
)
114
95
115
96
116
- def _iter_compile_routes (routes : Sequence [Route ]) -> Iterator [_CompiledRoute ]:
117
- for path , element in _iter_routes (routes ):
118
- pattern , _ , converters = compile_path (path )
119
- yield _CompiledRoute (
120
- pattern , {k : v .convert for k , v in converters .items ()}, element
121
- )
122
-
123
-
124
97
def _iter_routes (routes : Sequence [Route ]) -> Iterator [tuple [str , Any ]]:
125
98
for r in routes :
126
99
for path , element in _iter_routes (r .routes ):
127
100
yield r .path + path , element
128
101
yield r .path , r .element
129
102
130
103
131
- @dataclass
132
- class _CompiledRoute :
133
- pattern : re .Pattern [str ]
134
- converters : dict [str , Callable [[Any ], Any ]]
135
- element : Any
136
-
137
-
138
104
def _use_location_state () -> _LocationState :
139
105
location_state = use_context (_LocationStateContext )
140
106
assert location_state is not None , "No location state. Did you use a Router?"
@@ -151,10 +117,6 @@ class _LocationState:
151
117
_LocationStateContext : Context [_LocationState | None ] = create_context (None )
152
118
153
119
_Link = export (
154
- module_from_file (
155
- "idom-router" ,
156
- file = Path (__file__ ).parent / "bundle.js" ,
157
- fallback = "⏳" ,
158
- ),
120
+ module_from_file ("idom-router" , file = Path (__file__ ).parent / "bundle.js" ),
159
121
"Link" ,
160
122
)
0 commit comments