reactpy 2.0.0b4__py3-none-any.whl → 2.0.0b6__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.
- reactpy/__init__.py +3 -2
- reactpy/_console/rewrite_props.py +2 -2
- reactpy/_html.py +11 -9
- reactpy/_option.py +2 -1
- reactpy/config.py +2 -2
- reactpy/core/_life_cycle_hook.py +12 -10
- reactpy/core/_thread_local.py +2 -1
- reactpy/core/component.py +4 -38
- reactpy/core/events.py +61 -36
- reactpy/core/hooks.py +25 -35
- reactpy/core/layout.py +193 -201
- reactpy/core/serve.py +17 -22
- reactpy/core/vdom.py +9 -12
- reactpy/executors/asgi/__init__.py +9 -4
- reactpy/executors/asgi/middleware.py +1 -2
- reactpy/executors/asgi/pyscript.py +3 -7
- reactpy/executors/asgi/standalone.py +4 -6
- reactpy/executors/asgi/types.py +2 -2
- reactpy/pyscript/components.py +3 -3
- reactpy/pyscript/utils.py +49 -46
- reactpy/reactjs/__init__.py +353 -0
- reactpy/reactjs/module.py +203 -0
- reactpy/reactjs/types.py +7 -0
- reactpy/reactjs/utils.py +183 -0
- reactpy/static/index-h31022cd.js +5 -0
- reactpy/static/index-h31022cd.js.map +11 -0
- reactpy/static/index-sbddj6ms.js +5 -0
- reactpy/static/index-sbddj6ms.js.map +10 -0
- reactpy/static/index-y71bxs88.js +5 -0
- reactpy/static/index-y71bxs88.js.map +10 -0
- reactpy/static/index.js +2 -2
- reactpy/static/index.js.map +6 -10
- reactpy/static/react-dom.js +4 -0
- reactpy/static/react-dom.js.map +11 -0
- reactpy/static/react-jsx-runtime.js +4 -0
- reactpy/static/react-jsx-runtime.js.map +9 -0
- reactpy/static/react.js +4 -0
- reactpy/static/react.js.map +10 -0
- reactpy/testing/backend.py +6 -5
- reactpy/testing/common.py +3 -5
- reactpy/testing/display.py +2 -1
- reactpy/testing/logs.py +1 -1
- reactpy/transforms.py +2 -2
- reactpy/types.py +117 -58
- reactpy/utils.py +8 -8
- reactpy/web/__init__.py +0 -6
- reactpy/web/module.py +37 -470
- reactpy/web/utils.py +2 -158
- reactpy/widgets.py +2 -2
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/METADATA +4 -7
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/RECORD +54 -39
- reactpy/web/templates/react.js +0 -61
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/WHEEL +0 -0
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/entry_points.txt +0 -0
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, overload
|
|
6
|
+
|
|
7
|
+
from reactpy.reactjs.module import (
|
|
8
|
+
file_to_module,
|
|
9
|
+
import_reactjs,
|
|
10
|
+
module_to_vdom,
|
|
11
|
+
string_to_module,
|
|
12
|
+
url_to_module,
|
|
13
|
+
)
|
|
14
|
+
from reactpy.reactjs.types import (
|
|
15
|
+
NAME_SOURCE,
|
|
16
|
+
URL_SOURCE,
|
|
17
|
+
)
|
|
18
|
+
from reactpy.types import JavaScriptModule, VdomConstructor
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"NAME_SOURCE",
|
|
22
|
+
"URL_SOURCE",
|
|
23
|
+
"component_from_file",
|
|
24
|
+
"component_from_npm",
|
|
25
|
+
"component_from_string",
|
|
26
|
+
"component_from_url",
|
|
27
|
+
"import_reactjs",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
_URL_JS_MODULE_CACHE: dict[str, JavaScriptModule] = {}
|
|
31
|
+
_FILE_JS_MODULE_CACHE: dict[str, JavaScriptModule] = {}
|
|
32
|
+
_STRING_JS_MODULE_CACHE: dict[str, JavaScriptModule] = {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@overload
|
|
36
|
+
def component_from_url(
|
|
37
|
+
url: str,
|
|
38
|
+
import_names: str,
|
|
39
|
+
resolve_imports: bool = ...,
|
|
40
|
+
resolve_imports_depth: int = ...,
|
|
41
|
+
fallback: Any | None = ...,
|
|
42
|
+
unmount_before_update: bool = ...,
|
|
43
|
+
allow_children: bool = ...,
|
|
44
|
+
) -> VdomConstructor: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@overload
|
|
48
|
+
def component_from_url(
|
|
49
|
+
url: str,
|
|
50
|
+
import_names: list[str] | tuple[str, ...],
|
|
51
|
+
resolve_imports: bool = ...,
|
|
52
|
+
resolve_imports_depth: int = ...,
|
|
53
|
+
fallback: Any | None = ...,
|
|
54
|
+
unmount_before_update: bool = ...,
|
|
55
|
+
allow_children: bool = ...,
|
|
56
|
+
) -> list[VdomConstructor]: ...
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def component_from_url(
|
|
60
|
+
url: str,
|
|
61
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
62
|
+
resolve_imports: bool = False,
|
|
63
|
+
resolve_imports_depth: int = 5,
|
|
64
|
+
fallback: Any | None = None,
|
|
65
|
+
unmount_before_update: bool = False,
|
|
66
|
+
allow_children: bool = True,
|
|
67
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
68
|
+
"""Import a component from a URL.
|
|
69
|
+
|
|
70
|
+
Parameters:
|
|
71
|
+
url:
|
|
72
|
+
The URL to import the component from.
|
|
73
|
+
import_names:
|
|
74
|
+
One or more component names to import. If given as a string, a single component
|
|
75
|
+
will be returned. If a list is given, then a list of components will be
|
|
76
|
+
returned.
|
|
77
|
+
resolve_imports:
|
|
78
|
+
Whether to try and find all the named imports of this module.
|
|
79
|
+
resolve_imports_depth:
|
|
80
|
+
How deeply to search for those imports.
|
|
81
|
+
fallback:
|
|
82
|
+
What to temporarily display while the module is being loaded.
|
|
83
|
+
unmount_before_update:
|
|
84
|
+
Cause the component to be unmounted before each update. This option should
|
|
85
|
+
only be used if the imported package fails to re-render when props change.
|
|
86
|
+
Using this option has negative performance consequences since all DOM
|
|
87
|
+
elements must be changed on each render. See :issue:`461` for more info.
|
|
88
|
+
allow_children:
|
|
89
|
+
Whether or not these components can have children.
|
|
90
|
+
"""
|
|
91
|
+
key = f"{url}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
|
|
92
|
+
if key in _URL_JS_MODULE_CACHE:
|
|
93
|
+
module = _URL_JS_MODULE_CACHE[key]
|
|
94
|
+
else:
|
|
95
|
+
module = url_to_module(
|
|
96
|
+
url,
|
|
97
|
+
fallback=fallback,
|
|
98
|
+
resolve_imports=resolve_imports,
|
|
99
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
100
|
+
unmount_before_update=unmount_before_update,
|
|
101
|
+
)
|
|
102
|
+
_URL_JS_MODULE_CACHE[key] = module
|
|
103
|
+
return module_to_vdom(module, import_names, fallback, allow_children)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@overload
|
|
107
|
+
def component_from_npm(
|
|
108
|
+
package: str,
|
|
109
|
+
import_names: str,
|
|
110
|
+
resolve_imports: bool = ...,
|
|
111
|
+
resolve_imports_depth: int = ...,
|
|
112
|
+
version: str = "latest",
|
|
113
|
+
cdn: str = "https://esm.sh",
|
|
114
|
+
fallback: Any | None = ...,
|
|
115
|
+
unmount_before_update: bool = ...,
|
|
116
|
+
allow_children: bool = ...,
|
|
117
|
+
) -> VdomConstructor: ...
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@overload
|
|
121
|
+
def component_from_npm(
|
|
122
|
+
package: str,
|
|
123
|
+
import_names: list[str] | tuple[str, ...],
|
|
124
|
+
resolve_imports: bool = ...,
|
|
125
|
+
resolve_imports_depth: int = ...,
|
|
126
|
+
version: str = "latest",
|
|
127
|
+
cdn: str = "https://esm.sh",
|
|
128
|
+
fallback: Any | None = ...,
|
|
129
|
+
unmount_before_update: bool = ...,
|
|
130
|
+
allow_children: bool = ...,
|
|
131
|
+
) -> list[VdomConstructor]: ...
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def component_from_npm(
|
|
135
|
+
package: str,
|
|
136
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
137
|
+
resolve_imports: bool = False,
|
|
138
|
+
resolve_imports_depth: int = 5,
|
|
139
|
+
version: str = "latest",
|
|
140
|
+
cdn: str = "https://esm.sh",
|
|
141
|
+
fallback: Any | None = None,
|
|
142
|
+
unmount_before_update: bool = False,
|
|
143
|
+
allow_children: bool = True,
|
|
144
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
145
|
+
"""Import a component from an NPM package.
|
|
146
|
+
|
|
147
|
+
Is is mandatory to load `reactpy.reactjs.import_reactjs()` on your page before using this
|
|
148
|
+
function. It is recommended to put this within your HTML <head> content.
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
package:
|
|
152
|
+
The name of the NPM package.
|
|
153
|
+
import_names:
|
|
154
|
+
One or more component names to import. If given as a string, a single component
|
|
155
|
+
will be returned. If a list is given, then a list of components will be
|
|
156
|
+
returned.
|
|
157
|
+
resolve_imports:
|
|
158
|
+
Whether to try and find all the named imports of this module.
|
|
159
|
+
resolve_imports_depth:
|
|
160
|
+
How deeply to search for those imports.
|
|
161
|
+
version:
|
|
162
|
+
The version of the package to use. Defaults to "latest".
|
|
163
|
+
cdn:
|
|
164
|
+
The CDN to use. Defaults to "https://esm.sh".
|
|
165
|
+
fallback:
|
|
166
|
+
What to temporarily display while the module is being loaded.
|
|
167
|
+
unmount_before_update:
|
|
168
|
+
Cause the component to be unmounted before each update. This option should
|
|
169
|
+
only be used if the imported package fails to re-render when props change.
|
|
170
|
+
Using this option has negative performance consequences since all DOM
|
|
171
|
+
elements must be changed on each render. See :issue:`461` for more info.
|
|
172
|
+
allow_children:
|
|
173
|
+
Whether or not these components can have children.
|
|
174
|
+
"""
|
|
175
|
+
url = f"{cdn}/{package}@{version}"
|
|
176
|
+
|
|
177
|
+
if "esm.sh" in cdn:
|
|
178
|
+
if "?" in url:
|
|
179
|
+
url += "&external=react,react-dom"
|
|
180
|
+
else:
|
|
181
|
+
url += "?external=react,react-dom"
|
|
182
|
+
|
|
183
|
+
return component_from_url(
|
|
184
|
+
url,
|
|
185
|
+
import_names,
|
|
186
|
+
fallback=fallback,
|
|
187
|
+
resolve_imports=resolve_imports,
|
|
188
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
189
|
+
unmount_before_update=unmount_before_update,
|
|
190
|
+
allow_children=allow_children,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@overload
|
|
195
|
+
def component_from_file(
|
|
196
|
+
file: str | Path,
|
|
197
|
+
import_names: str,
|
|
198
|
+
resolve_imports: bool = ...,
|
|
199
|
+
resolve_imports_depth: int = ...,
|
|
200
|
+
name: str = "",
|
|
201
|
+
fallback: Any | None = ...,
|
|
202
|
+
unmount_before_update: bool = ...,
|
|
203
|
+
symlink: bool = ...,
|
|
204
|
+
allow_children: bool = ...,
|
|
205
|
+
) -> VdomConstructor: ...
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@overload
|
|
209
|
+
def component_from_file(
|
|
210
|
+
file: str | Path,
|
|
211
|
+
import_names: list[str] | tuple[str, ...],
|
|
212
|
+
resolve_imports: bool = ...,
|
|
213
|
+
resolve_imports_depth: int = ...,
|
|
214
|
+
name: str = "",
|
|
215
|
+
fallback: Any | None = ...,
|
|
216
|
+
unmount_before_update: bool = ...,
|
|
217
|
+
symlink: bool = ...,
|
|
218
|
+
allow_children: bool = ...,
|
|
219
|
+
) -> list[VdomConstructor]: ...
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def component_from_file(
|
|
223
|
+
file: str | Path,
|
|
224
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
225
|
+
resolve_imports: bool = False,
|
|
226
|
+
resolve_imports_depth: int = 5,
|
|
227
|
+
name: str = "",
|
|
228
|
+
fallback: Any | None = None,
|
|
229
|
+
unmount_before_update: bool = False,
|
|
230
|
+
symlink: bool = False,
|
|
231
|
+
allow_children: bool = True,
|
|
232
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
233
|
+
"""Import a component from a file.
|
|
234
|
+
|
|
235
|
+
Parameters:
|
|
236
|
+
file:
|
|
237
|
+
The file from which the content of the web module will be created.
|
|
238
|
+
import_names:
|
|
239
|
+
One or more component names to import. If given as a string, a single component
|
|
240
|
+
will be returned. If a list is given, then a list of components will be
|
|
241
|
+
returned.
|
|
242
|
+
resolve_imports:
|
|
243
|
+
Whether to try and find all the named imports of this module.
|
|
244
|
+
resolve_imports_depth:
|
|
245
|
+
How deeply to search for those imports.
|
|
246
|
+
name:
|
|
247
|
+
The human-readable name of the ReactJS package
|
|
248
|
+
fallback:
|
|
249
|
+
What to temporarily display while the module is being loaded.
|
|
250
|
+
unmount_before_update:
|
|
251
|
+
Cause the component to be unmounted before each update. This option should
|
|
252
|
+
only be used if the imported package fails to re-render when props change.
|
|
253
|
+
Using this option has negative performance consequences since all DOM
|
|
254
|
+
elements must be changed on each render. See :issue:`461` for more info.
|
|
255
|
+
symlink:
|
|
256
|
+
Whether the web module should be saved as a symlink to the given ``file``.
|
|
257
|
+
allow_children:
|
|
258
|
+
Whether or not these components can have children.
|
|
259
|
+
"""
|
|
260
|
+
name = name or hashlib.sha256(str(file).encode()).hexdigest()[:10]
|
|
261
|
+
key = f"{name}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
|
|
262
|
+
if key in _FILE_JS_MODULE_CACHE:
|
|
263
|
+
module = _FILE_JS_MODULE_CACHE[key]
|
|
264
|
+
else:
|
|
265
|
+
module = file_to_module(
|
|
266
|
+
name,
|
|
267
|
+
file,
|
|
268
|
+
fallback=fallback,
|
|
269
|
+
resolve_imports=resolve_imports,
|
|
270
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
271
|
+
unmount_before_update=unmount_before_update,
|
|
272
|
+
symlink=symlink,
|
|
273
|
+
)
|
|
274
|
+
_FILE_JS_MODULE_CACHE[key] = module
|
|
275
|
+
return module_to_vdom(module, import_names, fallback, allow_children)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@overload
|
|
279
|
+
def component_from_string(
|
|
280
|
+
content: str,
|
|
281
|
+
import_names: str,
|
|
282
|
+
resolve_imports: bool = ...,
|
|
283
|
+
resolve_imports_depth: int = ...,
|
|
284
|
+
name: str = "",
|
|
285
|
+
fallback: Any | None = ...,
|
|
286
|
+
unmount_before_update: bool = ...,
|
|
287
|
+
allow_children: bool = ...,
|
|
288
|
+
) -> VdomConstructor: ...
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@overload
|
|
292
|
+
def component_from_string(
|
|
293
|
+
content: str,
|
|
294
|
+
import_names: list[str] | tuple[str, ...],
|
|
295
|
+
resolve_imports: bool = ...,
|
|
296
|
+
resolve_imports_depth: int = ...,
|
|
297
|
+
name: str = "",
|
|
298
|
+
fallback: Any | None = ...,
|
|
299
|
+
unmount_before_update: bool = ...,
|
|
300
|
+
allow_children: bool = ...,
|
|
301
|
+
) -> list[VdomConstructor]: ...
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def component_from_string(
|
|
305
|
+
content: str,
|
|
306
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
307
|
+
resolve_imports: bool = False,
|
|
308
|
+
resolve_imports_depth: int = 5,
|
|
309
|
+
name: str = "",
|
|
310
|
+
fallback: Any | None = None,
|
|
311
|
+
unmount_before_update: bool = False,
|
|
312
|
+
allow_children: bool = True,
|
|
313
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
314
|
+
"""Import a component from a string.
|
|
315
|
+
|
|
316
|
+
Parameters:
|
|
317
|
+
content:
|
|
318
|
+
The contents of the web module
|
|
319
|
+
import_names:
|
|
320
|
+
One or more component names to import. If given as a string, a single component
|
|
321
|
+
will be returned. If a list is given, then a list of components will be
|
|
322
|
+
returned.
|
|
323
|
+
resolve_imports:
|
|
324
|
+
Whether to try and find all the named imports of this module.
|
|
325
|
+
resolve_imports_depth:
|
|
326
|
+
How deeply to search for those imports.
|
|
327
|
+
name:
|
|
328
|
+
The human-readable name of the ReactJS package
|
|
329
|
+
fallback:
|
|
330
|
+
What to temporarily display while the module is being loaded.
|
|
331
|
+
unmount_before_update:
|
|
332
|
+
Cause the component to be unmounted before each update. This option should
|
|
333
|
+
only be used if the imported package fails to re-render when props change.
|
|
334
|
+
Using this option has negative performance consequences since all DOM
|
|
335
|
+
elements must be changed on each render. See :issue:`461` for more info.
|
|
336
|
+
allow_children:
|
|
337
|
+
Whether or not these components can have children.
|
|
338
|
+
"""
|
|
339
|
+
name = name or hashlib.sha256(content.encode()).hexdigest()[:10]
|
|
340
|
+
key = f"{name}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
|
|
341
|
+
if key in _STRING_JS_MODULE_CACHE:
|
|
342
|
+
module = _STRING_JS_MODULE_CACHE[key]
|
|
343
|
+
else:
|
|
344
|
+
module = string_to_module(
|
|
345
|
+
name,
|
|
346
|
+
content,
|
|
347
|
+
fallback=fallback,
|
|
348
|
+
resolve_imports=resolve_imports,
|
|
349
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
350
|
+
unmount_before_update=unmount_before_update,
|
|
351
|
+
)
|
|
352
|
+
_STRING_JS_MODULE_CACHE[key] = module
|
|
353
|
+
return module_to_vdom(module, import_names, fallback, allow_children)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from reactpy import config
|
|
8
|
+
from reactpy.config import REACTPY_WEB_MODULES_DIR
|
|
9
|
+
from reactpy.core.vdom import Vdom
|
|
10
|
+
from reactpy.reactjs.types import NAME_SOURCE, URL_SOURCE
|
|
11
|
+
from reactpy.reactjs.utils import (
|
|
12
|
+
are_files_identical,
|
|
13
|
+
copy_file,
|
|
14
|
+
module_name_suffix,
|
|
15
|
+
resolve_from_module_file,
|
|
16
|
+
resolve_from_module_url,
|
|
17
|
+
)
|
|
18
|
+
from reactpy.types import ImportSourceDict, JavaScriptModule, VdomConstructor
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def url_to_module(
|
|
24
|
+
url: str,
|
|
25
|
+
fallback: Any | None = None,
|
|
26
|
+
resolve_imports: bool = True,
|
|
27
|
+
resolve_imports_depth: int = 5,
|
|
28
|
+
unmount_before_update: bool = False,
|
|
29
|
+
) -> JavaScriptModule:
|
|
30
|
+
return JavaScriptModule(
|
|
31
|
+
source=url,
|
|
32
|
+
source_type=URL_SOURCE,
|
|
33
|
+
default_fallback=fallback,
|
|
34
|
+
file=None,
|
|
35
|
+
import_names=(
|
|
36
|
+
resolve_from_module_url(url, resolve_imports_depth)
|
|
37
|
+
if resolve_imports
|
|
38
|
+
else None
|
|
39
|
+
),
|
|
40
|
+
unmount_before_update=unmount_before_update,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def file_to_module(
|
|
45
|
+
name: str,
|
|
46
|
+
file: str | Path,
|
|
47
|
+
fallback: Any | None = None,
|
|
48
|
+
resolve_imports: bool = True,
|
|
49
|
+
resolve_imports_depth: int = 5,
|
|
50
|
+
unmount_before_update: bool = False,
|
|
51
|
+
symlink: bool = False,
|
|
52
|
+
) -> JavaScriptModule:
|
|
53
|
+
name += module_name_suffix(name)
|
|
54
|
+
|
|
55
|
+
source_file = Path(file).resolve()
|
|
56
|
+
target_file = get_module_path(name)
|
|
57
|
+
if not source_file.exists():
|
|
58
|
+
msg = f"Source file does not exist: {source_file}"
|
|
59
|
+
raise FileNotFoundError(msg)
|
|
60
|
+
|
|
61
|
+
if not target_file.exists():
|
|
62
|
+
copy_file(target_file, source_file, symlink)
|
|
63
|
+
elif not are_files_identical(source_file, target_file):
|
|
64
|
+
logger.info(
|
|
65
|
+
f"Existing web module {name!r} will "
|
|
66
|
+
f"be replaced with {target_file.resolve()}"
|
|
67
|
+
)
|
|
68
|
+
target_file.unlink()
|
|
69
|
+
copy_file(target_file, source_file, symlink)
|
|
70
|
+
|
|
71
|
+
return JavaScriptModule(
|
|
72
|
+
source=name,
|
|
73
|
+
source_type=NAME_SOURCE,
|
|
74
|
+
default_fallback=fallback,
|
|
75
|
+
file=target_file,
|
|
76
|
+
import_names=(
|
|
77
|
+
resolve_from_module_file(source_file, resolve_imports_depth)
|
|
78
|
+
if resolve_imports
|
|
79
|
+
else None
|
|
80
|
+
),
|
|
81
|
+
unmount_before_update=unmount_before_update,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def string_to_module(
|
|
86
|
+
name: str,
|
|
87
|
+
content: str,
|
|
88
|
+
fallback: Any | None = None,
|
|
89
|
+
resolve_imports: bool = True,
|
|
90
|
+
resolve_imports_depth: int = 5,
|
|
91
|
+
unmount_before_update: bool = False,
|
|
92
|
+
) -> JavaScriptModule:
|
|
93
|
+
name += module_name_suffix(name)
|
|
94
|
+
|
|
95
|
+
target_file = get_module_path(name)
|
|
96
|
+
|
|
97
|
+
if target_file.exists() and target_file.read_text(encoding="utf-8") != content:
|
|
98
|
+
logger.info(
|
|
99
|
+
f"Existing web module {name!r} will "
|
|
100
|
+
f"be replaced with {target_file.resolve()}"
|
|
101
|
+
)
|
|
102
|
+
target_file.unlink()
|
|
103
|
+
|
|
104
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
target_file.write_text(content)
|
|
106
|
+
|
|
107
|
+
return JavaScriptModule(
|
|
108
|
+
source=name,
|
|
109
|
+
source_type=NAME_SOURCE,
|
|
110
|
+
default_fallback=fallback,
|
|
111
|
+
file=target_file,
|
|
112
|
+
import_names=(
|
|
113
|
+
resolve_from_module_file(target_file, resolve_imports_depth)
|
|
114
|
+
if resolve_imports
|
|
115
|
+
else None
|
|
116
|
+
),
|
|
117
|
+
unmount_before_update=unmount_before_update,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def module_to_vdom(
|
|
122
|
+
web_module: JavaScriptModule,
|
|
123
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
124
|
+
fallback: Any | None = None,
|
|
125
|
+
allow_children: bool = True,
|
|
126
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
127
|
+
"""Return one or more VDOM constructors from a :class:`JavaScriptModule`
|
|
128
|
+
|
|
129
|
+
Parameters:
|
|
130
|
+
import_names:
|
|
131
|
+
One or more names to import. If given as a string, a single component
|
|
132
|
+
will be returned. If a list is given, then a list of components will be
|
|
133
|
+
returned.
|
|
134
|
+
fallback:
|
|
135
|
+
What to temporarily display while the module is being loaded.
|
|
136
|
+
allow_children:
|
|
137
|
+
Whether or not these components can have children.
|
|
138
|
+
"""
|
|
139
|
+
if isinstance(import_names, str):
|
|
140
|
+
if (
|
|
141
|
+
web_module.import_names is not None
|
|
142
|
+
and import_names.split(".")[0] not in web_module.import_names
|
|
143
|
+
):
|
|
144
|
+
msg = f"{web_module.source!r} does not contain {import_names!r}"
|
|
145
|
+
raise ValueError(msg)
|
|
146
|
+
return make_module(web_module, import_names, fallback, allow_children)
|
|
147
|
+
else:
|
|
148
|
+
if web_module.import_names is not None:
|
|
149
|
+
missing = sorted(
|
|
150
|
+
{e.split(".")[0] for e in import_names}.difference(
|
|
151
|
+
web_module.import_names
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
if missing:
|
|
155
|
+
msg = f"{web_module.source!r} does not contain {missing!r}"
|
|
156
|
+
raise ValueError(msg)
|
|
157
|
+
return [
|
|
158
|
+
make_module(web_module, name, fallback, allow_children)
|
|
159
|
+
for name in import_names
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def make_module(
|
|
164
|
+
web_module: JavaScriptModule,
|
|
165
|
+
name: str,
|
|
166
|
+
fallback: Any | None,
|
|
167
|
+
allow_children: bool,
|
|
168
|
+
) -> VdomConstructor:
|
|
169
|
+
return Vdom(
|
|
170
|
+
name,
|
|
171
|
+
allow_children=allow_children,
|
|
172
|
+
import_source=ImportSourceDict(
|
|
173
|
+
source=web_module.source,
|
|
174
|
+
sourceType=web_module.source_type,
|
|
175
|
+
fallback=(fallback or web_module.default_fallback),
|
|
176
|
+
unmountBeforeUpdate=web_module.unmount_before_update,
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_module_path(name: str) -> Path:
|
|
182
|
+
directory = REACTPY_WEB_MODULES_DIR.current
|
|
183
|
+
path = directory.joinpath(*name.split("/"))
|
|
184
|
+
return path.with_suffix(path.suffix)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def import_reactjs():
|
|
188
|
+
from reactpy import html
|
|
189
|
+
|
|
190
|
+
base_url = config.REACTPY_PATH_PREFIX.current.strip("/")
|
|
191
|
+
return html.script(
|
|
192
|
+
{"type": "importmap"},
|
|
193
|
+
f"""
|
|
194
|
+
{{
|
|
195
|
+
"imports": {{
|
|
196
|
+
"react": "/{base_url}/static/react.js",
|
|
197
|
+
"react-dom": "/{base_url}/static/react-dom.js",
|
|
198
|
+
"react-dom/client": "/{base_url}/static/react-dom.js",
|
|
199
|
+
"react/jsx-runtime": "/{base_url}/static/react-jsx-runtime.js"
|
|
200
|
+
}}
|
|
201
|
+
}}
|
|
202
|
+
""",
|
|
203
|
+
)
|