PluginProbe
WindPress – Tailwind CSS integration for WordPress / 3.2.88
WindPress – Tailwind CSS integration for WordPress v3.2.88
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 3.0.7 All 142 releases
windpress / src / Core / Runtime.php

Runtime.php in WindPress – Tailwind CSS integration for WordPress 3.2.88, at src/Core/Runtime.php

240 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the WindPress package.
5 *
6 * (c) Joshua Gugun Siagian <suabahasa@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 declare (strict_types=1);
12 namespace WindPress\WindPress\Core;
13
14 use Exception;
15 use WIND_PRESS;
16 use WindPress\WindPress\Admin\AdminPage;
17 use WindPress\WindPress\Utils\Common;
18 use WindPress\WindPress\Utils\Config;
19 use WindPress\WindPress\Utils\Vite;
20 /**
21 * @since 3.0.0
22 */
23 class Runtime
24 {
25 /**
26 * Stores the instance, implementing a Singleton pattern.
27 */
28 private static self $instance;
29 /**
30 * The Singleton's constructor should always be private to prevent direct
31 * construction calls with the `new` operator.
32 */
33 private function __construct()
34 {
35 }
36 /**
37 * Singletons should not be cloneable.
38 */
39 private function __clone()
40 {
41 }
42 /**
43 * Singletons should not be restorable from strings.
44 *
45 * @throws Exception Cannot unserialize a singleton.
46 */
47 public function __wakeup()
48 {
49 throw new Exception('Cannot unserialize a singleton.');
50 }
51 /**
52 * This is the static method that controls the access to the singleton
53 * instance. On the first run, it creates a singleton object and places it
54 * into the static property. On subsequent runs, it returns the client existing
55 * object stored in the static property.
56 */
57 public static function get_instance(): self
58 {
59 if (!isset(self::$instance)) {
60 self::$instance = new self();
61 }
62 return self::$instance;
63 }
64 public function init()
65 {
66 if (!is_admin()) {
67 $is_prevent_load = apply_filters('f!windpress/core/runtime:is_prevent_load', \false);
68 if ($is_prevent_load) {
69 return;
70 }
71 $this->append_header();
72 }
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 }
83 public function append_header()
84 {
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 }
101 } else {
102 add_action('wp_head', fn() => $this->enqueue_play_cdn(), 1000001);
103 }
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';
115 }
116 return $mode;
117 }
118 public function is_cache_exists()
119 {
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));
121 }
122 public function enqueue_css_cache(bool $force_inline = \false)
123 {
124 if (!$this->is_cache_exists()) {
125 return;
126 }
127 do_action('a!windpress/core/runtime:enqueue_css_cache.before');
128 $handle = WIND_PRESS::WP_OPTION . '-cached';
129 if ($force_inline || Config::get('performance.cache.inline_load', \false)) {
130 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file
131 $css_clean = file_get_contents(\WindPress\WindPress\Core\Cache::get_cache_path(\WindPress\WindPress\Core\Cache::CSS_CACHE_FILE));
132 if ($css_clean === \false) {
133 return;
134 }
135 wp_register_style($handle, \false, [], \false);
136 wp_add_inline_style($handle, wp_strip_all_tags($css_clean));
137 wp_print_styles($handle);
138 } else {
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);
142 }
143 do_action('a!windpress/core/runtime:enqueue_css_cache.after', $handle);
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 }
157 public function enqueue_play_cdn($display = \true)
158 {
159 // Script content are base64 encoded to prevent it from being executed by the browser.
160 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
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);
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 }
197 public function enqueue_front_panel()
198 {
199 $handle = WIND_PRESS::WP_OPTION . ':admin';
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');
202 // do enqueue scripts manually as it already runned before
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;
238 }
239 }
240