Skip to content

Commit ac14da5

Browse files
committed
[AssetMapper] Always downloading vendor files
Adding entrypoint option to importmap:require Don't explode if downloading and the installed has a problem
1 parent d27190a commit ac14da5

40 files changed

+869
-989
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ CHANGELOG
2828
* Add `array $tokenAttributes = []` optional parameter to `KernelBrowser::loginUser()`
2929
* Add support for relative URLs in BrowserKit's redirect assertion.
3030
* Change BrowserKitAssertionsTrait::getClient() to be protected
31+
* Removed the `framework.asset_mapper.provider` config option.
3132

3233
6.3
3334
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class UnusedTagsPass implements CompilerPassInterface
2525
'annotations.cached_reader',
2626
'assets.package',
2727
'asset_mapper.compiler',
28-
'asset_mapper.importmap.resolver',
2928
'auto_alias',
3029
'cache.pool',
3130
'cache.pool.clearer',

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use Symfony\Bundle\FullStack;
1919
use Symfony\Component\Asset\Package;
2020
use Symfony\Component\AssetMapper\AssetMapper;
21-
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
2221
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
2322
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
2423
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
@@ -940,8 +939,7 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $
940939
->defaultValue('%kernel.project_dir%/assets/vendor')
941940
->end()
942941
->scalarNode('provider')
943-
->info('The provider (CDN) to use'.(class_exists(ImportMapManager::class) ? sprintf(' (e.g.: "%s").', implode('", "', ImportMapManager::PROVIDERS)) : '.'))
944-
->defaultValue('jsdelivr.esm')
942+
->setDeprecated('symfony/framework-bundle', '6.4', 'Option "%node%" at "%path%" is deprecated and does nothing. Remove it.')
945943
->end()
946944
->end()
947945
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
use Symfony\Component\AssetMapper\AssetMapper;
3535
use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface;
3636
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
37-
use Symfony\Component\AssetMapper\ImportMap\Resolver\PackageResolverInterface;
3837
use Symfony\Component\BrowserKit\AbstractBrowser;
3938
use Symfony\Component\Cache\Adapter\AdapterInterface;
4039
use Symfony\Component\Cache\Adapter\ArrayAdapter;
@@ -1364,28 +1363,24 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde
13641363
->setArgument(1, $config['missing_import_mode']);
13651364

13661365
$container
1367-
->getDefinition('asset_mapper.importmap.manager')
1368-
->replaceArgument(3, $config['vendor_dir'])
1366+
->getDefinition('asset_mapper.importmap.remote_package_downloader')
1367+
->replaceArgument(2, $config['vendor_dir'])
13691368
;
1370-
13711369
$container
1372-
->getDefinition('asset_mapper.importmap.config_reader')
1373-
->replaceArgument(0, $config['importmap_path'])
1370+
->getDefinition('asset_mapper.mapped_asset_factory')
1371+
->replaceArgument(2, $config['vendor_dir'])
13741372
;
13751373

13761374
$container
1377-
->getDefinition('asset_mapper.importmap.resolver')
1378-
->replaceArgument(0, $config['provider'])
1375+
->getDefinition('asset_mapper.importmap.config_reader')
1376+
->replaceArgument(0, $config['importmap_path'])
13791377
;
13801378

13811379
$container
13821380
->getDefinition('asset_mapper.importmap.renderer')
13831381
->replaceArgument(3, $config['importmap_polyfill'] ?? ImportMapManager::POLYFILL_URL)
13841382
->replaceArgument(4, $config['importmap_script_attributes'])
13851383
;
1386-
1387-
$container->registerForAutoconfiguration(PackageResolverInterface::class)
1388-
->addTag('asset_mapper.importmap.resolver');
13891384
}
13901385

