Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7d60341

Browse filesBrowse files
committed
Suggest alternative asset names from the manifest
1 parent 92d5fde commit 7d60341
Copy full SHA for 7d60341

7 files changed

+127
-9
lines changed
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Asset\Exception;
13+
14+
/**
15+
* Represents an asset not found in a manifest.
16+
*
17+
* @author Jérôme Tamarelle <jerome@tamarelle.net>
18+
*/
19+
class AssetNotFoundException extends RuntimeException
20+
{
21+
private $alternatives;
22+
23+
/**
24+
* @param string $message Exception message to throw
25+
* @param array $alternatives List of similar defined names
26+
* @param int $code Exception code
27+
* @param \Throwable $previous Previous exception used for the exception chaining
28+
*/
29+
public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null)
30+
{
31+
parent::__construct($message, $code, $previous);
32+
33+
$this->alternatives = $alternatives;
34+
}
35+
36+
/**
37+
* @return array A list of similar defined names
38+
*/
39+
public function getAlternatives()
40+
{
41+
return $this->alternatives;
42+
}
43+
}

‎src/Symfony/Component/Asset/Tests/VersionStrategy/JsonManifestVersionStrategyTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Asset/Tests/VersionStrategy/JsonManifestVersionStrategyTest.php
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Asset\Tests\VersionStrategy;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Asset\Exception\AssetNotFoundException;
1516
use Symfony\Component\Asset\Exception\RuntimeException;
1617
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
1718

@@ -57,10 +58,10 @@ public function testStrictExceptionWhenKeyDoesNotExistInManifest()
5758
{
5859
$strategy = $this->createStrategy('manifest-valid.json', true);
5960

60-
$this->expectException(RuntimeException::class);
61-
$this->expectExceptionMessage('Asset "css/other.css" not found in manifest "');
61+
$this->expectException(AssetNotFoundException::class);
62+
$this->expectExceptionMessageRegExp('~Asset "css/styles.555def.css" not found in manifest "(.*)/manifest-valid.json". Did you mean one of these\? "css/styles.css", "css/style.css".~');
6263

63-
$strategy->getVersion('css/other.css');
64+
$strategy->getVersion('css/styles.555def.css');
6465
}
6566

6667
private function createStrategy($manifestFilename, $strict = false)

‎src/Symfony/Component/Asset/Tests/VersionStrategy/RemoteJsonManifestVersionStrategyTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Asset/Tests/VersionStrategy/RemoteJsonManifestVersionStrategyTest.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Asset\Tests\VersionStrategy;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Asset\Exception\AssetNotFoundException;
1516
use Symfony\Component\Asset\VersionStrategy\RemoteJsonManifestVersionStrategy;
1617
use Symfony\Component\HttpClient\Exception\JsonException;
1718
use Symfony\Component\HttpClient\MockHttpClient;
@@ -56,6 +57,16 @@ public function testManifestFileWithBadJSONThrowsException()
5657
$strategy->getVersion('main.js');
5758
}
5859

