浏览代码

make unit test cases pass

Khaleel Al-Adhami 2 周之前
父节点
当前提交
2c51da2987

+ 1 - 1
pyi_hashes.json

@@ -10,7 +10,7 @@
   "reflex/components/base/head.pyi": "893047aa32da553711db8f1345adb6b0",
   "reflex/components/base/link.pyi": "e96179dc7823f354fb73a6c03e31028c",
   "reflex/components/base/meta.pyi": "da52c3212fac6b50560863146a7afcc3",
-  "reflex/components/base/script.pyi": "d3743541fd968e9b6aaa123da6a11c3c",
+  "reflex/components/base/script.pyi": "352ec2e82b06f2fa5b3015f7b8dd295e",
   "reflex/components/base/strict_mode.pyi": "d972e0ff2a6f961e7df90fc27b8bb51b",
   "reflex/components/core/__init__.pyi": "d99fbfd4207d8a3f7013221f428e0ed8",
   "reflex/components/core/auto_scroll.pyi": "d3012d2a4ccaab8dfebf9aa484020f59",

+ 2 - 8
reflex/app.py

@@ -1453,15 +1453,9 @@ class App(MiddlewareMixin, LifespanMixin):
         with console.timing("Install Frontend Packages"):
             self._get_frontend_packages(all_imports)
 
