Skip to content

Commit ebe85fd

Browse files
Merge branch '6.4' into 7.0
* 6.4: [Process] remove fixing of legacy bug, when PTS functionality is enabled DX: re-apply self_accessor and phpdoc_types_order by PHP CS Fixer [HttpClient] Psr18Client: parse HTTP Reason Phrase for Response fix test Fix wrong yaml parse null test [AssetMapper] Fixing merge from multiple PR's Bump Symfony version to 5.4.31 Update VERSION for 5.4.30 Update CONTRIBUTORS for 5.4.30 Update CHANGELOG for 5.4.30 Fix wrong merge [AssetMapper] Allowing circular references in JavaScriptImportPathCompiler [AssetMapper] Fix file deleting errors & remove nullable MappedAsset on JS import [Lock] Fix mongodb extension requirement in tests [Yaml] Remove dead code [AssetMapper] Fix in-file imports to resolve via filesystem throw better exception in TranslatableNormalizer, add to changelog Passing null to Inline::parse is not allowed Fix passing null to trim()
2 parents 1731694 + 60f9cb8 commit ebe85fd

18 files changed

+357
-438
lines changed

Compiler/AssetCompilerPathResolverTrait.php

Lines changed: 0 additions & 88 deletions
This file was deleted.

Compiler/CssAssetUrlCompiler.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\AssetMapper\AssetMapperInterface;
1616
use Symfony\Component\AssetMapper\Exception\RuntimeException;
1717
use Symfony\Component\AssetMapper\MappedAsset;
18+
use Symfony\Component\Filesystem\Path;
1819

