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