Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,35 @@ jobs:
sudo apt-get update
sudo apt-get install -y chromium-browser chromium-chromedriver xvfb

- name: Set Chromium binary (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
if [ -x /usr/bin/chromium-browser ]; then
echo "CHROME_BINARY=/usr/bin/chromium-browser" >> $GITHUB_ENV
elif [ -x /usr/bin/chromium ]; then
echo "CHROME_BINARY=/usr/bin/chromium" >> $GITHUB_ENV
fi

- name: Install browser for testing (macOS)
if: matrix.os == 'macos-latest'
run: |
brew install --cask chromium chromedriver

- name: Set Chromium binary (macOS)
if: matrix.os == 'macos-latest'
run: |
echo "CHROME_BINARY=/Applications/Chromium.app/Contents/MacOS/Chromium" >> $GITHUB_ENV

- name: Install browser for testing (Windows)
if: matrix.os == 'windows-latest'
run: |
choco install chromium chromedriver

- name: Set Chromium binary (Windows)
if: matrix.os == 'windows-latest'
run: |
"CHROME_BINARY=C:\Program Files\Chromium\Application\chrome.exe" | Out-File -FilePath $env:GITHUB_ENV -Append

- name: Start virtual display (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
Expand All @@ -53,7 +72,7 @@ jobs:

- name: Run tests
run: |
python -m pytest tests/ -v --cov=dash_vite_plugin --cov-report=xml
python -m pytest tests/ -v --headless --cov=dash_vite_plugin --cov-report=xml
env:
DISPLAY: :99

Expand Down
1 change: 1 addition & 0 deletions README-zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ VitePlugin 是负责管理 Vite 构建过程的主要插件类。
| download_node | bool | False | 如果未找到是否下载 Node.js |
| node_version | str | '18.17.0' | 要下载的 Node.js 版本 |
| clean_after | bool | False | 构建后是否清理生成的文件 |
| log_show_mode | Literal['all', 'slim', 'hide'] | 'all' | 日志显示模式 ('all' 显示所有日志, 'slim' 显示精简日志, 'hide' 不显示日志) |
| skip_build | bool | False | 是否跳过构建执行 |
| skip_build_if_recent | bool | True | 如果构建文件是最近生成的是否跳过构建 |
| skip_build_time_threshold | int | 5 | 考虑构建文件为最近的时间阈值(秒)|
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ VitePlugin is the main plugin class responsible for managing the Vite build proc
| download_node | bool | False | Whether to download Node.js if not found |
| node_version | str | '18.17.0' | Node.js version to download |
| clean_after | bool | False | Whether to clean up generated files after build |
| log_show_mode | Literal['all', 'slim', 'hide'] | 'all' | Log show mode ('all' for verbose, 'slim' for minimal output, 'hide' for no logs) |
| skip_build | bool | False | Whether to skip build execution |
| skip_build_if_recent | bool | True | Whether to skip build if built file was recently generated |
| skip_build_time_threshold | int | 5 | Time threshold in seconds to consider built file as recent |
Expand Down
15 changes: 10 additions & 5 deletions dash_vite_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from dash import Dash, hooks
from flask import send_from_directory
from py_node_manager import get_logger
from typing import List
from typing import List, Literal
from .utils import NpmPackage, ViteCommand


Expand All @@ -29,6 +29,7 @@ def __init__(
download_node: bool = False,
node_version: str = '18.20.8',
clean_after: bool = False,
log_show_mode: Literal['all', 'slim', 'hide'] = 'all',
skip_build: bool = False,
skip_build_if_recent: bool = True,
skip_build_time_threshold: int = 5,
Expand All @@ -46,6 +47,7 @@ def __init__(
download_node (bool): Whether to download Node.js if not found
node_version (str): Node.js version to download if download_node is True
clean_after (bool): Whether to clean up generated files after build
log_show_mode (Literal['all', 'slim', 'hide']): Log show mode
skip_build (bool): Whether to skip build execution
skip_build_if_recent (bool): Whether to skip build if built file was recently generated
skip_build_time_threshold (int): Time threshold in seconds to consider built file as recent
Expand All @@ -59,6 +61,7 @@ def __init__(
self.download_node = download_node
self.node_version = node_version
self.clean_after = clean_after
self.log_show_mode = log_show_mode
self.skip_build = skip_build
self.skip_build_if_recent = skip_build_if_recent
self.skip_build_time_threshold = skip_build_time_threshold
Expand All @@ -71,6 +74,7 @@ def __init__(
download_node=download_node,
node_version=node_version,
is_cli=False,
log_show_mode=log_show_mode,
)
self._clean_files = []
self._clean_dirs = []
Expand Down Expand Up @@ -205,10 +209,11 @@ def _should_skip_build(self) -> bool:
file_mod_time = os.path.getmtime(check_index_path)
current_time = time.time()
if current_time - file_mod_time < self.skip_build_time_threshold:
logger.info(
f'⚡ Built assets file was generated recently '
f'({current_time - file_mod_time:.2f}s ago), skipping build...'
)
if self.log_show_mode in ['all', 'slim']:
logger.info(
f'⚡ Built assets file was generated recently '
f'({current_time - file_mod_time:.2f}s ago), skipping build...'
)
return True
return False

Expand Down
91 changes: 59 additions & 32 deletions dash_vite_plugin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(
download_node: bool,
node_version: str,
is_cli: bool,
log_show_mode: Literal['all', 'slim', 'hide'] = 'all',
):
"""
Initialize the ViteCommand class
Expand All @@ -51,8 +52,11 @@ def __init__(
download_node (bool): Whether to download Node.js if not found
node_version (str): Node.js version to download if download_node is True
is_cli (bool): Whether the command is being run from the CLI
log_show_mode (Literal['all', 'slim', 'hide']): Log show mode
"""
node_manager = NodeManager(download_node=download_node, node_version=node_version, is_cli=False)
node_manager = NodeManager(
download_node=download_node, node_version=node_version, is_cli=False, log_show_mode=log_show_mode
)
self.node_path = node_manager.node_path
self.node_env = node_manager.node_env
self.npm_path = node_manager.npm_path
Expand All @@ -62,6 +66,7 @@ def __init__(
self.support_less = support_less
self.support_sass = support_sass
self.is_cli = is_cli
self.log_show_mode = log_show_mode
# Ensure the plugin_tmp_dir directory exists
self.plugin_tmp_dir = plugin_tmp_dir
if not os.path.exists(self.plugin_tmp_dir):
Expand Down Expand Up @@ -215,7 +220,8 @@ def _install_vite(self) -> None:
Returns:
None
"""
logger.info('📥 Start installing Vite...')
if self.log_show_mode == 'all':
logger.info('📥 Start installing Vite...')
try:
if not self._check_vite():
install_cmd = [
Expand All @@ -237,10 +243,12 @@ def _install_vite(self) -> None:
if result.returncode != 0:
raise RuntimeError(result.stderr)

logger.info('✅ Vite installed successfully!')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ Vite installed successfully!')

except Exception as e:
logger.error(f'❌ Error installing Vite: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error installing Vite: {e}')
raise e

def _install_less(self) -> None:
Expand All @@ -250,7 +258,8 @@ def _install_less(self) -> None:
Returns:
None
"""
logger.info('📥 Start installing Less...')
if self.log_show_mode == 'all':
logger.info('📥 Start installing Less...')
try:
if not self._check_less():
install_cmd = [
Expand All @@ -270,10 +279,12 @@ def _install_less(self) -> None:
if result.returncode != 0:
raise RuntimeError(result.stderr)

logger.info('✅ Less installed successfully!')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ Less installed successfully!')

except Exception as e:
logger.error(f'❌ Error installing Less: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error installing Less: {e}')
raise e

def _install_sass(self) -> None:
Expand All @@ -283,7 +294,8 @@ def _install_sass(self) -> None:
Returns:
None
"""
logger.info('📥 Start installing Sass...')
if self.log_show_mode == 'all':
logger.info('📥 Start installing Sass...')
try:
if not self._check_sass():
install_cmd = [
Expand All @@ -303,10 +315,12 @@ def _install_sass(self) -> None:
if result.returncode != 0:
raise RuntimeError(result.stderr)

logger.info('✅ Sass installed successfully!')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ Sass installed successfully!')

except Exception as e:
logger.error(f'❌ Error installing Sass: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error installing Sass: {e}')
raise e

def _install_npm_packages(self) -> None:
Expand All @@ -316,7 +330,8 @@ def _install_npm_packages(self) -> None:
Returns:
None
"""
logger.info('📥 Start installing npm packages...')
if self.log_show_mode == 'all':
logger.info('📥 Start installing npm packages...')
try:
for package in self.npm_packages:
install_cmd = [
Expand All @@ -336,10 +351,12 @@ def _install_npm_packages(self) -> None:
if result.returncode != 0:
raise RuntimeError(result.stderr)

logger.info('✅ npm packages installed successfully!')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ npm packages installed successfully!')

except Exception as e:
logger.error(f'❌ Error installing npm packages: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error installing npm packages: {e}')
raise e

def init(self) -> Self:
Expand All @@ -349,27 +366,28 @@ def init(self) -> Self:
Returns:
Self: The ViteCommand instance
"""
logger.info('🚀 Start initializing Vite...')
if self.log_show_mode == 'all':
logger.info('🚀 Start initializing Vite...')
try:
# Create default config if it doesn't exist
if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info('⚙️ Creating Vite config file...')

if not os.path.exists(self.config_js_path):
if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info(f'🔍 Config file {self.config_js_path} not found. Creating default config file...')

self.create_default_vite_config()

if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info(f'💾 Default config file created at: {self.config_js_path}')

if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info('⚙️ Creating index.html file...')

self.create_default_index_html()

if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info(f'💾 Default index.html file created at: {self.index_html_path}')

if not self._check_npm_init():
Expand All @@ -384,11 +402,12 @@ def init(self) -> Self:
)
if result.returncode != 0:
raise RuntimeError(result.stderr)

logger.info('✅ Vite initialized successfully!')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ Vite initialized successfully!')

except Exception as e:
logger.error(f'❌ Error initializing Vite: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error initializing Vite: {e}')
raise e

return self
Expand Down Expand Up @@ -416,7 +435,8 @@ def build(self) -> Self:
Returns:
Self: The ViteCommand instance
"""
logger.info('🔨 Building assets using Vite...')
if self.log_show_mode == 'all':
logger.info('🔨 Building assets using Vite...')
try:
build_cmd: List[str] = [self.npx_path, 'vite', 'build']

Expand All @@ -427,10 +447,12 @@ def build(self) -> Self:
if result.returncode != 0:
raise RuntimeError(result.stderr)

logger.info('✅ Build completed successfully!')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ Build completed successfully!')

except Exception as e:
logger.error(f'❌ Error building assets using Vite: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error building assets using Vite: {e}')
raise e

return self
Expand All @@ -446,7 +468,8 @@ def clean(self, extra_clean_files: List[str], extra_clean_dirs: List[str]) -> Se
Returns:
Self: The ViteCommand instance
"""
logger.info('🧹 Cleaning up generated files...')
if self.log_show_mode == 'all':
logger.info('🧹 Cleaning up generated files...')
try:
files_to_remove = [
self.config_js_path,
Expand All @@ -464,25 +487,29 @@ def clean(self, extra_clean_files: List[str], extra_clean_dirs: List[str]) -> Se
if os.path.exists(file_path):
try:
os.remove(file_path)
if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info(f'🗑️ Removed {file_path}')
except Exception as e:
logger.warning(f'⚠️ Warning: Could not remove {file_path}: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.warning(f'⚠️ Warning: Could not remove {file_path}: {e}')

# Remove directories
for dir_path in directories_to_remove:
if os.path.exists(dir_path):
try:
shutil.rmtree(dir_path)
if self.is_cli:
if self.is_cli and self.log_show_mode == 'all':
logger.info(f'🗑️ Removed {dir_path}')
except Exception as e:
logger.warning(f'⚠️ Warning: Could not remove {dir_path}: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.warning(f'⚠️ Warning: Could not remove {dir_path}: {e}')

logger.info('✅ Cleanup completed.')
if self.log_show_mode in ['all', 'slim']:
logger.info('✅ Cleanup completed.')

except Exception as e:
logger.error(f'❌ Error cleaning up: {e}')
if self.log_show_mode in ['all', 'slim']:
logger.error(f'❌ Error cleaning up: {e}')
raise e

return self
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import sys
import shutil
import tempfile
from selenium.webdriver.chrome.options import Options


# Add the project root to the Python path so we can import the package
Expand All @@ -26,3 +27,11 @@ def temp_working_directory():
yield temp_dir
os.chdir(original_cwd)
shutil.rmtree(temp_dir, ignore_errors=True)


def pytest_setup_options():
options = Options()
chrome_binary = os.environ.get('CHROME_BINARY')
if chrome_binary:
options.binary_location = chrome_binary
return options
Loading