60+
public function testStrictExceptionWhenKeyDoesNotExistInManifest()
61+
{
62+
$strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json', true);
63+
64+
$this->expectException(AssetNotFoundException::class);
65+
$this->expectExceptionMessageRegExp('~Asset "/home.css" not found in manifest "(.*)/manifest-valid.json". Did you mean one of these\? "main/home.css".~');
66+
67+
$strategy->getVersion('/home.css');
68+
}
69+
5970
private function createStrategy($manifestUrl, $strict = false)
6071
{
6172
$httpClient = new MockHttpClient(function ($method, $url, $options) {
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
22
"main.js": "main.123abc.js",
3-
"css/styles.css": "css/styles.555def.css"
3+
"css/styles.css": "css/styles.555def.css",
4+
"css/style.css": "css/style.abcdef.css",
5+
"main/home.css": "main/home.css"
46
}

‎src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
+11-2Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Asset\VersionStrategy;
1313

14+
use Symfony\Component\Asset\Exception\AssetNotFoundException;
1415
use Symfony\Component\Asset\Exception\RuntimeException;
1516

1617
/**
@@ -26,6 +27,8 @@
2627
*/
2728
class JsonManifestVersionStrategy implements VersionStrategyInterface
2829
{
30+
use ManifestAlternativesTrait;
31+
2932
private $manifestPath;
3033
private $manifestData;
3134
private $strict;
@@ -34,7 +37,7 @@ class JsonManifestVersionStrategy implements VersionStrategyInterface
3437
* @param string $manifestPath Absolute path to the manifest file
3538
* @param bool $strict Throws an exception for unknown paths
3639
*/
37-
public function __construct(string $manifestPath, $strict = false)
40+
public function __construct(string $manifestPath, bool $strict = false)
3841
{
3942
$this->manifestPath = $manifestPath;
4043
$this->strict = $strict;
@@ -68,7 +71,13 @@ public function applyVersion(string $path)
6871
}
6972

7073
if ($this->strict) {
71-
throw new RuntimeException(sprintf('Asset "%s" not found in manifest "%s".', $path, $this->manifestPath));
74+
$message = sprintf('Asset "%s" not found in manifest "%s".', $path, $this->manifestPath);
75+
$alternatives = $this->findAlternatives($path, $this->manifestData);
76+
if (\count($alternatives) > 0) {
77+
$message .= sprintf(' Did you mean one of these? "%s".', implode('", "', $alternatives));
78+
}
79+
80+
throw new AssetNotFoundException($message, $alternatives);
7281
}
7382

7483
return $path;
+44Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Asset\VersionStrategy;
13+
14+
/**
15+
* @author Jérôme Tamarelle <jerome@tamarelle.net>
16+
*/
17+
trait ManifestAlternativesTrait
18+
{
19+
/**
20+
* Finds alternative of $path among $manifestData.
21+
*
22+
* @return string[] A sorted array of similar string
23+
*/
24+
private function findAlternatives(string $path, array $manifestData): array
25+
{
26+
$alternatives = [];
27+
28+
foreach ($manifestData as $key => $value) {
29+
$lev = levenshtein($path, $key);
30+
if ($lev <= \strlen($path) / 3 || false !== strpos($key, $path)) {
31+
$alternatives[$key] = isset($alternatives[$key]) ? min($lev, $alternatives[$key]) : $lev;
32+
}
33+
34+
$lev = levenshtein($path, $value);
35+
if ($lev <= \strlen($path) / 3 || false !== strpos($key, $path)) {
36+
$alternatives[$key] = isset($alternatives[$key]) ? min($lev, $alternatives[$key]) : $lev;
37+
}
38+
}
39+
40+
asort($alternatives);
41+
42+
return array_keys($alternatives);
43+
}
44+
}

‎src/Symfony/Component/Asset/VersionStrategy/RemoteJsonManifestVersionStrategy.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Asset/VersionStrategy/RemoteJsonManifestVersionStrategy.php
+11-3Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Symfony\Component\Asset\VersionStrategy;
1313

14-
use Symfony\Component\Asset\Exception\RuntimeException;
14+
use Symfony\Component\Asset\Exception\AssetNotFoundException;
1515
use Symfony\Contracts\HttpClient\HttpClientInterface;
1616

1717
/**
@@ -27,6 +27,8 @@
2727
*/
2828
class RemoteJsonManifestVersionStrategy implements VersionStrategyInterface
2929
{
30+
use ManifestAlternativesTrait;
31+
3032
private $manifestData;
3133
private $manifestUrl;
3234
private $httpClient;
@@ -36,7 +38,7 @@ class RemoteJsonManifestVersionStrategy implements VersionStrategyInterface
3638
* @param string $manifestUrl Absolute URL to the manifest file
3739
* @param bool $strict Throws an exception for unknown paths
3840
*/
39-
public function __construct(string $manifestUrl, HttpClientInterface $httpClient, $strict = false)
41+
public function __construct(string $manifestUrl, HttpClientInterface $httpClient, bool $strict = false)
4042
{
4143
$this->manifestUrl = $manifestUrl;
4244
$this->httpClient = $httpClient;
@@ -66,7 +68,13 @@ public function applyVersion(string $path)
6668
}
6769

6870
if ($this->strict) {
69-
throw new RuntimeException(sprintf('Asset "%s" not found in manifest "%s".', $path, $this->manifestUrl));
71+
$message = sprintf('Asset "%s" not found in manifest "%s".', $path, $this->manifestUrl);
72+
$alternatives = $this->findAlternatives($path, $this->manifestData);
73+
if (\count($alternatives) > 0) {
74+
$message .= sprintf(' Did you mean one of these? "%s".', implode('", "', $alternatives));
75+
}
76+
77+
throw new AssetNotFoundException($message, $alternatives);
7078
}
7179

7280
return $path;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.