reflex 0.7.12a1__py3-none-any.whl → 0.7.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/app/rxconfig.py.jinja2 +1 -0
- reflex/.templates/web/postcss.config.js +0 -1
- reflex/.templates/web/utils/state.js +1 -1
- reflex/__init__.py +1 -0
- reflex/__init__.pyi +1 -0
- reflex/app.py +101 -29
- reflex/compiler/compiler.py +11 -62
- reflex/compiler/templates.py +12 -3
- reflex/compiler/utils.py +20 -4
- reflex/components/component.py +366 -88
- reflex/components/core/helmet.pyi +66 -0
- reflex/components/datadisplay/code.py +1 -1
- reflex/components/datadisplay/shiki_code_block.py +97 -86
- reflex/components/datadisplay/shiki_code_block.pyi +4 -2
- reflex/components/el/elements/forms.py +1 -1
- reflex/components/lucide/icon.py +2 -1
- reflex/components/lucide/icon.pyi +1 -0
- reflex/components/plotly/plotly.py +2 -2
- reflex/components/plotly/plotly.pyi +2 -3
- reflex/components/radix/primitives/accordion.py +1 -1
- reflex/components/radix/primitives/drawer.py +1 -1
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/themes/base.py +4 -11
- reflex/components/radix/themes/components/icon_button.py +2 -2
- reflex/components/radix/themes/components/text_field.py +3 -0
- reflex/components/radix/themes/components/text_field.pyi +2 -0
- reflex/components/radix/themes/layout/list.py +1 -1
- reflex/components/tags/iter_tag.py +3 -5
- reflex/config.py +57 -7
- reflex/constants/__init__.py +0 -2
- reflex/event.py +154 -93
- reflex/experimental/client_state.py +3 -1
- reflex/plugins/__init__.py +7 -0
- reflex/plugins/base.py +101 -0
- reflex/plugins/tailwind_v3.py +255 -0
- reflex/plugins/tailwind_v4.py +258 -0
- reflex/state.py +24 -3
- reflex/utils/build.py +1 -1
- reflex/utils/console.py +1 -1
- reflex/utils/exec.py +23 -0
- reflex/utils/path_ops.py +26 -6
- reflex/utils/prerequisites.py +21 -90
- reflex/utils/pyi_generator.py +12 -2
- reflex/utils/types.py +15 -1
- reflex/vars/base.py +59 -4
- reflex/vars/object.py +8 -0
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/METADATA +2 -2
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/RECORD +52 -50
- scripts/hatch_build.py +17 -0
- reflex/.templates/jinja/web/tailwind.config.js.jinja2 +0 -66
- reflex/.templates/web/styles/tailwind.css +0 -6
- reflex/constants/style.py +0 -16
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/WHEEL +0 -0
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -350,7 +350,7 @@ export const applyRestEvent = async (event, socket) => {
|
|
|
350
350
|
if (event.handler === "uploadFiles") {
|
|
351
351
|
if (event.payload.files === undefined || event.payload.files.length === 0) {
|
|
352
352
|
// Submit the event over the websocket to trigger the event handler.
|
|
353
|
-
return await applyEvent(Event(event.name), socket);
|
|
353
|
+
return await applyEvent(Event(event.name, { files: [] }), socket);
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
// Start upload, but do not wait for it, which would block other events.
|
reflex/__init__.py
CHANGED
reflex/__init__.pyi
CHANGED
|
@@ -12,6 +12,7 @@ from . import compiler as compiler
|
|
|
12
12
|
from . import components as components
|
|
13
13
|
from . import config as config
|
|
14
14
|
from . import model as model
|
|
15
|
+
from . import plugins as plugins
|
|
15
16
|
from . import style as style
|
|
16
17
|
from . import testing as testing
|
|
17
18
|
from . import utils as utils
|
reflex/app.py
CHANGED
|
@@ -18,7 +18,7 @@ from datetime import datetime
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from timeit import default_timer as timer
|
|
20
20
|
from types import SimpleNamespace
|
|
21
|
-
from typing import TYPE_CHECKING, Any, BinaryIO, get_args, get_type_hints
|
|
21
|
+
from typing import TYPE_CHECKING, Any, BinaryIO, ParamSpec, get_args, get_type_hints
|
|
22
22
|
|
|
23
23
|
from fastapi import FastAPI
|
|
24
24
|
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
@@ -314,6 +314,9 @@ class UnevaluatedPage:
|
|
|
314
314
|
)
|
|
315
315
|
|
|
316
316
|
|
|
317
|
+
P = ParamSpec("P")
|
|
318
|
+
|
|
319
|
+
|
|
317
320
|
@dataclasses.dataclass()
|
|
318
321
|
class App(MiddlewareMixin, LifespanMixin):
|
|
319
322
|
"""The main Reflex app that encapsulates the backend and frontend.
|
|
@@ -620,10 +623,12 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
620
623
|
compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
|
|
621
624
|
self._compile
|
|
622
625
|
)
|
|
623
|
-
|
|
626
|
+
|
|
627
|
+
def callback(f: concurrent.futures.Future):
|
|
624
628
|
# Force background compile errors to print eagerly
|
|
625
|
-
|
|
626
|
-
|
|
629
|
+
return f.result()
|
|
630
|
+
|
|
631
|
+
compile_future.add_done_callback(callback)
|
|
627
632
|
# Wait for the compile to finish to ensure all optional endpoints are mounted.
|
|
628
633
|
compile_future.result()
|
|
629
634
|
|
|
@@ -1029,11 +1034,6 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1029
1034
|
frontend_packages = get_config().frontend_packages
|
|
1030
1035
|
_frontend_packages = []
|
|
1031
1036
|
for package in frontend_packages:
|
|
1032
|
-
if package in (get_config().tailwind or {}).get("plugins", []):
|
|
1033
|
-
console.warn(
|
|
1034
|
-
f"Tailwind packages are inferred from 'plugins', remove `{package}` from `frontend_packages`"
|
|
1035
|
-
)
|
|
1036
|
-
continue
|
|
1037
1037
|
if package in page_imports:
|
|
1038
1038
|
console.warn(
|
|
1039
1039
|
f"React packages and their dependencies are inferred from Component.library and Component.lib_dependencies, remove `{package}` from `frontend_packages`"
|
|
@@ -1166,6 +1166,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1166
1166
|
|
|
1167
1167
|
Raises:
|
|
1168
1168
|
ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
|
|
1169
|
+
FileNotFoundError: When a plugin requires a file that does not exist.
|
|
1169
1170
|
"""
|
|
1170
1171
|
from reflex.utils.exceptions import ReflexRuntimeError
|
|
1171
1172
|
|
|
@@ -1234,14 +1235,16 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1234
1235
|
|
|
1235
1236
|
# try to be somewhat accurate - but still not 100%
|
|
1236
1237
|
adhoc_steps_without_executor = 7
|
|
1237
|
-
fixed_pages_within_executor =
|
|
1238
|
+
fixed_pages_within_executor = 4
|
|
1239
|
+
plugin_count = len(config.plugins)
|
|
1238
1240
|
progress.start()
|
|
1239
1241
|
task = progress.add_task(
|
|
1240
1242
|
f"[{get_compilation_time()}] Compiling:",
|
|
1241
1243
|
total=len(self._pages)
|
|
1242
1244
|
+ (len(self._unevaluated_pages) * 2)
|
|
1243
1245
|
+ fixed_pages_within_executor
|
|
1244
|
-
+ adhoc_steps_without_executor
|
|
1246
|
+
+ adhoc_steps_without_executor
|
|
1247
|
+
+ plugin_count,
|
|
1245
1248
|
)
|
|
1246
1249
|
|
|
1247
1250
|
with console.timing("Evaluate Pages (Frontend)"):
|
|
@@ -1380,10 +1383,20 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1380
1383
|
|
|
1381
1384
|
ExecutorSafeFunctions.STATE = self._state
|
|
1382
1385
|
|
|
1383
|
-
|
|
1384
|
-
result_futures: list[concurrent.futures.Future[tuple[str, str]]] = []
|
|
1386
|
+
modify_files_tasks: list[tuple[str, str, Callable[[str], str]]] = []
|
|
1385
1387
|
|
|
1386
|
-
|
|
1388
|
+
with console.timing("Compile to Javascript"), executor as executor:
|
|
1389
|
+
result_futures: list[
|
|
1390
|
+
concurrent.futures.Future[
|
|
1391
|
+
list[tuple[str, str]] | tuple[str, str] | None
|
|
1392
|
+
]
|
|
1393
|
+
] = []
|
|
1394
|
+
|
|
1395
|
+
def _submit_work(
|
|
1396
|
+
fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
|
|
1397
|
+
*args: P.args,
|
|
1398
|
+
**kwargs: P.kwargs,
|
|
1399
|
+
):
|
|
1387
1400
|
f = executor.submit(fn, *args, **kwargs)
|
|
1388
1401
|
f.add_done_callback(lambda _: progress.advance(task))
|
|
1389
1402
|
result_futures.append(f)
|
|
@@ -1401,20 +1414,36 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1401
1414
|
# Compile the theme.
|
|
1402
1415
|
_submit_work(compile_theme, self.style)
|
|
1403
1416
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1417
|
+
def _submit_work_without_advancing(
|
|
1418
|
+
fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
|
|
1419
|
+
*args: P.args,
|
|
1420
|
+
**kwargs: P.kwargs,
|
|
1421
|
+
):
|
|
1422
|
+
f = executor.submit(fn, *args, **kwargs)
|
|
1423
|
+
result_futures.append(f)
|
|
1424
|
+
|
|
1425
|
+
for plugin in config.plugins:
|
|
1426
|
+
plugin.pre_compile(
|
|
1427
|
+
add_save_task=_submit_work_without_advancing,
|
|
1428
|
+
add_modify_task=(
|
|
1429
|
+
lambda *args, plugin=plugin: modify_files_tasks.append(
|
|
1430
|
+
(
|
|
1431
|
+
plugin.__class__.__module__ + plugin.__class__.__name__,
|
|
1432
|
+
*args,
|
|
1433
|
+
)
|
|
1434
|
+
)
|
|
1435
|
+
),
|
|
1408
1436
|
)
|
|
1409
|
-
_submit_work(compiler.compile_tailwind, config.tailwind)
|
|
1410
|
-
else:
|
|
1411
|
-
_submit_work(compiler.remove_tailwind_from_postcss)
|
|
1412
1437
|
|
|
1413
1438
|
# Wait for all compilation tasks to complete.
|
|
1414
|
-
|
|
1415
|
-
future.result()
|
|
1416
|
-
|
|
1417
|
-
|
|
1439
|
+
for future in concurrent.futures.as_completed(result_futures):
|
|
1440
|
+
if (result := future.result()) is not None:
|
|
1441
|
+
if isinstance(result, list):
|
|
1442
|
+
compile_results.extend(result)
|
|
1443
|
+
else:
|
|
1444
|
+
compile_results.append(result)
|
|
1445
|
+
|
|
1446
|
+
progress.advance(task, advance=len(config.plugins))
|
|
1418
1447
|
|
|
1419
1448
|
app_root = self._app_root(app_wrappers=app_wrappers)
|
|
1420
1449
|
|
|
@@ -1429,7 +1458,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1429
1458
|
)
|
|
1430
1459
|
if self.theme is not None:
|
|
1431
1460
|
# Fix #2992 by removing the top-level appearance prop
|
|
1432
|
-
self.theme.appearance = None
|
|
1461
|
+
self.theme.appearance = None # pyright: ignore[reportAttributeAccessIssue]
|
|
1433
1462
|
progress.advance(task)
|
|
1434
1463
|
|
|
1435
1464
|
# Compile the app root.
|
|
@@ -1443,7 +1472,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1443
1472
|
custom_components_output,
|
|
1444
1473
|
custom_components_result,
|
|
1445
1474
|
custom_components_imports,
|
|
1446
|
-
) = compiler.compile_components(
|
|
1475
|
+
) = compiler.compile_components(dict.fromkeys(CUSTOM_COMPONENTS.values()))
|
|
1447
1476
|
compile_results.append((custom_components_output, custom_components_result))
|
|
1448
1477
|
all_imports.update(custom_components_imports)
|
|
1449
1478
|
|
|
@@ -1481,9 +1510,45 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1481
1510
|
# Remove pages that are no longer in the app.
|
|
1482
1511
|
p.unlink()
|
|
1483
1512
|
|
|
1513
|
+
output_mapping: dict[Path, str] = {}
|
|
1514
|
+
for output_path, code in compile_results:
|
|
1515
|
+
path = compiler_utils.resolve_path_of_web_dir(output_path)
|
|
1516
|
+
if path in output_mapping:
|
|
1517
|
+
console.warn(
|
|
1518
|
+
f"Path {path} has two different outputs. The first one will be used."
|
|
1519
|
+
)
|
|
1520
|
+
else:
|
|
1521
|
+
output_mapping[path] = code
|
|
1522
|
+
|
|
1523
|
+
for plugin in config.plugins:
|
|
1524
|
+
for static_file_path, content in plugin.get_static_assets():
|
|
1525
|
+
path = compiler_utils.resolve_path_of_web_dir(static_file_path)
|
|
1526
|
+
if path in output_mapping:
|
|
1527
|
+
console.warn(
|
|
1528
|
+
f"Plugin {plugin.__class__.__name__} is trying to write to {path} but it already exists. The plugin file will be ignored."
|
|
1529
|
+
)
|
|
1530
|
+
else:
|
|
1531
|
+
output_mapping[path] = (
|
|
1532
|
+
content.decode("utf-8")
|
|
1533
|
+
if isinstance(content, bytes)
|
|
1534
|
+
else content
|
|
1535
|
+
)
|
|
1536
|
+
|
|
1537
|
+
for plugin_name, file_path, modify_fn in modify_files_tasks:
|
|
1538
|
+
path = compiler_utils.resolve_path_of_web_dir(file_path)
|
|
1539
|
+
file_content = output_mapping.get(path)
|
|
1540
|
+
if file_content is None:
|
|
1541
|
+
if path.exists():
|
|
1542
|
+
file_content = path.read_text()
|
|
1543
|
+
else:
|
|
1544
|
+
raise FileNotFoundError(
|
|
1545
|
+
f"Plugin {plugin_name} is trying to modify {path} but it does not exist."
|
|
1546
|
+
)
|
|
1547
|
+
output_mapping[path] = modify_fn(file_content)
|
|
1548
|
+
|
|
1484
1549
|
with console.timing("Write to Disk"):
|
|
1485
|
-
for output_path, code in
|
|
1486
|
-
compiler_utils.
|
|
1550
|
+
for output_path, code in output_mapping.items():
|
|
1551
|
+
compiler_utils.write_file(output_path, code)
|
|
1487
1552
|
|
|
1488
1553
|
def _write_stateful_pages_marker(self):
|
|
1489
1554
|
"""Write list of routes that create dynamic states for the backend to use later."""
|
|
@@ -1996,6 +2061,13 @@ class EventNamespace(AsyncNamespace):
|
|
|
1996
2061
|
update: The state update to send.
|
|
1997
2062
|
sid: The Socket.IO session id.
|
|
1998
2063
|
"""
|
|
2064
|
+
if not sid:
|
|
2065
|
+
# If the sid is None, we are not connected to a client. Prevent sending
|
|
2066
|
+
# updates to all clients.
|
|
2067
|
+
return
|
|
2068
|
+
if sid not in self.sid_to_token:
|
|
2069
|
+
console.warn(f"Attempting to send delta to disconnected websocket {sid}")
|
|
2070
|
+
return
|
|
1999
2071
|
# Creating a task prevents the update from being blocked behind other coroutines.
|
|
2000
2072
|
await asyncio.create_task(
|
|
2001
2073
|
self.emit(str(constants.SocketEvent.EVENT), update, to=sid)
|
reflex/compiler/compiler.py
CHANGED
|
@@ -223,6 +223,9 @@ def _validate_stylesheet(stylesheet_full_path: Path, assets_app_path: Path) -> N
|
|
|
223
223
|
)
|
|
224
224
|
|
|
225
225
|
|
|
226
|
+
RADIX_THEMES_STYLESHEET = "@radix-ui/themes/styles.css"
|
|
227
|
+
|
|
228
|
+
|
|
226
229
|
def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|
227
230
|
"""Compile the root stylesheet.
|
|
228
231
|
|
|
@@ -235,12 +238,12 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|
|
235
238
|
Raises:
|
|
236
239
|
FileNotFoundError: If a specified stylesheet in assets directory does not exist.
|
|
237
240
|
"""
|
|
238
|
-
# Add
|
|
239
|
-
sheets =
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
# Add stylesheets from plugins.
|
|
242
|
+
sheets = [RADIX_THEMES_STYLESHEET] + [
|
|
243
|
+
sheet
|
|
244
|
+
for plugin in get_config().plugins
|
|
245
|
+
for sheet in plugin.get_stylesheet_paths()
|
|
246
|
+
]
|
|
244
247
|
|
|
245
248
|
failed_to_import_sass = False
|
|
246
249
|
assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
|
|
@@ -326,7 +329,7 @@ def _compile_component(component: Component | StatefulComponent) -> str:
|
|
|
326
329
|
|
|
327
330
|
|
|
328
331
|
def _compile_components(
|
|
329
|
-
components:
|
|
332
|
+
components: Iterable[CustomComponent],
|
|
330
333
|
) -> tuple[str, dict[str, list[ImportVar]]]:
|
|
331
334
|
"""Compile the components.
|
|
332
335
|
|
|
@@ -451,22 +454,6 @@ def _compile_stateful_components(
|
|
|
451
454
|
)
|
|
452
455
|
|
|
453
456
|
|
|
454
|
-
def _compile_tailwind(
|
|
455
|
-
config: dict,
|
|
456
|
-
) -> str:
|
|
457
|
-
"""Compile the Tailwind config.
|
|
458
|
-
|
|
459
|
-
Args:
|
|
460
|
-
config: The Tailwind config.
|
|
461
|
-
|
|
462
|
-
Returns:
|
|
463
|
-
The compiled Tailwind config.
|
|
464
|
-
"""
|
|
465
|
-
return templates.TAILWIND_CONFIG.render(
|
|
466
|
-
**config,
|
|
467
|
-
)
|
|
468
|
-
|
|
469
|
-
|
|
470
457
|
def compile_document_root(
|
|
471
458
|
head_components: list[Component],
|
|
472
459
|
html_lang: str | None = None,
|
|
@@ -572,7 +559,7 @@ def compile_page(
|
|
|
572
559
|
|
|
573
560
|
|
|
574
561
|
def compile_components(
|
|
575
|
-
components:
|
|
562
|
+
components: Iterable[CustomComponent],
|
|
576
563
|
) -> tuple[str, str, dict[str, list[ImportVar]]]:
|
|
577
564
|
"""Compile the custom components.
|
|
578
565
|
|
|
@@ -613,44 +600,6 @@ def compile_stateful_components(
|
|
|
613
600
|
return output_path, code, page_components
|
|
614
601
|
|
|
615
602
|
|
|
616
|
-
def compile_tailwind(
|
|
617
|
-
config: dict,
|
|
618
|
-
):
|
|
619
|
-
"""Compile the Tailwind config.
|
|
620
|
-
|
|
621
|
-
Args:
|
|
622
|
-
config: The Tailwind config.
|
|
623
|
-
|
|
624
|
-
Returns:
|
|
625
|
-
The compiled Tailwind config.
|
|
626
|
-
"""
|
|
627
|
-
# Get the path for the output file.
|
|
628
|
-
output_path = str((get_web_dir() / constants.Tailwind.CONFIG).absolute())
|
|
629
|
-
|
|
630
|
-
# Compile the config.
|
|
631
|
-
code = _compile_tailwind(config)
|
|
632
|
-
return output_path, code
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
def remove_tailwind_from_postcss() -> tuple[str, str]:
|
|
636
|
-
"""If tailwind is not to be used, remove it from postcss.config.js.
|
|
637
|
-
|
|
638
|
-
Returns:
|
|
639
|
-
The path and code of the compiled postcss.config.js.
|
|
640
|
-
"""
|
|
641
|
-
# Get the path for the output file.
|
|
642
|
-
output_path = str(get_web_dir() / constants.Dirs.POSTCSS_JS)
|
|
643
|
-
|
|
644
|
-
code = [
|
|
645
|
-
line
|
|
646
|
-
for line in Path(output_path).read_text().splitlines(keepends=True)
|
|
647
|
-
if "tailwindcss: " not in line
|
|
648
|
-
]
|
|
649
|
-
|
|
650
|
-
# Compile the config.
|
|
651
|
-
return output_path, "".join(code)
|
|
652
|
-
|
|
653
|
-
|
|
654
603
|
def purge_web_pages_dir():
|
|
655
604
|
"""Empty out .web/pages directory."""
|
|
656
605
|
if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR.get():
|
reflex/compiler/templates.py
CHANGED
|
@@ -98,6 +98,18 @@ def get_template(name: str) -> Template:
|
|
|
98
98
|
return ReflexJinjaEnvironment().get_template(name=name)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
def from_string(source: str) -> Template:
|
|
102
|
+
"""Get render function that work with a template.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
source: The template source.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
A render function.
|
|
109
|
+
"""
|
|
110
|
+
return ReflexJinjaEnvironment().from_string(source=source)
|
|
111
|
+
|
|
112
|
+
|
|
101
113
|
# Template for the Reflex config file.
|
|
102
114
|
RXCONFIG = get_template("app/rxconfig.py.jinja2")
|
|
103
115
|
|
|
@@ -113,9 +125,6 @@ THEME = get_template("web/utils/theme.js.jinja2")
|
|
|
113
125
|
# Template for the context file.
|
|
114
126
|
CONTEXT = get_template("web/utils/context.js.jinja2")
|
|
115
127
|
|
|
116
|
-
# Template for Tailwind config.
|
|
117
|
-
TAILWIND_CONFIG = get_template("web/tailwind.config.js.jinja2")
|
|
118
|
-
|
|
119
128
|
# Template to render a component tag.
|
|
120
129
|
COMPONENT = get_template("web/pages/component.js.jinja2")
|
|
121
130
|
|
reflex/compiler/utils.py
CHANGED
|
@@ -66,7 +66,7 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
|
|
|
66
66
|
default = next(iter({field.name for field in defaults}), "")
|
|
67
67
|
rest = {field.name for field in fields_set - defaults}
|
|
68
68
|
|
|
69
|
-
return default,
|
|
69
|
+
return default, sorted(rest)
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def validate_imports(import_dict: ParsedImportDict):
|
|
@@ -309,7 +309,7 @@ def compile_custom_component(
|
|
|
309
309
|
A tuple of the compiled component and the imports required by the component.
|
|
310
310
|
"""
|
|
311
311
|
# Render the component.
|
|
312
|
-
render = component.get_component(
|
|
312
|
+
render = component.get_component()
|
|
313
313
|
|
|
314
314
|
# Get the imports.
|
|
315
315
|
imports: ParsedImportDict = {
|
|
@@ -500,7 +500,23 @@ def add_meta(
|
|
|
500
500
|
return page
|
|
501
501
|
|
|
502
502
|
|
|
503
|
-
def
|
|
503
|
+
def resolve_path_of_web_dir(path: str | Path) -> Path:
|
|
504
|
+
"""Get the path under the web directory.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
path: The path to get. It can be a relative or absolute path.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
The path under the web directory.
|
|
511
|
+
"""
|
|
512
|
+
path = Path(path)
|
|
513
|
+
web_dir = get_web_dir()
|
|
514
|
+
if path.is_relative_to(web_dir):
|
|
515
|
+
return path.absolute()
|
|
516
|
+
return (web_dir / path).absolute()
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def write_file(path: str | Path, code: str):
|
|
504
520
|
"""Write the given code to the given path.
|
|
505
521
|
|
|
506
522
|
Args:
|
|
@@ -508,7 +524,7 @@ def write_page(path: str | Path, code: str):
|
|
|
508
524
|
code: The code to write.
|
|
509
525
|
"""
|
|
510
526
|
path = Path(path)
|
|
511
|
-
|
|
527
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
512
528
|
if path.exists() and path.read_text(encoding="utf-8") == code:
|
|
513
529
|
return
|
|
514
530
|
path.write_text(code, encoding="utf-8")
|