|
@@ -29,6 +29,23 @@ from reflex.config import Config, get_config
|
|
from reflex.utils import console, path_ops, processes
|
|
from reflex.utils import console, path_ops, processes
|
|
|
|
|
|
|
|
|
|
|
|
+def check_node_version() -> bool:
|
|
|
|
+ """Check the version of Node.js.
|
|
|
|
+
|
|
|
|
+ Returns:
|
|
|
|
+ Whether the version of Node.js is valid.
|
|
|
|
+ """
|
|
|
|
+ current_version = get_node_version()
|
|
|
|
+ if current_version:
|
|
|
|
+ # Compare the version numbers
|
|
|
|
+ return (
|
|
|
|
+ current_version >= version.parse(constants.Node.MIN_VERSION)
|
|
|
|
+ if constants.IS_WINDOWS
|
|
|
|
+ else current_version == version.parse(constants.Node.VERSION)
|
|
|
|
+ )
|
|
|
|
+ return False
|
|
|
|
+
|
|
|
|
+
|
|
def get_node_version() -> version.Version | None:
|
|
def get_node_version() -> version.Version | None:
|
|
"""Get the version of node.
|
|
"""Get the version of node.
|
|
|
|
|
|
@@ -70,35 +87,24 @@ def get_bun_version() -> version.Version | None:
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
|
|
-def get_package_manager() -> str | None:
|
|
|
|
|
|
+def get_install_package_manager() -> str | None:
|
|
"""Get the package manager executable for installation.
|
|
"""Get the package manager executable for installation.
|
|
Currently on unix systems, bun is used for installation only.
|
|
Currently on unix systems, bun is used for installation only.
|
|
|
|
|
|
Returns:
|
|
Returns:
|
|
The path to the package manager.
|
|
The path to the package manager.
|
|
"""
|
|
"""
|
|
- # On Windows or lower linux kernels(WSL1), we use npm instead of bun.
|
|
|
|
- if constants.IS_WINDOWS or constants.IS_LINUX and not is_valid_linux():
|
|
|
|
- return get_npm_package_manager()
|
|
|
|
|
|
+ # On Windows, we use npm instead of bun.
|
|
|
|
+ if constants.IS_WINDOWS:
|
|
|
|
+ return get_package_manager()
|
|
|
|
|
|
# On other platforms, we use bun.
|
|
# On other platforms, we use bun.
|
|
return get_config().bun_path
|
|
return get_config().bun_path
|
|
|
|
|
|
|
|
|
|
-def get_install_package_manager() -> str | None:
|
|
|
|
- """Get package manager to install dependencies.
|
|
|
|
-
|
|
|
|
- Returns:
|
|
|
|
- Path to install package manager.
|
|
|
|
- """
|
|
|
|
- if constants.IS_WINDOWS:
|
|
|
|
- return get_npm_package_manager()
|
|
|
|
- return get_config().bun_path
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-def get_npm_package_manager() -> str | None:
|
|
|
|
- """Get the npm package manager executable for installing and running app
|
|
|
|
- on windows.
|
|
|
|
|
|
+def get_package_manager() -> str | None:
|
|
|
|
+ """Get the package manager executable for running app.
|
|
|
|
+ Currently on unix systems, npm is used for running the app only.
|
|
|
|
|
|
Returns:
|
|
Returns:
|
|
The path to the package manager.
|
|
The path to the package manager.
|
|
@@ -354,6 +360,13 @@ def update_next_config(next_config: str, config: Config) -> str:
|
|
return next_config
|
|
return next_config
|
|
|
|
|
|
|
|
|
|
|
|
+def remove_existing_bun_installation():
|
|
|
|
+ """Remove existing bun installation."""
|
|
|
|
+ console.debug("Removing existing bun installation.")
|
|
|
|
+ if os.path.exists(get_config().bun_path):
|
|
|
|
+ path_ops.rm(constants.Bun.ROOT_PATH)
|
|
|
|
+
|
|
|
|
+
|
|
def download_and_run(url: str, *args, show_status: bool = False, **env):
|
|
def download_and_run(url: str, *args, show_status: bool = False, **env):
|
|
"""Download and run a script.
|
|
"""Download and run a script.
|
|
|
|
|
|
@@ -415,38 +428,52 @@ def download_and_extract_fnm_zip():
|
|
|
|
|
|
|
|
|
|
def install_node():
|
|
def install_node():
|
|
- """Install fnm and nodejs for use by Reflex."""
|
|
|
|
- if constants.IS_WINDOWS or constants.IS_LINUX and not is_valid_linux():
|
|
|
|
-
|
|
|
|
- path_ops.mkdir(constants.Fnm.DIR)
|
|
|
|
- if not os.path.exists(constants.Fnm.EXE):
|
|
|
|
- download_and_extract_fnm_zip()
|
|
|
|
-
|
|
|
|
- if constants.IS_WINDOWS:
|
|
|
|
- # Install node
|
|
|
|
- fnm_exe = Path(constants.Fnm.EXE).resolve()
|
|
|
|
- fnm_dir = Path(constants.Fnm.DIR).resolve()
|
|
|
|
- process = processes.new_process(
|
|
|
|
- [
|
|
|
|
- "powershell",
|
|
|
|
- "-Command",
|
|
|
|
- f'& "{fnm_exe}" install {constants.Node.VERSION} --fnm-dir "{fnm_dir}"',
|
|
|
|
- ],
|
|
|
|
- )
|
|
|
|
- else: # All other platforms (Linux, WSL1).
|
|
|
|
- # Add execute permissions to fnm executable.
|
|
|
|
- os.chmod(constants.Fnm.EXE, stat.S_IXUSR)
|
|
|
|
- # Install node.
|
|
|
|
- process = processes.new_process(
|
|
|
|
- [
|
|
|
|
- constants.Fnm.EXE,
|
|
|
|
- "install",
|
|
|
|
- constants.Node.VERSION,
|
|
|
|
- "--fnm-dir",
|
|
|
|
- constants.Fnm.DIR,
|
|
|
|
- ],
|
|
|
|
- )
|
|
|
|
- processes.show_status("Installing node", process)
|
|
|
|
|
|
+ """Install fnm and nodejs for use by Reflex.
|
|
|
|
+ Independent of any existing system installations.
|
|
|
|
+ """
|
|
|
|
+ if not constants.Fnm.FILENAME:
|
|
|
|
+ # fnm only support Linux, macOS and Windows distros.
|
|
|
|
+ console.debug("")
|
|
|
|
+ return
|
|
|
|
+
|
|
|
|
+ path_ops.mkdir(constants.Fnm.DIR)
|
|
|
|
+ if not os.path.exists(constants.Fnm.EXE):
|
|
|
|
+ download_and_extract_fnm_zip()
|
|
|
|
+
|
|
|
|
+ if constants.IS_WINDOWS:
|
|
|
|
+ # Install node
|
|
|
|
+ fnm_exe = Path(constants.Fnm.EXE).resolve()
|
|
|
|
+ fnm_dir = Path(constants.Fnm.DIR).resolve()
|
|
|
|
+ process = processes.new_process(
|
|
|
|
+ [
|
|
|
|
+ "powershell",
|
|
|
|
+ "-Command",
|
|
|
|
+ f'& "{fnm_exe}" install {constants.Node.VERSION} --fnm-dir "{fnm_dir}"',
|
|
|
|
+ ],
|
|
|
|
+ )
|
|
|
|
+ else: # All other platforms (Linux, MacOS).
|
|
|
|
+ # TODO we can skip installation if check_node_version() checks out
|
|
|
|
+ # Add execute permissions to fnm executable.
|
|
|
|
+ os.chmod(constants.Fnm.EXE, stat.S_IXUSR)
|
|
|
|
+ # Install node.
|
|
|
|
+ # Specify arm64 arch explicitly for M1s and M2s.
|
|
|
|
+ architecture_arg = (
|
|
|
|
+ ["--arch=arm64"]
|
|
|
|
+ if platform.system() == "Darwin" and platform.machine() == "arm64"
|
|
|
|
+ else []
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ process = processes.new_process(
|
|
|
|
+ [
|
|
|
|
+ constants.Fnm.EXE,
|
|
|
|
+ "install",
|
|
|
|
+ *architecture_arg,
|
|
|
|
+ constants.Node.VERSION,
|
|
|
|
+ "--fnm-dir",
|
|
|
|
+ constants.Fnm.DIR,
|
|
|
|
+ ],
|
|
|
|
+ )
|
|
|
|
+ processes.show_status("Installing node", process)
|
|
|
|
|
|
|
|
|
|
def install_bun():
|
|
def install_bun():
|
|
@@ -597,88 +624,50 @@ def validate_bun():
|
|
raise typer.Exit(1)
|
|
raise typer.Exit(1)
|
|
|
|
|
|
|
|
|
|
-def validate_node():
|
|
|
|
- """Check the version of Node.js is correct.
|
|
|
|
-
|
|
|
|
- Raises:
|
|
|
|
- Exit: If the version of Node.js is incorrect.
|
|
|
|
- """
|
|
|
|
- current_version = get_node_version()
|
|
|
|
-
|
|
|
|
- # Check if Node is installed.
|
|
|
|
- if not current_version:
|
|
|
|
- console.error(
|
|
|
|
- "Failed to obtain node version. Make sure node is installed and in your PATH."
|
|
|
|
- )
|
|
|
|
- raise typer.Exit(1)
|
|
|
|
-
|
|
|
|
- # Check if the version of Node is correct.
|
|
|
|
- if current_version < version.parse(constants.Node.MIN_VERSION):
|
|
|
|
- console.error(
|
|
|
|
- f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {current_version}."
|
|
|
|
- )
|
|
|
|
- raise typer.Exit(1)
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-def remove_existing_fnm_dir():
|
|
|
|
- """Remove existing fnm directory on linux and mac."""
|
|
|
|
- if os.path.exists(constants.Fnm.DIR):
|
|
|
|
- console.debug("Removing existing fnm installation.")
|
|
|
|
- path_ops.rm(constants.Fnm.DIR)
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-def validate_frontend_dependencies():
|
|
|
|
- """Validate frontend dependencies to ensure they meet requirements."""
|
|
|
|
- # Bun only supports linux and Mac. For Non-linux-or-mac, we use node.
|
|
|
|
- validate_bun() if constants.IS_LINUX_OR_MAC else validate_node()
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-def parse_non_semver_version(version_string: str) -> version.Version | None:
|
|
|
|
- """Parse unsemantic version string and produce
|
|
|
|
- a clean version that confirms to packaging.version.
|
|
|
|
|
|
+def validate_frontend_dependencies(init=True):
|
|
|
|
+ """Validate frontend dependencies to ensure they meet requirements.
|
|
|
|
|
|
Args:
|
|
Args:
|
|
- version_string: The version string
|
|
|
|
-
|
|
|
|
- Returns:
|
|
|
|
- A cleaned semantic packaging.version object.
|
|
|
|
|
|
+ init: whether running `reflex init`
|
|
|
|
|
|
|
|
+ Raises:
|
|
|
|
+ Exit: If the package manager is invalid.
|
|
"""
|
|
"""
|
|
- # Remove non-numeric characters from the version string
|
|
|
|
- cleaned_version_string = re.sub(r"[^\d.]+", "", version_string)
|
|
|
|
- try:
|
|
|
|
- parsed_version = version.parse(cleaned_version_string)
|
|
|
|
- return parsed_version
|
|
|
|
- except version.InvalidVersion:
|
|
|
|
- console.debug(f"could not parse version: {version_string}")
|
|
|
|
- return None
|
|
|
|
|
|
+ if not init:
|
|
|
|
+ # we only need to validate the package manager when running app.
|
|
|
|
+ # `reflex init` will install the deps anyway(if applied).
|
|
|
|
+ package_manager = get_package_manager()
|
|
|
|
+ if not package_manager:
|
|
|
|
+ console.error(
|
|
|
|
+ "Could not find NPM package manager. Make sure you have node installed."
|
|
|
|
+ )
|
|
|
|
+ raise typer.Exit(1)
|
|
|
|
|
|
|
|
+ if not check_node_version():
|
|
|
|
+ node_version = get_node_version()
|
|
|
|
+ console.error(
|
|
|
|
+ f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {node_version}",
|
|
|
|
+ )
|
|
|
|
+ raise typer.Exit(1)
|
|
|
|
|
|
-def is_valid_linux() -> bool:
|
|
|
|
- """Check if the linux kernel version is valid enough to use bun.
|
|
|
|
- This is typically used run npm at runtime for WSL 1 or lower linux versions.
|
|
|
|
|
|
+ if constants.IS_WINDOWS:
|
|
|
|
+ return
|
|
|
|
|
|
- Returns:
|
|
|
|
- If linux kernel version is valid enough.
|
|
|
|
- """
|
|
|
|
- if not constants.IS_LINUX:
|
|
|
|
- return False
|
|
|
|
- kernel_string = platform.release()
|
|
|
|
- kv = parse_non_semver_version(kernel_string)
|
|
|
|
- return kv.major > 5 or (kv.major == 5 and kv.minor >= 10) if kv else False
|
|
|
|
|
|
+ if init:
|
|
|
|
+ # we only need bun for package install on `reflex init`.
|
|
|
|
+ validate_bun()
|
|
|
|
|
|
|
|
|
|
def initialize_frontend_dependencies():
|
|
def initialize_frontend_dependencies():
|
|
"""Initialize all the frontend dependencies."""
|
|
"""Initialize all the frontend dependencies."""
|
|
# Create the reflex directory.
|
|
# Create the reflex directory.
|
|
path_ops.mkdir(constants.Reflex.DIR)
|
|
path_ops.mkdir(constants.Reflex.DIR)
|
|
|
|
+ # validate dependencies before install
|
|
|
|
+ validate_frontend_dependencies()
|
|
# Install the frontend dependencies.
|
|
# Install the frontend dependencies.
|
|
processes.run_concurrently(install_node, install_bun)
|
|
processes.run_concurrently(install_node, install_bun)
|
|
# Set up the web directory.
|
|
# Set up the web directory.
|
|
initialize_web_directory()
|
|
initialize_web_directory()
|
|
- # remove existing fnm dir on linux and mac
|
|
|
|
- if constants.IS_LINUX_OR_MAC and is_valid_linux():
|
|
|
|
- remove_existing_fnm_dir()
|
|
|
|
|
|
|
|
|
|
|
|
def check_db_initialized() -> bool:
|
|
def check_db_initialized() -> bool:
|