Skip to content

Commit e335394

Browse files
committed
bug #237 Fix missing integrity hash on preload tags (arnaud-ritti, Kocal)
This PR was merged into the 2.x branch. Discussion ---------- Fix missing integrity hash on preload tags I wasn't able to edit #161, so I've opened a new PR and made changes here. Fix #101, fix #225 On an existing app, integrity hashes and other attributes are nicely injected into `Link` header: <img width="1101" alt="image" src="https://github.com/user-attachments/assets/3733d80f-1927-49d9-8f0e-c8e340f7ed69"> Commits ------- b8219a8 Pass all attributes to `Link` header 707cd49 style: php-cs-fixer 2025c41 Fix missing integrity hash on preload
2 parents 16af8a3 + b8219a8 commit e335394

File tree

5 files changed

+83
-18
lines changed

5 files changed

+83
-18
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v2.2.0
4+
5+
- #236 Allow entrypoints.json to be hosted remotely (@rlvdx & @Kocal)
6+
- #232 fix: correctly wire the build-time file into kernel.build_dir (@dkarlovi)
7+
- #237 Fix missing integrity hash on preload (@arnaud-ritti & @Kocal)
8+
39
## v2.1.0
410

511
- #233 Add support for PHP 8.3 and PHP 8.4 (@Kocal)

src/Asset/TagRenderer.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ class TagRenderer implements ResetInterface
2828
private $defaultLinkAttributes;
2929
private $eventDispatcher;
3030

31+
// TODO WebpackEncoreBundle 3.0: remove this property
3132
private $renderedFiles = [];
33+
// TODO WebpackEncoreBundle 3.0: rename this property to $renderedFiles
34+
private $renderedFilesWithAttributes = [];
3235

3336
public function __construct(
3437
EntrypointLookupCollectionInterface $entrypointLookupCollection,
@@ -48,7 +51,7 @@ public function __construct(
4851
$this->reset();
4952
}
5053

51-
public function renderWebpackScriptTags(string $entryName, ?string $packageName = null, ?string $entrypointName = null, array $extraAttributes = []): string
54+
public function renderWebpackScriptTags(string $entryName, ?string $packageName = null, ?string $entrypointName = null, array $extraAttributes = [], bool $includeAttributes = false): string
5255
{
5356
$entrypointName = $entrypointName ?: '_default';
5457
$scriptTags = [];
@@ -80,6 +83,7 @@ public function renderWebpackScriptTags(string $entryName, ?string $packageName
8083
);
8184

8285
$this->renderedFiles['scripts'][] = $attributes['src'];
86+
$this->renderedFilesWithAttributes['scripts'][] = $attributes;
8387
}
8488

8589
return implode('', $scriptTags);
@@ -118,19 +122,36 @@ public function renderWebpackLinkTags(string $entryName, ?string $packageName =
118122
);
119123

120124
$this->renderedFiles['styles'][] = $attributes['href'];
125+
$this->renderedFilesWithAttributes['styles'][] = $attributes;
121126
}
122127

123128
return implode('', $scriptTags);
124129
}
125130

126-
public function getRenderedScripts(): array
131+
/**
132+
* @param bool $includeAttributes Whether to include the attributes or not.
133+
* In WebpackEncoreBundle 3.0, this parameter will be removed,
134+
* and the attributes will always be included.
135+
* TODO WebpackEncoreBundle 3.0
136+
*
137+
* @return ($includeAttributes is true ? list<array<string, mixed>> : list<string>)
138+
*/
139+
public function getRenderedScripts(bool $includeAttributes = false): array
127140
{
128-
return $this->renderedFiles['scripts'];
141+
return $includeAttributes ? $this->renderedFilesWithAttributes['scripts'] : $this->renderedFiles['scripts'];
129142
}
130143

131-
public function getRenderedStyles(): array
144+
/**
145+
* @param bool $includeAttributes Whether to include the attributes or not.
146+
* In WebpackEncoreBundle 3.0, this parameter will be removed,
147+
* and the attributes will always be included.
148+
* TODO WebpackEncoreBundle 3.0
149+
*
150+
* @return ($includeAttributes is true ? list<array<string, mixed>> : list<string>)
151+
*/
152+
public function getRenderedStyles(bool $includeAttributes = false): array
132153
{
133-
return $this->renderedFiles['styles'];
154+
return $includeAttributes ? $this->renderedFilesWithAttributes['styles'] : $this->renderedFiles['styles'];
134155
}
135156

136157
public function getDefaultAttributes(): array
@@ -140,7 +161,7 @@ public function getDefaultAttributes(): array
140161

