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 +139 -35 3.0.123.2.89 View file →
@@ -12,15 +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 17 use WindPress\WindPress\Utils\Common;
18 18 use WindPress\WindPress\Utils\Config;
19 +use WindPress\WindPress\Utils\Vite;
19 20 /**
20 21 * @since 3.0.0
21 - *
22 - * TODO: This class is not yet implemented, it's just temporary for Universal Editor testing purpose.
23 22 */
24 23 class Runtime
25 24 {
26 25 /**
@@ -54,9 +53,9 @@
54 53 * instance. On the first run, it creates a singleton object and places it
55 54 * into the static property. On subsequent runs, it returns the client existing
56 55 * object stored in the static property.
57 56 */
58 - public static function get_instance() : self
57 + public static function get_instance(): self
59 58 {
60 59 if (!isset(self::$instance)) {
61 60 self::$instance = new self();
62 61 }
@@ -63,10 +62,10 @@
63 62 return self::$instance;
64 63 }
65 64 public function init()
66 65 {
67 - if (!\is_admin()) {
68 - $is_prevent_load = \apply_filters('f!windpress/core/runtime:is_prevent_load', \false);
66 + if (!is_admin()) {
67 + $is_prevent_load = apply_filters('f!windpress/core/runtime:is_prevent_load', \false);
69 68 if ($is_prevent_load) {
70 69 return;
71 70 }
72 71 $this->append_header();
@@ -71,65 +70,170 @@
71 70 }
72 71 $this->append_header();
73 72 }
74 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 + }
75 83 public function append_header()
76 84 {
77 - $is_cache_enabled = Config::get('performance.cache.enabled', \false);
78 - $is_cache_enabled = \apply_filters('f!windpress/core/runtime:append_header.cache_enabled', $is_cache_enabled);
79 - $is_exclude_admin = Config::get('performance.cache.exclude_admin', \false) && \current_user_can('manage_options');
80 - $is_exclude_admin = \apply_filters('f!windpress/core/runtime:append_header.exclude_admin', $is_exclude_admin);
81 - if ($is_cache_enabled && $this->is_cache_exists() && !$is_exclude_admin) {
82 - \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 + }
83 101 } else {
84 - \add_action('wp_head', fn() => $this->enqueue_play_cdn(), 1000001);
102 + add_action('wp_head', fn() => $this->enqueue_play_cdn(), 1000001);
85 103 }
86 - 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)) {
87 - \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';
88 115 }
116 + return $mode;
89 117 }
90 118 public function is_cache_exists()
91 119 {
92 - 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));
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));
93 121 }
94 - public function enqueue_css_cache()
122 + public function enqueue_css_cache(bool $force_inline = \false)
95 123 {
96 124 if (!$this->is_cache_exists()) {
97 125 return;
98 126 }
127 + do_action('a!windpress/core/runtime:enqueue_css_cache.before');
99 128 $handle = WIND_PRESS::WP_OPTION . '-cached';
100 - if (Config::get('performance.cache.inline_load', \false)) {
129 + if ($force_inline || Config::get('performance.cache.inline_load', \false)) {
101 130 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file
102 - $css_clean = \file_get_contents(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
131 + $css_clean = file_get_contents(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
103 132 if ($css_clean === \false) {
104 133 return;
105 134 }
106 - \wp_register_style($handle, \false, [], \false);
107 - \wp_add_inline_style($handle, \wp_strip_all_tags($css_clean));
108 - \wp_print_styles($handle);
135 + wp_register_style($handle, \false, [], \false);
136 + wp_add_inline_style($handle, wp_strip_all_tags($css_clean));
137 + wp_print_styles($handle);
109 138 } else {
110 - $version = (string) \filemtime(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
111 - \wp_register_style($handle, \WindPress\WindPress\Core\Cache::get_cache_url(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE), [], $version);
112 - \wp_print_styles($handle);
139 + $version = (string) filemtime(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
140 + wp_register_style($handle, \WindPress\WindPress\Core\Cache::get_cache_url(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE), [], $version);
141 + wp_print_styles($handle);
113 142 }
143 + do_action('a!windpress/core/runtime:enqueue_css_cache.after', $handle);
114 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 + }
115 157 public function enqueue_play_cdn($display = \true)
116 158 {
117 - $volumeEntries = \array_reduce(\WindPress\WindPress\Core\Volume::get_entries(), fn($carry, $entry) => $carry + ['/' . $entry['relative_path'] => $entry['content']], []);
118 159 // Script content are base64 encoded to prevent it from being executed by the browser.
119 160 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
120 - echo \sprintf('<script id="windpress:vfs" type="text/tailwindcss">%s</script>', \base64_encode(\wp_json_encode($volumeEntries)));
121 - AssetVite::get_instance()->enqueue_asset('assets/packages/core/tailwind/play/autocomplete.js', ['handle' => WIND_PRESS::WP_OPTION . ':autocomplete', 'in-footer' => \true]);
122 - AssetVite::get_instance()->enqueue_asset('assets/packages/core/tailwind/play/sort.js', ['handle' => WIND_PRESS::WP_OPTION . ':sort', 'in-footer' => \true]);
123 - 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]);
124 - 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);
125 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 + }
126 197 public function enqueue_front_panel()
127 198 {
128 199 $handle = WIND_PRESS::WP_OPTION . ':admin';
129 - AssetVite::get_instance()->enqueue_asset('assets/apps/dashboard/main.js', ['handle' => $handle, 'in_footer' => \true, 'dependencies' => ['wp-i18n', 'wp-hooks']]);
130 - \wp_set_script_translations($handle, 'windpress');
131 - \wp_localize_script($handle, 'windpress', ['_version' => WIND_PRESS::VERSION, '_via_wp_org' => !Common::is_updater_library_available(), '_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]);
200 + Vite::assets()->enqueue('resources/dashboard/main.ts', ['handle' => $handle, 'in_footer' => \true, 'dependencies' => ['wp-i18n', 'wp-hooks']]);
201 + wp_set_script_translations($handle, 'windpress');
132 202 // do enqueue scripts manually as it already runned before
133 - \wp_enqueue_scripts();
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;
134 238 }
135 239 }