13911386
/**

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

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@
3232
use Symfony\Component\AssetMapper\ImportMap\ImportMapConfigReader;
3333
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
3434
use Symfony\Component\AssetMapper\ImportMap\ImportMapRenderer;
35+
use Symfony\Component\AssetMapper\ImportMap\RemotePackageDownloader;
3536
use Symfony\Component\AssetMapper\ImportMap\Resolver\JsDelivrEsmResolver;
36-
use Symfony\Component\AssetMapper\ImportMap\Resolver\JspmResolver;
37-
use Symfony\Component\AssetMapper\ImportMap\Resolver\PackageResolver;
3837
use Symfony\Component\AssetMapper\MapperAwareAssetPackage;
3938
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolver;
4039
use Symfony\Component\HttpKernel\Event\RequestEvent;
@@ -53,6 +52,7 @@
5352
->args([
5453
service('asset_mapper.public_assets_path_resolver'),
5554
service('asset_mapper_compiler'),
55+
abstract_arg('vendor directory'),
5656
])
5757

5858
->set('asset_mapper.cached_mapped_asset_factory', CachedMappedAssetFactory::class)
@@ -150,41 +150,20 @@
150150
service('asset_mapper'),
151151
service('asset_mapper.public_assets_path_resolver'),
152152
service('asset_mapper.importmap.config_reader'),
153-
abstract_arg('vendor directory'),
153+
service('asset_mapper.importmap.remote_package_downloader'),
154154
service('asset_mapper.importmap.resolver'),
155-
service('http_client'),
156155
])
157156
->alias(ImportMapManager::class, 'asset_mapper.importmap.manager')
158157

159-
->set('asset_mapper.importmap.resolver', PackageResolver::class)
158+
->set('asset_mapper.importmap.remote_package_downloader', RemotePackageDownloader::class)
160159
->args([
161-
abstract_arg('provider'),
162-
tagged_locator('asset_mapper.importmap.resolver'),
160+
service('asset_mapper.importmap.config_reader'),
161+
service('asset_mapper.importmap.resolver'),
162+
abstract_arg('vendor directory'),
163163
])
164164

165-
->set('asset_mapper.importmap.resolver.jsdelivr_esm', JsDelivrEsmResolver::class)
165+
->set('asset_mapper.importmap.resolver', JsDelivrEsmResolver::class)
166166
->args([service('http_client')])
167-
->tag('asset_mapper.importmap.resolver', ['resolver' => ImportMapManager::PROVIDER_JSDELIVR_ESM])
168-
169-
->set('asset_mapper.importmap.resolver.jspm', JspmResolver::class)
170-
->args([service('http_client'), ImportMapManager::PROVIDER_JSPM])
171-
->tag('asset_mapper.importmap.resolver', ['resolver' => ImportMapManager::PROVIDER_JSPM])
172-
173-
->set('asset_mapper.importmap.resolver.jspm_system', JspmResolver::class)
174-
->args([service('http_client'), ImportMapManager::PROVIDER_JSPM_SYSTEM])
175-
->tag('asset_mapper.importmap.resolver', ['resolver' => ImportMapManager::PROVIDER_JSPM_SYSTEM])
176-
177-
->set('asset_mapper.importmap.resolver.skypack', JspmResolver::class)
178-
->args([service('http_client'), ImportMapManager::PROVIDER_SKYPACK])
179-
->tag('asset_mapper.importmap.resolver', ['resolver' => ImportMapManager::PROVIDER_SKYPACK])
180-
181-
->set('asset_mapper.importmap.resolver.jsdelivr', JspmResolver::class)
182-
->args([service('http_client'), ImportMapManager::PROVIDER_JSDELIVR])
183-
->tag('asset_mapper.importmap.resolver', ['resolver' => ImportMapManager::PROVIDER_JSDELIVR])
184-
185-
->set('asset_mapper.importmap.resolver.unpkg', JspmResolver::class)
186-
->args([service('http_client'), ImportMapManager::PROVIDER_UNPKG])
187-
->tag('asset_mapper.importmap.resolver', ['resolver' => ImportMapManager::PROVIDER_UNPKG])
188167

189168
->set('asset_mapper.importmap.renderer', ImportMapRenderer::class)
190169
->args([
@@ -219,7 +198,10 @@
219198
->tag('console.command')
220199

221200
->set('asset_mapper.importmap.command.install', ImportMapInstallCommand::class)
222-
->args([service('asset_mapper.importmap.manager')])
201+
->args([
202+
service('asset_mapper.importmap.remote_package_downloader'),
203+
param('kernel.project_dir'),
204+
])
223205
->tag('console.command')
224206

225207
->set('asset_mapper.importmap.command.audit', ImportMapAuditCommand::class)

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ public function testAssetMapperCanBeEnabled()
134134
'importmap_path' => '%kernel.project_dir%/importmap.php',
135135
'importmap_polyfill' => null,
136136
'vendor_dir' => '%kernel.project_dir%/assets/vendor',
137-
'provider' => 'jsdelivr.esm',
138137
'importmap_script_attributes' => [],
139138
];
140139

@@ -671,7 +670,6 @@ protected static function getBundleDefaultConfig()
671670
'importmap_path' => '%kernel.project_dir%/importmap.php',
672671
'importmap_polyfill' => null,
673672
'vendor_dir' => '%kernel.project_dir%/assets/vendor',
674-
'provider' => 'jsdelivr.esm',
675673
'importmap_script_attributes' => [],
676674
],
677675
'cache' => [

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/asset_mapper.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
importmap-path="%kernel.project_dir%/importmap.php"
1818
importmap-polyfill="https://cdn.example.com/polyfill.js"
1919
vendor-dir="%kernel.project_dir%/assets/vendor"
20-
provider="jspm"
2120
>
2221
<framework:path>assets/</framework:path>
2322
<framework:path namespace="my_namespace">assets2/</framework:path>

src/Symfony/Component/AssetMapper/AssetMapperRepository.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ public function all(): array
111111
continue;
112112
}
113113

114+
// avoid potentially exposing PHP files
115+
if (str_ends_with($file->getPathname(), '.php')) {
116+
continue;
117+
}
118+
114119
/** @var RecursiveDirectoryIterator $innerIterator */
115120
$innerIterator = $iterator->getInnerIterator();
116121
$logicalPath = ($namespace ? rtrim($namespace, '/').'/' : '').$innerIterator->getSubPathName();