-        # Setup the next.config.js
-        transpile_packages = [
-            package
-            for package, import_vars in all_imports.items()
-            if any(import_var.transpile for import_var in import_vars)
-        ]
-        prerequisites.update_next_config(
+        # Setup the react-router.config.js
+        prerequisites.update_react_router_config(
             export=export,
-            transpile_packages=transpile_packages,
         )
 
         if is_prod_mode():

+ 8 - 0
reflex/components/base/script.py

@@ -24,6 +24,9 @@ class Script(elements.Script):
 
         Returns:
             The script element.
+
+        Raises:
+            ValueError: If neither children nor src is specified.
         """
         async_ = props.pop("async_", None)
         char_set = props.pop("char_set", None)
@@ -46,6 +49,11 @@ class Script(elements.Script):
                 f"rx.script does not support the following properties: {list(props.keys())}"
             )
 
+        if not children and not src:
+            raise ValueError(
+                "You must specify either children or src for the script element."
+            )
+
         return helmet(
             elements.Script.create(
                 *children,

+ 3 - 35
reflex/components/component.py

@@ -1339,20 +1339,6 @@ class Component(BaseComponent, ABC):
         # Return the dynamic imports
         return dynamic_imports
 
-    def _should_transpile(self, dep: str | None) -> bool:
-        """Check if a dependency should be transpiled.
-
-        Args:
-            dep: The dependency to check.
-
-        Returns:
-            True if the dependency should be transpiled.
-        """
-        return bool(self.transpile_packages) and (
-            dep in self.transpile_packages
-            or format.format_library_name(dep or "") in self.transpile_packages
-        )
-
     def _get_dependencies_imports(self) -> ParsedImportDict:
         """Get the imports from lib_dependencies for installing.
 
@@ -1360,14 +1346,7 @@ class Component(BaseComponent, ABC):
             The dependencies imports of the component.
         """
         return {
-            dep: [
-                ImportVar(
-                    tag=None,
-                    render=False,
-                    transpile=self._should_transpile(dep),
-                )
-            ]
-            for dep in self.lib_dependencies
+            dep: [ImportVar(tag=None, render=False)] for dep in self.lib_dependencies
         }
 
     def _get_hooks_imports(self) -> ParsedImportDict:
@@ -1690,12 +1669,7 @@ class Component(BaseComponent, ABC):
         # If the tag is dot-qualified, only import the left-most name.
         tag = self.tag.partition(".")[0] if self.tag else None
         alias = self.alias.partition(".")[0] if self.alias else None
-        return ImportVar(
-            tag=tag,
-            is_default=self.is_default,
-            alias=alias,
-            transpile=self._should_transpile(self.library),
-        )
+        return ImportVar(tag=tag, is_default=self.is_default, alias=alias)
 
     @staticmethod
     def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
@@ -2018,13 +1992,7 @@ class NoSSRComponent(Component):
         if import_name is not None:
             with contextlib.suppress(ValueError):
                 _imports[import_name].remove(self.import_var)
-            _imports[import_name].append(
-                ImportVar(
-                    tag=None,
-                    render=False,
-                    transpile=self._should_transpile(self.library),
-                )
-            )
+            _imports[import_name].append(ImportVar(tag=None, render=False))
 
         return imports.merge_imports(
             dynamic_import,

+ 1 - 0
reflex/constants/__init__.py

@@ -18,6 +18,7 @@ from .base import (
     LogLevel,
     Next,
     Ping,
+    ReactRouter,
     Reflex,
     ReflexHostingCLI,
     Templates,

+ 23 - 5
reflex/constants/base.py

@@ -198,17 +198,35 @@ class Templates(SimpleNamespace):
         CODE = "code"
 
 
-class Next(SimpleNamespace):
+class Javascript(SimpleNamespace):
+    """Constants related to Javascript."""
+
+    # The node modules directory.
+    NODE_MODULES = "node_modules"
+
+    # The package lock file.
+    PACKAGE_LOCK = "package-lock.json"
+
+
+class Next(Javascript):
     """Constants related to NextJS."""
 
     # The NextJS config file
     CONFIG_FILE = "next.config.js"
+
     # The sitemap config file.
     SITEMAP_CONFIG_FILE = "next-sitemap.config.js"
-    # The node modules directory.
-    NODE_MODULES = "node_modules"
-    # The package lock file.
-    PACKAGE_LOCK = "package-lock.json"
+
+    # Regex to check for message displayed when frontend comes up
+    FRONTEND_LISTENING_REGEX = "Local:[\\s]+(.*)"
+
+
+class ReactRouter(Javascript):
+    """Constants related to React Router."""
+
+    # The react router config file
+    CONFIG_FILE = "react-router-config.js"
+
     # Regex to check for message displayed when frontend comes up
     FRONTEND_LISTENING_REGEX = "Local:[\\s]+(.*)"
 

+ 1 - 1
reflex/testing.py

@@ -406,7 +406,7 @@ class AppHarness:
             if not line:
                 break
             print(line)  # for pytest diagnosis #noqa: T201
-            m = re.search(reflex.constants.Next.FRONTEND_LISTENING_REGEX, line)
+            m = re.search(reflex.constants.ReactRouter.FRONTEND_LISTENING_REGEX, line)
             if m is not None:
                 self.frontend_url = m.group(1)
                 config = reflex.config.get_config()

+ 1 - 1
reflex/utils/build.py

@@ -187,7 +187,7 @@ def build(
     wdir = prerequisites.get_web_dir()
 
     # Clean the static directory if it exists.
-    path_ops.rm(str(wdir / constants.Dirs.STATIC))
+    path_ops.rm(str(wdir / constants.Dirs.BUILD_DIR))
 
     # The export command to run.
     command = "export"

+ 8 - 2
reflex/utils/exec.py

@@ -12,6 +12,7 @@ import subprocess
 import sys
 from collections.abc import Sequence
 from pathlib import Path
+from typing import Any
 from urllib.parse import urljoin
 
 import psutil
@@ -92,7 +93,12 @@ def run_process_and_launch_url(
 
     while True:
         if process is None:
-            kwargs = {}
+            kwargs: dict[str, Any] = {
+                "env": {
+                    **os.environ,
+                    "NO_COLOR": "1",
+                }
+            }
             if constants.IS_WINDOWS and backend_present:
                 kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP  # pyright: ignore [reportAttributeAccessIssue]
             process = processes.new_process(
@@ -105,7 +111,7 @@ def run_process_and_launch_url(
             frontend_process = process
         if process.stdout:
             for line in processes.stream_logs("Starting frontend", process):
-                match = re.search(constants.Next.FRONTEND_LISTENING_REGEX, line)
+                match = re.search(constants.ReactRouter.FRONTEND_LISTENING_REGEX, line)
                 if match:
                     if first_run:
                         url = match.group(1)

+ 19 - 30
reflex/utils/prerequisites.py

@@ -44,7 +44,6 @@ from reflex.utils.exceptions import (
     GeneratedCodeHasNoFunctionDefsError,
     SystemPackageMissingError,
 )
-from reflex.utils.format import format_library_name
 from reflex.utils.registry import get_npm_registry
 
 if typing.TYPE_CHECKING:
@@ -988,8 +987,8 @@ def initialize_web_directory():
     console.debug("Initializing the public directory.")
     path_ops.mkdir(get_web_dir() / constants.Dirs.PUBLIC)
 
-    console.debug("Initializing the next.config.js file.")
-    update_next_config()
+    console.debug("Initializing the react-router.config.js file.")
+    update_react_router_config()
 
     console.debug("Initializing the reflex.json file.")
     # Initialize the reflex json file.
@@ -1074,47 +1073,37 @@ def init_reflex_json(project_hash: int | None):
     path_ops.update_json_file(get_web_dir() / constants.Reflex.JSON, reflex_json)
 
 
-def update_next_config(
-    export: bool = False, transpile_packages: list[str] | None = None
-):
-    """Update Next.js config from Reflex config.
+def update_react_router_config(export: bool = False):
+    """Update react-router.config.js config from Reflex config.
 
     Args:
         export: if the method run during reflex export.
-        transpile_packages: list of packages to transpile via next.config.js.
     """
-    next_config_file = get_web_dir() / constants.Next.CONFIG_FILE
+    react_router_config_file_path = get_web_dir() / constants.ReactRouter.CONFIG_FILE
 
-    next_config = _update_react_router_config(
-        get_config(), export=export, transpile_packages=transpile_packages
-    )
+    react_router_config = _update_react_router_config(get_config(), export=export)
 
-    # Overwriting the next.config.js triggers a full server reload, so make sure
+    # Overwriting the config file triggers a full server reload, so make sure
     # there is actually a diff.
-    orig_next_config = next_config_file.read_text() if next_config_file.exists() else ""
-    if orig_next_config != next_config:
-        next_config_file.write_text(next_config)
+    orig_next_config = (
+        react_router_config_file_path.read_text()
+        if react_router_config_file_path.exists()
+        else ""
+    )
+    if orig_next_config != react_router_config:
+        react_router_config_file_path.write_text(react_router_config)
 
 
-def _update_react_router_config(
-    config: Config, export: bool = False, transpile_packages: list[str] | None = None
-):
+def _update_react_router_config(config: Config, export: bool = False):
     react_router_config = {
-        "basePath": config.frontend_path or "",
-        "compress": config.next_compression,
-        "trailingSlash": True,
-        "staticPageGenerationTimeout": config.static_page_generation_timeout,
+        "basename": "/" + (config.frontend_path or "").removeprefix("/"),
+        "future": {
+            "unstable_optimizeDeps": True,
+        },
         "ssr": False,
     }
-    if not config.next_dev_indicators:
-        react_router_config["devIndicators"] = False
 
-    if transpile_packages:
-        react_router_config["transpilePackages"] = list(
-            {format_library_name(p) for p in transpile_packages}
-        )
     if export:
-        react_router_config["output"] = "export"
         react_router_config["build"] = constants.Dirs.BUILD_DIR
 
     return f"export default {json.dumps(react_router_config)};"

+ 4 - 48
tests/units/components/base/test_script.py

@@ -2,16 +2,14 @@
 
 import pytest
 
-import reflex as rx
 from reflex.components.base.script import Script
-from reflex.state import BaseState
 
 
 def test_script_inline():
     """Test inline scripts are rendered as children."""
     component = Script.create("let x = 42")
-    render_dict = component.render()
-    assert render_dict["name"] == "Script"
+    render_dict = component.render()["children"][0]
+    assert render_dict["name"] == '"script"'
     assert not render_dict["contents"]
     assert len(render_dict["children"]) == 1
     assert render_dict["children"][0]["contents"] == '"let x = 42"'
@@ -20,8 +18,8 @@ def test_script_inline():
 def test_script_src():
     """Test src prop is rendered without children."""
     component = Script.create(src="foo.js")
-    render_dict = component.render()
-    assert render_dict["name"] == "Script"
+    render_dict = component.render()["children"][0]
+    assert render_dict["name"] == '"script"'
     assert not render_dict["contents"]
     assert not render_dict["children"]
     assert 'src:"foo.js"' in render_dict["props"]
@@ -31,45 +29,3 @@ def test_script_neither():
     """Specifying neither children nor src is a ValueError."""
     with pytest.raises(ValueError):
         Script.create()
-
-
-class EvState(BaseState):
-    """State for testing event handlers."""
-
-    @rx.event
-    def on_ready(self):
-        """Empty event handler."""
-        pass
-
-    @rx.event
-    def on_load(self):
-        """Empty event handler."""
-        pass
-
-    @rx.event
-    def on_error(self):
-        """Empty event handler."""
-        pass
-
-
-def test_script_event_handler():
-    """Test event handlers are rendered as expected."""
-    component = Script.create(
-        src="foo.js",
-        on_ready=EvState.on_ready,
-        on_load=EvState.on_load,
-        on_error=EvState.on_error,
-    )
-    render_dict = component.render()
-    assert (
-        f'onReady:((...args) => (addEvents([(Event("{EvState.get_full_name()}.on_ready", ({{  }}), ({{  }})))], args, ({{  }}))))'
-        in render_dict["props"]
-    )
-    assert (
-        f'onLoad:((...args) => (addEvents([(Event("{EvState.get_full_name()}.on_load", ({{  }}), ({{  }})))], args, ({{  }}))))'
-        in render_dict["props"]
-    )
-    assert (
-        f'onError:((...args) => (addEvents([(Event("{EvState.get_full_name()}.on_error", ({{  }}), ({{  }})))], args, ({{  }}))))'
-        in render_dict["props"]
-    )

+ 6 - 56
tests/units/test_app.py

@@ -2,9 +2,7 @@ from __future__ import annotations
 
 import functools
 import io
-import json
 import os.path
-import re
 import unittest.mock
 import uuid
 from collections.abc import Generator
@@ -1359,7 +1357,9 @@ def test_app_wrap_compile_theme(
     app, web_dir = compilable_app
     app.theme = rx.theme(accent_color="plum")
     app._compile()
-    app_js_contents = (web_dir / "pages" / "_app.js").read_text()
+    app_js_contents = (
+        web_dir / constants.Dirs.PAGES / constants.PageNames.APP_ROOT
+    ).read_text()
     app_js_lines = [
         line.strip() for line in app_js_contents.splitlines() if line.strip()
     ]
@@ -1425,7 +1425,9 @@ def test_app_wrap_priority(
 
     app.add_page(page)
     app._compile()
-    app_js_contents = (web_dir / "pages" / "_app.js").read_text()
+    app_js_contents = (
+        web_dir / constants.Dirs.PAGES / constants.PageNames.APP_ROOT
+    ).read_text()
     app_js_lines = [
         line.strip() for line in app_js_contents.splitlines() if line.strip()
     ]
@@ -1583,58 +1585,6 @@ def test_add_page_component_returning_tuple():
     assert str(third_text.children[0].contents) == '"third"'
 
 
-@pytest.mark.parametrize("export", (True, False))
-def test_app_with_transpile_packages(compilable_app: tuple[App, Path], export: bool):
-    class C1(rx.Component):
-        library = "foo@1.2.3"
-        tag = "Foo"
-        transpile_packages: list[str] = ["foo"]
-
-    class C2(rx.Component):
-        library = "bar@4.5.6"
-        tag = "Bar"
-        transpile_packages: list[str] = ["bar@4.5.6"]
-
-    class C3(rx.NoSSRComponent):
-        library = "baz@7.8.10"
-        tag = "Baz"
-        transpile_packages: list[str] = ["baz@7.8.9"]
-
-    class C4(rx.NoSSRComponent):
-        library = "quuc@2.3.4"
-        tag = "Quuc"
-        transpile_packages: list[str] = ["quuc"]
-
-    class C5(rx.Component):
-        library = "quuc"
-        tag = "Quuc"
-
-    app, web_dir = compilable_app
-    page = Fragment.create(
-        C1.create(), C2.create(), C3.create(), C4.create(), C5.create()
-    )
-    app.add_page(page, route="/")
-    app._compile(export=export)
-
-    next_config = (web_dir / "next.config.js").read_text()
-    transpile_packages_match = re.search(r"transpilePackages: (\[.*?\])", next_config)
-    transpile_packages_json = transpile_packages_match.group(1)  # pyright: ignore [reportOptionalMemberAccess]
-    transpile_packages = sorted(json.loads(transpile_packages_json))
-
-    assert transpile_packages == [
-        "bar",
-        "foo",
-        "quuc",
-    ]
-
-    if export:
-        assert 'output: "export"' in next_config
-        assert f'distDir: "{constants.Dirs.STATIC}"' in next_config
-    else:
-        assert 'output: "export"' not in next_config
-        assert f'distDir: "{constants.Dirs.STATIC}"' not in next_config
-
-
 def test_app_with_valid_var_dependencies(compilable_app: tuple[App, Path]):
     app, _ = compilable_app
 

+ 7 - 35
tests/units/test_prerequisites.py

@@ -1,5 +1,3 @@
-import json
-import re
 import shutil
 import tempfile
 from pathlib import Path
@@ -29,7 +27,7 @@ runner = CliRunner()
                 app_name="test",
             ),
             False,
-            'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60, devIndicators: false};',
+            'export default {"basename": "/", "future": {"unstable_optimizeDeps": true}, "ssr": false};',
         ),
         (
             Config(
@@ -37,7 +35,7 @@ runner = CliRunner()
                 static_page_generation_timeout=30,
             ),
             False,
-            'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 30, devIndicators: false};',
+            'export default {"basename": "/", "future": {"unstable_optimizeDeps": true}, "ssr": false};',
         ),
         (
             Config(
@@ -45,7 +43,7 @@ runner = CliRunner()
                 next_compression=False,
             ),
             False,
-            'module.exports = {basePath: "", compress: false, trailingSlash: true, staticPageGenerationTimeout: 60, devIndicators: false};',
+            'export default {"basename": "/", "future": {"unstable_optimizeDeps": true}, "ssr": false};',
         ),
         (
             Config(
@@ -53,7 +51,7 @@ runner = CliRunner()
                 frontend_path="/test",
             ),
             False,
-            'module.exports = {basePath: "/test", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60, devIndicators: false};',
+            'export default {"basename": "/test", "future": {"unstable_optimizeDeps": true}, "ssr": false};',
         ),
         (
             Config(
@@ -62,14 +60,14 @@ runner = CliRunner()
                 next_compression=False,
             ),
             False,
-            'module.exports = {basePath: "/test", compress: false, trailingSlash: true, staticPageGenerationTimeout: 60, devIndicators: false};',
+            'export default {"basename": "/test", "future": {"unstable_optimizeDeps": true}, "ssr": false};',
         ),
         (
             Config(
                 app_name="test",
             ),
             True,
-            'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60, devIndicators: false, output: "export", distDir: "_static"};',
+            'export default {"basename": "/", "future": {"unstable_optimizeDeps": true}, "ssr": false, "build": "build"};',
         ),
         (
             Config(
@@ -77,7 +75,7 @@ runner = CliRunner()
                 next_dev_indicators=True,
             ),
             True,
-            'module.exports = {basePath: "", compress: true, trailingSlash: true, staticPageGenerationTimeout: 60, output: "export", distDir: "_static"};',
+            'export default {"basename": "/", "future": {"unstable_optimizeDeps": true}, "ssr": false, "build": "build"};',
         ),
     ],
 )
