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

This commit is contained in:
jackie-pc 2024-02-08 11:12:20 -08:00 committed by GitHub
parent ea2a5904f2
commit a4ee985509
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 135 additions and 0 deletions

View File

@ -100,6 +100,9 @@ def _init(
# Migrate Pynecone projects to Reflex. # Migrate Pynecone projects to Reflex.
prerequisites.migrate_to_reflex() prerequisites.migrate_to_reflex()
if prerequisites.should_show_rx_chakra_migration_instructions():
prerequisites.show_rx_chakra_migration_instructions()
# Initialize the .gitignore. # Initialize the .gitignore.
prerequisites.initialize_gitignore() prerequisites.initialize_gitignore()
@ -336,6 +339,7 @@ def logout(
db_cli = typer.Typer() db_cli = typer.Typer()
script_cli = typer.Typer()
def _skip_compile(): 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() @cli.command()
def deploy( def deploy(
key: Optional[str] = typer.Option( 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(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( cli.add_typer(
deployments_cli, deployments_cli,
name="deployments", name="deployments",

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import glob import glob
import importlib import importlib
import inspect
import json import json
import os import os
import platform import platform
@ -25,6 +26,7 @@ from alembic.util.exc import CommandError
from packaging import version from packaging import version
from redis.asyncio import Redis from redis.asyncio import Redis
import reflex
from reflex import constants, model from reflex import constants, model
from reflex.compiler import templates from reflex.compiler import templates
from reflex.config import Config, get_config from reflex.config import Config, get_config
@ -938,6 +940,110 @@ def prompt_for_template() -> constants.Templates.Kind:
return constants.Templates.Kind(template) 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(): def migrate_to_reflex():
"""Migration from Pynecone to Reflex.""" """Migration from Pynecone to Reflex."""
# Check if the old config file exists. # Check if the old config file exists.

View File

@ -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()