diff --git a/reflex/app.py b/reflex/app.py index 5f1ef96f5..00a2d916b 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -818,6 +818,7 @@ class App(Base): compile_results.append((stateful_components_path, stateful_components_code)) result_futures = [] + custom_components_future = None def submit_work(fn, *args, **kwargs): """Submit work to the thread pool and add a callback to mark the task as complete. @@ -847,7 +848,10 @@ class App(Base): submit_work(compiler.compile_app, app_root) # Compile the custom components. - submit_work(compiler.compile_components, custom_components) + custom_components_future = thread_pool.submit( + compiler.compile_components, custom_components + ) + custom_components_future.add_done_callback(mark_complete) # Compile the root stylesheet with base styles. submit_work(compiler.compile_root_stylesheet, self.stylesheets) @@ -878,14 +882,15 @@ class App(Base): # Get imports from AppWrap components. all_imports.update(app_root.get_imports()) - # Iterate through all the custom components and add their imports to the all_imports. - for component in custom_components: - all_imports.update(component.get_imports()) - # Wait for all compilation tasks to complete. for future in concurrent.futures.as_completed(result_futures): compile_results.append(future.result()) + # Iterate through all the custom components and add their imports to the all_imports. + custom_components_result = custom_components_future.result() + compile_results.append(custom_components_result[:2]) + all_imports.update(custom_components_result[2]) + # Empty the .web pages directory. compiler.purge_web_pages_dir() diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index a3e6fb7fa..603c8b969 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -186,7 +186,9 @@ def _compile_component(component: Component) -> str: return templates.COMPONENT.render(component=component) -def _compile_components(components: set[CustomComponent]) -> str: +def _compile_components( + components: set[CustomComponent], +) -> tuple[str, Dict[str, list[ImportVar]]]: """Compile the components. Args: @@ -208,9 +210,12 @@ def _compile_components(components: set[CustomComponent]) -> str: imports = utils.merge_imports(imports, component_imports) # Compile the components page. - return templates.COMPONENTS.render( - imports=utils.compile_imports(imports), - components=component_renders, + return ( + templates.COMPONENTS.render( + imports=utils.compile_imports(imports), + components=component_renders, + ), + imports, ) @@ -401,7 +406,9 @@ def compile_page( return output_path, code -def compile_components(components: set[CustomComponent]): +def compile_components( + components: set[CustomComponent], +) -> tuple[str, str, Dict[str, list[ImportVar]]]: """Compile the custom components. Args: @@ -414,8 +421,8 @@ def compile_components(components: set[CustomComponent]): output_path = utils.get_components_path() # Compile the components. - code = _compile_components(components) - return output_path, code + code, imports = _compile_components(components) + return output_path, code, imports def compile_stateful_components( diff --git a/reflex/components/component.py b/reflex/components/component.py index a26ded175..b085ee352 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -1339,31 +1339,6 @@ class CustomComponent(Component): """ return hash(self.tag) - def _get_imports(self) -> Dict[str, List[ImportVar]]: - """Get the imports for the component. - - This is needed because otherwise the imports for the component are not - installed during compile time, but they are rendered into the page. - - Returns: - The imports for the component and any custom component props. - """ - return imports.merge_imports( - super()._get_imports(), - # Sweep up any imports from CustomComponent props for frontend installation. - { - library: [ - ImportVar( - tag=None, - render=False, - install=any(imp.install for imp in imps), - ), - ] - for comp in self.get_custom_components() - for library, imps in comp.get_component(comp).get_imports().items() - }, - ) - @classmethod def get_props(cls) -> Set[str]: """Get the props for the component. diff --git a/tests/components/test_component.py b/tests/components/test_component.py index d5413229f..8dfe6dedc 100644 --- a/tests/components/test_component.py +++ b/tests/components/test_component.py @@ -4,6 +4,7 @@ import pytest import reflex as rx from reflex.base import Base +from reflex.compiler.compiler import compile_components from reflex.components.base.bare import Bare from reflex.components.chakra.layout.box import Box from reflex.components.component import ( @@ -1289,8 +1290,21 @@ def test_custom_component_get_imports(): return Other.create(c) custom_comp = wrapper() - assert "inner" in custom_comp.get_imports() + + # Inner is not imported directly, but it is imported by the custom component. + assert "inner" not in custom_comp.get_imports() + + # The imports are only resolved during compilation. + _, _, imports_inner = compile_components(custom_comp.get_custom_components()) + assert "inner" in imports_inner outer_comp = outer(c=wrapper()) - assert "inner" in outer_comp.get_imports() - assert "other" in outer_comp.get_imports() + + # Libraries are not imported directly, but are imported by the custom component. + assert "inner" not in outer_comp.get_imports() + assert "other" not in outer_comp.get_imports() + + # The imports are only resolved during compilation. + _, _, imports_outer = compile_components(outer_comp.get_custom_components()) + assert "inner" in imports_outer + assert "other" in imports_outer