PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.2.89
WindPress – Tailwind CSS integration for WordPress v3.2.89
3.2.89 3.2.88 3.2.87 3.2.86 3.2.85 3.2.84 3.2.83 3.2.82 3.2.81 trunk 3.0.0 3.0.1 3.0.10 3.0.11 3.0.12 3.0.13 3.0.14 3.0.15 3.0.16 3.0.17 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 All 143 releases
← All changes | src/Core/Runtime.php +126 -28 3.0.13.2.89 View file →
@@ -12,14 +12,14 @@
12 12 namespace WindPress\WindPress\Core;
13 13
14 14 use Exception;
15 15 use WIND_PRESS;
16 -use WindPress\WindPress\Utils\AssetVite;
16 +use WindPress\WindPress\Admin\AdminPage;
17 +use WindPress\WindPress\Utils\Common;
17 18 use WindPress\WindPress\Utils\Config;
19 +use WindPress\WindPress\Utils\Vite;
18 20 /**
19 21 * @since 3.0.0
20 - *
21 - * TODO: This class is not yet implemented, it's just temporary for Universal Editor testing purpose.
22 22 */
23 23 class Runtime
24 24 {
25 25 /**
@@ -70,34 +70,64 @@
70 70 }
71 71 $this->append_header();
72 72 }
73 73 }
74 + /**
75 + * Get the used Tailwind CSS version.
76 + *
77 + * @return int
78 + */
79 + public static function tailwindcss_version()
80 + {
81 + return (int) apply_filters('f!windpress/core/runtime:tailwindcss_version', Config::get('general.tailwindcss.version', 4));
82 + }
74 83 public function append_header()
75 84 {
76 - $is_cache_enabled = Config::get('performance.cache.enabled', \false);
77 - $is_cache_enabled = apply_filters('f!windpress/core/runtime:append_header.cache_enabled', $is_cache_enabled);
78 - $is_exclude_admin = Config::get('performance.cache.exclude_admin', \false) && current_user_can('manage_options');
79 - $is_exclude_admin = apply_filters('f!windpress/core/runtime:append_header.exclude_admin', $is_exclude_admin);
80 - if ($is_cache_enabled && $this->is_cache_exists() && !$is_exclude_admin) {
81 - add_action('wp_head', fn() => $this->enqueue_css_cache(), 1000001);
85 + $mode = $this->performance_mode();
86 + // Keep the filter name for integrations that need to bypass cached CSS (e.g., builder previews).
87 + $should_skip_cache = apply_filters('f!windpress/core/runtime:append_header.exclude_admin', \false);
88 + add_action('wp_head', fn() => $this->print_windpress_metadata(), 1000001);
89 + $has_cache = $this->is_cache_exists();
90 + if ($mode === 'hybrid') {
91 + if ($has_cache && !$should_skip_cache) {
92 + add_action('wp_head', fn() => $this->enqueue_css_cache(\true), 1000001);
93 + }
94 + add_action('wp_head', fn() => $this->enqueue_play_cdn(), 1000001);
95 + } elseif ($mode === 'cached') {
96 + if ($has_cache && !$should_skip_cache) {
97 + add_action('wp_head', fn() => $this->enqueue_css_cache(), 1000001);
98 + } else {
99 + add_action('wp_head', fn() => $this->enqueue_play_cdn(), 1000001);
100 + }
82 101 } else {
83 102 add_action('wp_head', fn() => $this->enqueue_play_cdn(), 1000001);
84 103 }
85 - if (Config::get('general.ubiquitous-panel.enabled', \true) && current_user_can('manage_options') && !apply_filters('f!windpress/core/runtime:append_header.ubiquitous_panel.is_prevent_load', \false)) {
86 - add_action('wp_head', fn() => $this->enqueue_front_panel(), 1000001);
104 + // if ($this->is_ubiquitous_panel()) {
105 + // add_action('wp_head', fn() => $this->enqueue_front_panel(), 1_000_001);
106 + // }
107 + }
108 + private function performance_mode(): string
109 + {
110 + $mode = Config::get('performance.mode', 'hybrid');
111 + $mode = apply_filters('f!windpress/core/runtime:append_header.mode', $mode);
112 + $allowed = ['cached', 'hybrid', 'compiler'];
113 + if (!in_array($mode, $allowed, \true)) {
114 + return 'hybrid';
87 115 }
116 + return $mode;
88 117 }
89 118 public function is_cache_exists()
90 119 {
91 120 return file_exists(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE)) && is_readable(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
92 121 }
93 - public function enqueue_css_cache()
122 + public function enqueue_css_cache(bool $force_inline = \false)
94 123 {
95 124 if (!$this->is_cache_exists()) {
96 125 return;
97 126 }
127 + do_action('a!windpress/core/runtime:enqueue_css_cache.before');
98 128 $handle = WIND_PRESS::WP_OPTION . '-cached';
99 - if (Config::get('performance.cache.inline_load', \false)) {
129 + if ($force_inline || Config::get('performance.cache.inline_load', \false)) {
100 130 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file
101 131 $css_clean = file_get_contents(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
102 132 if ($css_clean === \false) {
103 133 return;
@@ -109,33 +139,101 @@
109 139 $version = (string) filemtime(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
110 140 wp_register_style($handle, \WindPress\WindPress\Core\Cache::get_cache_url(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE), [], $version);
111 141 wp_print_styles($handle);
112 142 }
143 + do_action('a!windpress/core/runtime:enqueue_css_cache.after', $handle);
113 144 }
145 + public function getVFSHtml()
146 + {
147 + $volumeEntries = array_reduce(\WindPress\WindPress\Core\Volume::get_entries(), function ($carry, $entry) {
148 + if (!empty($entry['directory'])) {
149 + return $carry;
150 + }
151 + return $carry + ['/' . $entry['relative_path'] => $entry['content']];
152 + }, []);
153 + // Script content are base64 encoded to prevent it from being executed by the browser.
154 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
155 + return sprintf('<script id="windpress:vfs" type="text/plain">%s</script>', base64_encode(wp_json_encode($volumeEntries)));
156 + }
114 157 public function enqueue_play_cdn($display = \true)
115 158 {
116 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file
117 - $stub_main_css = file_get_contents(dirname(WIND_PRESS::FILE) . '/stubs/main.css');
118 - $main_css = $stub_main_css;
119 - $main_css_path = wp_upload_dir()['basedir'] . WIND_PRESS::DATA_DIR . 'main.css';
120 - if (file_exists($main_css_path)) {
121 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file
122 - $main_css = file_get_contents($main_css_path);
123 - }
124 159 // Script content are base64 encoded to prevent it from being executed by the browser.
125 160 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
126 - echo sprintf("<script id=\"windpress:tw-main-css\" type=\"text/tailwindcss\">%s</script>", esc_html(base64_encode($main_css)));
127 - AssetVite::get_instance()->enqueue_asset('assets/packages/core/tailwind/play/autocomplete.js', ['handle' => WIND_PRESS::WP_OPTION . ':autocomplete', 'in-footer' => \true]);
128 - AssetVite::get_instance()->enqueue_asset('assets/packages/core/tailwind/play/sort.js', ['handle' => WIND_PRESS::WP_OPTION . ':sort', 'in-footer' => \true]);
129 - AssetVite::get_instance()->enqueue_asset('assets/packages/core/tailwind/play/classname-to-css.js', ['handle' => WIND_PRESS::WP_OPTION . ':classname-to-css', 'in-footer' => \true]);
130 - AssetVite::get_instance()->enqueue_asset('assets/packages/core/tailwind/play/observer.js', ['handle' => WIND_PRESS::WP_OPTION . ':observer', 'in-footer' => \true]);
161 + echo $this->getVFSHtml();
162 + $tailwindcss_version = static::tailwindcss_version();
163 + do_action('a!windpress/core/runtime:enqueue_play_cdn.before');
164 + $can_load_modules = current_user_can('manage_options', $tailwindcss_version);
165 + if ($tailwindcss_version === 3) {
166 + $this->enqueue_play_modules_v3($can_load_modules);
167 + } elseif ($tailwindcss_version === 4) {
168 + $this->enqueue_play_modules($can_load_modules);
169 + }
170 + wp_enqueue_script(WIND_PRESS::WP_OPTION . ':observer');
171 + do_action('a!windpress/core/runtime:enqueue_play_cdn.after', $tailwindcss_version);
131 172 }
173 + public function enqueue_play_modules($can_load_modules)
174 + {
175 + // Register the modules
176 + $loaded_modules = [];
177 + if ($can_load_modules) {
178 + Vite::assets()->register('resources/packages/core/tailwindcss/play/intellisense.ts', ['handle' => WIND_PRESS::WP_OPTION . ':intellisense', 'in_footer' => \true]);
179 + $loaded_modules[] = WIND_PRESS::WP_OPTION . ':intellisense';
180 + Vite::assets()->register('resources/packages/core/tailwindcss/play/worker.ts', ['handle' => WIND_PRESS::WP_OPTION . ':worker', 'in_footer' => \true]);
181 + $loaded_modules[] = WIND_PRESS::WP_OPTION . ':worker';
182 + do_action('a!windpress/core/runtime:enqueue_play_modules.loaded_modules');
183 + $loaded_modules = apply_filters('f!windpress/core/runtime:enqueue_play_modules.loaded_modules', $loaded_modules);
184 + }
185 + Vite::assets()->register('resources/packages/core/tailwindcss/play/observer.ts', ['handle' => WIND_PRESS::WP_OPTION . ':observer', 'in_footer' => \true, 'dependencies' => array_merge(['wp-i18n', 'wp-hooks'], is_array($loaded_modules) ? $loaded_modules : iterator_to_array($loaded_modules))]);
186 + }
187 + public function enqueue_play_modules_v3($can_load_modules)
188 + {
189 + // Register the modules
190 + $loaded_modules = [];
191 + if ($can_load_modules) {
192 + Vite::assets()->register('resources/packages/core/tailwindcss-v3/play/intellisense.ts', ['handle' => WIND_PRESS::WP_OPTION . ':intellisense', 'in_footer' => \true]);
193 + $loaded_modules[] = WIND_PRESS::WP_OPTION . ':intellisense';
194 + }
195 + Vite::assets()->register('resources/packages/core/tailwindcss-v3/play/observer.ts', ['handle' => WIND_PRESS::WP_OPTION . ':observer', 'in_footer' => \true, 'dependencies' => array_merge(['wp-i18n', 'wp-hooks'], is_array($loaded_modules) ? $loaded_modules : iterator_to_array($loaded_modules))]);
196 + }
132 197 public function enqueue_front_panel()
133 198 {
134 199 $handle = WIND_PRESS::WP_OPTION . ':admin';
135 - AssetVite::get_instance()->enqueue_asset('assets/apps/dashboard/main.js', ['handle' => $handle, 'in_footer' => \true]);
200 + Vite::assets()->enqueue('resources/dashboard/main.ts', ['handle' => $handle, 'in_footer' => \true, 'dependencies' => ['wp-i18n', 'wp-hooks']]);
136 201 wp_set_script_translations($handle, 'windpress');
137 - wp_localize_script($handle, 'windpress', ['_version' => WIND_PRESS::VERSION, '_wpnonce' => wp_create_nonce(WIND_PRESS::WP_OPTION), 'rest_api' => ['nonce' => wp_create_nonce('wp_rest'), 'root' => esc_url_raw(rest_url()), 'namespace' => WIND_PRESS::REST_NAMESPACE, 'url' => esc_url_raw(rest_url(WIND_PRESS::REST_NAMESPACE))], 'assets' => ['url' => AssetVite::asset_base_url()], 'site_meta' => ['name' => get_bloginfo('name'), 'site_url' => get_site_url()], 'is_universal' => \true]);
138 202 // do enqueue scripts manually as it already runned before
139 203 wp_enqueue_scripts();
204 + }
205 + public function is_ubiquitous_panel()
206 + {
207 + return Config::get('general.ubiquitous-panel.enabled', \false) && current_user_can('manage_options') && !apply_filters('f!windpress/core/runtime:append_header.ubiquitous_panel.is_prevent_load', \false);
208 + }
209 + public function print_windpress_metadata()
210 + {
211 + $metadata = $this->assets_metadata();
212 + $metadata = apply_filters('f!windpress/core/runtime:print_windpress_metadata', $metadata);
213 + // /**
214 + // * @see \WP_Scripts::localize()
215 + // */
216 + // foreach ($metadata as $key => $value) {
217 + // if (! is_scalar($value)) {
218 + // continue;
219 + // }
220 + // $metadata[$key] = html_entity_decode((string) $value, ENT_QUOTES, 'UTF-8');
221 + // }
222 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
223 + echo sprintf('<script id="windpress:metadata">var windpress = %s;</script>', wp_json_encode($metadata));
224 + }
225 + public function assets_metadata()
226 + {
227 + $metadata = ['_version' => WIND_PRESS::VERSION, '_wp_version' => get_bloginfo('version'), '_tailwindcss_version' => static::tailwindcss_version(), '_via_wp_org' => !Common::is_updater_library_available(), 'is_ubiquitous' => $this->is_ubiquitous_panel(), 'assets' => ['url' => Vite::base_url()], 'user_data' => ['data_dir' => [
228 + // 'path' => Volume::data_dir_path(),
229 + 'url' => \WindPress\WindPress\Core\Volume::data_dir_url(),
230 + ], 'cache_dir' => []]];
231 + if (current_user_can('manage_options')) {
232 + $metadata['_wpnonce'] = wp_create_nonce(WIND_PRESS::WP_OPTION);
233 + $metadata['rest_api'] = ['nonce' => wp_create_nonce('wp_rest'), 'root' => esc_url_raw(rest_url()), 'namespace' => WIND_PRESS::REST_NAMESPACE, 'url' => esc_url_raw(rest_url(WIND_PRESS::REST_NAMESPACE))];
234 + $metadata['site_meta'] = ['name' => get_bloginfo('name'), 'site_url' => get_site_url(), 'web_history' => AdminPage::get_page_url()];
235 + $metadata['is_debug'] = defined('WP_DEBUG') && \WP_DEBUG;
236 + }
237 + return $metadata;
140 238 }
141 239 }