From 987091413aee2869b8b2b197145d7448078d96ee Mon Sep 17 00:00:00 2001 From: Daan van den Bergh <18595395+Dan0sz@users.noreply.github.com> Date: Thu, 1 Oct 2026 12:47:02 +0200 Subject: [PATCH] Exclude the plugin's scripts from Hummingbird's optimizations Hummingbird's Asset Optimization combines the tracker with other scripts and moves (or duplicates) the inline script that initializes it. That can leave the inline plausible() calls, e.g. the search queries event, without the plausible.init() call, so window.plausible is still the tracker's placeholder object: "plausible is not a function". It also serves its own copy of the (proxied) tracker, which goes stale when the plugin updates the local file, and in our tests sent the search event twice. Return our handles (plausible-*) to WordPress untouched through wphb_dont_add_handle_to_collection, which Hummingbird itself uses to leave GTranslate's scripts alone, and exclude them from Delay JavaScript through wphb_delay_js_exclusions (matched against each script tag, including its inline code). --- readme.txt | 1 + src/Compatibility.php | 49 +++++++++++++++++++++++++ tests/integration/CompatibilityTest.php | 27 ++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/readme.txt b/readme.txt index a44374c8..27566ec5 100644 --- a/readme.txt +++ b/readme.txt @@ -280,6 +280,7 @@ Please make sure you make a backup of your database before updating any version = 2.6.3 = * Improved: the tracker script now loads asynchronously (like Plausible Analytics' own snippet) instead of deferred, so it can no longer hold up the rest of the page when it can't be reached. +* Added: Hummingbird compatibility. Its Asset Optimization and Delay JavaScript no longer touch the plugin's scripts, which could cause a "plausible is not a function" error (e.g. on search results pages). = 2.6.2 = * *Important!* This release rebuilds the WooCommerce (and EDD) purchase funnel for sites using WPML with a translated product base. If you built your own funnels using the same goals, please rebuild them after installing this update. diff --git a/src/Compatibility.php b/src/Compatibility.php index ff761c76..d070085e 100644 --- a/src/Compatibility.php +++ b/src/Compatibility.php @@ -33,6 +33,12 @@ public function __construct() { // Global Exclusion from Minification add_filter( 'plausible_analytics_script_params', [ $this, 'exclude_from_minification' ] ); + // Hummingbird + if ( defined( 'WPHB_VERSION' ) ) { + add_filter( 'wphb_dont_add_handle_to_collection', [ $this, 'exclude_from_hummingbird_asset_optimization' ], 10, 4 ); + add_filter( 'wphb_delay_js_exclusions', [ $this, 'exclude_from_hummingbird_delay_js' ] ); + } + // LiteSpeed Cache if ( defined( 'LSCWP_V' ) ) { add_filter( 'litespeed_optimize_js_excludes', [ $this, 'exclude_plausible_js' ] ); @@ -151,6 +157,49 @@ public function exclude_from_minification( $params ) { return $params; } + /** + * Dear Hummingbird, leave our scripts alone, please. Its Asset Optimization combines the tracker with other scripts + * and moves (or duplicates) the inline script that initializes it, which can leave calls to plausible() without the + * initialization ("plausible is not a function"), and serves its own copy of the tracker, which goes stale. + * + * Handles that aren't added to the collection are returned to WordPress untouched. + * + * @filter wphb_dont_add_handle_to_collection + * @since 2.6.3 + * + * @param bool $value Whether to leave the handle alone. + * @param string $handle Resource handle. + * @param string $source_url Resource URL. + * @param string $type scripts|styles + * + * @return bool + */ + public function exclude_from_hummingbird_asset_optimization( $value, $handle, $source_url, $type ) { + if ( $type === 'scripts' && strpos( (string) $handle, 'plausible-' ) === 0 ) { + return true; + } + + return $value; + } + + /** + * Dear Hummingbird, don't delay our scripts, please. The exclusions are matched against each script tag, including + * its inline code, so this covers our handles (plausible-*), our URLs (plausible.io, /plugins/plausible-analytics/) + * and the inline code calling window.plausible. + * + * @filter wphb_delay_js_exclusions + * @since 2.6.3 + * + * @param array $exclusions + * + * @return array + */ + public function exclude_from_hummingbird_delay_js( $exclusions ) { + $exclusions[] = 'plausible'; + + return $exclusions; + } + /** * Dear WP Rocket/SG Optimizer/Etc., don't minify/combine our inline JS, please. * diff --git a/tests/integration/CompatibilityTest.php b/tests/integration/CompatibilityTest.php index 2e69e04f..ccb57ed3 100644 --- a/tests/integration/CompatibilityTest.php +++ b/tests/integration/CompatibilityTest.php @@ -25,4 +25,31 @@ public function testTrackerScriptLoadsAsync() { $this->assertStringContainsString( " async data-cfasync='false'", $tag ); $this->assertStringNotContainsString( 'defer', $tag ); } + + /** + * Hummingbird's Asset Optimization should leave our scripts alone, and only ours. + * + * @see Compatibility::exclude_from_hummingbird_asset_optimization() + * @return void + */ + public function testExcludeFromHummingbirdAssetOptimization() { + $class = new Compatibility(); + + $this->assertTrue( $class->exclude_from_hummingbird_asset_optimization( false, 'plausible-analytics', '', 'scripts' ) ); + $this->assertTrue( $class->exclude_from_hummingbird_asset_optimization( false, 'plausible-form-submit-integration', '', 'scripts' ) ); + $this->assertFalse( $class->exclude_from_hummingbird_asset_optimization( false, 'jquery-core', '', 'scripts' ) ); + $this->assertFalse( $class->exclude_from_hummingbird_asset_optimization( false, 'plausible-analytics', '', 'styles' ) ); + // Another plugin's decision is kept. + $this->assertTrue( $class->exclude_from_hummingbird_asset_optimization( true, 'jquery-core', '', 'scripts' ) ); + } + + /** + * Hummingbird's Delay JavaScript should leave our scripts alone. + * + * @see Compatibility::exclude_from_hummingbird_delay_js() + * @return void + */ + public function testExcludeFromHummingbirdDelayJs() { + $this->assertEquals( [ 'existing', 'plausible' ], ( new Compatibility() )->exclude_from_hummingbird_delay_js( [ 'existing' ] ) ); + } }