Skip to content

Commit aba725f

Browse files
bug #51345 [AssetMapper] Fixing bug where a circular exception could be thrown while making error message (weaverryan)
This PR was squashed before being merged into the 6.3 branch. Discussion ---------- [AssetMapper] Fixing bug where a circular exception could be thrown while making error message | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | yes | New feature? | no | Deprecations? | none | Tickets | Fix #51291 | License | MIT | Doc PR | Not needed AssetMapper's `JavaScriptImportPathCompiler` parses import statements. That process is imperfect and will over-match in some cases (e.g. matching `import()` that is commented-out). but that's not a huge issue: any matches are simply added to the importmap and matches for not-found-files are ignored. However, in #51291, we hit a spot where, while trying to improve the log message (`Try adding ".js" to the end of the import - i.e. "%s.js"`), we triggered a circular exception. This PR suppresses that. We may need to improve the parsing logic later to handle more edge-cases, but we'll handle those if/when they come up. Cheers! Commits ------- 63b9635 [AssetMapper] Fixing bug where a circular exception could be thrown while making error message
2 parents 340f18e + 63b9635 commit aba725f

File tree

4 files changed

+54
-3
lines changed

4 files changed

+54
-3
lines changed

src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Log\NullLogger;
1616
use Symfony\Component\AssetMapper\AssetDependency;
1717
use Symfony\Component\AssetMapper\AssetMapperInterface;
18+
use Symfony\Component\AssetMapper\Exception\CircularAssetsException;
1819
use Symfony\Component\AssetMapper\Exception\RuntimeException;
1920
use Symfony\Component\AssetMapper\MappedAsset;
2021

@@ -57,8 +58,12 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
5758
if (!$dependentAsset) {
5859
$message = sprintf('Unable to find asset "%s" imported from "%s".', $matches[1], $asset->sourcePath);
5960

60-
if (null !== $assetMapper->getAsset(sprintf('%s.js', $resolvedPath))) {
61-
$message .= sprintf(' Try adding ".js" to the end of the import - i.e. "%s.js".', $matches[1]);
61+
try {
62+
if (null !== $assetMapper->getAsset(sprintf('%s.js', $resolvedPath))) {
63+
$message .= sprintf(' Try adding ".js" to the end of the import - i.e. "%s.js".', $matches[1]);
64+
}
65+
} catch (CircularAssetsException $e) {
66+
// avoid circular error if there is self-referencing import comments
6267
}
6368

6469
$this->handleMissingImport($message);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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\AssetMapper\Exception;
13+
14+
/**
15+
* Thrown when a circular reference is detected while creating an asset.
16+
*/
17+
class CircularAssetsException extends RuntimeException
18+
{
19+
}

src/Symfony/Component/AssetMapper/Factory/MappedAssetFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\AssetMapper\Factory;
1313

1414
use Symfony\Component\AssetMapper\AssetMapperCompiler;
15+
use Symfony\Component\AssetMapper\Exception\CircularAssetsException;
1516
use Symfony\Component\AssetMapper\Exception\RuntimeException;
1617
use Symfony\Component\AssetMapper\MappedAsset;
1718
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface;
@@ -36,7 +37,7 @@ public function __construct(
3637
public function createMappedAsset(string $logicalPath, string $sourcePath): ?MappedAsset
3738
{
3839
if (\in_array($logicalPath, $this->assetsBeingCreated, true)) {
39-
throw new RuntimeException(sprintf('Circular reference detected while creating asset for "%s": "%s".', $logicalPath, implode(' -> ', $this->assetsBeingCreated).' -> '.$logicalPath));
40+
throw new CircularAssetsException(sprintf('Circular reference detected while creating asset for "%s": "%s".', $logicalPath, implode(' -> ', $this->assetsBeingCreated).' -> '.$logicalPath));
4041
}
4142

4243
if (!isset($this->assetsCache[$logicalPath])) {

src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\AssetMapper\AssetMapperInterface;
1717
use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface;
1818
use Symfony\Component\AssetMapper\Compiler\JavaScriptImportPathCompiler;
19+
use Symfony\Component\AssetMapper\Exception\CircularAssetsException;
1920
use Symfony\Component\AssetMapper\Exception\RuntimeException;
2021
use Symfony\Component\AssetMapper\MappedAsset;
2122

@@ -277,6 +278,31 @@ public static function provideMissingImportModeTests(): iterable
277278
];
278279
}
279280

281+
public function testErrorMessageAvoidsCircularException()
282+
{
283+
$assetMapper = $this->createMock(AssetMapperInterface::class);
284+
$assetMapper->expects($this->any())
285+
->method('getAsset')
286+
->willReturnCallback(function ($logicalPath) {
287+
if ('htmx' === $logicalPath) {
288+
return null;
289+
}
290+
291+
if ('htmx.js' === $logicalPath) {
292+
throw new CircularAssetsException();
293+
}
294+
});
295+
296+
$asset = new MappedAsset('htmx.js', '/path/to/app.js');
297+
$compiler = new JavaScriptImportPathCompiler();
298+
$content = '//** @type {import("./htmx").HtmxApi} */';
299+
$compiled = $compiler->compile($content, $asset, $assetMapper);
300+
// To form a good exception message, the compiler will check for the
301+
// htmx.js asset, which will throw a CircularAssetsException. This
302+
// should not be caught.
303+
$this->assertSame($content, $compiled);
304+
}
305+
280306
private function createAssetMapper(): AssetMapperInterface
281307
{
282308
$assetMapper = $this->createMock(AssetMapperInterface::class);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy