PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.4
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.4
1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 All 30 releases
← All changes | includes/modules/Bloat/BloatModule.php +92 -14 1.2.41.3.4 View file →
@@ -34,11 +34,11 @@
34 34 public const VERSION = '1.0.0';
35 35
36 36 public function ui_metadata(): array {
37 37 return array(
38 - 'label' => 'Bloat Control',
38 + 'label' => __( 'Bloat Control', 'xspeed' ),
39 39 'icon' => 'Sliders',
40 - 'description' => 'Turn off WordPress defaults you do not use — saves bytes, requests, and attack surface.',
40 + 'description' => __( 'Turn off WordPress defaults you do not use — saves bytes, requests, and attack surface.', 'xspeed' ),
41 41 );
42 42 }
43 43
44 44 public function settings_schema(): array {
@@ -45,45 +45,70 @@
45 45 return array(
46 46 'disable_dashicons_frontend' => array(
47 47 'type' => 'bool',
48 48 'default' => false,
49 - 'label' => 'Disable Dashicons on Frontend',
50 - 'description' => 'Drop the dashicons stylesheet from non-admin pages. Most themes do not need it. Saves ~45 KB per visitor.',
49 + 'label' => __( 'Disable Dashicons on Frontend', 'xspeed' ),
50 + 'description' => __( 'Drop the dashicons stylesheet from non-admin pages. Most themes do not need it. Saves ~45 KB per visitor.', 'xspeed' ),
51 51 ),
52 52 'disable_oembed' => array(
53 53 'type' => 'bool',
54 54 'default' => false,
55 - 'label' => 'Disable oEmbed Discovery + wp-embed.min.js',
56 - 'description' => 'Strip the auto-embed handlers + the embed script. Posts that paste a YouTube URL will no longer auto-render the player — embed it via a block instead. Saves a request per page.',
55 + 'label' => __( 'Disable oEmbed Discovery + wp-embed.min.js', 'xspeed' ),
56 + 'description' => __( 'Strip the auto-embed handlers + the embed script. Posts that paste a YouTube URL will no longer auto-render the player — embed it via a block instead. Saves a request per page.', 'xspeed' ),
57 57 ),
58 58 'disable_rss_feeds' => array(
59 59 'type' => 'bool',
60 60 'default' => false,
61 - 'label' => 'Disable RSS Feeds',
62 - 'description' => 'Return a 404 on /feed/ and similar endpoints. Useful for sites that do not publish feeds and want to cut feed-fetcher traffic.',
61 + 'label' => __( 'Disable RSS Feeds', 'xspeed' ),
62 + 'description' => __( 'Return a 404 on /feed/ and similar endpoints. Useful for sites that do not publish feeds and want to cut feed-fetcher traffic.', 'xspeed' ),
63 63 ),
64 64 'disable_xmlrpc' => array(
65 65 'type' => 'bool',
66 66 'default' => false,
67 - 'label' => 'Disable XML-RPC',
68 - 'description' => 'Disable the legacy xmlrpc.php endpoint. Cuts pingback brute-force noise; safe to disable unless you use a remote WP client (Jetpack, WordPress mobile app).',
67 + 'label' => __( 'Disable XML-RPC', 'xspeed' ),
68 + 'description' => __( 'Disable the legacy xmlrpc.php endpoint. Cuts pingback brute-force noise; safe to disable unless you use a remote WP client (Jetpack, WordPress mobile app).', 'xspeed' ),
69 69 ),
70 70 'strip_jquery_migrate' => array(
71 71 'type' => 'bool',
72 72 'default' => false,
73 - 'label' => 'Strip jQuery Migrate on Frontend',
74 - 'description' => 'Remove the jquery-migrate compatibility shim from non-admin pages. Saves ~10 KB; safe on modern themes / plugins.',
73 + 'label' => __( 'Strip jQuery Migrate on Frontend', 'xspeed' ),
74 + 'description' => __( 'Remove the jquery-migrate compatibility shim from non-admin pages. Saves ~10 KB; safe on modern themes / plugins.', 'xspeed' ),
75 75 ),
76 + 'strip_editor_styles' => array(
77 + 'type' => 'bool',
78 + 'default' => false,
79 + 'label' => __( 'Strip Block-Editor Styles on Frontend', 'xspeed' ),
80 + 'description' => __( 'Drop editor-only stylesheets (wp-editor, wp-components, and friends) from anonymous pages. A plugin that enqueues them on the frontend usually does so by accident — they can add hundreds of KB of render-blocking CSS. Frontend block styles (wp-block-library) are never touched.', 'xspeed' ),
81 + ),
76 82 'restrict_rest_to_authed' => array(
77 83 'type' => 'bool',
78 84 'default' => false,
79 - 'label' => 'Restrict REST API to Logged-In Users',
80 - 'description' => 'Block /wp-json/ for anonymous requests. WooCommerce checkout, contact-form submissions, and many block-editor previews need anonymous REST — keep this off unless you know your site does not depend on it.',
85 + 'label' => __( 'Restrict REST API to Logged-In Users', 'xspeed' ),
86 + 'description' => __( 'Block /wp-json/ for anonymous requests. WooCommerce checkout, contact-form submissions, and many block-editor previews need anonymous REST — keep this off unless you know your site does not depend on it.', 'xspeed' ),
81 87 ),
82 88 );
83 89 }
84 90
85 91 public function boot(): void {
92 + /*
93 + * Deferred to `init` priority 0. This reads the module's settings,
94 + * which builds settings_schema(), whose labels go through __(), and
95 + * boot() runs on `plugins_loaded` — before `after_setup_theme`, the
96 + * earliest point WordPress 6.7+ treats as safe to translate.
97 + *
98 + * Priority 0 (not the default 10) because the body itself registers
99 + * an `init` callback at priority 9: adding a hook to the action that
100 + * is currently running only takes effect if the new priority is still
101 + * ahead of the running position, so we have to be first. Every other
102 + * hook it registers fires later than `init`.
103 + */
104 + add_action( 'init', array( $this, 'boot_on_init' ), 0 );
105 + }
106 +
107 + /**
108 + * The real boot body — see boot() for why it runs on `init`.
109 + */
110 + public function boot_on_init(): void {
86 111 $opts = Settings_Manager::get( self::SLUG );
87 112
88 113 if ( ! empty( $opts['disable_dashicons_frontend'] ) ) {
89 114 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_dashicons' ), 100 );
@@ -112,8 +137,13 @@
112 137 if ( ! empty( $opts['strip_jquery_migrate'] ) ) {
113 138 add_action( 'wp_default_scripts', array( __CLASS__, 'strip_jquery_migrate' ) );
114 139 }
115 140
141 + if ( ! empty( $opts['strip_editor_styles'] ) ) {
142 + // Late, so anything enqueued at normal priority is already queued.
143 + add_action( 'wp_enqueue_scripts', array( __CLASS__, 'dequeue_editor_styles' ), PHP_INT_MAX );
144 + }
145 +
116 146 if ( ! empty( $opts['restrict_rest_to_authed'] ) ) {
117 147 add_filter( 'rest_authentication_errors', array( __CLASS__, 'restrict_rest' ) );
118 148 }
119 149 }
@@ -153,8 +183,56 @@
153 183 }
154 184 return $rules;
155 185 }
156 186 );
187 + }
188 +
189 + /**
190 + * Editor-only style handles that have no business on an anonymous
191 + * frontend page. Deliberately NOT wp-block-library /
192 + * wp-block-library-theme / global-styles — those style the blocks
193 + * visitors actually see. Observed live: a plugin pulled wp-editor +
194 + * wp-components (and their deps) onto a marketing homepage, several
195 + * hundred KB of render-blocking CSS nothing on the page used.
196 + */
197 + private const EDITOR_STYLE_HANDLES = array(
198 + 'wp-editor',
199 + 'wp-block-editor',
200 + 'wp-block-directory',
201 + 'wp-components',
202 + 'wp-preferences',
203 + 'wp-media-utils',
204 + 'wp-reusable-blocks',
205 + 'wp-patterns',
206 + 'wp-edit-blocks',
207 + 'wp-edit-post',
208 + 'wp-edit-site',
209 + 'wp-edit-widgets',
210 + 'wp-format-library',
211 + 'wp-list-reusable-blocks',
212 + 'wp-nux',
213 + );
214 +
215 + public static function dequeue_editor_styles(): void {
216 + // Logged-in views legitimately reach editor surfaces (front-end
217 + // editing, admin bar flows), and a builder editing screen is a
218 + // front-end URL — same guard set as the other frontend strips.
219 + if ( is_user_logged_in() || is_admin() || \XSpeed\Builder_Editor::is_active() ) {
220 + return;
221 + }
222 + $styles = wp_styles();
223 + foreach ( self::EDITOR_STYLE_HANDLES as $handle ) {
224 + wp_dequeue_style( $handle );
225 + }
226 + // Dequeue alone is not enough: dependencies are resolved again at
227 + // print time, so any queued sheet that lists one of these as a dep
228 + // pulls it straight back. Strip the handles from every registered
229 + // sheet's deps too — same technique strip_jquery_migrate() uses.
230 + foreach ( $styles->registered as $dependency ) {
231 + if ( is_array( $dependency->deps ?? null ) && array_intersect( $dependency->deps, self::EDITOR_STYLE_HANDLES ) ) {
232 + $dependency->deps = array_values( array_diff( $dependency->deps, self::EDITOR_STYLE_HANDLES ) );
233 + }
234 + }
157 235 }
158 236
159 237 public static function block_feed(): void {
160 238 wp_die(