Source code for pysepal.solara.asset_merger

"""Asset merging utilities for Solara applications.

This module handles the merging of CSS and JS files from multiple locations
to work around Solara's limitation of serving assets from a single location.
"""

import logging
import shutil
import tempfile
from pathlib import Path
from typing import List

logger = logging.getLogger("sepalui.solara.asset_merger")


[docs] def merge_asset_files(sepal_assets_dir: Path, extra_locations: List[Path], temp_dir: Path) -> None: """Merge CSS and JS files from sepal_ui and extra locations into combined files. Args: sepal_assets_dir: Path to sepal_ui common assets extra_locations: List of extra asset directory paths temp_dir: Temporary directory to create merged files in """ # Merge CSS files css_content = [] js_content = [] # Start with sepal_ui common assets sepal_css = sepal_assets_dir / "custom.css" sepal_js = sepal_assets_dir / "custom.js" if sepal_css.exists(): css_content.append(f"/* === sepal_ui common CSS === */\n{sepal_css.read_text()}\n") logger.debug(f"Added sepal_ui CSS: {sepal_css}") else: logger.warning(f"sepal_ui common CSS not found: {sepal_css}") if sepal_js.exists(): js_content.append(f"/* === sepal_ui common JS === */\n{sepal_js.read_text()}\n") logger.debug(f"Added sepal_ui JS: {sepal_js}") else: logger.warning(f"sepal_ui common JS not found: {sepal_js}") # Add extra location assets for location in extra_locations: location_path = Path(location) if not location_path.exists(): logger.warning(f"Extra asset location does not exist: {location_path}") continue # Look for CSS files for css_file in location_path.glob("**/*.css"): css_content.append( f"/* === {css_file.relative_to(location_path)} === */\n{css_file.read_text()}\n" ) logger.debug(f"Added CSS from {location}: {css_file}") # Look for JS files for js_file in location_path.glob("**/*.js"): js_content.append( f"/* === {js_file.relative_to(location_path)} === */\n{js_file.read_text()}\n" ) logger.debug(f"Added JS from {location}: {js_file}") # Create merged assets directory structure merged_assets_dir = temp_dir / "assets" merged_assets_dir.mkdir(exist_ok=True) # Write merged files if css_content: merged_css = merged_assets_dir / "custom.css" merged_css.write_text("\n".join(css_content)) logger.debug(f"Created merged CSS file: {merged_css}") if js_content: merged_js = merged_assets_dir / "custom.js" merged_js.write_text("\n".join(js_content)) logger.debug(f"Created merged JS file: {merged_js}") # Copy any other files from sepal_ui common assets if sepal_assets_dir.exists(): for file_path in sepal_assets_dir.iterdir(): if file_path.suffix not in [".css", ".js"]: # Skip CSS/JS as they're merged target_file = merged_assets_dir / file_path.name shutil.copy2(file_path, target_file) logger.debug(f"Copied additional asset: {file_path.name}")
[docs] def create_merged_assets_directory(sepal_assets_dir: Path, extra_locations: List[Path]) -> Path: """Create a temporary directory with merged assets from all locations. Args: sepal_assets_dir: Path to sepal_ui common assets extra_locations: List of extra asset directory paths Returns: Path to the directory containing the merged assets (ready for Solara) """ # Create temporary directory for merged assets temp_dir = Path(tempfile.mkdtemp(prefix="sepal_ui_assets_")) logger.debug(f"Created temporary assets directory: {temp_dir}") # Merge all assets merge_asset_files(sepal_assets_dir, extra_locations, temp_dir) # Return the assets subdirectory (where the actual files are) merged_assets_dir = temp_dir / "assets" logger.debug(f"Merged assets available at: {merged_assets_dir}") return merged_assets_dir