1+ import logging
12import os
23import time
34import uuid
45from dash import Dash , hooks
56from flask import Response , send_file
67from 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
1015class _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
207252def setup_tailwindcss_plugin (
0 commit comments