|
@@ -29,23 +29,6 @@ from reflex.config import Config, get_config
|
|
|
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:
|
|
|
"""Get the version of node.
|
|
|
|
|
@@ -87,24 +70,35 @@ def get_bun_version() -> version.Version | None:
|
|
|
return None
|
|
|
|
|
|
|
|
|
-def get_install_package_manager() -> str | None:
|
|
|
+def get_package_manager() -> str | None:
|
|
|
"""Get the package manager executable for installation.
|
|
|
Currently on unix systems, bun is used for installation only.
|
|
|
|
|
|
Returns:
|
|
|
The path to the package manager.
|
|
|
"""
|
|
|
- # On Windows, we use npm instead of bun.
|
|
|
- if constants.IS_WINDOWS:
|
|
|
- return get_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 other platforms, we use bun.
|
|
|
return get_config().bun_path
|
|
|
|
|
|
|
|
|
-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.
|
|
|
+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.
|
|
|
|
|
|
Returns:
|
|
|
The path to the package manager.
|
|
@@ -360,13 +354,6 @@ def update_next_config(next_config: str, config: Config) -> str:
|
|
|
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):
|
|
|
"""Download and run a script.
|
|
|
|
|
@@ -428,52 +415,38 @@ def download_and_extract_fnm_zip():
|
|
|
|
|
|
|
|
|
def install_node():
|
|
|
- """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)
|
|
|
+ """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)
|
|
|
|
|
|
|
|
|
def install_bun():
|
|
@@ -624,50 +597,88 @@ def validate_bun():
|
|
|
raise typer.Exit(1)
|
|
|
|
|
|
|
|
|
-def validate_frontend_dependencies(init=True):
|
|
|
- """Validate frontend dependencies to ensure they meet requirements.
|
|
|
+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.
|
|
|
|
|
|
Args:
|
|
|
- init: whether running `reflex init`
|
|
|
+ version_string: The version string
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ A cleaned semantic packaging.version object.
|
|
|
|
|
|
- Raises:
|
|
|
- Exit: If the package manager is invalid.
|
|
|
"""
|
|
|
- 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)
|
|
|
+ # 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 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)
|
|
|
|
|
|
- if constants.IS_WINDOWS:
|
|
|
- return
|
|
|
+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 init:
|
|
|
- # we only need bun for package install on `reflex init`.
|
|
|
- validate_bun()
|
|
|
+ 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
|
|
|
|
|
|
|
|
|
def initialize_frontend_dependencies():
|
|
|
"""Initialize all the frontend dependencies."""
|
|
|
# Create the reflex directory.
|
|
|
path_ops.mkdir(constants.Reflex.DIR)
|
|
|
- # validate dependencies before install
|
|
|
- validate_frontend_dependencies()
|
|
|
# Install the frontend dependencies.
|
|
|
processes.run_concurrently(install_node, install_bun)
|
|
|
# Set up the 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:
|