Upgrade to NextJS 14 (#2142)
This commit is contained in:
parent
5e6520cb5d
commit
39cc1b2f12
@ -3,4 +3,5 @@ module.exports = {
|
|||||||
compress: true,
|
compress: true,
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
trailingSlash: true,
|
trailingSlash: true,
|
||||||
|
output: "",
|
||||||
};
|
};
|
||||||
|
@ -95,8 +95,8 @@ class PackageJson(SimpleNamespace):
|
|||||||
"""The commands to define in package.json."""
|
"""The commands to define in package.json."""
|
||||||
|
|
||||||
DEV = "next dev"
|
DEV = "next dev"
|
||||||
EXPORT = "next build && next export -o _static"
|
EXPORT = "next build"
|
||||||
EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
|
EXPORT_SITEMAP = "next build && next-sitemap"
|
||||||
PROD = "next start"
|
PROD = "next start"
|
||||||
|
|
||||||
PATH = os.path.join(Dirs.WEB, "package.json")
|
PATH = os.path.join(Dirs.WEB, "package.json")
|
||||||
@ -106,7 +106,7 @@ class PackageJson(SimpleNamespace):
|
|||||||
"focus-visible": "5.2.0",
|
"focus-visible": "5.2.0",
|
||||||
"framer-motion": "10.16.4",
|
"framer-motion": "10.16.4",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"next": "13.5.4",
|
"next": "14.0.1",
|
||||||
"next-sitemap": "4.1.8",
|
"next-sitemap": "4.1.8",
|
||||||
"next-themes": "0.2.0",
|
"next-themes": "0.2.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
@ -184,6 +184,7 @@ def _run(
|
|||||||
console.rule("[bold]Starting Reflex App")
|
console.rule("[bold]Starting Reflex App")
|
||||||
|
|
||||||
if frontend:
|
if frontend:
|
||||||
|
prerequisites.update_next_config()
|
||||||
# Get the app module.
|
# Get the app module.
|
||||||
prerequisites.get_app()
|
prerequisites.get_app()
|
||||||
|
|
||||||
@ -337,6 +338,8 @@ def export(
|
|||||||
console.rule("[bold]Compiling production app and preparing for export.")
|
console.rule("[bold]Compiling production app and preparing for export.")
|
||||||
|
|
||||||
if frontend:
|
if frontend:
|
||||||
|
# Update some parameters for export
|
||||||
|
prerequisites.update_next_config(export=True)
|
||||||
# Ensure module can be imported and app.compile() is called.
|
# Ensure module can be imported and app.compile() is called.
|
||||||
prerequisites.get_app()
|
prerequisites.get_app()
|
||||||
# Set up .web directory and install frontend dependencies.
|
# Set up .web directory and install frontend dependencies.
|
||||||
|
@ -677,7 +677,7 @@ 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.constants.Dirs.WEB_STATIC
|
||||||
error_page_map = {
|
error_page_map = {
|
||||||
404: web_root / "404.html",
|
404: web_root / "404.html",
|
||||||
}
|
}
|
||||||
|
@ -35,21 +35,25 @@ def set_os_env(**kwargs):
|
|||||||
os.environ[key.upper()] = value
|
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.
|
"""Generate the sitemap config file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
deploy_url: The URL of the deployed app.
|
deploy_url: The URL of the deployed app.
|
||||||
|
export: If the sitemap are generated for an export.
|
||||||
"""
|
"""
|
||||||
# Import here to avoid circular imports.
|
# Import here to avoid circular imports.
|
||||||
from reflex.compiler import templates
|
from reflex.compiler import templates
|
||||||
|
|
||||||
config = json.dumps(
|
config = {
|
||||||
{
|
"siteUrl": deploy_url,
|
||||||
"siteUrl": deploy_url,
|
"generateRobotsTxt": True,
|
||||||
"generateRobotsTxt": True,
|
}
|
||||||
}
|
|
||||||
)
|
if export:
|
||||||
|
config["outDir"] = constants.Dirs.STATIC
|
||||||
|
|
||||||
|
config = json.dumps(config)
|
||||||
|
|
||||||
with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
|
with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
|
||||||
f.write(templates.SITEMAP_CONFIG(config=config))
|
f.write(templates.SITEMAP_CONFIG(config=config))
|
||||||
@ -115,7 +119,7 @@ def _zip(
|
|||||||
|
|
||||||
with progress, zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED) as zipf:
|
with progress, zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED) as zipf:
|
||||||
for file in files_to_zip:
|
for file in files_to_zip:
|
||||||
console.debug(f"{target}: {file}")
|
console.debug(f"{target}: {file}", progress=progress)
|
||||||
progress.advance(task)
|
progress.advance(task)
|
||||||
zipf.write(file, os.path.relpath(file, root_dir))
|
zipf.write(file, os.path.relpath(file, root_dir))
|
||||||
|
|
||||||
@ -145,22 +149,23 @@ def export(
|
|||||||
command = "export"
|
command = "export"
|
||||||
|
|
||||||
if frontend:
|
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 = [
|
checkpoints = [
|
||||||
"Linting and checking ",
|
"Linting and checking ",
|
||||||
"Compiled successfully",
|
"Creating an optimized production build",
|
||||||
"Route (pages)",
|
"Route (pages)",
|
||||||
|
"prerendered as static HTML",
|
||||||
"Collecting page data",
|
"Collecting page data",
|
||||||
"automatically rendered as static HTML",
|
|
||||||
'Copying "static build" directory',
|
|
||||||
'Copying "public" directory',
|
|
||||||
"Finalizing page optimization",
|
"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.
|
# 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],
|
||||||
@ -181,7 +186,7 @@ def export(
|
|||||||
target=os.path.join(
|
target=os.path.join(
|
||||||
zip_dest_dir, constants.ComponentName.FRONTEND.zip()
|
zip_dest_dir, constants.ComponentName.FRONTEND.zip()
|
||||||
),
|
),
|
||||||
root_dir=".web/_static",
|
root_dir=constants.Dirs.WEB_STATIC,
|
||||||
files_to_exclude=files_to_exclude,
|
files_to_exclude=files_to_exclude,
|
||||||
exclude_venv_dirs=False,
|
exclude_venv_dirs=False,
|
||||||
)
|
)
|
||||||
|
@ -45,7 +45,11 @@ def debug(msg: str, **kwargs):
|
|||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if _LOG_LEVEL <= LogLevel.DEBUG:
|
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):
|
def info(msg: str, **kwargs):
|
||||||
|
@ -25,7 +25,7 @@ from redis.asyncio import Redis
|
|||||||
|
|
||||||
from reflex import constants, model
|
from reflex import constants, model
|
||||||
from reflex.compiler import templates
|
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
|
from reflex.utils import console, path_ops, processes
|
||||||
|
|
||||||
|
|
||||||
@ -288,15 +288,7 @@ def initialize_web_directory():
|
|||||||
|
|
||||||
path_ops.mkdir(constants.Dirs.WEB_ASSETS)
|
path_ops.mkdir(constants.Dirs.WEB_ASSETS)
|
||||||
|
|
||||||
# update nextJS config based on rxConfig
|
update_next_config()
|
||||||
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)
|
|
||||||
|
|
||||||
# Initialize the reflex json file.
|
# Initialize the reflex json file.
|
||||||
init_reflex_json()
|
init_reflex_json()
|
||||||
@ -337,27 +329,34 @@ def init_reflex_json():
|
|||||||
path_ops.update_json_file(constants.Reflex.JSON, reflex_json)
|
path_ops.update_json_file(constants.Reflex.JSON, reflex_json)
|
||||||
|
|
||||||
|
|
||||||
def update_next_config(next_config: str, config: Config) -> str:
|
def update_next_config(export=False):
|
||||||
"""Update Next.js config from Reflex config. Is its own function for testing.
|
"""Update Next.js config from Reflex config.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
next_config: Content of next.config.js.
|
export: if the method run during reflex export.
|
||||||
config: A reflex Config object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The next_config updated from config.
|
|
||||||
"""
|
"""
|
||||||
next_config = re.sub(
|
next_config_file = os.path.join(constants.Dirs.WEB, constants.Next.CONFIG_FILE)
|
||||||
"compress: (true|false)",
|
|
||||||
f'compress: {"true" if config.next_compression else "false"}',
|
next_config = _update_next_config(get_config(), export=export)
|
||||||
next_config,
|
|
||||||
)
|
with open(next_config_file, "w") as file:
|
||||||
next_config = re.sub(
|
file.write(next_config)
|
||||||
'basePath: ".*?"',
|
file.write("\n")
|
||||||
f'basePath: "{config.frontend_path or ""}"',
|
|
||||||
next_config,
|
|
||||||
)
|
def _update_next_config(config, export=False):
|
||||||
return next_config
|
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():
|
def remove_existing_bun_installation():
|
||||||
|
@ -193,12 +193,13 @@ def run_concurrently(*fns: Union[Callable, Tuple]) -> None:
|
|||||||
pass
|
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.
|
"""Stream the logs for a process.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: The message to display.
|
message: The message to display.
|
||||||
process: The process.
|
process: The process.
|
||||||
|
progress: The ongoing progress bar if one is being used.
|
||||||
|
|
||||||
Yields:
|
Yields:
|
||||||
The lines of the process output.
|
The lines of the process output.
|
||||||
@ -209,11 +210,11 @@ def stream_logs(message: str, process: subprocess.Popen):
|
|||||||
# Store the tail of the logs.
|
# Store the tail of the logs.
|
||||||
logs = collections.deque(maxlen=512)
|
logs = collections.deque(maxlen=512)
|
||||||
with process:
|
with process:
|
||||||
console.debug(message)
|
console.debug(message, progress=progress)
|
||||||
if process.stdout is None:
|
if process.stdout is None:
|
||||||
return
|
return
|
||||||
for line in process.stdout:
|
for line in process.stdout:
|
||||||
console.debug(line, end="")
|
console.debug(line, end="", progress=progress)
|
||||||
logs.append(line)
|
logs.append(line)
|
||||||
yield line
|
yield line
|
||||||
|
|
||||||
@ -260,7 +261,7 @@ def show_progress(message: str, process: subprocess.Popen, checkpoints: List[str
|
|||||||
# Iterate over the process output.
|
# Iterate over the process output.
|
||||||
with console.progress() as progress:
|
with console.progress() as progress:
|
||||||
task = progress.add_task(f"{message}: ", total=len(checkpoints))
|
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.
|
# Check for special strings and update the progress bar.
|
||||||
for special_string in checkpoints:
|
for special_string in checkpoints:
|
||||||
if special_string in line:
|
if special_string in line:
|
||||||
|
@ -4,106 +4,56 @@ import pytest
|
|||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.config import Config
|
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(
|
@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(
|
Config(
|
||||||
app_name="test",
|
app_name="test",
|
||||||
),
|
),
|
||||||
"""
|
False,
|
||||||
module.exports = {
|
'module.exports = {basePath: "", compress: true, reactStrictMode: true, trailingSlash: true};',
|
||||||
basePath: "",
|
|
||||||
compress: true,
|
|
||||||
reactStrictMode: true,
|
|
||||||
trailingSlash: true,
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"""
|
|
||||||
module.exports = {
|
|
||||||
basePath: "",
|
|
||||||
compress: true,
|
|
||||||
reactStrictMode: true,
|
|
||||||
trailingSlash: true,
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
Config(
|
Config(
|
||||||
app_name="test",
|
app_name="test",
|
||||||
next_compression=False,
|
next_compression=False,
|
||||||
),
|
),
|
||||||
"""
|
False,
|
||||||
module.exports = {
|
'module.exports = {basePath: "", compress: false, reactStrictMode: true, trailingSlash: true};',
|
||||||
basePath: "",
|
|
||||||
compress: false,
|
|
||||||
reactStrictMode: true,
|
|
||||||
trailingSlash: true,
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"""
|
|
||||||
module.exports = {
|
|
||||||
basePath: "",
|
|
||||||
compress: true,
|
|
||||||
reactStrictMode: true,
|
|
||||||
trailingSlash: true,
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
Config(
|
Config(
|
||||||
app_name="test",
|
app_name="test",
|
||||||
frontend_path="/test",
|
frontend_path="/test",
|
||||||
),
|
),
|
||||||
"""
|
False,
|
||||||
module.exports = {
|
'module.exports = {basePath: "/test", compress: true, reactStrictMode: true, trailingSlash: true};',
|
||||||
basePath: "/test",
|
|
||||||
compress: true,
|
|
||||||
reactStrictMode: true,
|
|
||||||
trailingSlash: true,
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"""
|
|
||||||
module.exports = {
|
|
||||||
basePath: "",
|
|
||||||
compress: true,
|
|
||||||
reactStrictMode: true,
|
|
||||||
trailingSlash: true,
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
Config(
|
Config(
|
||||||
app_name="test",
|
app_name="test",
|
||||||
frontend_path="/test",
|
frontend_path="/test",
|
||||||
next_compression=False,
|
next_compression=False,
|
||||||
),
|
),
|
||||||
"""
|
False,
|
||||||
module.exports = {
|
'module.exports = {basePath: "/test", compress: false, reactStrictMode: true, trailingSlash: true};',
|
||||||
basePath: "/test",
|
),
|
||||||
compress: false,
|
(
|
||||||
reactStrictMode: true,
|
Config(
|
||||||
trailingSlash: true,
|
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):
|
def test_update_next_config(config, export, expected_output):
|
||||||
assert (
|
output = _update_next_config(config, export=export)
|
||||||
update_next_config(template_next_config, reflex_config) == expected_next_config
|
assert output == expected_output
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_initialize_requirements_txt(mocker):
|
def test_initialize_requirements_txt(mocker):
|
||||||
|
Loading…
Reference in New Issue
Block a user