Просмотр исходного кода

CLI script to maintain Chakra backed components in rx namespace in older apps (#2322)

jackie-pc 1 год назад
Родитель
Сommit
a4ee985509
3 измененных файлов с 135 добавлено и 0 удалено
  1. 16 0
      reflex/reflex.py
  2. 106 0
      reflex/utils/prerequisites.py
  3. 13 0
      scripts/migrate_project_to_rx_chakra.py

+ 16 - 0
reflex/reflex.py

@@ -100,6 +100,9 @@ def _init(
     # Migrate Pynecone projects to Reflex.
     prerequisites.migrate_to_reflex()
 
+    if prerequisites.should_show_rx_chakra_migration_instructions():
+        prerequisites.show_rx_chakra_migration_instructions()
+
     # Initialize the .gitignore.
     prerequisites.initialize_gitignore()
 
@@ -336,6 +339,7 @@ def logout(
 
 
 db_cli = typer.Typer()
+script_cli = typer.Typer()
 
 
 def _skip_compile():
@@ -414,6 +418,17 @@ def makemigrations(
             )
 
 
+@script_cli.command(
+    name="keep-chakra",
+    help="Change all rx.<component> references to rx.chakra.<component>, to preserve Chakra UI usage.",
+)
+def keep_chakra():
+    """Change all rx.<component> references to rx.chakra.<component>, to preserve Chakra UI usage."""
+    from reflex.utils import prerequisites
+
+    prerequisites.migrate_to_rx_chakra()
+
+
 @cli.command()
 def deploy(
     key: Optional[str] = typer.Option(
@@ -555,6 +570,7 @@ def demo(
 
 
 cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
+cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
 cli.add_typer(
     deployments_cli,
     name="deployments",

+ 106 - 0
reflex/utils/prerequisites.py

@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import glob
 import importlib
+import inspect
 import json
 import os
 import platform
@@ -25,6 +26,7 @@ from alembic.util.exc import CommandError
 from packaging import version
 from redis.asyncio import Redis
 
+import reflex
 from reflex import constants, model
 from reflex.compiler import templates
 from reflex.config import Config, get_config
@@ -938,6 +940,110 @@ def prompt_for_template() -> constants.Templates.Kind:
     return constants.Templates.Kind(template)
 
 
+def should_show_rx_chakra_migration_instructions() -> bool:
+    """Should we show the migration instructions for rx.chakra.* => rx.*?.
+
+    Returns:
+        bool: True if we should show the migration instructions.
+    """
+    if os.getenv("REFLEX_PROMPT_MIGRATE_TO_RX_CHAKRA") == "yes":
+        return True
+
+    with open(constants.Dirs.REFLEX_JSON, "r") as f:
+        data = json.load(f)
+        existing_init_reflex_version = data.get("version", None)
+
+    if existing_init_reflex_version is None:
+        # They clone a reflex app from git for the first time.
+        # That app may or may not be 0.4 compatible.
+        # So let's just show these instructions THIS TIME.
+        return True
+
+    if constants.Reflex.VERSION < "0.4":
+        return False
+    else:
+        return existing_init_reflex_version < "0.4"
+
+
+def show_rx_chakra_migration_instructions():
+    """Show the migration instructions for rx.chakra.* => rx.*."""
+    console.log(
+        "Prior to reflex 0.4.0, rx.* components are based on Chakra UI. They are now based on Radix UI. To stick to Chakra UI, use rx.chakra.*."
+    )
+    console.log("")
+    console.log(
+        "[bold]Run `reflex script keep-chakra` to automatically update your app."
+    )
+    console.log("")
+    console.log("For more details, please see https://TODO")  # TODO add link to docs
+
+
+def migrate_to_rx_chakra():
+    """Migrate rx.button => r.chakra.button, etc."""
+    file_pattern = os.path.join(get_config().app_name, "**/*.py")
+    file_list = glob.glob(file_pattern, recursive=True)
+
+    # Populate with all rx.<x> components that have been moved to rx.chakra.<x>
+    patterns = {
+        rf"\brx\.{name}\b": f"rx.chakra.{name}"
+        for name in _get_rx_chakra_component_to_migrate()
+    }
+
+    for file_path in file_list:
+        with FileInput(file_path, inplace=True) as file:
+            for _line_num, line in enumerate(file):
+                for old, new in patterns.items():
+                    line = re.sub(old, new, line)
+                print(line, end="")
+
+
+def _get_rx_chakra_component_to_migrate() -> set[str]:
+    from reflex.components import ChakraComponent
+
+    rx_chakra_names = set(dir(reflex.chakra))
+
+    names_to_migrate = set()
+    whitelist = {
+        "CodeBlock",
+        "ColorModeIcon",
+        "MultiSelect",
+        "MultiSelectOption",
+        "base",
+        "code_block",
+        "color_mode_cond",
+        "color_mode_icon",
+        "multi_select",
+        "multi_select_option",
+    }
+    for rx_chakra_name in sorted(rx_chakra_names):
+        if rx_chakra_name.startswith("_"):
+            continue
+
+        rx_chakra_object = getattr(reflex.chakra, rx_chakra_name)
+        try:
+            if (
+                inspect.ismethod(rx_chakra_object)
+                and inspect.isclass(rx_chakra_object.__self__)
+                and issubclass(rx_chakra_object.__self__, ChakraComponent)
+            ):
+                names_to_migrate.add(rx_chakra_name)
+
+            elif inspect.isclass(rx_chakra_object) and issubclass(
+                rx_chakra_object, ChakraComponent
+            ):
+                names_to_migrate.add(rx_chakra_name)
+                pass
+            else:
+                # For the given rx.chakra.<x>, does rx.<x> exist?
+                # And of these, should we include in migration?
+                if hasattr(reflex, rx_chakra_name) and rx_chakra_name in whitelist:
+                    names_to_migrate.add(rx_chakra_name)
+
+        except Exception:
+            raise
+    return names_to_migrate
+
+
 def migrate_to_reflex():
     """Migration from Pynecone to Reflex."""
     # Check if the old config file exists.

+ 13 - 0
scripts/migrate_project_to_rx_chakra.py

@@ -0,0 +1,13 @@
+"""Migrate project to rx.chakra. I.e. switch usage of rx.<component> to rx.chakra.<component>."""
+
+import argparse
+
+if __name__ == "__main__":
+    # parse args just for the help message (-h, etc)
+    parser = argparse.ArgumentParser(
+        description="Migrate project to rx.chakra. I.e. switch usage of rx.<component> to rx.chakra.<component>."
+    )
+    args = parser.parse_args()
+    from reflex.utils.prerequisites import migrate_to_rx_chakra
+
+    migrate_to_rx_chakra()