Skip to content

Commit

Permalink
Be aware of routes using parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
authanram committed Aug 9, 2024
1 parent 24fd672 commit a79389a
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 43 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ This is the contents of the published config file:
```php
return [
'prerender' => [
//
[
//
],
],
'prefetch' => [
//
[
//
],
],
];
```
Expand Down Expand Up @@ -80,6 +84,14 @@ Route::get('/page-1', function () {
})->prefetch('eager');
```

### Prerender/Prefetch

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.

For more information refer to the following pages:
- [Prerender](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#using_prerendering)
- [Prefetch](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API#unsafe_prefetching)

### Eagerness Levels (available as of Chrome 122)

- `eager` Immediately prerender/prefetch the URL.
Expand Down
33 changes: 24 additions & 9 deletions src/LaravelSpeculationRulesApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,44 @@ class LaravelSpeculationRulesApi
private static function prerenderRules(): array
{
return collect(self::$routeSpeculationRules['prerender'] ?? [])
->map(fn (array $urls, string $eagerness) => [
'source' => 'list',
'urls' => $urls,
'eagerness' => $eagerness,
->map(fn (array $item) => [
'where' => ['and' => self::createRule($item['uri'])],
'eagerness' => $item['eagerness'],
])
->values()
->merge(config('speculation-rules-api.prerender', []))
->merge(array_filter(config('speculation-rules-api.prerender', [])))
->toArray();
}

private static function prefetchRules(): array
{
return collect(self::$routeSpeculationRules['prefetch'] ?? [])
->map(fn (array $urls, string $eagerness) => [
'urls' => $urls,
'eagerness' => $eagerness,
->map(fn (array $item) => [
'where' => ['and' => self::createRule($item['uri'])],
'eagerness' => $item['eagerness'],
'referrer_policy' => 'no-referrer',
])
->values()
->merge(config('speculation-rules-api.prefetch', []))
->merge(array_filter(config('speculation-rules-api.prefetch', [])))
->toArray();
}

public static function createRule(string $uri): array
{
preg_match_all('/{[^}]+}/', $uri, $matches);

if (count($matches[0]) === 0) {
return [['href_matches' => $uri]];
}

$hrefMatches = trim(preg_replace('/{[^}]+}/', '*', $uri), '/');

return [
['href_matches' => "/$hrefMatches"],
['not' => ['href_matches' => "/$hrefMatches/*"]],
];
}

public static function speculationRules(): array
{
return array_filter([
Expand Down
10 changes: 8 additions & 2 deletions src/LaravelSpeculationRulesApiServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,19 @@ public function configurePackage(Package $package): void
public function bootingPackage(): void
{
Route::macro('prerender', function (string $eagerness = 'moderate') {
LaravelSpeculationRulesApi::$routeSpeculationRules['prerender'][$eagerness][] = $this->uri;
LaravelSpeculationRulesApi::$routeSpeculationRules['prerender'][] = [
'eagerness' => $eagerness,
'uri' => $this->uri,
];

return $this;
});

Route::macro('prefetch', function (string $eagerness = 'moderate') {
LaravelSpeculationRulesApi::$routeSpeculationRules['prefetch'][$eagerness][] = $this->uri;
LaravelSpeculationRulesApi::$routeSpeculationRules['prefetch'][] = [
'eagerness' => $eagerness,
'uri' => $this->uri,
];

return $this;
});
Expand Down
36 changes: 20 additions & 16 deletions tests/.pest/snapshots/SpeculationRulesApiTest/route_macros.snap
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
{
"prerender": {
"moderate": [
"page-1"
],
"eager": [
"page-2"
]
},
"prefetch": {
"moderate": [
"page-3"
],
"eager": [
"page-4"
]
}
"prerender": [
{
"eagerness": "moderate",
"uri": "page-1"
},
{
"eagerness": "eager",
"uri": "page-2"
}
],
"prefetch": [
{
"eagerness": "moderate",
"uri": "page-3"
},
{
"eagerness": "eager",
"uri": "page-4"
}
]
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,57 @@
{
"prerender": [
{
"source": "list",
"urls": [
"page-1"
],
"where": {
"and": [
{
"href_matches": "\/page-1\/*"
},
{
"not": {
"href_matches": "\/page-1\/*\/*"
}
}
]
},
"eagerness": "moderate"
},
{
"source": "list",
"urls": [
"page-2"
],
"where": {
"and": [
{
"href_matches": "\/page-2\/*\/*"
},
{
"not": {
"href_matches": "\/page-2\/*\/*\/*"
}
}
]
},
"eagerness": "eager"
},
{
"where": {
"and": [
{
"href_matches": "page-3"
}
]
},
"eagerness": "eager"
},
{
"source": "list",
"urls": [
"page-3"
"page-4"
],
"eagerness": "conservative"
}
],
"prefetch": [
{
"urls": [
"page-4"
"page-5"
],
"referrer_policy": "no-referrer",
"eagerness": "moderate"
Expand Down
26 changes: 22 additions & 4 deletions tests/SpeculationRulesApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,21 @@
'prefetch' => [],
];

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

config()->set('speculation-rules-api', [
'prerender' => [
[
'source' => 'list',
'urls' => ['page-3'],
'urls' => ['page-4'],
'eagerness' => 'conservative',
],
],
'prefetch' => [
[
'urls' => ['page-4'],
'urls' => ['page-5'],
'referrer_policy' => 'no-referrer',
'eagerness' => 'moderate',
],
Expand All @@ -56,3 +57,20 @@

expect(LaravelSpeculationRulesApi::speculationRules())->toMatchSnapshot();
});

test('rule creation', function () {
expect(LaravelSpeculationRulesApi::createRule('page'))
->toBe([
['href_matches' => 'page'],
])
->and(LaravelSpeculationRulesApi::createRule('page/{param}'))
->toBe([
['href_matches' => '/page/*'],
['not' => ['href_matches' => '/page/*/*']],
])
->and(LaravelSpeculationRulesApi::createRule('page/{param1}/{param2}/{param3}'))
->toBe([
['href_matches' => '/page/*/*/*'],
['not' => ['href_matches' => '/page/*/*/*/*']],
]);
});

0 comments on commit a79389a

Please sign in to comment.