1920
/**
2021
* Resolves url() paths in CSS files.
@@ -23,8 +24,6 @@
2324
*/
2425
final class CssAssetUrlCompiler implements AssetCompilerInterface
2526
{
26-
use AssetCompilerPathResolverTrait;
27-
2827
// https://regex101.com/r/BOJ3vG/1
2928
public const ASSET_URL_PATTERN = '/url\(\s*["\']?(?!(?:\/|\#|%23|data|http|\/\/))([^"\'\s?#)]+)([#?][^"\')]+)?\s*["\']?\)/';
3029

@@ -38,23 +37,29 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
3837
{
3938
return preg_replace_callback(self::ASSET_URL_PATTERN, function ($matches) use ($asset, $assetMapper) {
4039
try {
41-
$resolvedPath = $this->resolvePath(\dirname($asset->logicalPath), $matches[1]);
40+
$resolvedSourcePath = Path::join(\dirname($asset->sourcePath), $matches[1]);
4241
} catch (RuntimeException $e) {
4342
$this->handleMissingImport(sprintf('Error processing import in "%s": ', $asset->sourcePath).$e->getMessage(), $e);
4443

4544
return $matches[0];
4645
}
47-
$dependentAsset = $assetMapper->getAsset($resolvedPath);
46+
$dependentAsset = $assetMapper->getAssetFromSourcePath($resolvedSourcePath);
4847

4948
if (null === $dependentAsset) {
50-
$this->handleMissingImport(sprintf('Unable to find asset "%s" referenced in "%s".', $matches[1], $asset->sourcePath));
49+
$message = sprintf('Unable to find asset "%s" referenced in "%s". The file "%s" ', $matches[1], $asset->sourcePath, $resolvedSourcePath);
50+
if (is_file($resolvedSourcePath)) {
51+
$message .= 'exists, but it is not in a mapped asset path. Add it to the "paths" config.';
52+
} else {
53+
$message .= 'does not exist.';
54+
}
55+
$this->handleMissingImport($message);
5156

5257
// return original, unchanged path
5358
return $matches[0];
5459
}
5560

5661
$asset->addDependency($dependentAsset);
57-
$relativePath = $this->createRelativePath($asset->publicPathWithoutDigest, $dependentAsset->publicPath);
62+
$relativePath = Path::makeRelative($dependentAsset->publicPath, \dirname($asset->publicPathWithoutDigest));
5863

5964
return 'url("'.$relativePath.'")';
6065
}, $content);

Compiler/JavaScriptImportPathCompiler.php

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\AssetMapper\ImportMap\ImportMapConfigReader;
1919
use Symfony\Component\AssetMapper\ImportMap\JavaScriptImport;
2020
use Symfony\Component\AssetMapper\MappedAsset;
21+
use Symfony\Component\Filesystem\Path;
2122

2223
/**
2324
* Resolves import paths in JS files.
@@ -26,8 +27,6 @@
2627
*/
2728
final class JavaScriptImportPathCompiler implements AssetCompilerInterface
2829
{
29-
use AssetCompilerPathResolverTrait;
30-
3130
// https://regex101.com/r/fquriB/1
3231
private const IMPORT_PATTERN = '/(?:import\s*(?:(?:\*\s*as\s+\w+|[\w\s{},*]+)\s*from\s*)?|\bimport\()\s*[\'"`](\.\/[^\'"`]+|(\.\.\/)*[^\'"`]+)[\'"`]\s*[;\)]?/m';
3332

@@ -62,15 +61,19 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
6261
$dependentAsset = $this->findAssetForRelativeImport($importedModule, $asset, $assetMapper);
6362
}
6463

64+
if (!$dependentAsset) {
65+
return $fullImportString;
66+
}
67+
6568
// List as a JavaScript import.
6669
// This will cause the asset to be included in the importmap (for relative imports)
6770
// and will be used to generate the preloads in the importmap.
6871
$isLazy = str_contains($fullImportString, 'import(');
69-
$addToImportMap = $isRelativeImport && $dependentAsset;
72+
$addToImportMap = $isRelativeImport;
7073
$asset->addJavaScriptImport(new JavaScriptImport(
7174
$addToImportMap ? $dependentAsset->publicPathWithoutDigest : $importedModule,
72-
$isLazy,
7375
$dependentAsset,
76+
$isLazy,
7477
$addToImportMap,
7578
));
7679

@@ -80,7 +83,7 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
8083
}
8184

8285
// support possibility where the final public files have moved relative to each other
83-
$relativeImportPath = $this->createRelativePath($asset->publicPathWithoutDigest, $dependentAsset->publicPathWithoutDigest);
86+
$relativeImportPath = Path::makeRelative($dependentAsset->publicPathWithoutDigest, \dirname($asset->publicPathWithoutDigest));
8487
$relativeImportPath = $this->makeRelativeForJavaScript($relativeImportPath);
8588

8689
return str_replace($importedModule, $relativeImportPath, $fullImportString);
@@ -145,17 +148,21 @@ private function findAssetForBareImport(string $importedModule, AssetMapperInter
145148
return null;
146149
}
147150

148-
if ($asset = $assetMapper->getAsset($importMapEntry->path)) {
149-
return $asset;
150-
}
151+
try {
152+
if ($asset = $assetMapper->getAsset($importMapEntry->path)) {
153+
return $asset;
154+
}
151155

152-
return $assetMapper->getAssetFromSourcePath($importMapEntry->path);
156+
return $assetMapper->getAssetFromSourcePath($importMapEntry->path);
157+
} catch (CircularAssetsException $exception) {
158+
return $exception->getIncompleteMappedAsset();
159+
}
153160
}
154161

