Upgrade to NextJS 14 (#2142)
This commit is contained in:
parent
5e6520cb5d
commit
39cc1b2f12
@ -3,4 +3,5 @@ module.exports = {
|
||||
compress: true,
|
||||
reactStrictMode: true,
|
||||
trailingSlash: true,
|
||||
output: "",
|
||||
};
|
||||
|
@ -95,8 +95,8 @@ class PackageJson(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"
|
||||
PROD = "next start"
|
||||
|
||||
PATH = os.path.join(Dirs.WEB, "package.json")
|
||||
@ -106,7 +106,7 @@ class PackageJson(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",
|
||||
|
@ -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()
|
||||
|
||||
@ -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.
|
||||
|
@ -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",
|
||||
}
|
||||
|
@ -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:
|
||||
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))
|
||||
@ -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))
|
||||
|
||||
@ -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],
|
||||
@ -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,
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -25,7 +25,7 @@ from redis.asyncio import Redis
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -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()
|
||||
@ -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:
|
||||
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():
|
||||
|
@ -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.
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
@ -4,106 +4,56 @@ import pytest
|
||||
|
||||
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):
|
||||
|
Loading…
Reference in New Issue
Block a user