Skip to content

Commit a79389a

Browse files
committed
Be aware of routes using parameters
1 parent 24fd672 commit a79389a

6 files changed

+124
-43
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ This is the contents of the published config file:
3030
```php
3131
return [
3232
'prerender' => [
33-
//
33+
[
34+
//
35+
],
3436
],
3537
'prefetch' => [
36-
//
38+
[
39+
//
40+
],
3741
],
3842
];
3943
```
@@ -80,6 +84,14 @@ Route::get('/page-1', function () {
8084
})->prefetch('eager');
8185
```
8286

87+
### Prerender/Prefetch
88+
89+
If you prerender an url, all resources will be fetched and the DOM will be rendered in the background. This will avoid most of the layout shifts you had before. If you prefetch a page, only the resources will be fetched. This can lead to a much faster page load.
90+
91+
For more information refer to the following pages:
92+
- [Prerender](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#using_prerendering)
93+
- [Prefetch](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#unsafe_prefetching)
94+
8395
### Eagerness Levels (available as of Chrome 122)
8496

8597
- `eager` Immediately prerender/prefetch the URL.

src/LaravelSpeculationRulesApi.php

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,44 @@ class LaravelSpeculationRulesApi
99
private static function prerenderRules(): array
1010
{
1111
return collect(self::$routeSpeculationRules['prerender'] ?? [])
12-
->map(fn (array $urls, string $eagerness) => [
13-
'source' => 'list',
14-
'urls' => $urls,
15-
'eagerness' => $eagerness,
12+
->map(fn (array $item) => [
13+
'where' => ['and' => self::createRule($item['uri'])],
14+
'eagerness' => $item['eagerness'],
1615
])
1716
->values()
18-
->merge(config('speculation-rules-api.prerender', []))
17+
->merge(array_filter(config('speculation-rules-api.prerender', [])))
1918
->toArray();
2019
}
2120

2221
private static function prefetchRules(): array
2322
{
2423
return collect(self::$routeSpeculationRules['prefetch'] ?? [])
25-
->map(fn (array $urls, string $eagerness) => [
26-
'urls' => $urls,
27-
'eagerness' => $eagerness,
24+
->map(fn (array $item) => [
25+
'where' => ['and' => self::createRule($item['uri'])],
26+
'eagerness' => $item['eagerness'],
2827
'referrer_policy' => 'no-referrer',
2928
])
3029
->values()
31-
->merge(config('speculation-rules-api.prefetch', []))
30+
->merge(array_filter(config('speculation-rules-api.prefetch', [])))
3231
->toArray();
3332
}
3433

34+
public static function createRule(string $uri): array
35+
{
36+
preg_match_all('/{[^}]+}/', $uri, $matches);
37+
38+
if (count($matches[0]) === 0) {
39+
return [['href_matches' => $uri]];
40+
}
41+
42+
$hrefMatches = trim(preg_replace('/{[^}]+}/', '*', $uri), '/');
43+
44+
return [
45+
['href_matches' => "/$hrefMatches"],
46+
['not' => ['href_matches' => "/$hrefMatches/*"]],
47+
];
48+
}
49+
3550
public static function speculationRules(): array
3651
{
3752
return array_filter([

src/LaravelSpeculationRulesApiServiceProvider.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,19 @@ public function configurePackage(Package $package): void
1919
public function bootingPackage(): void
2020
{
2121
Route::macro('prerender', function (string $eagerness = 'moderate') {
22-
LaravelSpeculationRulesApi::$routeSpeculationRules['prerender'][$eagerness][] = $this->uri;
22+
LaravelSpeculationRulesApi::$routeSpeculationRules['prerender'][] = [
23+
'eagerness' => $eagerness,
24+
'uri' => $this->uri,
25+
];
2326

2427
return $this;
2528
});
2629

2730
Route::macro('prefetch', function (string $eagerness = 'moderate') {
28-
LaravelSpeculationRulesApi::$routeSpeculationRules['prefetch'][$eagerness][] = $this->uri;
31+
LaravelSpeculationRulesApi::$routeSpeculationRules['prefetch'][] = [
32+
'eagerness' => $eagerness,
33+
'uri' => $this->uri,
34+
];
2935

3036
return $this;
3137
});
Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
{
2-
"prerender": {
3-
"moderate": [
4-
"page-1"
5-
],
6-
"eager": [
7-
"page-2"
8-
]
9-
},
10-
"prefetch": {
11-
"moderate": [
12-
"page-3"
13-
],
14-
"eager": [
15-
"page-4"
16-
]
17-
}
2+
"prerender": [
3+
{
4+
"eagerness": "moderate",
5+
"uri": "page-1"
6+
},
7+
{
8+
"eagerness": "eager",
9+
"uri": "page-2"
10+
}
11+
],
12+
"prefetch": [
13+
{
14+
"eagerness": "moderate",
15+
"uri": "page-3"
16+
},
17+
{
18+
"eagerness": "eager",
19+
"uri": "page-4"
20+
}
21+
]
1822
}

tests/.pest/snapshots/SpeculationRulesApiTest/speculation_rules_are_merged_properly.snap

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,57 @@
11
{
22
"prerender": [
33
{
4-
"source": "list",
5-
"urls": [
6-
"page-1"
7-
],
4+
"where": {
5+
"and": [
6+
{
7+
"href_matches": "\/page-1\/*"
8+
},
9+
{
10+
"not": {
11+
"href_matches": "\/page-1\/*\/*"
12+
}
13+
}
14+
]
15+
},
816
"eagerness": "moderate"
917
},
1018
{
11-
"source": "list",
12-
"urls": [
13-
"page-2"
14-
],
19+
"where": {
20+
"and": [
21+
{
22+
"href_matches": "\/page-2\/*\/*"
23+
},
24+
{
25+
"not": {
26+
"href_matches": "\/page-2\/*\/*\/*"
27+
}
28+
}
29+
]
30+
},
31+
"eagerness": "eager"
32+
},
33+
{
34+
"where": {
35+
"and": [
36+
{
37+
"href_matches": "page-3"
38+
}
39+
]
40+
},
1541
"eagerness": "eager"
1642
},
1743
{
1844
"source": "list",
1945
"urls": [
20-
"page-3"
46+
"page-4"
2147
],
2248
"eagerness": "conservative"
2349
}
2450
],
2551
"prefetch": [
2652
{
2753
"urls": [
28-
"page-4"
54+
"page-5"
2955
],
3056
"referrer_policy": "no-referrer",
3157
"eagerness": "moderate"

tests/SpeculationRulesApiTest.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,21 @@
3434
'prefetch' => [],
3535
];
3636

37-
Route::get('page-1', fn () => null)->prerender();
38-
Route::get('page-2', fn () => null)->prerender('eager');
37+
Route::get('page-1/{param1}', fn () => null)->prerender();
38+
Route::get('page-2/{param1}/{param2}', fn () => null)->prerender('eager');
39+
Route::get('page-3', fn () => null)->prerender('eager');
3940

4041
config()->set('speculation-rules-api', [
4142
'prerender' => [
4243
[
4344
'source' => 'list',
44-
'urls' => ['page-3'],
45+
'urls' => ['page-4'],
4546
'eagerness' => 'conservative',
4647
],
4748
],
4849
'prefetch' => [
4950
[
50-
'urls' => ['page-4'],
51+
'urls' => ['page-5'],
5152
'referrer_policy' => 'no-referrer',
5253
'eagerness' => 'moderate',
5354
],
@@ -56,3 +57,20 @@
5657

5758
expect(LaravelSpeculationRulesApi::speculationRules())->toMatchSnapshot();
5859
});
60+
61+
test('rule creation', function () {
62+
expect(LaravelSpeculationRulesApi::createRule('page'))
63+
->toBe([
64+
['href_matches' => 'page'],
65+
])
66+
->and(LaravelSpeculationRulesApi::createRule('page/{param}'))
67+
->toBe([
68+
['href_matches' => '/page/*'],
69+
['not' => ['href_matches' => '/page/*/*']],
70+
])
71+
->and(LaravelSpeculationRulesApi::createRule('page/{param1}/{param2}/{param3}'))
72+
->toBe([
73+
['href_matches' => '/page/*/*/*'],
74+
['not' => ['href_matches' => '/page/*/*/*/*']],
75+
]);
76+
});

0 commit comments

Comments
 (0)