Skip to content

Commit 9480848

Browse files
committed
refactor: use py-node-manager
1 parent 2a5e82e commit 9480848

13 files changed

Lines changed: 1279 additions & 2531 deletions

dash_tailwindcss_plugin/cli.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import argparse
2+
import logging
23
import json
3-
from .utils import logger, NodeManager, TailwindCommand
4+
from py_node_manager import get_logger
5+
from .utils import TailwindCommand
6+
7+
8+
logger = get_logger(logging.getLogger(__name__))
49

510

611
class _TailwindCLI:
@@ -90,23 +95,16 @@ def run(self):
9095
logger.error(f'Invalid JSON for theme config: {e}')
9196
theme_config = None
9297

93-
node_manager = NodeManager(
94-
download_node=args.download_node,
95-
node_version=args.node_version,
96-
is_cli=True,
97-
)
9898
self.tailwind_command = TailwindCommand(
99-
node_path=node_manager.node_path,
100-
node_env=node_manager.node_env,
101-
npm_path=node_manager.npm_path,
102-
npx_path=node_manager.npx_path,
10399
tailwind_version=args.tailwind_version,
104100
content_path=args.content_path if args.content_path else ['**/*.py'],
105101
plugin_tmp_dir=args.plugin_tmp_dir,
106102
input_css_path=args.input_css_path,
107103
output_css_path=args.output_css_path,
108104
config_js_path=args.config_js_path,
109105
is_cli=True,
106+
download_node=args.download_node,
107+
node_version=args.node_version,
110108
theme_config=theme_config,
111109
)
112110

