8000 [AssetMapper] Automatically preload CSS files if WebLink available · symfony/symfony@2995c16 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2995c16

Browse files
weaverryanfabpot
authored andcommitted
[AssetMapper] Automatically preload CSS files if WebLink available
1 parent fc12885 commit 2995c16

File tree

5 files changed

+75
-1
lines changed

5 files changed

+75
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php

+1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
param('kernel.charset'),
175175
abstract_arg('polyfill URL'),
176176
abstract_arg('script HTML attributes'),
177+
service('request_stack'),
177178
])
178179

179180
->set('asset_mapper.importmap.auditor', ImportMapAuditor::class)

src/Symfony/Component/AssetMapper/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Add "entrypoints" concept to the importmap
1010
* Always download packages locally instead of using a CDN
1111
* Allow relative path strings in the importmap
12+
* Automatically set `_links` attribute for preload CSS files for WebLink integration
1213
* Add `PreAssetsCompileEvent` event when running `asset-map:compile`
1314
* Add support for importmap paths to use the Asset component (for subdirectories)
1415
* Removed the `importmap:export` command

src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php

+32
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111

1212
namespace Symfony\Component\AssetMapper\ImportMap;
1313

14+
use Psr\Link\EvolvableLinkProviderInterface;
1415
use Symfony\Component\Asset\Packages;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpFoundation\RequestStack;
18+
use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener;
19+
use Symfony\Component\WebLink\GenericLinkProvider;
20+
use Symfony\Component\WebLink\Link;
1521

1622
/**
1723
* @author Kévin Dunglas <kevin@dunglas.dev>
@@ -27,6 +33,7 @@ public function __construct(
2733
private readonly string $charset = 'UTF-8',
2834
private readonly string|false $polyfillUrl = ImportMapManager::POLYFILL_URL,
2935
private readonly array $scriptAttributes = [],
36+
private readonly ?RequestStack $requestStack = null,
3037
) {
3138
}
3239

@@ -68,6 +75,10 @@ public function render(string|array $entryPoint, array $attributes = []): string
6875
$output .= "\n<link rel=\"stylesheet\" href=\"$url\">";
6976
}
7077

78+
if (class_exists(AddLinkHeaderListener::class) && $request = $this->requestStack?->getCurrentRequest()) {
79+
$this->addWebLinkPreloads($request, $cssLinks);
80+
}
81+
7182
$scriptAttributes = $this->createAttributesString($attributes);
7283
$importMapJson = json_encode(['imports' => $importMap], \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_HEX_TAG);
7384
$output .= <<<HTML
@@ -131,4 +142,25 @@ private function createAttributesString(array $attributes): string
131142

132143
return $attributeString;
133144
}
145+
146+
private function addWebLinkPreloads(Request $request, array $cssLinks): void
147+
{
148+
$cssPreloadLinks = array_map(fn ($url) => new Link('preload', $url), $cssLinks);
149+
150+
if (null === $linkProvider = $request->attributes->get('_links')) {
151+
$request->attributes->set('_links', new GenericLinkProvider($cssPreloadLinks));
152+
153+
return;
154+
}
155+
156+
if (!$linkProvider instanceof EvolvableLinkProviderInterface) {
157+
return;
158+
}
159+
160+
foreach ($cssPreloadLinks as $link) {
161+
$linkProvider = $linkProvider->withLink($link);
162+
}
163+
164+
$request->attributes->set('_links', $linkProvider);
165+
}
134166
}

src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapRendererTest.php

+39
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use Symfony\Component\Asset\Packages;
1616
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
1717
use Symfony\Component\AssetMapper\ImportMap\ImportMapRenderer;
18+
use Symfony\Component\HttpFoundation\Request;
19+
use Symfony\Component\HttpFoundation\RequestStack;
20+
use Symfony\Component\WebLink\GenericLinkProvider;
1821

1922
class ImportMapRendererTest extends TestCase
2023
{
@@ -130,4 +133,40 @@ private function createBasicImportMapManager(): ImportMapManager
130133

131134
return $importMapManager;
132135
}
136+
137+
public function testItAddsPreloadLinks()
138+
{
139+
$importMapManager = $this->createMock(ImportMapManager::class);
140+
$importMapManager->expects($this->once())
141+
->method('getImportMapData')
142+
->willReturn([
143+
'app_js_preload' => [
144+
'path' => '/assets/app-preload-d1g35t.js',
145+
'type' => 'js',
146+
'preload' => true,
147+
],
148+
'app_css_preload' => [
149+
'path' => '/assets/styles/app-preload-d1g35t.css',
150+
'type' => 'css',
151+
'preload' => true,
152+
],
153+
'app_css_no_preload' => [
154+
'path' => '/assets/styles/app-nopreload-d1g35t.css',
155+
'type' => 'css',
156+
],
157+
]);
158+
159+
$request = Request::create('/foo');
160+
$requestStack = new RequestStack();
161+
$requestStack->push($request);
162+
163+
$renderer = new ImportMapRenderer($importMapManager, requestStack: $requestStack);
164+
$renderer->render(['app']);
165+
166+
$linkProvider = $request->attributes->get('_links');
167+
$this->assertInstanceOf(GenericLinkProvider::class, $linkProvider);
168+
$this->assertCount(1, $linkProvider->getLinks());
169+
$this->assertSame(['preload'], $linkProvider->getLinks()[0]->getRels());
170+
$this->assertSame('/assets/styles/app-preload-d1g35t.css', $linkProvider->getLinks()[0]->getHref());
171+
}
133172
}

src/Symfony/Component/AssetMapper/composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"symfony/finder": "^5.4|^6.0|^7.0",
3030
"symfony/framework-bundle": "^6.4|^7.0",
3131
"symfony/http-foundation": "^5.4|^6.0|^7.0",
32-
"symfony/http-kernel": "^5.4|^6.0|^7.0"
32+
"symfony/http-kernel": "^5.4|^6.0|^7.0",
33+
"symfony/web-link": "^5.4|^6.0|^7.0"
3334
},
3435
"conflict": {
3536
"symfony/framework-bundle": "<6.4"

0 commit comments

Comments
 (0)
0