Make .web
configurable with REFLEX_WEB_WORKDIR (#3462)
This commit is contained in:
parent
6fdc5a84db
commit
ed05f57fc9
@ -13,6 +13,9 @@ from reflex import constants
|
|||||||
from reflex.compiler import utils
|
from reflex.compiler import utils
|
||||||
from reflex.testing import AppHarness, chdir
|
from reflex.testing import AppHarness, chdir
|
||||||
from reflex.utils import build
|
from reflex.utils import build
|
||||||
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
|
|
||||||
|
web_pages = get_web_dir() / constants.Dirs.PAGES
|
||||||
|
|
||||||
|
|
||||||
def render_component(num: int):
|
def render_component(num: int):
|
||||||
@ -231,7 +234,7 @@ def test_app_10_compile_time_cold(benchmark, app_with_10_components):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_10_components.app_path):
|
with chdir(app_with_10_components.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, ["_app.js"])
|
||||||
app_with_10_components._initialize_app()
|
app_with_10_components._initialize_app()
|
||||||
build.setup_frontend(app_with_10_components.app_path)
|
build.setup_frontend(app_with_10_components.app_path)
|
||||||
|
|
||||||
@ -284,7 +287,7 @@ def test_app_100_compile_time_cold(benchmark, app_with_100_components):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_100_components.app_path):
|
with chdir(app_with_100_components.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, ["_app.js"])
|
||||||
app_with_100_components._initialize_app()
|
app_with_100_components._initialize_app()
|
||||||
build.setup_frontend(app_with_100_components.app_path)
|
build.setup_frontend(app_with_100_components.app_path)
|
||||||
|
|
||||||
@ -337,7 +340,7 @@ def test_app_1000_compile_time_cold(benchmark, app_with_1000_components):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_1000_components.app_path):
|
with chdir(app_with_1000_components.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, keep_files=["_app.js"])
|
||||||
app_with_1000_components._initialize_app()
|
app_with_1000_components._initialize_app()
|
||||||
build.setup_frontend(app_with_1000_components.app_path)
|
build.setup_frontend(app_with_1000_components.app_path)
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ from reflex import constants
|
|||||||
from reflex.compiler import utils
|
from reflex.compiler import utils
|
||||||
from reflex.testing import AppHarness, chdir
|
from reflex.testing import AppHarness, chdir
|
||||||
from reflex.utils import build
|
from reflex.utils import build
|
||||||
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
|
|
||||||
|
web_pages = get_web_dir() / constants.Dirs.PAGES
|
||||||
|
|
||||||
|
|
||||||
def render_multiple_pages(app, num: int):
|
def render_multiple_pages(app, num: int):
|
||||||
@ -320,7 +323,7 @@ def test_app_1_compile_time_cold(benchmark, app_with_one_page):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_one_page.app_path):
|
with chdir(app_with_one_page.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, keep_files=["_app.js"])
|
||||||
app_with_one_page._initialize_app()
|
app_with_one_page._initialize_app()
|
||||||
build.setup_frontend(app_with_one_page.app_path)
|
build.setup_frontend(app_with_one_page.app_path)
|
||||||
|
|
||||||
@ -375,7 +378,7 @@ def test_app_10_compile_time_cold(benchmark, app_with_ten_pages):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_ten_pages.app_path):
|
with chdir(app_with_ten_pages.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, keep_files=["_app.js"])
|
||||||
app_with_ten_pages._initialize_app()
|
app_with_ten_pages._initialize_app()
|
||||||
build.setup_frontend(app_with_ten_pages.app_path)
|
build.setup_frontend(app_with_ten_pages.app_path)
|
||||||
|
|
||||||
@ -430,7 +433,7 @@ def test_app_100_compile_time_cold(benchmark, app_with_hundred_pages):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_hundred_pages.app_path):
|
with chdir(app_with_hundred_pages.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, keep_files=["_app.js"])
|
||||||
app_with_hundred_pages._initialize_app()
|
app_with_hundred_pages._initialize_app()
|
||||||
build.setup_frontend(app_with_hundred_pages.app_path)
|
build.setup_frontend(app_with_hundred_pages.app_path)
|
||||||
|
|
||||||
@ -485,7 +488,7 @@ def test_app_1000_compile_time_cold(benchmark, app_with_thousand_pages):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_thousand_pages.app_path):
|
with chdir(app_with_thousand_pages.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, keep_files=["_app.js"])
|
||||||
app_with_thousand_pages._initialize_app()
|
app_with_thousand_pages._initialize_app()
|
||||||
build.setup_frontend(app_with_thousand_pages.app_path)
|
build.setup_frontend(app_with_thousand_pages.app_path)
|
||||||
|
|
||||||
@ -540,7 +543,7 @@ def test_app_10000_compile_time_cold(benchmark, app_with_ten_thousand_pages):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
with chdir(app_with_ten_thousand_pages.app_path):
|
with chdir(app_with_ten_thousand_pages.app_path):
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(web_pages, keep_files=["_app.js"])
|
||||||
app_with_ten_thousand_pages._initialize_app()
|
app_with_ten_thousand_pages._initialize_app()
|
||||||
build.setup_frontend(app_with_ten_thousand_pages.app_path)
|
build.setup_frontend(app_with_ten_thousand_pages.app_path)
|
||||||
|
|
||||||
|
@ -751,10 +751,12 @@ class App(LifespanMixin, Base):
|
|||||||
if should_skip_compile():
|
if should_skip_compile():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE
|
||||||
|
|
||||||
# Check the nocompile file.
|
# Check the nocompile file.
|
||||||
if os.path.exists(constants.NOCOMPILE_FILE):
|
if nocompile.exists():
|
||||||
# Delete the nocompile file
|
# Delete the nocompile file
|
||||||
os.remove(constants.NOCOMPILE_FILE)
|
nocompile.unlink()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# By default, compile the app.
|
# By default, compile the app.
|
||||||
|
@ -20,6 +20,7 @@ from reflex.state import BaseState
|
|||||||
from reflex.style import SYSTEM_COLOR_MODE
|
from reflex.style import SYSTEM_COLOR_MODE
|
||||||
from reflex.utils.exec import is_prod_mode
|
from reflex.utils.exec import is_prod_mode
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
|
|
||||||
@ -469,7 +470,7 @@ def compile_tailwind(
|
|||||||
The compiled Tailwind config.
|
The compiled Tailwind config.
|
||||||
"""
|
"""
|
||||||
# Get the path for the output file.
|
# Get the path for the output file.
|
||||||
output_path = constants.Tailwind.CONFIG
|
output_path = get_web_dir() / constants.Tailwind.CONFIG
|
||||||
|
|
||||||
# Compile the config.
|
# Compile the config.
|
||||||
code = _compile_tailwind(config)
|
code = _compile_tailwind(config)
|
||||||
@ -483,7 +484,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
|
|||||||
The path and code of the compiled postcss.config.js.
|
The path and code of the compiled postcss.config.js.
|
||||||
"""
|
"""
|
||||||
# Get the path for the output file.
|
# Get the path for the output file.
|
||||||
output_path = constants.Dirs.POSTCSS_JS
|
output_path = str(get_web_dir() / constants.Dirs.POSTCSS_JS)
|
||||||
|
|
||||||
code = [
|
code = [
|
||||||
line
|
line
|
||||||
@ -502,7 +503,7 @@ def purge_web_pages_dir():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Empty out the web pages directory.
|
# Empty out the web pages directory.
|
||||||
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["_app.js"])
|
||||||
|
|
||||||
|
|
||||||
class ExecutorSafeFunctions:
|
class ExecutorSafeFunctions:
|
||||||
|
@ -3,9 +3,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, Optional, Type, Union
|
from typing import Any, Callable, Dict, Optional, Type, Union
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pydantic.v1.fields import ModelField
|
from pydantic.v1.fields import ModelField
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
@ -330,7 +333,7 @@ def get_page_path(path: str) -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the compiled JS file.
|
The path of the compiled JS file.
|
||||||
"""
|
"""
|
||||||
return os.path.join(constants.Dirs.WEB_PAGES, path + constants.Ext.JS)
|
return str(get_web_dir() / constants.Dirs.PAGES / (path + constants.Ext.JS))
|
||||||
|
|
||||||
|
|
||||||
def get_theme_path() -> str:
|
def get_theme_path() -> str:
|
||||||
@ -339,8 +342,10 @@ def get_theme_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the theme style.
|
The path of the theme style.
|
||||||
"""
|
"""
|
||||||
return os.path.join(
|
return str(
|
||||||
constants.Dirs.WEB_UTILS, constants.PageNames.THEME + constants.Ext.JS
|
get_web_dir()
|
||||||
|
/ constants.Dirs.UTILS
|
||||||
|
/ (constants.PageNames.THEME + constants.Ext.JS)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -350,8 +355,10 @@ def get_root_stylesheet_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the app root file.
|
The path of the app root file.
|
||||||
"""
|
"""
|
||||||
return os.path.join(
|
return str(
|
||||||
constants.STYLES_DIR, constants.PageNames.STYLESHEET_ROOT + constants.Ext.CSS
|
get_web_dir()
|
||||||
|
/ constants.Dirs.STYLES
|
||||||
|
/ (constants.PageNames.STYLESHEET_ROOT + constants.Ext.CSS)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -361,9 +368,7 @@ def get_context_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the context module.
|
The path of the context module.
|
||||||
"""
|
"""
|
||||||
return os.path.join(
|
return str(get_web_dir() / (constants.Dirs.CONTEXTS_PATH + constants.Ext.JS))
|
||||||
constants.Dirs.WEB, constants.Dirs.CONTEXTS_PATH + constants.Ext.JS
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_components_path() -> str:
|
def get_components_path() -> str:
|
||||||
@ -372,7 +377,11 @@ def get_components_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the compiled components.
|
The path of the compiled components.
|
||||||
"""
|
"""
|
||||||
return os.path.join(constants.Dirs.WEB_UTILS, "components" + constants.Ext.JS)
|
return str(
|
||||||
|
get_web_dir()
|
||||||
|
/ constants.Dirs.UTILS
|
||||||
|
/ (constants.PageNames.COMPONENTS + constants.Ext.JS),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_stateful_components_path() -> str:
|
def get_stateful_components_path() -> str:
|
||||||
@ -381,9 +390,10 @@ def get_stateful_components_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the compiled stateful components.
|
The path of the compiled stateful components.
|
||||||
"""
|
"""
|
||||||
return os.path.join(
|
return str(
|
||||||
constants.Dirs.WEB_UTILS,
|
get_web_dir()
|
||||||
constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JS,
|
/ constants.Dirs.UTILS
|
||||||
|
/ (constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JS)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -437,23 +447,24 @@ def write_page(path: str, code: str):
|
|||||||
f.write(code)
|
f.write(code)
|
||||||
|
|
||||||
|
|
||||||
def empty_dir(path: str, keep_files: list[str] | None = None):
|
def empty_dir(path: str | Path, keep_files: list[str] | None = None):
|
||||||
"""Remove all files and folders in a directory except for the keep_files.
|
"""Remove all files and folders in a directory except for the keep_files.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: The path to the directory that will be emptied
|
path: The path to the directory that will be emptied
|
||||||
keep_files: List of filenames or foldernames that will not be deleted.
|
keep_files: List of filenames or foldernames that will not be deleted.
|
||||||
"""
|
"""
|
||||||
|
path = Path(path)
|
||||||
|
|
||||||
# If the directory does not exist, return.
|
# If the directory does not exist, return.
|
||||||
if not os.path.exists(path):
|
if not path.exists():
|
||||||
return
|
return
|
||||||
|
|
||||||
# Remove all files and folders in the directory.
|
# Remove all files and folders in the directory.
|
||||||
keep_files = keep_files or []
|
keep_files = keep_files or []
|
||||||
directory_contents = os.listdir(path)
|
for element in path.iterdir():
|
||||||
for element in directory_contents:
|
if element.name not in keep_files:
|
||||||
if element not in keep_files:
|
path_ops.rm(element)
|
||||||
path_ops.rm(os.path.join(path, element))
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid_url(url) -> bool:
|
def is_valid_url(url) -> bool:
|
||||||
|
@ -62,7 +62,7 @@ from .route import (
|
|||||||
RouteRegex,
|
RouteRegex,
|
||||||
RouteVar,
|
RouteVar,
|
||||||
)
|
)
|
||||||
from .style import STYLES_DIR, Tailwind
|
from .style import Tailwind
|
||||||
|
|
||||||
__ALL__ = [
|
__ALL__ = [
|
||||||
ALEMBIC_CONFIG,
|
ALEMBIC_CONFIG,
|
||||||
@ -113,7 +113,6 @@ __ALL__ = [
|
|||||||
SETTER_PREFIX,
|
SETTER_PREFIX,
|
||||||
SKIP_COMPILE_ENV_VAR,
|
SKIP_COMPILE_ENV_VAR,
|
||||||
SocketEvent,
|
SocketEvent,
|
||||||
STYLES_DIR,
|
|
||||||
Tailwind,
|
Tailwind,
|
||||||
Templates,
|
Templates,
|
||||||
CompileVars,
|
CompileVars,
|
||||||
|
@ -25,30 +25,26 @@ class Dirs(SimpleNamespace):
|
|||||||
EXTERNAL_APP_ASSETS = "external"
|
EXTERNAL_APP_ASSETS = "external"
|
||||||
# The name of the utils file.
|
# The name of the utils file.
|
||||||
UTILS = "utils"
|
UTILS = "utils"
|
||||||
# The name of the output static directory.
|
|
||||||
STATIC = "_static"
|
|
||||||
# The name of the public html directory served at "/"
|
|
||||||
PUBLIC = "public"
|
|
||||||
# The name of the state file.
|
# The name of the state file.
|
||||||
STATE_PATH = "/".join([UTILS, "state"])
|
STATE_PATH = "/".join([UTILS, "state"])
|
||||||
# The name of the components file.
|
# The name of the components file.
|
||||||
COMPONENTS_PATH = "/".join([UTILS, "components"])
|
COMPONENTS_PATH = "/".join([UTILS, "components"])
|
||||||
# The name of the contexts file.
|
# The name of the contexts file.
|
||||||
CONTEXTS_PATH = "/".join([UTILS, "context"])
|
CONTEXTS_PATH = "/".join([UTILS, "context"])
|
||||||
# The directory where the app pages are compiled to.
|
# The name of the output static directory.
|
||||||
WEB_PAGES = os.path.join(WEB, "pages")
|
STATIC = "_static"
|
||||||
# The directory where the static build is located.
|
# The name of the public html directory served at "/"
|
||||||
WEB_STATIC = os.path.join(WEB, STATIC)
|
PUBLIC = "public"
|
||||||
# The directory where the utils file is located.
|
# The directory where styles are located.
|
||||||
WEB_UTILS = os.path.join(WEB, UTILS)
|
STYLES = "styles"
|
||||||
# The directory where the assets are located.
|
# The name of the pages directory.
|
||||||
WEB_ASSETS = os.path.join(WEB, PUBLIC)
|
PAGES = "pages"
|
||||||
# The env json file.
|
# The name of the env json file.
|
||||||
ENV_JSON = os.path.join(WEB, "env.json")
|
ENV_JSON = "env.json"
|
||||||
# The reflex json file.
|
# The name of the reflex json file.
|
||||||
REFLEX_JSON = os.path.join(WEB, "reflex.json")
|
REFLEX_JSON = "reflex.json"
|
||||||
# The path to postcss.config.js
|
# The name of the postcss config file.
|
||||||
POSTCSS_JS = os.path.join(WEB, "postcss.config.js")
|
POSTCSS_JS = "postcss.config.js"
|
||||||
|
|
||||||
|
|
||||||
class Reflex(SimpleNamespace):
|
class Reflex(SimpleNamespace):
|
||||||
@ -61,7 +57,7 @@ class Reflex(SimpleNamespace):
|
|||||||
VERSION = metadata.version(MODULE_NAME)
|
VERSION = metadata.version(MODULE_NAME)
|
||||||
|
|
||||||
# The reflex json file.
|
# The reflex json file.
|
||||||
JSON = os.path.join(Dirs.WEB, "reflex.json")
|
JSON = "reflex.json"
|
||||||
|
|
||||||
# Files and directories used to init a new project.
|
# Files and directories used to init a new project.
|
||||||
# The directory to store reflex dependencies.
|
# The directory to store reflex dependencies.
|
||||||
@ -117,7 +113,7 @@ class Next(SimpleNamespace):
|
|||||||
# The NextJS config file
|
# The NextJS config file
|
||||||
CONFIG_FILE = "next.config.js"
|
CONFIG_FILE = "next.config.js"
|
||||||
# The sitemap config file.
|
# The sitemap config file.
|
||||||
SITEMAP_CONFIG_FILE = os.path.join(Dirs.WEB, "next-sitemap.config.js")
|
SITEMAP_CONFIG_FILE = "next-sitemap.config.js"
|
||||||
# The node modules directory.
|
# The node modules directory.
|
||||||
NODE_MODULES = "node_modules"
|
NODE_MODULES = "node_modules"
|
||||||
# The package lock file.
|
# The package lock file.
|
||||||
|
@ -12,7 +12,7 @@ from reflex.utils.imports import ImportVar
|
|||||||
SETTER_PREFIX = "set_"
|
SETTER_PREFIX = "set_"
|
||||||
|
|
||||||
# The file used to specify no compilation.
|
# The file used to specify no compilation.
|
||||||
NOCOMPILE_FILE = ".web/nocompile"
|
NOCOMPILE_FILE = "nocompile"
|
||||||
|
|
||||||
|
|
||||||
class Ext(SimpleNamespace):
|
class Ext(SimpleNamespace):
|
||||||
@ -80,6 +80,8 @@ class PageNames(SimpleNamespace):
|
|||||||
DOCUMENT_ROOT = "_document"
|
DOCUMENT_ROOT = "_document"
|
||||||
# The name of the theme page.
|
# The name of the theme page.
|
||||||
THEME = "theme"
|
THEME = "theme"
|
||||||
|
# The module containing components.
|
||||||
|
COMPONENTS = "components"
|
||||||
# The module containing shared stateful components
|
# The module containing shared stateful components
|
||||||
STATEFUL_COMPONENTS = "stateful_components"
|
STATEFUL_COMPONENTS = "stateful_components"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
import platform
|
import platform
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
from .base import IS_WINDOWS, Dirs, Reflex
|
from .base import IS_WINDOWS, Reflex
|
||||||
|
|
||||||
|
|
||||||
def get_fnm_name() -> str | None:
|
def get_fnm_name() -> str | None:
|
||||||
@ -105,7 +105,7 @@ class PackageJson(SimpleNamespace):
|
|||||||
EXPORT_SITEMAP = "next build && next-sitemap"
|
EXPORT_SITEMAP = "next build && next-sitemap"
|
||||||
PROD = "next start"
|
PROD = "next start"
|
||||||
|
|
||||||
PATH = os.path.join(Dirs.WEB, "package.json")
|
PATH = "package.json"
|
||||||
|
|
||||||
DEPENDENCIES = {
|
DEPENDENCIES = {
|
||||||
"@emotion/react": "11.11.1",
|
"@emotion/react": "11.11.1",
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
"""Style constants."""
|
"""Style constants."""
|
||||||
|
|
||||||
import os
|
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
from reflex.constants.base import Dirs
|
|
||||||
|
|
||||||
# The directory where styles are located.
|
|
||||||
STYLES_DIR = os.path.join(Dirs.WEB, "styles")
|
|
||||||
|
|
||||||
|
|
||||||
class Tailwind(SimpleNamespace):
|
class Tailwind(SimpleNamespace):
|
||||||
"""Tailwind constants."""
|
"""Tailwind constants."""
|
||||||
@ -15,8 +8,8 @@ class Tailwind(SimpleNamespace):
|
|||||||
# The Tailwindcss version
|
# The Tailwindcss version
|
||||||
VERSION = "tailwindcss@3.3.2"
|
VERSION = "tailwindcss@3.3.2"
|
||||||
# The Tailwind config.
|
# The Tailwind config.
|
||||||
CONFIG = os.path.join(Dirs.WEB, "tailwind.config.js")
|
CONFIG = "tailwind.config.js"
|
||||||
# Default Tailwind content paths
|
# Default Tailwind content paths
|
||||||
CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}", "./utils/**/*.{js,ts,jsx,tsx}"]
|
CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}", "./utils/**/*.{js,ts,jsx,tsx}"]
|
||||||
# Relative tailwind style path to root stylesheet in STYLES_DIR.
|
# Relative tailwind style path to root stylesheet in Dirs.STYLES.
|
||||||
ROOT_STYLE_PATH = "./tailwind.css"
|
ROOT_STYLE_PATH = "./tailwind.css"
|
||||||
|
@ -330,7 +330,7 @@ class AppHarness:
|
|||||||
# Start the frontend.
|
# Start the frontend.
|
||||||
self.frontend_process = reflex.utils.processes.new_process(
|
self.frontend_process = reflex.utils.processes.new_process(
|
||||||
[reflex.utils.prerequisites.get_package_manager(), "run", "dev"],
|
[reflex.utils.prerequisites.get_package_manager(), "run", "dev"],
|
||||||
cwd=self.app_path / reflex.constants.Dirs.WEB,
|
cwd=self.app_path / reflex.utils.prerequisites.get_web_dir(),
|
||||||
env={"PORT": "0"},
|
env={"PORT": "0"},
|
||||||
**FRONTEND_POPEN_ARGS,
|
**FRONTEND_POPEN_ARGS,
|
||||||
)
|
)
|
||||||
@ -854,7 +854,11 @@ class AppHarnessProd(AppHarness):
|
|||||||
frontend_server: Optional[Subdir404TCPServer] = None
|
frontend_server: Optional[Subdir404TCPServer] = None
|
||||||
|
|
||||||
def _run_frontend(self):
|
def _run_frontend(self):
|
||||||
web_root = self.app_path / reflex.constants.Dirs.WEB_STATIC
|
web_root = (
|
||||||
|
self.app_path
|
||||||
|
/ reflex.utils.prerequisites.get_web_dir()
|
||||||
|
/ reflex.constants.Dirs.STATIC
|
||||||
|
)
|
||||||
error_page_map = {
|
error_page_map = {
|
||||||
404: web_root / "404.html",
|
404: web_root / "404.html",
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ from reflex.utils import console, path_ops, prerequisites, processes
|
|||||||
def set_env_json():
|
def set_env_json():
|
||||||
"""Write the upload url to a REFLEX_JSON."""
|
"""Write the upload url to a REFLEX_JSON."""
|
||||||
path_ops.update_json_file(
|
path_ops.update_json_file(
|
||||||
constants.Dirs.ENV_JSON,
|
str(prerequisites.get_web_dir() / constants.Dirs.ENV_JSON),
|
||||||
{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
|
{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ def generate_sitemap_config(deploy_url: str, export=False):
|
|||||||
|
|
||||||
config = json.dumps(config)
|
config = json.dumps(config)
|
||||||
|
|
||||||
with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
|
sitemap = prerequisites.get_web_dir() / constants.Next.SITEMAP_CONFIG_FILE
|
||||||
f.write(templates.SITEMAP_CONFIG(config=config))
|
sitemap.write_text(templates.SITEMAP_CONFIG(config=config))
|
||||||
|
|
||||||
|
|
||||||
def _zip(
|
def _zip(
|
||||||
@ -129,85 +129,89 @@ def _zip(
|
|||||||
zipf.write(file, os.path.relpath(file, root_dir))
|
zipf.write(file, os.path.relpath(file, root_dir))
|
||||||
|
|
||||||
|
|
||||||
def export(
|
def zip_app(
|
||||||
backend: bool = True,
|
|
||||||
frontend: bool = True,
|
frontend: bool = True,
|
||||||
zip: bool = False,
|
backend: bool = True,
|
||||||
zip_dest_dir: str = os.getcwd(),
|
zip_dest_dir: str = os.getcwd(),
|
||||||
deploy_url: str | None = None,
|
|
||||||
upload_db_file: bool = False,
|
upload_db_file: bool = False,
|
||||||
):
|
):
|
||||||
"""Export the app for deployment.
|
"""Zip up the app.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
backend: Whether to zip up the backend app.
|
|
||||||
frontend: Whether to zip up the frontend app.
|
frontend: Whether to zip up the frontend app.
|
||||||
zip: Whether to zip the app.
|
backend: Whether to zip up the backend app.
|
||||||
zip_dest_dir: The destination directory for created zip files (if any)
|
zip_dest_dir: The directory to export the zip file to.
|
||||||
deploy_url: The URL of the deployed app.
|
upload_db_file: Whether to upload the database file.
|
||||||
upload_db_file: Whether to include local sqlite db files from the backend zip.
|
|
||||||
"""
|
"""
|
||||||
# Remove the static folder.
|
files_to_exclude = {
|
||||||
path_ops.rm(constants.Dirs.WEB_STATIC)
|
constants.ComponentName.FRONTEND.zip(),
|
||||||
|
constants.ComponentName.BACKEND.zip(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if frontend:
|
||||||
|
_zip(
|
||||||
|
component_name=constants.ComponentName.FRONTEND,
|
||||||
|
target=os.path.join(zip_dest_dir, constants.ComponentName.FRONTEND.zip()),
|
||||||
|
root_dir=str(prerequisites.get_web_dir() / constants.Dirs.STATIC),
|
||||||
|
files_to_exclude=files_to_exclude,
|
||||||
|
exclude_venv_dirs=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if backend:
|
||||||
|
_zip(
|
||||||
|
component_name=constants.ComponentName.BACKEND,
|
||||||
|
target=os.path.join(zip_dest_dir, constants.ComponentName.BACKEND.zip()),
|
||||||
|
root_dir=".",
|
||||||
|
dirs_to_exclude={"__pycache__"},
|
||||||
|
files_to_exclude=files_to_exclude,
|
||||||
|
top_level_dirs_to_exclude={"assets"},
|
||||||
|
exclude_venv_dirs=True,
|
||||||
|
upload_db_file=upload_db_file,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build(
|
||||||
|
deploy_url: str | None = None,
|
||||||
|
for_export: bool = False,
|
||||||
|
):
|
||||||
|
"""Build the app for deployment.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
deploy_url: The deployment URL.
|
||||||
|
for_export: Whether the build is for export.
|
||||||
|
"""
|
||||||
|
wdir = prerequisites.get_web_dir()
|
||||||
|
|
||||||
|
# Clean the static directory if it exists.
|
||||||
|
path_ops.rm(str(wdir / constants.Dirs.STATIC))
|
||||||
|
|
||||||
# The export command to run.
|
# The export command to run.
|
||||||
command = "export"
|
command = "export"
|
||||||
|
|
||||||
if frontend:
|
checkpoints = [
|
||||||
checkpoints = [
|
"Linting and checking ",
|
||||||
"Linting and checking ",
|
"Creating an optimized production build",
|
||||||
"Creating an optimized production build",
|
"Route (pages)",
|
||||||
"Route (pages)",
|
"prerendered as static HTML",
|
||||||
"prerendered as static HTML",
|
"Collecting page data",
|
||||||
"Collecting page data",
|
"Finalizing page optimization",
|
||||||
"Finalizing page optimization",
|
"Collecting build traces",
|
||||||
"Collecting build traces",
|
]
|
||||||
]
|
|
||||||
|
|
||||||
# Generate a sitemap if a deploy URL is provided.
|
# Generate a sitemap if a deploy URL is provided.
|
||||||
if deploy_url is not None:
|
if deploy_url is not None:
|
||||||
generate_sitemap_config(deploy_url, export=zip)
|
generate_sitemap_config(deploy_url, export=for_export)
|
||||||
command = "export-sitemap"
|
command = "export-sitemap"
|
||||||
|
|
||||||
checkpoints.extend(["Loading next-sitemap", "Generation completed"])
|
checkpoints.extend(["Loading next-sitemap", "Generation completed"])
|
||||||
|
|
||||||
# Start the subprocess with the progress bar.
|
# Start the subprocess with the progress bar.
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[prerequisites.get_package_manager(), "run", command],
|
[prerequisites.get_package_manager(), "run", command],
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=wdir,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_progress("Creating Production Build", process, checkpoints)
|
processes.show_progress("Creating Production Build", process, checkpoints)
|
||||||
|
|
||||||
# Zip up the app.
|
|
||||||
if zip:
|
|
||||||
files_to_exclude = {
|
|
||||||
constants.ComponentName.FRONTEND.zip(),
|
|
||||||
constants.ComponentName.BACKEND.zip(),
|
|
||||||
}
|
|
||||||
if frontend:
|
|
||||||
_zip(
|
|
||||||
component_name=constants.ComponentName.FRONTEND,
|
|
||||||
target=os.path.join(
|
|
||||||
zip_dest_dir, constants.ComponentName.FRONTEND.zip()
|
|
||||||
),
|
|
||||||
root_dir=constants.Dirs.WEB_STATIC,
|
|
||||||
files_to_exclude=files_to_exclude,
|
|
||||||
exclude_venv_dirs=False,
|
|
||||||
)
|
|
||||||
if backend:
|
|
||||||
_zip(
|
|
||||||
component_name=constants.ComponentName.BACKEND,
|
|
||||||
target=os.path.join(
|
|
||||||
zip_dest_dir, constants.ComponentName.BACKEND.zip()
|
|
||||||
),
|
|
||||||
root_dir=".",
|
|
||||||
dirs_to_exclude={"__pycache__"},
|
|
||||||
files_to_exclude=files_to_exclude,
|
|
||||||
top_level_dirs_to_exclude={"assets"},
|
|
||||||
exclude_venv_dirs=True,
|
|
||||||
upload_db_file=upload_db_file,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_frontend(
|
def setup_frontend(
|
||||||
@ -226,7 +230,7 @@ def setup_frontend(
|
|||||||
# Copy asset files to public folder.
|
# Copy asset files to public folder.
|
||||||
path_ops.cp(
|
path_ops.cp(
|
||||||
src=str(root / constants.Dirs.APP_ASSETS),
|
src=str(root / constants.Dirs.APP_ASSETS),
|
||||||
dest=str(root / constants.Dirs.WEB_ASSETS),
|
dest=str(root / prerequisites.get_web_dir() / constants.Dirs.PUBLIC),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the environment variables in client (env.json).
|
# Set the environment variables in client (env.json).
|
||||||
@ -242,7 +246,7 @@ def setup_frontend(
|
|||||||
"telemetry",
|
"telemetry",
|
||||||
"disable",
|
"disable",
|
||||||
],
|
],
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=prerequisites.get_web_dir(),
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
@ -259,7 +263,7 @@ def setup_frontend_prod(
|
|||||||
disable_telemetry: Whether to disable the Next telemetry.
|
disable_telemetry: Whether to disable the Next telemetry.
|
||||||
"""
|
"""
|
||||||
setup_frontend(root, disable_telemetry)
|
setup_frontend(root, disable_telemetry)
|
||||||
export(deploy_url=get_config().deploy_url)
|
build(deploy_url=get_config().deploy_url)
|
||||||
|
|
||||||
|
|
||||||
def _looks_like_venv_dir(dir_to_check: str) -> bool:
|
def _looks_like_venv_dir(dir_to_check: str) -> bool:
|
||||||
|
@ -17,6 +17,7 @@ import psutil
|
|||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.config import get_config
|
from reflex.config import get_config
|
||||||
from reflex.utils import console, path_ops
|
from reflex.utils import console, path_ops
|
||||||
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
from reflex.utils.watch import AssetFolderWatch
|
from reflex.utils.watch import AssetFolderWatch
|
||||||
|
|
||||||
# For uvicorn windows bug fix (#2335)
|
# For uvicorn windows bug fix (#2335)
|
||||||
@ -82,8 +83,8 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
|
|||||||
"""
|
"""
|
||||||
from reflex.utils import processes
|
from reflex.utils import processes
|
||||||
|
|
||||||
json_file_path = os.path.join(constants.Dirs.WEB, "package.json")
|
json_file_path = get_web_dir() / constants.PackageJson.PATH
|
||||||
last_hash = detect_package_change(json_file_path)
|
last_hash = detect_package_change(str(json_file_path))
|
||||||
process = None
|
process = None
|
||||||
first_run = True
|
first_run = True
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
|
|||||||
kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore
|
kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
run_command,
|
run_command,
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=get_web_dir(),
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
@ -128,7 +129,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
|
|||||||
"`REFLEX_USE_NPM=1 reflex init`\n"
|
"`REFLEX_USE_NPM=1 reflex init`\n"
|
||||||
"`REFLEX_USE_NPM=1 reflex run`"
|
"`REFLEX_USE_NPM=1 reflex run`"
|
||||||
)
|
)
|
||||||
new_hash = detect_package_change(json_file_path)
|
new_hash = detect_package_change(str(json_file_path))
|
||||||
if new_hash != last_hash:
|
if new_hash != last_hash:
|
||||||
last_hash = new_hash
|
last_hash = new_hash
|
||||||
kill(process.pid)
|
kill(process.pid)
|
||||||
@ -201,10 +202,10 @@ def run_backend(
|
|||||||
config = get_config()
|
config = get_config()
|
||||||
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
||||||
|
|
||||||
|
web_dir = get_web_dir()
|
||||||
# Create a .nocompile file to skip compile for backend.
|
# Create a .nocompile file to skip compile for backend.
|
||||||
if os.path.exists(constants.Dirs.WEB):
|
if web_dir.exists():
|
||||||
with open(constants.NOCOMPILE_FILE, "w"):
|
(web_dir / constants.NOCOMPILE_FILE).touch()
|
||||||
pass
|
|
||||||
|
|
||||||
# Run the backend in development mode.
|
# Run the backend in development mode.
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
@ -214,7 +215,7 @@ def run_backend(
|
|||||||
log_level=loglevel.value,
|
log_level=loglevel.value,
|
||||||
reload=True,
|
reload=True,
|
||||||
reload_dirs=[config.app_name],
|
reload_dirs=[config.app_name],
|
||||||
reload_excludes=[constants.Dirs.WEB],
|
reload_excludes=[str(web_dir)],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,15 +55,18 @@ def export(
|
|||||||
# Set up .web directory and install frontend dependencies.
|
# Set up .web directory and install frontend dependencies.
|
||||||
build.setup_frontend(Path.cwd())
|
build.setup_frontend(Path.cwd())
|
||||||
|
|
||||||
# Export the app.
|
# Build the static app.
|
||||||
build.export(
|
if frontend:
|
||||||
backend=backend,
|
build.build(deploy_url=config.deploy_url, for_export=True)
|
||||||
frontend=frontend,
|
|
||||||
zip=zipping,
|
# Zip up the app.
|
||||||
zip_dest_dir=zip_dest_dir,
|
if zipping:
|
||||||
deploy_url=config.deploy_url,
|
build.zip_app(
|
||||||
upload_db_file=upload_db_file,
|
frontend=frontend,
|
||||||
)
|
backend=backend,
|
||||||
|
zip_dest_dir=zip_dest_dir,
|
||||||
|
upload_db_file=upload_db_file,
|
||||||
|
)
|
||||||
|
|
||||||
# Post a telemetry event.
|
# Post a telemetry event.
|
||||||
telemetry.send("export")
|
telemetry.send("export")
|
||||||
|
@ -14,19 +14,20 @@ from reflex import constants
|
|||||||
join = os.linesep.join
|
join = os.linesep.join
|
||||||
|
|
||||||
|
|
||||||
def rm(path: str):
|
def rm(path: str | Path):
|
||||||
"""Remove a file or directory.
|
"""Remove a file or directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: The path to the file or directory.
|
path: The path to the file or directory.
|
||||||
"""
|
"""
|
||||||
if os.path.isdir(path):
|
path = Path(path)
|
||||||
|
if path.is_dir():
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path)
|
||||||
elif os.path.isfile(path):
|
elif path.is_file():
|
||||||
os.remove(path)
|
path.unlink()
|
||||||
|
|
||||||
|
|
||||||
def cp(src: str, dest: str, overwrite: bool = True) -> bool:
|
def cp(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
|
||||||
"""Copy a file or directory.
|
"""Copy a file or directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -37,11 +38,12 @@ def cp(src: str, dest: str, overwrite: bool = True) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
Whether the copy was successful.
|
Whether the copy was successful.
|
||||||
"""
|
"""
|
||||||
|
src, dest = Path(src), Path(dest)
|
||||||
if src == dest:
|
if src == dest:
|
||||||
return False
|
return False
|
||||||
if not overwrite and os.path.exists(dest):
|
if not overwrite and dest.exists():
|
||||||
return False
|
return False
|
||||||
if os.path.isdir(src):
|
if src.is_dir():
|
||||||
rm(dest)
|
rm(dest)
|
||||||
shutil.copytree(src, dest)
|
shutil.copytree(src, dest)
|
||||||
else:
|
else:
|
||||||
@ -49,7 +51,7 @@ def cp(src: str, dest: str, overwrite: bool = True) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def mv(src: str, dest: str, overwrite: bool = True) -> bool:
|
def mv(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
|
||||||
"""Move a file or directory.
|
"""Move a file or directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -60,25 +62,26 @@ def mv(src: str, dest: str, overwrite: bool = True) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
Whether the move was successful.
|
Whether the move was successful.
|
||||||
"""
|
"""
|
||||||
|
src, dest = Path(src), Path(dest)
|
||||||
if src == dest:
|
if src == dest:
|
||||||
return False
|
return False
|
||||||
if not overwrite and os.path.exists(dest):
|
if not overwrite and dest.exists():
|
||||||
return False
|
return False
|
||||||
rm(dest)
|
rm(dest)
|
||||||
shutil.move(src, dest)
|
shutil.move(src, dest)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def mkdir(path: str):
|
def mkdir(path: str | Path):
|
||||||
"""Create a directory.
|
"""Create a directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: The path to the directory.
|
path: The path to the directory.
|
||||||
"""
|
"""
|
||||||
os.makedirs(path, exist_ok=True)
|
Path(path).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def ln(src: str, dest: str, overwrite: bool = False) -> bool:
|
def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
|
||||||
"""Create a symbolic link.
|
"""Create a symbolic link.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -89,19 +92,20 @@ def ln(src: str, dest: str, overwrite: bool = False) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
Whether the link was successful.
|
Whether the link was successful.
|
||||||
"""
|
"""
|
||||||
|
src, dest = Path(src), Path(dest)
|
||||||
if src == dest:
|
if src == dest:
|
||||||
return False
|
return False
|
||||||
if not overwrite and (os.path.exists(dest) or os.path.islink(dest)):
|
if not overwrite and (dest.exists() or dest.is_symlink()):
|
||||||
return False
|
return False
|
||||||
if os.path.isdir(src):
|
if src.is_dir():
|
||||||
rm(dest)
|
rm(dest)
|
||||||
os.symlink(src, dest, target_is_directory=True)
|
src.symlink_to(dest, target_is_directory=True)
|
||||||
else:
|
else:
|
||||||
os.symlink(src, dest)
|
src.symlink_to(dest)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def which(program: str) -> str | None:
|
def which(program: str | Path) -> str | Path | None:
|
||||||
"""Find the path to an executable.
|
"""Find the path to an executable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -110,7 +114,7 @@ def which(program: str) -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the executable.
|
The path to the executable.
|
||||||
"""
|
"""
|
||||||
return shutil.which(program)
|
return shutil.which(str(program))
|
||||||
|
|
||||||
|
|
||||||
def get_node_bin_path() -> str | None:
|
def get_node_bin_path() -> str | None:
|
||||||
@ -119,10 +123,11 @@ def get_node_bin_path() -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the node bin folder.
|
The path to the node bin folder.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.Node.BIN_PATH):
|
bin_path = Path(constants.Node.BIN_PATH)
|
||||||
|
if not bin_path.exists():
|
||||||
str_path = which("node")
|
str_path = which("node")
|
||||||
return str(Path(str_path).parent.resolve()) if str_path else str_path
|
return str(Path(str_path).parent.resolve()) if str_path else str_path
|
||||||
return str(Path(constants.Node.BIN_PATH).resolve())
|
return str(bin_path.resolve())
|
||||||
|
|
||||||
|
|
||||||
def get_node_path() -> str | None:
|
def get_node_path() -> str | None:
|
||||||
@ -131,9 +136,10 @@ def get_node_path() -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the node binary file.
|
The path to the node binary file.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.Node.PATH):
|
node_path = Path(constants.Node.PATH)
|
||||||
return which("node")
|
if not node_path.exists():
|
||||||
return constants.Node.PATH
|
return str(which("node"))
|
||||||
|
return str(node_path)
|
||||||
|
|
||||||
|
|
||||||
def get_npm_path() -> str | None:
|
def get_npm_path() -> str | None:
|
||||||
@ -142,12 +148,13 @@ def get_npm_path() -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the npm binary file.
|
The path to the npm binary file.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.Node.PATH):
|
npm_path = Path(constants.Node.NPM_PATH)
|
||||||
return which("npm")
|
if not npm_path.exists():
|
||||||
return constants.Node.NPM_PATH
|
return str(which("npm"))
|
||||||
|
return str(npm_path)
|
||||||
|
|
||||||
|
|
||||||
def update_json_file(file_path: str, update_dict: dict[str, int | str]):
|
def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
|
||||||
"""Update the contents of a json file.
|
"""Update the contents of a json file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -176,7 +183,7 @@ def update_json_file(file_path: str, update_dict: dict[str, int | str]):
|
|||||||
json.dump(json_object, f, ensure_ascii=False)
|
json.dump(json_object, f, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
def find_replace(directory: str, find: str, replace: str):
|
def find_replace(directory: str | Path, find: str, replace: str):
|
||||||
"""Recursively find and replace text in files in a directory.
|
"""Recursively find and replace text in files in a directory.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -184,11 +191,10 @@ def find_replace(directory: str, find: str, replace: str):
|
|||||||
find: The text to find.
|
find: The text to find.
|
||||||
replace: The text to replace.
|
replace: The text to replace.
|
||||||
"""
|
"""
|
||||||
|
directory = Path(directory)
|
||||||
for root, _dirs, files in os.walk(directory):
|
for root, _dirs, files in os.walk(directory):
|
||||||
for file in files:
|
for file in files:
|
||||||
filepath = os.path.join(root, file)
|
filepath = Path(root, file)
|
||||||
with open(filepath, "r", encoding="utf-8") as f:
|
text = filepath.read_text(encoding="utf-8")
|
||||||
text = f.read()
|
|
||||||
text = re.sub(find, replace, text)
|
text = re.sub(find, replace, text)
|
||||||
with open(filepath, "w") as f:
|
filepath.write_text(text)
|
||||||
f.write(text)
|
|
||||||
|
@ -58,6 +58,18 @@ class CpuInfo(Base):
|
|||||||
address_width: Optional[int]
|
address_width: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
|
def get_web_dir() -> Path:
|
||||||
|
"""Get the working directory for the next.js commands.
|
||||||
|
|
||||||
|
Can be overriden with REFLEX_WEB_WORKDIR.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The working directory.
|
||||||
|
"""
|
||||||
|
workdir = Path(os.getenv("REFLEX_WEB_WORKDIR", constants.Dirs.WEB))
|
||||||
|
return workdir
|
||||||
|
|
||||||
|
|
||||||
def check_latest_package_version(package_name: str):
|
def check_latest_package_version(package_name: str):
|
||||||
"""Check if the latest version of the package is installed.
|
"""Check if the latest version of the package is installed.
|
||||||
|
|
||||||
@ -91,15 +103,15 @@ def get_or_set_last_reflex_version_check_datetime():
|
|||||||
Returns:
|
Returns:
|
||||||
The last version check datetime.
|
The last version check datetime.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.Reflex.JSON):
|
reflex_json_file = get_web_dir() / constants.Reflex.JSON
|
||||||
|
if not reflex_json_file.exists():
|
||||||
return None
|
return None
|
||||||
# Open and read the file
|
# Open and read the file
|
||||||
with open(constants.Reflex.JSON, "r") as file:
|
data = json.loads(reflex_json_file.read_text())
|
||||||
data: dict = json.load(file)
|
|
||||||
last_version_check_datetime = data.get("last_version_check_datetime")
|
last_version_check_datetime = data.get("last_version_check_datetime")
|
||||||
if not last_version_check_datetime:
|
if not last_version_check_datetime:
|
||||||
data.update({"last_version_check_datetime": str(datetime.now())})
|
data.update({"last_version_check_datetime": str(datetime.now())})
|
||||||
path_ops.update_json_file(constants.Reflex.JSON, data)
|
path_ops.update_json_file(reflex_json_file, data)
|
||||||
return last_version_check_datetime
|
return last_version_check_datetime
|
||||||
|
|
||||||
|
|
||||||
@ -513,12 +525,11 @@ def get_project_hash(raise_on_fail: bool = False) -> int | None:
|
|||||||
Returns:
|
Returns:
|
||||||
project_hash: The app hash.
|
project_hash: The app hash.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.Reflex.JSON) and not raise_on_fail:
|
json_file = get_web_dir() / constants.Reflex.JSON
|
||||||
|
if not json_file.exists() and not raise_on_fail:
|
||||||
return None
|
return None
|
||||||
# Open and read the file
|
data = json.loads(json_file.read_text())
|
||||||
with open(constants.Reflex.JSON, "r") as file:
|
return data.get("project_hash")
|
||||||
data = json.load(file)
|
|
||||||
return data.get("project_hash")
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_web_directory():
|
def initialize_web_directory():
|
||||||
@ -528,11 +539,11 @@ def initialize_web_directory():
|
|||||||
# Re-use the hash if one is already created, so we don't over-write it when running reflex init
|
# Re-use the hash if one is already created, so we don't over-write it when running reflex init
|
||||||
project_hash = get_project_hash()
|
project_hash = get_project_hash()
|
||||||
|
|
||||||
path_ops.cp(constants.Templates.Dirs.WEB_TEMPLATE, constants.Dirs.WEB)
|
path_ops.cp(constants.Templates.Dirs.WEB_TEMPLATE, str(get_web_dir()))
|
||||||
|
|
||||||
initialize_package_json()
|
initialize_package_json()
|
||||||
|
|
||||||
path_ops.mkdir(constants.Dirs.WEB_ASSETS)
|
path_ops.mkdir(get_web_dir() / constants.Dirs.PUBLIC)
|
||||||
|
|
||||||
update_next_config()
|
update_next_config()
|
||||||
|
|
||||||
@ -555,10 +566,9 @@ def _compile_package_json():
|
|||||||
|
|
||||||
def initialize_package_json():
|
def initialize_package_json():
|
||||||
"""Render and write in .web the package.json file."""
|
"""Render and write in .web the package.json file."""
|
||||||
output_path = constants.PackageJson.PATH
|
output_path = get_web_dir() / constants.PackageJson.PATH
|
||||||
code = _compile_package_json()
|
code = _compile_package_json()
|
||||||
with open(output_path, "w") as file:
|
output_path.write_text(code)
|
||||||
file.write(code)
|
|
||||||
|
|
||||||
|
|
||||||
def init_reflex_json(project_hash: int | None):
|
def init_reflex_json(project_hash: int | None):
|
||||||
@ -583,7 +593,7 @@ def init_reflex_json(project_hash: int | None):
|
|||||||
"version": constants.Reflex.VERSION,
|
"version": constants.Reflex.VERSION,
|
||||||
"project_hash": project_hash,
|
"project_hash": project_hash,
|
||||||
}
|
}
|
||||||
path_ops.update_json_file(constants.Reflex.JSON, reflex_json)
|
path_ops.update_json_file(get_web_dir() / constants.Reflex.JSON, reflex_json)
|
||||||
|
|
||||||
|
|
||||||
def update_next_config(export=False, transpile_packages: Optional[List[str]] = None):
|
def update_next_config(export=False, transpile_packages: Optional[List[str]] = None):
|
||||||
@ -593,7 +603,7 @@ def update_next_config(export=False, transpile_packages: Optional[List[str]] = N
|
|||||||
export: if the method run during reflex export.
|
export: if the method run during reflex export.
|
||||||
transpile_packages: list of packages to transpile via next.config.js.
|
transpile_packages: list of packages to transpile via next.config.js.
|
||||||
"""
|
"""
|
||||||
next_config_file = Path(constants.Dirs.WEB, constants.Next.CONFIG_FILE)
|
next_config_file = get_web_dir() / constants.Next.CONFIG_FILE
|
||||||
|
|
||||||
next_config = _update_next_config(
|
next_config = _update_next_config(
|
||||||
get_config(), export=export, transpile_packages=transpile_packages
|
get_config(), export=export, transpile_packages=transpile_packages
|
||||||
@ -845,9 +855,7 @@ def cached_procedure(cache_file: str, payload_fn: Callable[..., str]):
|
|||||||
|
|
||||||
|
|
||||||
@cached_procedure(
|
@cached_procedure(
|
||||||
cache_file=os.path.join(
|
cache_file=str(get_web_dir() / "reflex.install_frontend_packages.cached"),
|
||||||
constants.Dirs.WEB, "reflex.install_frontend_packages.cached"
|
|
||||||
),
|
|
||||||
payload_fn=lambda p, c: f"{repr(sorted(list(p)))},{c.json()}",
|
payload_fn=lambda p, c: f"{repr(sorted(list(p)))},{c.json()}",
|
||||||
)
|
)
|
||||||
def install_frontend_packages(packages: set[str], config: Config):
|
def install_frontend_packages(packages: set[str], config: Config):
|
||||||
@ -874,7 +882,7 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|||||||
fallback=fallback_command,
|
fallback=fallback_command,
|
||||||
analytics_enabled=True,
|
analytics_enabled=True,
|
||||||
show_status_message="Installing base frontend packages",
|
show_status_message="Installing base frontend packages",
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=get_web_dir(),
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -890,7 +898,7 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|||||||
fallback=fallback_command,
|
fallback=fallback_command,
|
||||||
analytics_enabled=True,
|
analytics_enabled=True,
|
||||||
show_status_message="Installing tailwind",
|
show_status_message="Installing tailwind",
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=get_web_dir(),
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -901,7 +909,7 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|||||||
fallback=fallback_command,
|
fallback=fallback_command,
|
||||||
analytics_enabled=True,
|
analytics_enabled=True,
|
||||||
show_status_message="Installing frontend packages from config and components",
|
show_status_message="Installing frontend packages from config and components",
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=get_web_dir(),
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -933,7 +941,7 @@ def needs_reinit(frontend: bool = True) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# Make sure the .web directory exists in frontend mode.
|
# Make sure the .web directory exists in frontend mode.
|
||||||
if not os.path.exists(constants.Dirs.WEB):
|
if not get_web_dir().exists():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# If the template is out of date, then we need to re-init
|
# If the template is out of date, then we need to re-init
|
||||||
@ -971,10 +979,10 @@ def is_latest_template() -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
Whether the app is using the latest template.
|
Whether the app is using the latest template.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.Reflex.JSON):
|
json_file = get_web_dir() / constants.Reflex.JSON
|
||||||
|
if not json_file.exists():
|
||||||
return False
|
return False
|
||||||
with open(constants.Reflex.JSON) as f: # type: ignore
|
app_version = json.load(json_file.open()).get("version")
|
||||||
app_version = json.load(f)["version"]
|
|
||||||
return app_version == constants.Reflex.VERSION
|
return app_version == constants.Reflex.VERSION
|
||||||
|
|
||||||
|
|
||||||
@ -1170,7 +1178,7 @@ def should_show_rx_chakra_migration_instructions() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
existing_init_reflex_version = None
|
existing_init_reflex_version = None
|
||||||
reflex_json = Path(constants.Dirs.REFLEX_JSON)
|
reflex_json = get_web_dir() / constants.Dirs.REFLEX_JSON
|
||||||
if reflex_json.exists():
|
if reflex_json.exists():
|
||||||
with reflex_json.open("r") as f:
|
with reflex_json.open("r") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
@ -9,6 +9,7 @@ from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
|||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
from reflex.constants import Dirs
|
from reflex.constants import Dirs
|
||||||
|
from reflex.utils.prerequisites import get_web_dir
|
||||||
|
|
||||||
|
|
||||||
class AssetFolderWatch:
|
class AssetFolderWatch:
|
||||||
@ -90,5 +91,6 @@ class AssetFolderHandler(FileSystemEventHandler):
|
|||||||
The public file path.
|
The public file path.
|
||||||
"""
|
"""
|
||||||
return src_path.replace(
|
return src_path.replace(
|
||||||
str(self.root / Dirs.APP_ASSETS), str(self.root / Dirs.WEB_ASSETS)
|
str(self.root / Dirs.APP_ASSETS),
|
||||||
|
str(self.root / get_web_dir() / Dirs.PUBLIC),
|
||||||
)
|
)
|
||||||
|
@ -1271,7 +1271,7 @@ def compilable_app(tmp_path) -> Generator[tuple[App, Path], None, None]:
|
|||||||
app_path = tmp_path / "app"
|
app_path = tmp_path / "app"
|
||||||
web_dir = app_path / ".web"
|
web_dir = app_path / ".web"
|
||||||
web_dir.mkdir(parents=True)
|
web_dir.mkdir(parents=True)
|
||||||
(web_dir / "package.json").touch()
|
(web_dir / constants.PackageJson.PATH).touch()
|
||||||
app = App(theme=None)
|
app = App(theme=None)
|
||||||
app._get_frontend_packages = unittest.mock.Mock()
|
app._get_frontend_packages = unittest.mock.Mock()
|
||||||
with chdir(app_path):
|
with chdir(app_path):
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import httpx
|
|
||||||
import pytest
|
import pytest
|
||||||
from packaging.version import parse as parse_python_version
|
from packaging.version import parse as parse_python_version
|
||||||
|
|
||||||
@ -34,20 +33,23 @@ def test_disable():
|
|||||||
|
|
||||||
@pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"])
|
@pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"])
|
||||||
def test_send(mocker, event):
|
def test_send(mocker, event):
|
||||||
mocker.patch("httpx.post")
|
httpx_post_mock = mocker.patch("httpx.post")
|
||||||
mocker.patch(
|
# mocker.patch(
|
||||||
"builtins.open",
|
# "builtins.open",
|
||||||
mocker.mock_open(
|
# mocker.mock_open(
|
||||||
read_data='{"project_hash": "78285505863498957834586115958872998605"}'
|
# read_data='{"project_hash": "78285505863498957834586115958872998605"}'
|
||||||
),
|
# ),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Mock the read_text method of Path
|
||||||
|
pathlib_path_read_text_mock = mocker.patch(
|
||||||
|
"pathlib.Path.read_text",
|
||||||
|
return_value='{"project_hash": "78285505863498957834586115958872998605"}',
|
||||||
)
|
)
|
||||||
|
|
||||||
mocker.patch("platform.platform", return_value="Mocked Platform")
|
mocker.patch("platform.platform", return_value="Mocked Platform")
|
||||||
|
|
||||||
telemetry._send(event, telemetry_enabled=True)
|
telemetry._send(event, telemetry_enabled=True)
|
||||||
httpx.post.assert_called_once()
|
httpx_post_mock.assert_called_once()
|
||||||
if telemetry.get_os() == "Windows":
|
|
||||||
open.assert_called_with(".web\\reflex.json", "r")
|
pathlib_path_read_text_mock.assert_called_once()
|
||||||
elif telemetry.get_os() == "Linux":
|
|
||||||
open.assert_called_with("/proc/meminfo", "rb", buffering=32768)
|
|
||||||
else:
|
|
||||||
open.assert_called_with(".web/reflex.json", "r")
|
|
||||||
|
Loading…
Reference in New Issue
Block a user