@@ -176,7 +174,3 @@ def main():
176174
"""
177175
cli = _TailwindCLI()
178176
cli.run()
179-
180-
181-
if __name__ == '__main__':
182-
main()

dash_tailwindcss_plugin/plugin.py

Lines changed: 130 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import logging
12
import os
23
import time
34
import uuid
45
from dash import Dash, hooks
56
from flask import Response, send_file
67
from typing import Any, Dict, List, Literal, Optional
7-
from .utils import dict_to_js_object, logger, NodeManager, TailwindCommand
8+
from py_node_manager import get_logger
9+
from .utils import dict_to_js_object, TailwindCommand
10+
11+
12+
logger = get_logger(logging.getLogger(__name__))
813

914

1015
class _TailwindCSSPlugin:
@@ -26,7 +31,7 @@ def __init__(
2631
clean_after: bool = True,
2732
skip_build_if_recent: bool = True,
2833
skip_build_time_threshold: int = 5,
29-
):
34+
) -> None:
3035
"""
3136
Initialize Tailwind CSS plugin with specified configuration.
3237
@@ -45,25 +50,21 @@ def __init__(
4550
clean_after (bool): Whether to clean up generated files after build
4651
skip_build_if_recent (bool): Whether to skip build if CSS file was recently generated
4752
skip_build_time_threshold (int): Time threshold in seconds to consider CSS file as recent
53+
54+
Returns:
55+
None
4856
"""
4957
if mode == 'offline':
50-
node_manager = NodeManager(
51-
download_node=download_node,
52-
node_version=node_version,
53-
is_cli=False,
54-
)
5558
self.tailwind_command = TailwindCommand(
56-
node_path=node_manager.node_path,
57-
node_env=node_manager.node_env,
58-
npm_path=node_manager.npm_path,
59-
npx_path=node_manager.npx_path,
6059
tailwind_version=tailwind_version,
6160
content_path=content_path,
6261
plugin_tmp_dir=plugin_tmp_dir,
6362
input_css_path=input_css_path,
6463
output_css_path=output_css_path,
6564
config_js_path=config_js_path,
6665
is_cli=False,
66+
download_node=download_node,
67+
node_version=node_version,
6768
theme_config=tailwind_theme_config,
6869
)
6970
self.mode = mode
@@ -88,46 +89,135 @@ def __init__(
8889
)
8990
self.cdn_url = new_cdn_url
9091

91-
def setup_online_mode(self):
92+
def _process_online_html(self, index_string: str) -> str:
9293
"""
93-
Setup Tailwind CSS using CDN
94+
Process HTML string for online mode by adding Tailwind CSS CDN script.
95+
96+
Args:
97+
index_string (str): Original HTML string
9498
9599
Returns:
96-
None
100+
str: Modified HTML string with Tailwind CSS CDN script
97101
"""
102+
# Create Tailwind CSS CDN script with theme configuration
103+
tailwind_script = f'<script src="{self.cdn_url}"></script>\n'
98104

99-
@hooks.index()
100-
def add_tailwindcss_cdn(index_string: str) -> str:
101-
# Create Tailwind CSS CDN script with theme configuration
102-
tailwind_script = f'<script src="{self.cdn_url}"></script>\n'
105+
# Add theme configuration script if provided
106+
if self.tailwind_theme_config:
107+
# Convert Python dict to JavaScript object using the utility function
108+
theme_config_js = dict_to_js_object(self.tailwind_theme_config)
103109

104-
# Add theme configuration script if provided
105-
if self.tailwind_theme_config:
106-
# Convert Python dict to JavaScript object using the utility function
107-
theme_config_js = dict_to_js_object(self.tailwind_theme_config)
108-
109-
# Add configuration script
110-
config_script = f"""<script>
110+
# Add configuration script
111+
config_script = f"""<script>
111112
tailwind.config = {{
112113
theme: {{
113114
extend: {theme_config_js}
114115
}}
115116
}};
116117
</script>
117118
"""
118-
tailwind_script += config_script
119+
tailwind_script += config_script
120+
121+
# Look for the closing head tag and insert the script before it
122+
if '</head>' in index_string:
123+
index_string = index_string.replace('</head>', f'{tailwind_script}</head>')
124+
# If no head tag, look for opening body tag and insert before it
125+
elif '<body>' in index_string:
126+
index_string = index_string.replace('<body>', f'<head>\n{tailwind_script}</head>\n<body>')
127+
# If neither head nor body tag, append to the beginning
128+
else:
129+
index_string = f'<head>\n{tailwind_script}</head>\n' + index_string
119130

120-
# Look for the closing head tag and insert the script before it
121-
if '</head>' in index_string:
122-
index_string = index_string.replace('</head>', f'{tailwind_script}</head>')
123-
# If no head tag, look for opening body tag and insert before it
124-
elif '<body>' in index_string:
125-
index_string = index_string.replace('<body>', f'<head>\n{tailwind_script}</head>\n<body>')
126-
# If neither head nor body tag, append to the beginning
127-
else:
128-
index_string = f'<head>\n{tailwind_script}</head>\n' + index_string
131+
return index_string
129132

130-
return index_string
133+
def _should_skip_build(self) -> bool:
134+
"""
135+
Check if the build should be skipped based on the skip_build_if_recent setting
136+
137+
Returns:
138+
bool: True if the build should be skipped, False otherwise
139+
"""
140+
# Check if CSS file exists and was generated recently (within threshold seconds)
141+
if self.skip_build_if_recent and os.path.exists(self.output_css_path):
142+
file_mod_time = os.path.getmtime(self.output_css_path)
143+
current_time = time.time()
144+
if current_time - file_mod_time < self.skip_build_time_threshold:
145+
logger.info(
146+
f'⚡ CSS file {self.output_css_path} was generated recently '
147+
f'({current_time - file_mod_time:.2f}s ago), skipping build...'
148+
)
149+
return True
150+
return False
151+
152+
def _build_tailwindcss(self) -> None:
153+
"""
154+
Build Tailwind CSS using Tailwind CLI
155+
156+
Returns:
157+
None
158+
"""
159+
built = self.tailwind_command.init().install().build()
160+
if self.clean_after:
161+
built.clean()
162+
163+
def _serve_tailwindcss(self) -> Response:
164+
"""
165+
Serve Tailwind CSS file.
166+
167+
Returns:
168+
Response: CSS file response or 404 if file not found
169+
"""
170+
# Check if the CSS file exists
171+
if os.path.exists(self.output_css_path):
172+
try:
173+
# Return the CSS file
174+
return send_file(self.output_css_path, mimetype='text/css')
175+
except Exception:
176+
# If there's an error return the file, return the content directly
177+
with open(self.output_css_path, 'r', encoding='utf-8') as f:
178+
css_content = f.read()
179+
return Response(css_content, mimetype='text/css')
180+
else:
181+
# Return 404 if file not found
182+
return Response('CSS file not found', status=404, mimetype='text/plain')
183+
184+
def _process_offline_html(self, built_tailwindcss_link: str, index_string: str) -> str:
185+
"""
186+
Process HTML string for offline mode by adding Tailwind CSS link.
187+
188+
Args:
189+
built_tailwindcss_link (str): Link to the built Tailwind CSS file
190+
index_string (str): Original HTML string
191+
192+
Returns:
193+
str: Modified HTML string with Tailwind CSS link
194+
"""
195+
# Insert Tailwind CSS link into the head section
196+
tailwindcss_link = f'<link rel="stylesheet" href="{built_tailwindcss_link}"></link>\n'
197+
198+
# Look for the closing head tag and insert the link before it
199+
if '</head>' in index_string:
200+
index_string = index_string.replace('</head>', f'{tailwindcss_link}</head>')
201+
# If no head tag, look for opening body tag and insert before it
202+
elif '<body>' in index_string:
203+
index_string = index_string.replace('<body>', f'<head>\n{tailwindcss_link}</head>\n<body>')
204+
# If neither head nor body tag, append to the beginning
205+
else:
206+
index_string = f'<head>\n{tailwindcss_link}</head>\n' + index_string
207+
208+
return index_string
209+
210+
def setup_online_mode(self):
211+
"""
212+
Setup Tailwind CSS using CDN
213+
214+
Returns:
215+
None
216+
"""
217+
218+
@hooks.index()
219+
def add_tailwindcss_cdn(index_string: str) -> str:
220+
return self._process_online_html(index_string)
131221

132222
def setup_offline_mode(self):
133223
"""
@@ -146,62 +236,17 @@ def setup_offline_mode(self):
146236
# Generate Tailwind CSS on app startup
147237
@hooks.setup(priority=3)
148238
def generate_tailwindcss(app: Dash):
149-
# Check if CSS file exists and was generated recently (within threshold seconds)
150-
if self.skip_build_if_recent and os.path.exists(self.output_css_path):
151-
file_mod_time = os.path.getmtime(self.output_css_path)
152-
current_time = time.time()
153-
if current_time - file_mod_time < self.skip_build_time_threshold:
154-
logger.info(
155-
f'⚡ CSS file {self.output_css_path} was generated recently '
156-
f'({current_time - file_mod_time:.2f}s ago), skipping build...'
157-
)
158-
return
159-
239+
if self._should_skip_build():
240+
return
160241
self._build_tailwindcss()
161242

162243
@hooks.route(name=built_tailwindcss_link, methods=('GET',), priority=2)
163244
def serve_tailwindcss():
164-
# Check if the CSS file exists
165-
if os.path.exists(self.output_css_path):
166-
try:
167-
# Return the CSS file
168-
return send_file(self.output_css_path, mimetype='text/css')
169-
except Exception:
170-
# If there's an error return the file, return the content directly
171-
with open(self.output_css_path, 'r', encoding='utf-8') as f:
172-
css_content = f.read()
173-
return Response(css_content, mimetype='text/css')
174-
else:
175-
# Return 404 if file not found
176-
return Response('CSS file not found', status=404, mimetype='text/plain')
245+
return self._serve_tailwindcss()
177246

178247
@hooks.index(priority=1)
179248
def add_tailwindcss_link(index_string: str) -> str:
180-
# Insert Tailwind CSS link into the head section
181-
tailwindcss_link = f'<link rel="stylesheet" href="{built_tailwindcss_link}"></link>\n'
182-
183-
# Look for the closing head tag and insert the link before it
184-
if '</head>' in index_string:
185-
index_string = index_string.replace('</head>', f'{tailwindcss_link}</head>')
186-
# If no head tag, look for opening body tag and insert before it
187-
elif '<body>' in index_string:
188-
index_string = index_string.replace('<body>', f'<head>\n{tailwindcss_link}</head>\n<body>')
189-
# If neither head nor body tag, append to the beginning
190-
else:
191-
index_string = f'<head>\n{tailwindcss_link}</head>\n' + index_string
192-
193-
return index_string
194-
195-
def _build_tailwindcss(self):
196-
"""
197-
Build Tailwind CSS using Tailwind CLI
198-
199-
Returns:
200-
None
201-
"""
202-
built = self.tailwind_command.init().install().build()
203-
if self.clean_after:
204-
built.clean()
249+
return self._process_offline_html(built_tailwindcss_link, index_string)
205250

206251

207252
def setup_tailwindcss_plugin(

0 commit comments

Comments
 (0)