src/Symfony/Component/AssetMapper/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Mark the component as non experimental
88
* Add CSS support to the importmap
99
* Add "entrypoints" concept to the importmap
10+
* Always download packages locally instead of using a CDN
1011
* Allow relative path strings in the importmap
1112
* Add `PreAssetsCompileEvent` event when running `asset-map:compile`
1213
* Add support for importmap paths to use the Asset component (for subdirectories)

src/Symfony/Component/AssetMapper/Command/ImportMapInstallCommand.php

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111

1212
namespace Symfony\Component\AssetMapper\Command;
1313

14-
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
14+
use Symfony\Component\AssetMapper\ImportMap\RemotePackageDownloader;
1515
use Symfony\Component\Console\Attribute\AsCommand;
1616
use Symfony\Component\Console\Command\Command;
17+
use Symfony\Component\Console\Helper\ProgressBar;
1718
use Symfony\Component\Console\Input\InputInterface;
1819
use Symfony\Component\Console\Output\OutputInterface;
1920
use Symfony\Component\Console\Style\SymfonyStyle;
21+
use Symfony\Contracts\HttpClient\ResponseInterface;
2022

2123
/**
2224
* Downloads all assets that should be downloaded.
@@ -27,7 +29,8 @@
2729
final class ImportMapInstallCommand extends Command
2830
{
2931
public function __construct(
30-
private readonly ImportMapManager $importMapManager,
32+
private readonly RemotePackageDownloader $packageDownloader,
33+
private readonly string $projectDir,
3134
) {
3235
parent::__construct();
3336
}
@@ -36,8 +39,36 @@ protected function execute(InputInterface $input, OutputInterface $output): int
3639
{
3740
$io = new SymfonyStyle($input, $output);
3841

39-
$downloadedPackages = $this->importMapManager->downloadMissingPackages();
40-
$io->success(sprintf('Downloaded %d assets.', \count($downloadedPackages)));
42+
$finishedCount = 0;
43+
$progressBar = new ProgressBar($output);
44+
$progressBar->setFormat('<info>%current%/%max%</info> %bar% %url%');
45+
$downloadedPackages = $this->packageDownloader->downloadPackages(function (string $package, string $event, ResponseInterface $response, int $totalPackages) use (&$finishedCount, $progressBar) {
46+
$progressBar->setMessage($response->getInfo('url'), 'url');
47+
if (0 === $progressBar->getMaxSteps()) {
48+
$progressBar->setMaxSteps($totalPackages);
49+
$progressBar->start();
50+
}
51+
52+
if ('finished' === $event) {
53+
++$finishedCount;
54+
$progressBar->advance();
55+
}
56+
});
57+
$progressBar->finish();
58+
$progressBar->clear();
59+
60+
if (0 === \count($downloadedPackages)) {
61+
$io->success('No assets to install.');
62+
63+
return Command::SUCCESS;
64+
}
65+
66+
$io->success(sprintf(
67+
'Downloaded %d asset%s into %s.',
68+
\count($downloadedPackages),
69+
1 == \count($downloadedPackages) ? '' : 's',
70+
str_replace($this->projectDir.'/', '', $this->packageDownloader->getVendorDir()),
71+
));
4172

4273
return Command::SUCCESS;
4374
}

src/Symfony/Component/AssetMapper/Command/ImportMapRequireCommand.php

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Component\AssetMapper\Command;
1313

14-
use Symfony\Bundle\FrameworkBundle\Console\Application;
1514
use Symfony\Component\AssetMapper\AssetMapperInterface;
1615
use Symfony\Component\AssetMapper\ImportMap\ImportMapEntry;
1716
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
@@ -42,7 +41,7 @@ protected function configure(): void
4241
{
4342
$this
4443
->addArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The packages to add')
45-
->addOption('download', 'd', InputOption::VALUE_NONE, 'Download packages locally')
44+
->addOption('entrypoint', null, InputOption::VALUE_NONE, 'Make the package an entrypoint?')
4645
->addOption('path', null, InputOption::VALUE_REQUIRED, 'The local path where the package lives relative to the project root')
4746
->setHelp(<<<'EOT'
4847
The <info>%command.name%</info> command adds packages to <comment>importmap.php</comment> usually
@@ -113,10 +112,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
113112
$packages[] = new PackageRequireOptions(
114113
$parts['package'],
115114
$parts['version'] ?? null,
116-
$input->getOption('download'),
117115
$parts['alias'] ?? $parts['package'],
118-
isset($parts['registry']) && $parts['registry'] ? $parts['registry'] : null,
119116
$path,
117+
$input->getOption('entrypoint'),
120118
);
121119
}
122120

@@ -125,19 +123,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
125123
$newPackage = $newPackages[0];
126124
$message = sprintf('Package "%s" added to importmap.php', $newPackage->importName);
127125

128-
if ($newPackage->isDownloaded && null !== $downloadedAsset = $this->assetMapper->getAsset($newPackage->path)) {
129-
$application = $this->getApplication();
130-
if ($application instanceof Application) {
131-
$projectDir = $application->getKernel()->getProjectDir();
132-
$downloadedPath = $downloadedAsset->sourcePath;
133-
if (str_starts_with($downloadedPath, $projectDir)) {
134-
$downloadedPath = substr($downloadedPath, \strlen($projectDir) + 1);
135-
}
136-
137-
$message .= sprintf(' and downloaded locally to "%s"', $downloadedPath);
138-
}
139-
}
140-
141126
$message .= '.';
142127
} else {
143128
$names = array_map(fn (ImportMapEntry $package) => $package->importName, $newPackages);

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ private function findAssetForBareImport(string $importedModule, AssetMapperInter
146146
}
147147

148148
// remote entries have no MappedAsset
149-
if ($importMapEntry->isRemote()) {
149+
if ($importMapEntry->isRemotePackage()) {
150150
return null;
151151
}
152152

@@ -158,7 +158,10 @@ private function findAssetForRelativeImport(string $importedModule, MappedAsset
158158
try {
159159
$resolvedPath = $this->resolvePath(\dirname($asset->logicalPath), $importedModule);
160160
} catch (RuntimeException $e) {
161-
$this->handleMissingImport(sprintf('Error processing import in "%s": ', $asset->sourcePath).$e->getMessage(), $e);
161+
// avoid warning about vendor imports - these are often comments
162+
if (!$asset->isVendor) {
163+
$this->handleMissingImport(sprintf('Error processing import in "%s": ', $asset->sourcePath).$e->getMessage(), $e);
164+
}
162165

163166
return null;
164167
}
@@ -179,7 +182,10 @@ private function findAssetForRelativeImport(string $importedModule, MappedAsset
179182
// avoid circular error if there is self-referencing import comments
180183
}
181184

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

184190
return null;
185191
}

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ class MappedAssetFactory implements MappedAssetFactoryInterface
2929
private array $fileContentsCache = [];
3030

3131
public function __construct(
32-
private PublicAssetsPathResolverInterface $assetsPathResolver,
33-
private AssetMapperCompiler $compiler,
32+
private readonly PublicAssetsPathResolverInterface $assetsPathResolver,
33+
private readonly AssetMapperCompiler $compiler,
34+
private readonly string $vendorDir,
3435
) {
3536
}
3637

@@ -43,7 +44,8 @@ public function createMappedAsset(string $logicalPath, string $sourcePath): ?Map
4344
if (!isset($this->assetsCache[$logicalPath])) {
4445
$this->assetsBeingCreated[] = $logicalPath;
4546

46-
$asset = new MappedAsset($logicalPath, $sourcePath, $this->assetsPathResolver->resolvePublicPath($logicalPath));
47+
$isVendor = $this->isVendor($sourcePath);
48+
$asset = new MappedAsset($logicalPath, $sourcePath, $this->assetsPathResolver->resolvePublicPath($logicalPath), isVendor: $isVendor);
4749

4850
[$digest, $isPredigested] = $this->getDigest($asset);
4951

@@ -55,6 +57,7 @@ public function createMappedAsset(string $logicalPath, string $sourcePath): ?Map
5557
$this->calculateContent($asset),
5658
$digest,
5759
$isPredigested,
60+
$isVendor,
5861
$asset->getDependencies(),
5962
$asset->getFileDependencies(),
6063
$asset->getJavaScriptImports(),
@@ -116,4 +119,12 @@ private function getPublicPath(MappedAsset $asset): ?string
116119

117120
return $this->assetsPathResolver->resolvePublicPath($digestedPath);
118121
}
122+
123+
private function isVendor(string $sourcePath): bool
124+
{
125+
$sourcePath = realpath($sourcePath);
126+
$vendorDir = realpath($this->vendorDir);
127+
128+
return $sourcePath && str_starts_with($sourcePath, $vendorDir);
129+
}
119130
}

0 commit comments

Comments
 (0)