155162
private function findAssetForRelativeImport(string $importedModule, MappedAsset $asset, AssetMapperInterface $assetMapper): ?MappedAsset
156163
{
157164
try {
158-
$resolvedPath = $this->resolvePath(\dirname($asset->logicalPath), $importedModule);
165+
$resolvedSourcePath = Path::join(\dirname($asset->sourcePath), $importedModule);
159166
} catch (RuntimeException $e) {
160167
// avoid warning about vendor imports - these are often comments
161168
if (!$asset->isVendor) {
@@ -165,26 +172,36 @@ private function findAssetForRelativeImport(string $importedModule, MappedAsset
165172
return null;
166173
}
167174

168-
$dependentAsset = $assetMapper->getAsset($resolvedPath);
175+
try {
176+
$dependentAsset = $assetMapper->getAssetFromSourcePath($resolvedSourcePath);
177+
} catch (CircularAssetsException $exception) {
178+
$dependentAsset = $exception->getIncompleteMappedAsset();
179+
}
169180

170181
if ($dependentAsset) {
171182
return $dependentAsset;
172183
}
173184

185+
// avoid warning about vendor imports - these are often comments
186+
if ($asset->isVendor) {
187+
return null;
188+
}
189+
174190
$message = sprintf('Unable to find asset "%s" imported from "%s".', $importedModule, $asset->sourcePath);
175191

176-
try {
177-
if (null !== $assetMapper->getAsset(sprintf('%s.js', $resolvedPath))) {
178-
$message .= sprintf(' Try adding ".js" to the end of the import - i.e. "%s.js".', $importedModule);
192+
if (is_file($resolvedSourcePath)) {
193+
$message .= sprintf('The file "%s" exists, but it is not in a mapped asset path. Add it to the "paths" config.', $resolvedSourcePath);
194+
} else {
195+
try {
196+
if (null !== $assetMapper->getAssetFromSourcePath(sprintf('%s.js', $resolvedSourcePath))) {
197+
$message .= sprintf(' Try adding ".js" to the end of the import - i.e. "%s.js".', $importedModule);
198+
}
199+
} catch (CircularAssetsException) {
200+
// avoid circular error if there is self-referencing import comments
179201
}
180-
} catch (CircularAssetsException) {
181-
// avoid circular error if there is self-referencing import comments
182202
}
183203

184-
// avoid warning about vendor imports - these are often comments
185-
if (!$asset->isVendor) {
186-
$this->handleMissingImport($message);
187-
}
204+
$this->handleMissingImport($message);
188205

189206
return null;
190207
}

Compiler/SourceMappingUrlsCompiler.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\AssetMapper\AssetMapperInterface;
1515
use Symfony\Component\AssetMapper\MappedAsset;
16+
use Symfony\Component\Filesystem\Path;
1617

1718
/**
1819
* Rewrites already-existing source map URLs to their final digested path.
@@ -21,8 +22,6 @@
2122
*/
2223
final class SourceMappingUrlsCompiler implements AssetCompilerInterface
2324
{
24-
use AssetCompilerPathResolverTrait;
25-
2625
private const SOURCE_MAPPING_PATTERN = '/^(\/\/|\/\*)# sourceMappingURL=(.+\.map)/m';
2726

2827
public function supports(MappedAsset $asset): bool
@@ -33,16 +32,16 @@ public function supports(MappedAsset $asset): bool
3332
public function compile(string $content, MappedAsset $asset, AssetMapperInterface $assetMapper): string
3433
{
3534
return preg_replace_callback(self::SOURCE_MAPPING_PATTERN, function ($matches) use ($asset, $assetMapper) {
36-
$resolvedPath = $this->resolvePath(\dirname($asset->logicalPath), $matches[2]);
35+
$resolvedPath = Path::join(\dirname($asset->sourcePath), $matches[2]);
3736

38-
$dependentAsset = $assetMapper->getAsset($resolvedPath);
37+
$dependentAsset = $assetMapper->getAssetFromSourcePath($resolvedPath);
3938
if (!$dependentAsset) {
4039
// return original, unchanged path
4140
return $matches[0];
4241
}
4342

4443
$asset->addDependency($dependentAsset);
45-
$relativePath = $this->createRelativePath($asset->publicPathWithoutDigest, $dependentAsset->publicPath);
44+
$relativePath = Path::makeRelative($dependentAsset->publicPath, \dirname($asset->publicPathWithoutDigest));
4645

4746
return $matches[1].'# sourceMappingURL='.$relativePath;
4847
}, $content);

Exception/CircularAssetsException.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,26 @@
1111

1212
namespace Symfony\Component\AssetMapper\Exception;
1313

14+
use Symfony\Component\AssetMapper\MappedAsset;
15+
1416
/**
1517
* Thrown when a circular reference is detected while creating an asset.
1618
*/
1719
class CircularAssetsException extends RuntimeException
1820
{
21+
public function __construct(private MappedAsset $mappedAsset, string $message = '', int $code = 0, \Throwable $previous = null)
22+
{
23+
parent::__construct($message, $code, $previous);
24+
}
25+
26+
/**
27+
* Returns the asset that was being created when the circular reference was detected.
28+
*
29+
* This asset will not be fully initialized: it will be missing some
30+
* properties like digest and content.
31+
*/
32+
public function getIncompleteMappedAsset(): MappedAsset
33+
{
34+
return $this->mappedAsset;
35+
}
1936
}

Factory/CachedMappedAssetFactory.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\AssetMapper\MappedAsset;
1515
use Symfony\Component\Config\ConfigCache;
1616
use Symfony\Component\Config\Resource\DirectoryResource;
17+
use Symfony\Component\Config\Resource\FileExistenceResource;
1718
use Symfony\Component\Config\Resource\FileResource;
1819
use Symfony\Component\Config\Resource\ResourceInterface;
1920

@@ -67,6 +68,10 @@ private function collectResourcesFromAsset(MappedAsset $mappedAsset): array
6768
$resources = array_merge($resources, $this->collectResourcesFromAsset($assetDependency));
6869
}
6970

71+
foreach ($mappedAsset->getJavaScriptImports() as $import) {
72+
$resources[] = new FileExistenceResource($import->asset->sourcePath);
73+
}
74+
7075
return $resources;
7176
}
7277
}

Factory/MappedAssetFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,14 @@ public function __construct(
3737
public function createMappedAsset(string $logicalPath, string $sourcePath): ?MappedAsset
3838
{
3939
if (isset($this->assetsBeingCreated[$logicalPath])) {
40-
throw new CircularAssetsException(sprintf('Circular reference detected while creating asset for "%s": "%s".', $logicalPath, implode(' -> ', $this->assetsBeingCreated).' -> '.$logicalPath));
40+
throw new CircularAssetsException($this->assetsCache[$logicalPath], sprintf('Circular reference detected while creating asset for "%s": "%s".', $logicalPath, implode(' -> ', $this->assetsBeingCreated).' -> '.$logicalPath));
4141
}
4242
$this->assetsBeingCreated[$logicalPath] = $logicalPath;
4343

4444
if (!isset($this->assetsCache[$logicalPath])) {
4545
$isVendor = $this->isVendor($sourcePath);
4646
$asset = new MappedAsset($logicalPath, $sourcePath, $this->assetsPathResolver->resolvePublicPath($logicalPath), isVendor: $isVendor);
47+
$this->assetsCache[$logicalPath] = $asset;
4748

4849
$content = $this->compileContent($asset);
4950
[$digest, $isPredigested] = $this->getDigest($asset, $content);

ImportMap/ImportMapGenerator.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ private function addImplicitEntries(ImportMapEntry $entry, array $currentImportE
178178
}
179179

180180
// check if this import requires an automatic importmap entry
181-
if ($javaScriptImport->addImplicitlyToImportMap && $javaScriptImport->asset) {
181+
if ($javaScriptImport->addImplicitlyToImportMap) {
182182
$nextEntry = ImportMapEntry::createLocal(
183183
$importName,
184184
ImportMapType::tryFrom($javaScriptImport->asset->publicExtension) ?: ImportMapType::JS,
@@ -226,10 +226,8 @@ private function findEagerImports(MappedAsset $asset): array
226226

227227
$dependencies[] = $javaScriptImport->importName;
228228

229-
// the import is for a MappedAsset? Follow its imports!
230-
if ($javaScriptImport->asset) {
231-
$dependencies = array_merge($dependencies, $this->findEagerImports($javaScriptImport->asset));
232-
}
229+
// Follow its imports!
230+
$dependencies = array_merge($dependencies, $this->findEagerImports($javaScriptImport->asset));
233231
}
234232

235233
return $dependencies;

0 commit comments

Comments
 (0)