mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 13:12:12 -03:00
- Add LoraManagerDemoNode to node mappings for Vue widget demonstration - Update .gitignore to exclude Vue widget development artifacts (node_modules, .vite, dist) - Implement automatic Vue widget build check in development mode with fallback handling - Maintain pytest compatibility with proper import error handling
242 lines
7.6 KiB
Python
242 lines
7.6 KiB
Python
"""
|
|
Vue Widget Build Checker and Auto-builder
|
|
|
|
This module checks if Vue widgets are built and attempts to build them if needed.
|
|
Useful for development mode where source code might be newer than build output.
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class VueWidgetBuilder:
|
|
"""Manages Vue widget build checking and auto-building."""
|
|
|
|
def __init__(self, project_root: Optional[Path] = None):
|
|
"""
|
|
Initialize the builder.
|
|
|
|
Args:
|
|
project_root: Project root directory. If None, auto-detects.
|
|
"""
|
|
if project_root is None:
|
|
# Auto-detect project root (where __init__.py is)
|
|
project_root = Path(__file__).parent.parent
|
|
|
|
self.project_root = Path(project_root)
|
|
self.vue_widgets_dir = self.project_root / "vue-widgets"
|
|
self.build_output_dir = self.project_root / "web" / "comfyui" / "vue-widgets"
|
|
self.src_dir = self.vue_widgets_dir / "src"
|
|
|
|
def check_build_exists(self) -> bool:
|
|
"""
|
|
Check if build output exists.
|
|
|
|
Returns:
|
|
True if at least one built .js file exists
|
|
"""
|
|
if not self.build_output_dir.exists():
|
|
return False
|
|
|
|
js_files = list(self.build_output_dir.glob("*.js"))
|
|
return len(js_files) > 0
|
|
|
|
def check_build_outdated(self) -> bool:
|
|
"""
|
|
Check if source code is newer than build output.
|
|
|
|
Returns:
|
|
True if source is newer, False otherwise or if can't determine
|
|
"""
|
|
if not self.src_dir.exists():
|
|
return False
|
|
|
|
if not self.check_build_exists():
|
|
return True
|
|
|
|
try:
|
|
# Get newest file in source directory
|
|
src_files = [f for f in self.src_dir.rglob("*") if f.is_file()]
|
|
if not src_files:
|
|
return False
|
|
|
|
newest_src_time = max(f.stat().st_mtime for f in src_files)
|
|
|
|
# Get oldest file in build directory
|
|
build_files = [f for f in self.build_output_dir.rglob("*.js") if f.is_file()]
|
|
if not build_files:
|
|
return True
|
|
|
|
oldest_build_time = min(f.stat().st_mtime for f in build_files)
|
|
|
|
return newest_src_time > oldest_build_time
|
|
|
|
except Exception as e:
|
|
logger.debug(f"Error checking build timestamps: {e}")
|
|
return False
|
|
|
|
def check_node_available(self) -> bool:
|
|
"""
|
|
Check if Node.js is available.
|
|
|
|
Returns:
|
|
True if node/npm are available
|
|
"""
|
|
try:
|
|
result = subprocess.run(
|
|
["npm", "--version"],
|
|
capture_output=True,
|
|
timeout=5,
|
|
check=False
|
|
)
|
|
return result.returncode == 0
|
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
return False
|
|
|
|
def build_widgets(self, force: bool = False) -> bool:
|
|
"""
|
|
Build Vue widgets.
|
|
|
|
Args:
|
|
force: If True, build even if not needed
|
|
|
|
Returns:
|
|
True if build succeeded or not needed, False if failed
|
|
"""
|
|
if not force and self.check_build_exists() and not self.check_build_outdated():
|
|
logger.debug("Vue widgets build is up to date")
|
|
return True
|
|
|
|
if not self.vue_widgets_dir.exists():
|
|
logger.warning(f"Vue widgets directory not found: {self.vue_widgets_dir}")
|
|
return False
|
|
|
|
if not self.check_node_available():
|
|
logger.warning(
|
|
"Node.js/npm not found. Cannot build Vue widgets. "
|
|
"Please install Node.js or build manually: cd vue-widgets && npm run build"
|
|
)
|
|
return False
|
|
|
|
logger.info("Building Vue widgets...")
|
|
|
|
try:
|
|
# Check if node_modules exists, if not run npm install first
|
|
node_modules = self.vue_widgets_dir / "node_modules"
|
|
if not node_modules.exists():
|
|
logger.info("Installing npm dependencies...")
|
|
install_result = subprocess.run(
|
|
["npm", "install"],
|
|
cwd=self.vue_widgets_dir,
|
|
capture_output=True,
|
|
timeout=300, # 5 minutes for install
|
|
check=False
|
|
)
|
|
|
|
if install_result.returncode != 0:
|
|
logger.error(f"npm install failed: {install_result.stderr.decode()}")
|
|
return False
|
|
|
|
# Run build
|
|
build_result = subprocess.run(
|
|
["npm", "run", "build"],
|
|
cwd=self.vue_widgets_dir,
|
|
capture_output=True,
|
|
timeout=120, # 2 minutes for build
|
|
check=False
|
|
)
|
|
|
|
if build_result.returncode == 0:
|
|
logger.info("✓ Vue widgets built successfully")
|
|
return True
|
|
else:
|
|
logger.error(f"Build failed: {build_result.stderr.decode()}")
|
|
return False
|
|
|
|
except subprocess.TimeoutExpired:
|
|
logger.error("Build timed out")
|
|
return False
|
|
except Exception as e:
|
|
logger.error(f"Build error: {e}")
|
|
return False
|
|
|
|
def ensure_built(self, auto_build: bool = True, warn_only: bool = True) -> bool:
|
|
"""
|
|
Ensure Vue widgets are built, optionally auto-building if needed.
|
|
|
|
Args:
|
|
auto_build: If True, attempt to build if needed
|
|
warn_only: If True, only warn on failure instead of raising
|
|
|
|
Returns:
|
|
True if widgets are available (built or successfully auto-built)
|
|
|
|
Raises:
|
|
RuntimeError: If warn_only=False and build is missing/failed
|
|
"""
|
|
if self.check_build_exists():
|
|
# Build exists, check if outdated
|
|
if self.check_build_outdated():
|
|
logger.info("Vue widget source code is newer than build")
|
|
if auto_build:
|
|
return self.build_widgets()
|
|
else:
|
|
logger.warning(
|
|
"Vue widget build is outdated. "
|
|
"Please rebuild: cd vue-widgets && npm run build"
|
|
)
|
|
return True
|
|
|
|
# No build exists
|
|
logger.warning("Vue widget build not found")
|
|
|
|
if auto_build:
|
|
if self.build_widgets():
|
|
return True
|
|
else:
|
|
msg = (
|
|
"Failed to build Vue widgets. "
|
|
"Please build manually: cd vue-widgets && npm install && npm run build"
|
|
)
|
|
if warn_only:
|
|
logger.warning(msg)
|
|
return False
|
|
else:
|
|
raise RuntimeError(msg)
|
|
else:
|
|
msg = "Vue widgets not built. Please run: cd vue-widgets && npm install && npm run build"
|
|
if warn_only:
|
|
logger.warning(msg)
|
|
return False
|
|
else:
|
|
raise RuntimeError(msg)
|
|
|
|
|
|
def check_and_build_vue_widgets(
|
|
auto_build: bool = True,
|
|
warn_only: bool = True,
|
|
force: bool = False
|
|
) -> bool:
|
|
"""
|
|
Convenience function to check and build Vue widgets.
|
|
|
|
Args:
|
|
auto_build: If True, attempt to build if needed
|
|
warn_only: If True, only warn on failure instead of raising
|
|
force: If True, force rebuild even if up to date
|
|
|
|
Returns:
|
|
True if widgets are available
|
|
"""
|
|
builder = VueWidgetBuilder()
|
|
|
|
if force:
|
|
return builder.build_widgets(force=True)
|
|
|
|
return builder.ensure_built(auto_build=auto_build, warn_only=warn_only)
|