Skip to content

Commit 53982b7

Browse files
committed
initial implementation of reactjy_component_from_npm
1 parent d4c3177 commit 53982b7

File tree

6 files changed

+109
-6
lines changed

6 files changed

+109
-6
lines changed
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
export { mountReactPy } from "@reactpy/client";
1+
export {
2+
mountReactPy,
3+
React,
4+
ReactDOM,
5+
jsx,
6+
jsxs,
7+
Fragment,
8+
} from "@reactpy/client";

src/js/packages/@reactpy/client/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export * from "./vdom";
66
export * from "./websocket";
77
export { default as React } from "preact/compat";
88
export { default as ReactDOM } from "preact/compat";
9+
export { jsx, jsxs, Fragment } from "preact/jsx-runtime";
910
export * as preact from "preact";

src/reactpy/testing/backend.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
list_logged_exceptions,
2323
)
2424
from reactpy.testing.utils import find_available_port
25-
from reactpy.types import ComponentConstructor, ReactPyConfig
25+
from reactpy.types import ComponentConstructor
2626
from reactpy.utils import Ref
2727

2828

@@ -48,7 +48,7 @@ def __init__(
4848
host: str = "127.0.0.1",
4949
port: int | None = None,
5050
timeout: float | None = None,
51-
reactpy_config: ReactPyConfig | None = None,
51+
**reactpy_config: Any,
5252
) -> None:
5353
self.host = host
5454
self.port = port or find_available_port(host)
@@ -62,12 +62,12 @@ def __init__(
6262
self._app = ReactPyMiddleware(
6363
app,
6464
root_components=["reactpy.testing.backend.root_hotswap_component"],
65-
**(reactpy_config or {}),
65+
**reactpy_config,
6666
)
6767
else:
6868
self._app = ReactPy(
6969
root_hotswap_component,
70-
**(reactpy_config or {}),
70+
**reactpy_config,
7171
)
7272
self.webserver = uvicorn.Server(
7373
uvicorn.Config(

src/reactpy/testing/display.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ async def __aenter__(self) -> DisplayFixture:
5757
self.page = await browser.new_page()
5858

5959
self.page.set_default_timeout(REACTPY_TESTS_DEFAULT_TIMEOUT.current * 1000)
60-
60+
self.page.on("console", lambda msg: print(f"BROWSER CONSOLE: {msg.text}")) # noqa: T201
61+
self.page.on("pageerror", lambda exc: print(f"BROWSER ERROR: {exc}")) # noqa: T201
6162
if not hasattr(self, "backend"): # nocov
6263
self.backend = BackendFixture()
6364
await es.enter_async_context(self.backend)

src/reactpy/web/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module_from_string,
55
module_from_url,
66
reactjs_component_from_file,
7+
reactjs_component_from_npm,
78
reactjs_component_from_string,
89
reactjs_component_from_url,
910
)
@@ -14,6 +15,7 @@
1415
"module_from_string",
1516
"module_from_url",
1617
"reactjs_component_from_file",
18+
"reactjs_component_from_npm",
1719
"reactjs_component_from_string",
1820
"reactjs_component_from_url",
1921
]

src/reactpy/web/module.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,98 @@ def reactjs_component_from_url(
105105
return _vdom_from_web_module(module, import_names, fallback, allow_children)
106106

107107

108+
@overload
109+
def reactjs_component_from_npm(
110+
package: str,
111+
import_names: str,
112+
version: str = "latest",
113+
file: str = "",
114+
cdn: str = "https://esm.sh",
115+
fallback: Any | None = ...,
116+
resolve_imports: bool | None = ...,
117+
resolve_imports_depth: int = ...,
118+
unmount_before_update: bool = ...,
119+
allow_children: bool = ...,
120+
) -> VdomConstructor: ...
121+
122+
123+
@overload
124+
def reactjs_component_from_npm(
125+
package: str,
126+
import_names: list[str] | tuple[str, ...],
127+
version: str = "latest",
128+
file: str = "",
129+
cdn: str = "https://esm.sh",
130+
fallback: Any | None = ...,
131+
resolve_imports: bool | None = ...,
132+
resolve_imports_depth: int = ...,
133+
unmount_before_update: bool = ...,
134+
allow_children: bool = ...,
135+
) -> list[VdomConstructor]: ...
136+
137+
138+
def reactjs_component_from_npm(
139+
package: str,
140+
import_names: str | list[str] | tuple[str, ...],
141+
version: str = "latest",
142+
file: str = "",
143+
cdn: str = "https://esm.sh",
144+
fallback: Any | None = None,
145+
resolve_imports: bool | None = None,
146+
resolve_imports_depth: int = 5,
147+
unmount_before_update: bool = False,
148+
allow_children: bool = True,
149+
) -> VdomConstructor | list[VdomConstructor]:
150+
"""Import a component from an NPM package.
151+
152+
Parameters:
153+
package:
154+
The name of the NPM package.
155+
import_names:
156+
One or more component names to import. If given as a string, a single component
157+
will be returned. If a list is given, then a list of components will be
158+
returned.
159+
version:
160+
The version of the package to use. Defaults to "latest".
161+
file:
162+
A specific file to import from the package.
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+
resolve_imports:
168+
Whether to try and find all the named imports of this module.
169+
resolve_imports_depth:
170+
How deeply to search for those imports.
171+
unmount_before_update:
172+
Cause the component to be unmounted before each update. This option should
173+
only be used if the imported package fails to re-render when props change.
174+
Using this option has negative performance consequences since all DOM
175+
elements must be changed on each render. See :issue:`461` for more info.
176+
allow_children:
177+
Whether or not these components can have children.
178+
"""
179+
url = f"{cdn}/{package}@{version}"
180+
if file:
181+
url += f"/{file}"
182+
183+
if "esm.sh" in cdn:
184+
if "?" in url:
185+
url += "&external=react,react-dom"
186+
else:
187+
url += "?external=react,react-dom"
188+
189+
return reactjs_component_from_url(
190+
url,
191+
import_names,
192+
fallback=fallback,
193+
resolve_imports=resolve_imports,
194+
resolve_imports_depth=resolve_imports_depth,
195+
unmount_before_update=unmount_before_update,
196+
allow_children=allow_children,
197+
)
198+
199+
108200
@overload
109201
def reactjs_component_from_file(
110202
file: str | Path,

0 commit comments

Comments
 (0)