141162
public function reset(): void
142163
{
143-
$this->renderedFiles = [
164+
$this->renderedFiles = $this->renderedFilesWithAttributes = [
144165
'scripts' => [],
145166
'styles' => [],
146167
];

src/EventListener/PreLoadAssetsEventListener.php

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,31 @@ public function onKernelResponse(ResponseEvent $event): void
4747
/** @var GenericLinkProvider $linkProvider */
4848
$linkProvider = $request->attributes->get('_links');
4949
$defaultAttributes = $this->tagRenderer->getDefaultAttributes();
50-
$crossOrigin = $defaultAttributes['crossorigin'] ?? false;
5150

52-
foreach ($this->tagRenderer->getRenderedScripts() as $href) {
53-
$link = $this->createLink('preload', $href)->withAttribute('as', 'script');
51+
foreach ($this->tagRenderer->getRenderedScripts(true) as $attributes) {
52+
$src = $attributes['src'];
53+
unset($attributes['src']);
54+
$attributes = [...$defaultAttributes, ...$attributes];
5455

55-
if (false !== $crossOrigin) {
56-
$link = $link->withAttribute('crossorigin', $crossOrigin);
56+
$link = $this->createLink('preload', $src)
57+
->withAttribute('as', 'script');
58+
59+
foreach ($attributes as $k => $v) {
60+
$link = $link->withAttribute($k, $v);
5761
}
5862

5963
$linkProvider = $linkProvider->withLink($link);
6064
}
6165

62-
foreach ($this->tagRenderer->getRenderedStyles() as $href) {
66+
foreach ($this->tagRenderer->getRenderedStyles(true) as $attributes) {
67+
$href = $attributes['href'];
68+
unset($attributes['href']);
69+
$attributes = [...$defaultAttributes, ...$attributes];
70+
6371
$link = $this->createLink('preload', $href)->withAttribute('as', 'style');
6472

65-
if (false !== $crossOrigin) {
66-
$link = $link->withAttribute('crossorigin', $crossOrigin);
73+
foreach ($attributes as $k => $v) {
74+
$link = $link->withAttribute($k, $v);
6775
}
6876

6977
$linkProvider = $linkProvider->withLink($link);

tests/Asset/TagRendererTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,25 @@ public function testGetRenderedFilesAndReset()
301301
$this->assertSame(['http://localhost:8080/build/file1.js', 'http://localhost:8080/build/file2.js'], $renderer->getRenderedScripts());
302302
$this->assertSame(['http://localhost:8080/build/file1.css'], $renderer->getRenderedStyles());
303303

304+
$this->assertSame([
305+
[
306+
'src' => 'http://localhost:8080/build/file1.js',
307+
],
308+
[
309+
'src' => 'http://localhost:8080/build/file2.js',
310+
],
311+
], $renderer->getRenderedScripts(true));
312+
$this->assertSame([
313+
[
314+
'rel' => 'stylesheet',
315+
'href' => 'http://localhost:8080/build/file1.css',
316+
],
317+
], $renderer->getRenderedStyles(true));
318+
304319
$renderer->reset();
305320
$this->assertEmpty($renderer->getRenderedScripts());
306321
$this->assertEmpty($renderer->getRenderedStyles());
322+
$this->assertEmpty($renderer->getRenderedScripts(true));
323+
$this->assertEmpty($renderer->getRenderedStyles(true));
307324
}
308325
}

tests/EventListener/PreLoadAssetsEventListenerTest.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,17 @@ public function testItPreloadsAssets()
3030
{
3131
$tagRenderer = $this->createMock(TagRenderer::class);
3232
$tagRenderer->expects($this->once())->method('getDefaultAttributes')->willReturn(['crossorigin' => 'anonymous']);
33-
$tagRenderer->expects($this->once())->method('getRenderedScripts')->willReturn(['/file1.js']);
34-
$tagRenderer->expects($this->once())->method('getRenderedStyles')->willReturn(['/css/file1.css']);
33+
$tagRenderer->expects($this->once())->method('getRenderedScripts')->with(true)->willReturn([
34+
[
35+
'src' => '/file1.js',
36+
],
37+
]);
38+
$tagRenderer->expects($this->once())->method('getRenderedStyles')->with(true)->willReturn([
39+
[
40+
'rel' => 'stylesheet',
41+
'href' => '/css/file1.css',
42+
],
43+
]);
3544

3645
$request = new Request();
3746
$response = new Response();
@@ -53,14 +62,18 @@ public function testItPreloadsAssets()
5362

5463
$this->assertSame('/css/file1.css', $links[1]->getHref());
5564
$this->assertSame(['preload'], $links[1]->getRels());
56-
$this->assertSame(['as' => 'style', 'crossorigin' => 'anonymous'], $links[1]->getAttributes());
65+
$this->assertSame(['as' => 'style', 'crossorigin' => 'anonymous', 'rel' => 'stylesheet'], $links[1]->getAttributes());
5766
}
5867

5968
public function testItReusesExistingLinkProvider()
6069
{
6170
$tagRenderer = $this->createMock(TagRenderer::class);
6271
$tagRenderer->expects($this->once())->method('getDefaultAttributes')->willReturn(['crossorigin' => 'anonymous']);
63-
$tagRenderer->expects($this->once())->method('getRenderedScripts')->willReturn(['/file1.js']);
72+
$tagRenderer->expects($this->once())->method('getRenderedScripts')->willReturn([
73+
[
74+
'src' => '/file1.js',
75+
],
76+
]);
6477
$tagRenderer->expects($this->once())->method('getRenderedStyles')->willReturn([]);
6578

6679
$request = new Request();

0 commit comments

Comments
 (0)