@@ -86,32 +84,6 @@ def test_update_next_config(config, export, expected_output):
     assert output == expected_output
 
 
-@pytest.mark.parametrize(
-    ("transpile_packages", "expected_transpile_packages"),
-    (
-        (
-            ["foo", "@bar/baz"],
-            ["@bar/baz", "foo"],
-        ),
-        (
-            ["foo", "@bar/baz", "foo", "@bar/baz@3.2.1"],
-            ["@bar/baz", "foo"],
-        ),
-        (["@bar/baz", {"name": "foo"}], ["@bar/baz", "foo"]),
-        (["@bar/baz", {"name": "@foo/baz"}], ["@bar/baz", "@foo/baz"]),
-    ),
-)
-def test_transpile_packages(transpile_packages, expected_transpile_packages):
-    output = _update_react_router_config(
-        Config(app_name="test"),
-        transpile_packages=transpile_packages,
-    )
-    transpile_packages_match = re.search(r"transpilePackages: (\[.*?\])", output)
-    transpile_packages_json = transpile_packages_match.group(1)  # pyright: ignore [reportOptionalMemberAccess]
-    actual_transpile_packages = sorted(json.loads(transpile_packages_json))
-    assert actual_transpile_packages == expected_transpile_packages
-
-
 def test_cached_procedure():
     call_count = 0