Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lendemor/next 14 #2142

Merged
merged 15 commits into from
Nov 13, 2023
1 change: 1 addition & 0 deletions reflex/.templates/web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ module.exports = {
compress: true,
reactStrictMode: true,
trailingSlash: true,
output: "",
};
6 changes: 3 additions & 3 deletions reflex/constants/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ class Commands(SimpleNamespace):
"""The commands to define in package.json."""

DEV = "next dev"
EXPORT = "next build && next export -o _static"
EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
EXPORT = "next build"
EXPORT_SITEMAP = "next build && next-sitemap"
Lendemor marked this conversation as resolved.
Show resolved Hide resolved
PROD = "next start"

PATH = os.path.join(Dirs.WEB, "package.json")
Expand All @@ -106,7 +106,7 @@ class Commands(SimpleNamespace):
"focus-visible": "5.2.0",
"framer-motion": "10.16.4",
"json5": "2.2.3",
"next": "13.5.4",
"next": "14.0.1",
"next-sitemap": "4.1.8",
"next-themes": "0.2.0",
"react": "18.2.0",
Expand Down
3 changes: 3 additions & 0 deletions reflex/reflex.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ def _run(
console.rule("[bold]Starting Reflex App")

if frontend:
prerequisites.update_next_config()
# Get the app module.
prerequisites.get_app()

Expand Down Expand Up @@ -337,6 +338,8 @@ def export(
console.rule("[bold]Compiling production app and preparing for export.")

if frontend:
# Update some parameters for export
prerequisites.update_next_config(export=True)
# Ensure module can be imported and app.compile() is called.
prerequisites.get_app()
# Set up .web directory and install frontend dependencies.
Expand Down
2 changes: 1 addition & 1 deletion reflex/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ class AppHarnessProd(AppHarness):
frontend_server: Optional[Subdir404TCPServer] = None

def _run_frontend(self):
web_root = self.app_path / reflex.constants.Dirs.WEB / "_static"
web_root = self.app_path / reflex.constants.Dirs.WEB_STATIC
error_page_map = {
404: web_root / "404.html",
}
Expand Down
43 changes: 24 additions & 19 deletions reflex/utils/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,25 @@ def set_os_env(**kwargs):
os.environ[key.upper()] = value


def generate_sitemap_config(deploy_url: str):
def generate_sitemap_config(deploy_url: str, export=False):
"""Generate the sitemap config file.

Args:
deploy_url: The URL of the deployed app.
export: If the sitemap are generated for an export.
"""
# Import here to avoid circular imports.
from reflex.compiler import templates

config = json.dumps(
{
"siteUrl": deploy_url,
"generateRobotsTxt": True,
}
)
config = {
"siteUrl": deploy_url,
"generateRobotsTxt": True,
}

if export:
Lendemor marked this conversation as resolved.
Show resolved Hide resolved
config["outDir"] = constants.Dirs.STATIC

config = json.dumps(config)

with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
f.write(templates.SITEMAP_CONFIG(config=config))
Expand Down Expand Up @@ -115,7 +119,7 @@ def _zip(

with progress, zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED) as zipf:
for file in files_to_zip:
console.debug(f"{target}: {file}")
console.debug(f"{target}: {file}", progress=progress)
progress.advance(task)
zipf.write(file, os.path.relpath(file, root_dir))

Expand Down Expand Up @@ -145,22 +149,23 @@ def export(
command = "export"

if frontend:
# Generate a sitemap if a deploy URL is provided.
if deploy_url is not None:
generate_sitemap_config(deploy_url)
command = "export-sitemap"

checkpoints = [
"Linting and checking ",
"Compiled successfully",
"Creating an optimized production build",
"Route (pages)",
"prerendered as static HTML",
"Collecting page data",
"automatically rendered as static HTML",
'Copying "static build" directory',
'Copying "public" directory',
"Finalizing page optimization",
"Export successful",
"Collecting build traces",
]

# Generate a sitemap if a deploy URL is provided.
if deploy_url is not None:
generate_sitemap_config(deploy_url, export=zip)
command = "export-sitemap"

checkpoints.extend(["Loading next-sitemap", "Generation completed"])

# Start the subprocess with the progress bar.
process = processes.new_process(
[prerequisites.get_package_manager(), "run", command],
Expand All @@ -181,7 +186,7 @@ def export(
target=os.path.join(
zip_dest_dir, constants.ComponentName.FRONTEND.zip()
),
root_dir=".web/_static",
root_dir=constants.Dirs.WEB_STATIC,
files_to_exclude=files_to_exclude,
exclude_venv_dirs=False,
)
Expand Down
6 changes: 5 additions & 1 deletion reflex/utils/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ def debug(msg: str, **kwargs):
kwargs: Keyword arguments to pass to the print function.
"""
if _LOG_LEVEL <= LogLevel.DEBUG:
print(f"[blue]Debug: {msg}[/blue]", **kwargs)
msg_ = f"[blue]Debug: {msg}[/blue]"
if progress := kwargs.pop("progress", None):
progress.console.print(msg_, **kwargs)
else:
print(msg_, **kwargs)


def info(msg: str, **kwargs):
Expand Down
55 changes: 27 additions & 28 deletions reflex/utils/prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from reflex import constants, model
from reflex.compiler import templates
from reflex.config import Config, get_config
from reflex.config import get_config
from reflex.utils import console, path_ops, processes


Expand Down Expand Up @@ -288,15 +288,7 @@ def initialize_web_directory():

path_ops.mkdir(constants.Dirs.WEB_ASSETS)

# update nextJS config based on rxConfig
next_config_file = os.path.join(constants.Dirs.WEB, constants.Next.CONFIG_FILE)

with open(next_config_file, "r") as file:
next_config = file.read()
next_config = update_next_config(next_config, get_config())

with open(next_config_file, "w") as file:
file.write(next_config)
update_next_config()

# Initialize the reflex json file.
init_reflex_json()
Expand Down Expand Up @@ -337,27 +329,34 @@ def init_reflex_json():
path_ops.update_json_file(constants.Reflex.JSON, reflex_json)


def update_next_config(next_config: str, config: Config) -> str:
"""Update Next.js config from Reflex config. Is its own function for testing.
def update_next_config(export=False):
"""Update Next.js config from Reflex config.

Args:
next_config: Content of next.config.js.
config: A reflex Config object.

Returns:
The next_config updated from config.
export: if the method run during reflex export.
"""
next_config = re.sub(
"compress: (true|false)",
f'compress: {"true" if config.next_compression else "false"}',
next_config,
)
next_config = re.sub(
'basePath: ".*?"',
f'basePath: "{config.frontend_path or ""}"',
next_config,
)
return next_config
next_config_file = os.path.join(constants.Dirs.WEB, constants.Next.CONFIG_FILE)

next_config = _update_next_config(get_config(), export=export)

with open(next_config_file, "w") as file:
file.write(next_config)
file.write("\n")


def _update_next_config(config, export=False):
next_config = {
"basePath": config.frontend_path or "",
"compress": config.next_compression,
"reactStrictMode": True,
"trailingSlash": True,
}
if export:
Lendemor marked this conversation as resolved.
Show resolved Hide resolved
Lendemor marked this conversation as resolved.
Show resolved Hide resolved
next_config["output"] = "export"
next_config["distDir"] = constants.Dirs.STATIC

next_config_json = re.sub(r'"([^"]+)"(?=:)', r"\1", json.dumps(next_config))
return f"module.exports = {next_config_json};"


def remove_existing_bun_installation():
Expand Down
9 changes: 5 additions & 4 deletions reflex/utils/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,13 @@ def run_concurrently(*fns: Union[Callable, Tuple]) -> None:
pass


def stream_logs(message: str, process: subprocess.Popen):
def stream_logs(message: str, process: subprocess.Popen, progress=None):
"""Stream the logs for a process.

Args:
message: The message to display.
process: The process.
progress: The ongoing progress bar if one is being used.

Yields:
The lines of the process output.
Expand All @@ -209,11 +210,11 @@ def stream_logs(message: str, process: subprocess.Popen):
# Store the tail of the logs.
logs = collections.deque(maxlen=512)
with process:
console.debug(message)
console.debug(message, progress=progress)
if process.stdout is None:
return
for line in process.stdout:
console.debug(line, end="")
console.debug(line, end="", progress=progress)
logs.append(line)
yield line

Expand Down Expand Up @@ -260,7 +261,7 @@ def show_progress(message: str, process: subprocess.Popen, checkpoints: List[str
# Iterate over the process output.
with console.progress() as progress:
task = progress.add_task(f"{message}: ", total=len(checkpoints))
for line in stream_logs(message, process):
for line in stream_logs(message, process, progress=progress):
# Check for special strings and update the progress bar.
for special_string in checkpoints:
if special_string in line:
Expand Down
90 changes: 20 additions & 70 deletions tests/test_prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,56 @@

from reflex import constants
from reflex.config import Config
from reflex.utils.prerequisites import initialize_requirements_txt, update_next_config
from reflex.utils.prerequisites import _update_next_config, initialize_requirements_txt


@pytest.mark.parametrize(
"template_next_config, reflex_config, expected_next_config",
"config, export, expected_output",
[
(
"""
module.exports = {
basePath: "",
compress: true,
reactStrictMode: true,
trailingSlash: true,
};
""",
Config(
app_name="test",
),
"""
module.exports = {
basePath: "",
compress: true,
reactStrictMode: true,
trailingSlash: true,
};
""",
False,
'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true};',
),
(
"""
module.exports = {
basePath: "",
compress: true,
reactStrictMode: true,
trailingSlash: true,
};
""",
Config(
app_name="test",
next_compression=False,
),
"""
module.exports = {
basePath: "",
compress: false,
reactStrictMode: true,
trailingSlash: true,
};
""",
False,
'module.exports = {basePath: "", compress: false, reactStrictMode: true, trailingSlash: true};',
),
(
"""
module.exports = {
basePath: "",
compress: true,
reactStrictMode: true,
trailingSlash: true,
};
""",
Config(
app_name="test",
frontend_path="/test",
),
"""
module.exports = {
basePath: "/test",
compress: true,
reactStrictMode: true,
trailingSlash: true,
};
""",
False,
'module.exports = {basePath: "/test", compress: true, reactStrictMode: true, trailingSlash: true};',
),
(
"""
module.exports = {
basePath: "",
compress: true,
reactStrictMode: true,
trailingSlash: true,
};
""",
Config(
app_name="test",
frontend_path="/test",
next_compression=False,
),
"""
module.exports = {
basePath: "/test",
compress: false,
reactStrictMode: true,
trailingSlash: true,
};
""",
False,
'module.exports = {basePath: "/test", compress: false, reactStrictMode: true, trailingSlash: true};',
),
(
Config(
app_name="test",
),
True,
'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true, output: "export", distDir: "_static"};',
),
],
)
def test_update_next_config(template_next_config, reflex_config, expected_next_config):
assert (
update_next_config(template_next_config, reflex_config) == expected_next_config
)
def test_update_next_config(config, export, expected_output):
output = _update_next_config(config, export=export)
assert output == expected_output


def test_initialize_requirements_txt(mocker):
Expand Down