Skip to content

Commit 049852c

Browse files
committed
Release v3.1.0 - Critical Bug Fixes & Compatibility Improvements
🐛 Critical Bug Fixes: - #164: Debug tools compatibility (Debugbar, Horizon, Telescope, Ignition, Clockwork) - #165: Livewire & Filament compatibility (wire:* directives) - #166: Scripts and Livewire not running (related to #165) - #167: URLs in JavaScript preserved (http://, https://) - #170: Code blocks preservation (<pre>, <code>, <textarea>) ✅ Enhancements: - ServiceProvider configuration key fixed - Custom debug tool routes support documented - Livewire/Alpine.js spacing preservation - 117 tests, 501 assertions, 100% passing 📚 Documentation: - Complete README updates with compatibility badges - RELEASE_v3.1.0.md with comprehensive changelog - Config file with detailed comments for custom routes
1 parent 4fedf5a commit 049852c

10 files changed

+2131
-4
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ This middleware invoke **RemoveComments::class** filter before executation.
6565

6666
> **Note**: Do not register the "RemoveComments::class" filter with it. Because it will be called automatically by "CollapseWhitespace::class"
6767
68+
> **Important**: Whitespace is automatically **preserved** inside `<pre>`, `<code>`, and `<textarea>` tags to maintain proper formatting for code blocks and user input fields. This makes it safe to use on blogs, documentation sites, and technical content. (Issue [#170](https://github.com/vinkius-labs/laravel-page-speed/issues/170))
69+
70+
> **✅ Livewire & Filament Compatible**: Fully compatible with [Laravel Livewire](https://laravel-livewire.com/) and [Filament](https://filamentphp.com/). The middleware preserves the necessary spacing between HTML tags to ensure `wire:*` directives and Alpine.js `x-*` attributes work correctly. (Issue [#165](https://github.com/vinkius-labs/laravel-page-speed/issues/165))
71+
6872
#### Before
6973

7074
![Before of Laravel Page Speed][link-before]
@@ -130,6 +134,14 @@ You would probably like to configure the package to skip some routes.
130134

131135
//You can use * as wildcard.
132136
'skip' => [
137+
// Development/Debug Tools (automatically skipped by default)
138+
'_debugbar/*', // Laravel Debugbar
139+
'horizon/*', // Laravel Horizon
140+
'_ignition/*', // Laravel Ignition (error pages)
141+
'telescope/*', // Laravel Telescope
142+
'clockwork/*', // Clockwork
143+
144+
// Binary/Document Files
133145
'*.pdf', //Ignore all routes with final .pdf
134146
'*/downloads/*',//Ignore all routes that contain 'downloads'
135147
'assets/*', // Ignore all routes with the 'assets' prefix
@@ -140,6 +152,19 @@ By default this field comes configured with some options, so feel free to config
140152

141153
> *Notice*: This package skip automatically 'binary' and 'streamed' responses. See [File Downloads][link-file-download].
142154
155+
> **✅ Development Tools Compatible**: Development and debugging tools like **Laravel Debugbar**, **Horizon**, **Telescope**, **Ignition**, and **Clockwork** are automatically skipped by default, ensuring they work correctly without interference from HTML minification. (Issue [#164](https://github.com/vinkius-labs/laravel-page-speed/issues/164))
156+
157+
> **⚠️ Important for Custom Routes**: If you've customized the routes for debug tools in your application (e.g., Horizon at `/admin/horizon` instead of `/horizon`), you MUST update the skip patterns in your config file to match your custom routes. For example:
158+
> ```php
159+
> 'skip' => [
160+
> 'admin/horizon/*', // Custom Horizon route
161+
> 'debug/telescope/*', // Custom Telescope route
162+
> '_debugbar/*', // Debugbar (usually fixed)
163+
> // ... other routes
164+
> ],
165+
> ```
166+
> See your `HorizonServiceProvider`, `TelescopeServiceProvider`, or other debug tool configurations for custom path settings.
167+
143168
## Testing
144169
145170
```sh

config/laravel-page-speed.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,26 @@
2121
| Skip Routes paths to exclude.
2222
| You can use * as wildcard.
2323
|
24+
| Development/Debug Tools (Issue #164):
25+
| If you've customized the routes for debug tools in your application
26+
| (e.g., Horizon at '/admin/horizon' or Telescope at '/debug/telescope'),
27+
| you MUST update these patterns to match your custom routes.
28+
|
29+
| Examples of custom routes:
30+
| 'admin/horizon/*' // Horizon::routes(['domain' => null, 'path' => 'admin/horizon'])
31+
| 'debug/telescope/*' // Telescope::$path = 'debug/telescope'
32+
| 'my-path/clockwork/*' // Custom Clockwork path
33+
|
2434
*/
2535
'skip' => [
36+
// Development/Debug Tools (Issue #164)
37+
'_debugbar/*', // Laravel Debugbar (usually fixed path)
38+
'horizon/*', // Laravel Horizon (default, change if customized)
39+
'_ignition/*', // Laravel Ignition - Error Pages (usually fixed path)
40+
'clockwork/*', // Clockwork (default, change if customized)
41+
'telescope/*', // Laravel Telescope (default, change if customized)
42+
43+
// Binary/Document Files
2644
'*.xml',
2745
'*.less',
2846
'*.pdf',

src/Middleware/CollapseWhitespace.php

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,94 @@
44

55
class CollapseWhitespace extends PageSpeed
66
{
7+
/**
8+
* Tags where whitespace should be preserved
9+
*
10+
* Note: <script> and <style> are NOT included because:
11+
* - JavaScript and CSS minification is handled by their specific optimizers
12+
* - Collapsing whitespace in JS/CSS is generally safe and desired
13+
* - This middleware focuses on preserving user-visible formatted content
14+
*/
15+
protected const PRESERVE_TAGS = [
16+
'pre',
17+
'code',
18+
'textarea',
19+
];
20+
21+
/**
22+
* Apply whitespace collapse to buffer while preserving content in specific tags
23+
*/
724
public function apply($buffer)
825
{
26+
// First remove comments
27+
$buffer = $this->removeComments($buffer);
28+
29+
// Extract and preserve content from whitespace-sensitive tags
30+
$preserved = [];
31+
$buffer = $this->extractPreservedContent($buffer, $preserved);
32+
33+
// Apply whitespace collapse to the remaining content
934
$replace = [
1035
"/\n([\S])/" => '$1',
1136
"/\r/" => '',
1237
"/\n/" => '',
1338
"/\t/" => '',
1439
"/ +/" => ' ',
15-
"/> +</" => '><',
40+
// Keep one space between tags for Livewire/Alpine.js compatibility (Issue #165)
41+
// This prevents breaking wire:* directives and x-* attributes
42+
"/> +</" => '> <',
1643
];
1744

18-
return $this->replace($replace, $this->removeComments($buffer));
45+
$buffer = $this->replace($replace, $buffer);
46+
47+
// Restore preserved content
48+
$buffer = $this->restorePreservedContent($buffer, $preserved);
49+
50+
return $buffer;
51+
}
52+
53+
/**
54+
* Extract content from tags that should preserve whitespace
55+
*/
56+
protected function extractPreservedContent(string $buffer, array &$preserved): string
57+
{
58+
$index = 0;
59+
60+
foreach (self::PRESERVE_TAGS as $tag) {
61+
// Match opening and closing tags with all content in between
62+
// This regex handles:
63+
// - Tags with or without attributes
64+
// - Self-closing tags (though not common for these tags)
65+
// - Nested content
66+
// - Case-insensitive matching
67+
$pattern = '/<(' . $tag . ')(\s[^>]*)?>(.*?)<\/\1>/is';
68+
69+
$buffer = preg_replace_callback($pattern, function ($matches) use (&$preserved, &$index) {
70+
$placeholder = "___PRESERVED_CONTENT_{$index}___";
71+
$preserved[$placeholder] = $matches[0]; // Store the entire tag with content
72+
$index++;
73+
return $placeholder;
74+
}, $buffer);
75+
}
76+
77+
return $buffer;
78+
}
79+
80+
/**
81+
* Restore preserved content back into the buffer
82+
*/
83+
protected function restorePreservedContent(string $buffer, array $preserved): string
84+
{
85+
foreach ($preserved as $placeholder => $content) {
86+
$buffer = str_replace($placeholder, $content, $buffer);
87+
}
88+
89+
return $buffer;
1990
}
2091

92+
/**
93+
* Remove comments before collapsing whitespace
94+
*/
2195
protected function removeComments($buffer)
2296
{
2397
return (new RemoveComments)->apply($buffer);

src/ServiceProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ public function boot(): void
2121
*/
2222
public function register(): void
2323
{
24-
$this->mergeConfigFrom(__DIR__ . '/../config/laravel-page-speed.php', 'laravel-page-speed.php');
24+
$this->mergeConfigFrom(__DIR__ . '/../config/laravel-page-speed.php', 'laravel-page-speed');
2525
}
2626
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Issue #170 - JavaScript Comment Problem</title>
5+
</head>
6+
<body>
7+
<h1>Testing JavaScript Comments After Statements</h1>
8+
9+
<script>
10+
// Test Case 1: Comment after console.log without semicolon
11+
console.log('foo'); // undefined
12+
var foo = 'bar';
13+
console.log(foo); // bar
14+
15+
// Test Case 2: Multiple statements with comments
16+
var x = 10; // initialize x
17+
var y = 20; // initialize y
18+
var z = x + y; // sum
19+
console.log(z); // should print 30
20+
21+
// Test Case 3: Comment after statement without semicolon
22+
var name = 'John' // no semicolon here
23+
console.log(name) // should print John
24+
25+
// Test Case 4: Comment after function call without semicolon
26+
alert('test') // show alert
27+
console.log('after alert') // this should work
28+
29+
// Test Case 5: If statement with comment
30+
if (true) { // condition
31+
console.log('inside if'); // log
32+
} // end if
33+
34+
// Test Case 6: Comment with URLs (should preserve http://)
35+
var url = "http://example.com"; // website URL
36+
var secure = "https://example.com"; // secure URL
37+
38+
// Test Case 7: Return statement
39+
function test() {
40+
return 42; // answer
41+
}
42+
43+
// Test Case 8: Array with comment
44+
var arr = [1, 2, 3]; // numbers
45+
var first = arr[0]; // get first
46+
</script>
47+
48+
<p>Check console for results</p>
49+
</body>
50+
</html>

0 commit comments

Comments
 (0)