Skip to content

Commit 6982747

Browse files
committed
create new reactpy.reactjs module
1 parent 1fb7174 commit 6982747

25 files changed

+1080
-841
lines changed

src/reactpy/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from reactpy import config, logging, types, web, widgets
1+
from reactpy import config, logging, reactjs, types, web, widgets
22
from reactpy._html import html
33
from reactpy.core import hooks
44
from reactpy.core.component import component
@@ -36,6 +36,7 @@
3636
"html",
3737
"logging",
3838
"pyscript_component",
39+
"reactjs",
3940
"reactpy_to_string",
4041
"string_to_reactpy",
4142
"types",

src/reactpy/reactjs/__init__.py

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 = True,
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 = True,
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 = True,
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 = True,
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 commit comments

Comments
 (0)