reactpy 2.0.0b5__py3-none-any.whl → 2.0.0b7__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 +4 -3
- reactpy/_html.py +11 -9
- reactpy/config.py +1 -1
- reactpy/core/_life_cycle_hook.py +4 -1
- reactpy/core/_thread_local.py +1 -1
- reactpy/core/hooks.py +11 -19
- reactpy/core/layout.py +43 -38
- reactpy/core/serve.py +11 -16
- reactpy/core/vdom.py +3 -5
- reactpy/executors/asgi/pyscript.py +4 -1
- reactpy/executors/asgi/standalone.py +1 -1
- reactpy/{pyscript → executors/pyscript}/component_template.py +1 -1
- reactpy/{pyscript → executors/pyscript}/components.py +1 -1
- reactpy/{pyscript → executors/pyscript}/utils.py +1 -1
- reactpy/executors/utils.py +32 -7
- reactpy/reactjs/__init__.py +351 -0
- reactpy/reactjs/module.py +267 -0
- reactpy/reactjs/types.py +7 -0
- reactpy/reactjs/utils.py +212 -0
- reactpy/static/index-64wy0fss.js +5 -0
- reactpy/static/index-64wy0fss.js.map +10 -0
- reactpy/static/index-beq660xy.js +5 -0
- reactpy/static/index-beq660xy.js.map +12 -0
- reactpy/static/index.js +2 -2
- reactpy/static/index.js.map +7 -10
- reactpy/static/preact-dom.js +4 -0
- reactpy/static/preact-dom.js.map +11 -0
- reactpy/static/preact-jsx-runtime.js +4 -0
- reactpy/static/preact-jsx-runtime.js.map +9 -0
- reactpy/static/preact.js +4 -0
- reactpy/static/preact.js.map +10 -0
- reactpy/templatetags/jinja.py +4 -1
- reactpy/testing/__init__.py +2 -7
- reactpy/testing/backend.py +24 -12
- reactpy/testing/common.py +1 -9
- reactpy/testing/display.py +65 -28
- reactpy/testing/logs.py +1 -1
- reactpy/transforms.py +2 -2
- reactpy/types.py +17 -11
- reactpy/utils.py +1 -1
- reactpy/web/__init__.py +0 -6
- reactpy/web/module.py +37 -473
- reactpy/web/utils.py +2 -158
- {reactpy-2.0.0b5.dist-info → reactpy-2.0.0b7.dist-info}/METADATA +1 -1
- {reactpy-2.0.0b5.dist-info → reactpy-2.0.0b7.dist-info}/RECORD +50 -38
- reactpy/testing/utils.py +0 -27
- reactpy/web/templates/react.js +0 -61
- /reactpy/{pyscript → executors/pyscript}/__init__.py +0 -0
- /reactpy/{pyscript → executors/pyscript}/layout_handler.py +0 -0
- {reactpy-2.0.0b5.dist-info → reactpy-2.0.0b7.dist-info}/WHEEL +0 -0
- {reactpy-2.0.0b5.dist-info → reactpy-2.0.0b7.dist-info}/entry_points.txt +0 -0
- {reactpy-2.0.0b5.dist-info → reactpy-2.0.0b7.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
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
|
+
url += "&" if "?" in url else "?"
|
|
179
|
+
url += "external=react,react-dom,react/jsx-runtime&bundle&target=es2020"
|
|
180
|
+
|
|
181
|
+
return component_from_url(
|
|
182
|
+
url,
|
|
183
|
+
import_names,
|
|
184
|
+
fallback=fallback,
|
|
185
|
+
resolve_imports=resolve_imports,
|
|
186
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
187
|
+
unmount_before_update=unmount_before_update,
|
|
188
|
+
allow_children=allow_children,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@overload
|
|
193
|
+
def component_from_file(
|
|
194
|
+
file: str | Path,
|
|
195
|
+
import_names: str,
|
|
196
|
+
resolve_imports: bool = ...,
|
|
197
|
+
resolve_imports_depth: int = ...,
|
|
198
|
+
name: str = "",
|
|
199
|
+
fallback: Any | None = ...,
|
|
200
|
+
unmount_before_update: bool = ...,
|
|
201
|
+
symlink: bool = ...,
|
|
202
|
+
allow_children: bool = ...,
|
|
203
|
+
) -> VdomConstructor: ...
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@overload
|
|
207
|
+
def component_from_file(
|
|
208
|
+
file: str | Path,
|
|
209
|
+
import_names: list[str] | tuple[str, ...],
|
|
210
|
+
resolve_imports: bool = ...,
|
|
211
|
+
resolve_imports_depth: int = ...,
|
|
212
|
+
name: str = "",
|
|
213
|
+
fallback: Any | None = ...,
|
|
214
|
+
unmount_before_update: bool = ...,
|
|
215
|
+
symlink: bool = ...,
|
|
216
|
+
allow_children: bool = ...,
|
|
217
|
+
) -> list[VdomConstructor]: ...
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def component_from_file(
|
|
221
|
+
file: str | Path,
|
|
222
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
223
|
+
resolve_imports: bool = False,
|
|
224
|
+
resolve_imports_depth: int = 5,
|
|
225
|
+
name: str = "",
|
|
226
|
+
fallback: Any | None = None,
|
|
227
|
+
unmount_before_update: bool = False,
|
|
228
|
+
symlink: bool = False,
|
|
229
|
+
allow_children: bool = True,
|
|
230
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
231
|
+
"""Import a component from a file.
|
|
232
|
+
|
|
233
|
+
Parameters:
|
|
234
|
+
file:
|
|
235
|
+
The file from which the content of the web module will be created.
|
|
236
|
+
import_names:
|
|
237
|
+
One or more component names to import. If given as a string, a single component
|
|
238
|
+
will be returned. If a list is given, then a list of components will be
|
|
239
|
+
returned.
|
|
240
|
+
resolve_imports:
|
|
241
|
+
Whether to try and find all the named imports of this module.
|
|
242
|
+
resolve_imports_depth:
|
|
243
|
+
How deeply to search for those imports.
|
|
244
|
+
name:
|
|
245
|
+
The human-readable name of the ReactJS package
|
|
246
|
+
fallback:
|
|
247
|
+
What to temporarily display while the module is being loaded.
|
|
248
|
+
unmount_before_update:
|
|
249
|
+
Cause the component to be unmounted before each update. This option should
|
|
250
|
+
only be used if the imported package fails to re-render when props change.
|
|
251
|
+
Using this option has negative performance consequences since all DOM
|
|
252
|
+
elements must be changed on each render. See :issue:`461` for more info.
|
|
253
|
+
symlink:
|
|
254
|
+
Whether the web module should be saved as a symlink to the given ``file``.
|
|
255
|
+
allow_children:
|
|
256
|
+
Whether or not these components can have children.
|
|
257
|
+
"""
|
|
258
|
+
name = name or hashlib.sha256(str(file).encode()).hexdigest()[:10]
|
|
259
|
+
key = f"{name}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
|
|
260
|
+
if key in _FILE_JS_MODULE_CACHE:
|
|
261
|
+
module = _FILE_JS_MODULE_CACHE[key]
|
|
262
|
+
else:
|
|
263
|
+
module = file_to_module(
|
|
264
|
+
name,
|
|
265
|
+
file,
|
|
266
|
+
fallback=fallback,
|
|
267
|
+
resolve_imports=resolve_imports,
|
|
268
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
269
|
+
unmount_before_update=unmount_before_update,
|
|
270
|
+
symlink=symlink,
|
|
271
|
+
)
|
|
272
|
+
_FILE_JS_MODULE_CACHE[key] = module
|
|
273
|
+
return module_to_vdom(module, import_names, fallback, allow_children)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@overload
|
|
277
|
+
def component_from_string(
|
|
278
|
+
content: str,
|
|
279
|
+
import_names: str,
|
|
280
|
+
resolve_imports: bool = ...,
|
|
281
|
+
resolve_imports_depth: int = ...,
|
|
282
|
+
name: str = "",
|
|
283
|
+
fallback: Any | None = ...,
|
|
284
|
+
unmount_before_update: bool = ...,
|
|
285
|
+
allow_children: bool = ...,
|
|
286
|
+
) -> VdomConstructor: ...
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@overload
|
|
290
|
+
def component_from_string(
|
|
291
|
+
content: str,
|
|
292
|
+
import_names: list[str] | tuple[str, ...],
|
|
293
|
+
resolve_imports: bool = ...,
|
|
294
|
+
resolve_imports_depth: int = ...,
|
|
295
|
+
name: str = "",
|
|
296
|
+
fallback: Any | None = ...,
|
|
297
|
+
unmount_before_update: bool = ...,
|
|
298
|
+
allow_children: bool = ...,
|
|
299
|
+
) -> list[VdomConstructor]: ...
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def component_from_string(
|
|
303
|
+
content: str,
|
|
304
|
+
import_names: str | list[str] | tuple[str, ...],
|
|
305
|
+
resolve_imports: bool = False,
|
|
306
|
+
resolve_imports_depth: int = 5,
|
|
307
|
+
name: str = "",
|
|
308
|
+
fallback: Any | None = None,
|
|
309
|
+
unmount_before_update: bool = False,
|
|
310
|
+
allow_children: bool = True,
|
|
311
|
+
) -> VdomConstructor | list[VdomConstructor]:
|
|
312
|
+
"""Import a component from a string.
|
|
313
|
+
|
|
314
|
+
Parameters:
|
|
315
|
+
content:
|
|
316
|
+
The contents of the web module
|
|
317
|
+
import_names:
|
|
318
|
+
One or more component names to import. If given as a string, a single component
|
|
319
|
+
will be returned. If a list is given, then a list of components will be
|
|
320
|
+
returned.
|
|
321
|
+
resolve_imports:
|
|
322
|
+
Whether to try and find all the named imports of this module.
|
|
323
|
+
resolve_imports_depth:
|
|
324
|
+
How deeply to search for those imports.
|
|
325
|
+
name:
|
|
326
|
+
The human-readable name of the ReactJS package
|
|
327
|
+
fallback:
|
|
328
|
+
What to temporarily display while the module is being loaded.
|
|
329
|
+
unmount_before_update:
|
|
330
|
+
Cause the component to be unmounted before each update. This option should
|
|
331
|
+
only be used if the imported package fails to re-render when props change.
|
|
332
|
+
Using this option has negative performance consequences since all DOM
|
|
333
|
+
elements must be changed on each render. See :issue:`461` for more info.
|
|
334
|
+
allow_children:
|
|
335
|
+
Whether or not these components can have children.
|
|
336
|
+
"""
|
|
337
|
+
name = name or hashlib.sha256(content.encode()).hexdigest()[:10]
|
|
338
|
+
key = f"{name}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
|
|
339
|
+
if key in _STRING_JS_MODULE_CACHE:
|
|
340
|
+
module = _STRING_JS_MODULE_CACHE[key]
|
|
341
|
+
else:
|
|
342
|
+
module = string_to_module(
|
|
343
|
+
name,
|
|
344
|
+
content,
|
|
345
|
+
fallback=fallback,
|
|
346
|
+
resolve_imports=resolve_imports,
|
|
347
|
+
resolve_imports_depth=resolve_imports_depth,
|
|
348
|
+
unmount_before_update=unmount_before_update,
|
|
349
|
+
)
|
|
350
|
+
_STRING_JS_MODULE_CACHE[key] = module
|
|
351
|
+
return module_to_vdom(module, import_names, fallback, allow_children)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path, PurePosixPath
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from reactpy.config import REACTPY_DEBUG, REACTPY_WEB_MODULES_DIR
|
|
8
|
+
from reactpy.core.vdom import Vdom
|
|
9
|
+
from reactpy.reactjs.types import NAME_SOURCE, URL_SOURCE
|
|
10
|
+
from reactpy.reactjs.utils import (
|
|
11
|
+
are_files_identical,
|
|
12
|
+
copy_file,
|
|
13
|
+
file_lock,
|
|
14
|
+
resolve_names_from_file,
|
|
15
|
+
resolve_names_from_url,
|
|
16
|
+
)
|
|
17
|
+
from reactpy.types import ImportSourceDict, JavaScriptModule, VdomConstructor, VdomDict
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def url_to_module(
|
|
23
|
+
url: str,
|
|
24
|
+
fallback: Any | None = None,
|
|
25
|
+
resolve_imports: bool = True,
|
|
26
|
+
resolve_imports_depth: int = 5,
|
|
27
|
+
unmount_before_update: bool = False,
|
|
28
|
+
) -> JavaScriptModule:
|
|
29
|
+
return JavaScriptModule(
|
|
30
|
+
source=url,
|
|
31
|
+
source_type=URL_SOURCE,
|
|
32
|
+
default_fallback=fallback,
|
|
33
|
+
file=None,
|
|
34
|
+
import_names=(
|
|
35
|
+
resolve_names_from_url(url, resolve_imports_depth)
|
|
36
|
+
if resolve_imports
|
|
37
|
+
else None
|
|
38
|
+
),
|
|
39
|
+
unmount_before_update=unmount_before_update,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def file_to_module(
|
|
44
|
+
name: str,
|
|
45
|
+
file: str | Path,
|
|
46
|
+
fallback: Any | None = None,
|
|
47
|
+
resolve_imports: bool = True,
|
|
48
|
+
resolve_imports_depth: int = 5,
|
|
49
|
+
unmount_before_update: bool = False,
|
|
50
|
+
symlink: bool = False,
|
|
51
|
+
) -> JavaScriptModule:
|
|
52
|
+
name += module_name_suffix(name)
|
|
53
|
+
|
|
54
|
+
source_file = Path(file).resolve()
|
|
55
|
+
target_file = get_module_path(name)
|
|
56
|
+
|
|
57
|
+
with file_lock(target_file.with_name(f"{target_file.name}.lock")):
|
|
58
|
+
if not source_file.exists():
|
|
59
|
+
msg = f"Source file does not exist: {source_file}"
|
|
60
|
+
raise FileNotFoundError(msg)
|
|
61
|
+
|
|
62
|
+
if not target_file.exists():
|
|
63
|
+
copy_file(target_file, source_file, symlink)
|
|
64
|
+
elif not are_files_identical(source_file, target_file):
|
|
65
|
+
logger.info(
|
|
66
|
+
f"Existing web module {name!r} will "
|
|
67
|
+
f"be replaced with {target_file.resolve()}"
|
|
68
|
+
)
|
|
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_names_from_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_names_from_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 import_reactjs(
|
|
182
|
+
framework: Literal["preact", "react"] | None = None,
|
|
183
|
+
version: str | None = None,
|
|
184
|
+
use_local: bool = False,
|
|
185
|
+
) -> VdomDict:
|
|
186
|
+
"""
|
|
187
|
+
Return an import map script tag for ReactJS or Preact.
|
|
188
|
+
Parameters:
|
|
189
|
+
framework:
|
|
190
|
+
The framework to use, either "preact" or "react". Defaults to "preact" for
|
|
191
|
+
performance reasons. Set this to `react` if you are experiencing compatibility
|
|
192
|
+
issues with your component library.
|
|
193
|
+
version:
|
|
194
|
+
The version of the framework to use. Example values include "18", "10.2.4",
|
|
195
|
+
or "latest". If left as `None`, a default version will be used depending on the
|
|
196
|
+
selected framework.
|
|
197
|
+
use_local:
|
|
198
|
+
Whether to use the local framework ReactPy is bundled with (Preact).
|
|
199
|
+
Raises:
|
|
200
|
+
ValueError:
|
|
201
|
+
If both `framework` and `react_url_prefix` are provided, or if
|
|
202
|
+
`framework` is not one of "preact" or "react".
|
|
203
|
+
Returns:
|
|
204
|
+
A VDOM script tag containing the import map.
|
|
205
|
+
"""
|
|
206
|
+
from reactpy import html
|
|
207
|
+
from reactpy.executors.utils import default_import_map
|
|
208
|
+
|
|
209
|
+
if use_local and (framework or version): # nocov
|
|
210
|
+
raise ValueError("use_local cannot be used with framework or version")
|
|
211
|
+
|
|
212
|
+
framework = framework or "preact"
|
|
213
|
+
if framework and framework not in {"preact", "react"}: # nocov
|
|
214
|
+
raise ValueError("framework must be 'preact' or 'react'")
|
|
215
|
+
|
|
216
|
+
# Import map for ReactPy's local framework (re-exported/bundled/minified version of Preact)
|
|
217
|
+
if use_local:
|
|
218
|
+
return html.script(
|
|
219
|
+
{"type": "importmap", "id": "reactpy-importmap"},
|
|
220
|
+
default_import_map(),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Import map for ReactJS from esm.sh
|
|
224
|
+
if framework == "react":
|
|
225
|
+
version = version or "19"
|
|
226
|
+
postfix = "?dev" if REACTPY_DEBUG.current else ""
|
|
227
|
+
return html.script(
|
|
228
|
+
{"type": "importmap", "id": "reactpy-importmap"},
|
|
229
|
+
f"""{{
|
|
230
|
+
"imports": {{
|
|
231
|
+
"react": "https://esm.sh/react@{version}{postfix}",
|
|
232
|
+
"react-dom": "https://esm.sh/react-dom@{version}{postfix}",
|
|
233
|
+
"react-dom/client": "https://esm.sh/react-dom@{version}/client{postfix}",
|
|
234
|
+
"react/jsx-runtime": "https://esm.sh/react@{version}/jsx-runtime{postfix}"
|
|
235
|
+
}}
|
|
236
|
+
}}""".replace("\n", "").replace(" ", ""),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Import map for Preact from esm.sh
|
|
240
|
+
if framework == "preact":
|
|
241
|
+
version = version or "10"
|
|
242
|
+
postfix = "?dev" if REACTPY_DEBUG.current else ""
|
|
243
|
+
return html.script(
|
|
244
|
+
{"type": "importmap", "id": "reactpy-importmap"},
|
|
245
|
+
f"""{{
|
|
246
|
+
"imports": {{
|
|
247
|
+
"react": "https://esm.sh/preact@{version}/compat{postfix}",
|
|
248
|
+
"react-dom": "https://esm.sh/preact@{version}/compat{postfix}",
|
|
249
|
+
"react-dom/client": "https://esm.sh/preact@{version}/compat/client{postfix}",
|
|
250
|
+
"react/jsx-runtime": "https://esm.sh/preact@{version}/compat/jsx-runtime{postfix}"
|
|
251
|
+
}}
|
|
252
|
+
}}""".replace("\n", "").replace(" ", ""),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def module_name_suffix(name: str) -> str:
|
|
257
|
+
if name.startswith("@"):
|
|
258
|
+
name = name[1:]
|
|
259
|
+
head, _, tail = name.partition("@") # handle version identifier
|
|
260
|
+
_, _, tail = tail.partition("/") # get section after version
|
|
261
|
+
return PurePosixPath(tail or head).suffix or ".js"
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def get_module_path(name: str) -> Path:
|
|
265
|
+
directory = REACTPY_WEB_MODULES_DIR.current
|
|
266
|
+
path = directory.joinpath(*name.split("/"))
|
|
267
|
+
return path.with_suffix(path.suffix)
|