diff --git a/src/Symfony/Component/AssetMapper/CHANGELOG.md b/src/Symfony/Component/AssetMapper/CHANGELOG.md
index 93d622101c0c8..d71897373f4b4 100644
--- a/src/Symfony/Component/AssetMapper/CHANGELOG.md
+++ b/src/Symfony/Component/AssetMapper/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+7.4
+---
+
+ * Add "full" type for a package to download all files
+
7.3
---
diff --git a/src/Symfony/Component/AssetMapper/Command/ImportMapRequireCommand.php b/src/Symfony/Component/AssetMapper/Command/ImportMapRequireCommand.php
index 3a1efabc9cd7b..914ec5a297f2d 100644
--- a/src/Symfony/Component/AssetMapper/Command/ImportMapRequireCommand.php
+++ b/src/Symfony/Component/AssetMapper/Command/ImportMapRequireCommand.php
@@ -14,6 +14,7 @@
use Symfony\Component\AssetMapper\ImportMap\ImportMapEntries;
use Symfony\Component\AssetMapper\ImportMap\ImportMapEntry;
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
+use Symfony\Component\AssetMapper\ImportMap\ImportMapType;
use Symfony\Component\AssetMapper\ImportMap\ImportMapVersionChecker;
use Symfony\Component\AssetMapper\ImportMap\PackageRequireOptions;
use Symfony\Component\Console\Attribute\AsCommand;
@@ -51,6 +52,7 @@ protected function configure(): void
->addArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The packages to add')
->addOption('entrypoint', null, InputOption::VALUE_NONE, 'Make the packages an entrypoint?')
->addOption('path', null, InputOption::VALUE_REQUIRED, 'The local path where the package lives relative to the project root')
+ ->addOption('type', null, InputOption::VALUE_REQUIRED, 'The package type, specified for specific download.')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Simulate the installation of the packages')
->setHelp(<<<'EOT'
The %command.name% command adds packages to importmap.php usually
@@ -124,6 +126,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$parts['alias'] ?? null,
$path,
$input->getOption('entrypoint'),
+ $input->getOption('type') ? ImportMapType::tryfrom($input->getOption('type')) : null,
);
}
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php
index 00c265bc4635d..7a0ea8de95643 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php
@@ -162,7 +162,7 @@ public function requirePackages(array $packagesToRequire, ImportMapEntries $impo
$newEntry = ImportMapEntry::createLocal(
$requireOptions->importName,
- self::getImportMapTypeFromFilename($requireOptions->path),
+ $requireOptions->importMapType ?? self::getImportMapTypeFromFilename($requireOptions->path),
$path,
$requireOptions->entrypoint,
);
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapType.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapType.php
index 99c8ff270c739..989c45c70f411 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapType.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapType.php
@@ -15,4 +15,15 @@ enum ImportMapType: string
{
case JS = 'js';
case CSS = 'css';
+ case FULL = 'full';
+
+ public function hasMainFile(): bool
+ {
+ if($this == self::FULL)
+ {
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/PackageRequireOptions.php b/src/Symfony/Component/AssetMapper/ImportMap/PackageRequireOptions.php
index c1bb34a8f66cd..b09642d1b85f1 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/PackageRequireOptions.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/PackageRequireOptions.php
@@ -29,6 +29,7 @@ public function __construct(
?string $importName = null,
public readonly ?string $path = null,
public readonly bool $entrypoint = false,
+ public readonly ?ImportMapType $importMapType = null,
) {
$this->importName = $importName ?: $packageModuleSpecifier;
}
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/RemotePackageDownloader.php b/src/Symfony/Component/AssetMapper/ImportMap/RemotePackageDownloader.php
index 47b6a14598728..eaec80f254256 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/RemotePackageDownloader.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/RemotePackageDownloader.php
@@ -76,7 +76,10 @@ public function downloadPackages(?callable $progressCallback = null): array
throw new \LogicException(\sprintf('The package "%s" was not downloaded.', $package));
}
- $this->remotePackageStorage->save($entry, $contents[$package]['content']);
+ if($contents[$package]['hasMainFile']) {
+ $this->remotePackageStorage->save($entry, $contents[$package]['content']);
+ }
+
foreach ($contents[$package]['extraFiles'] as $extraFilename => $extraFileContents) {
$this->remotePackageStorage->saveExtraFile($entry, $extraFilename, $extraFileContents);
}
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php b/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php
index b88f0e792d44f..19c1141736a87 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/Resolver/JsDelivrEsmResolver.php
@@ -27,6 +27,7 @@ final class JsDelivrEsmResolver implements PackageResolverInterface
public const URL_PATTERN_DIST_CSS = 'https://cdn.jsdelivr.net/npm/%s@%s%s';
public const URL_PATTERN_DIST = self::URL_PATTERN_DIST_CSS.'/+esm';
public const URL_PATTERN_ENTRYPOINT = 'https://data.jsdelivr.com/v1/packages/npm/%s@%s/entrypoints';
+ public const URL_PATTERN_FULL_DOWNLOAD = 'https://data.jsdelivr.com/v1/packages/npm/%s@%s';
public const IMPORT_REGEX = '#(?:import\s*(?:[\w$]+,)?(?:(?:\{[^}]*\}|[\w$]+|\*\s*as\s+[\w$]+)\s*\bfrom\s*)?|export\s*(?:\{[^}]*\}|\*)\s*from\s*|await\simport\()("/npm/((?:@[^/]+/)?[^@]+?)(?:@([^/]+))?((?:/[^/]+)*?)/\+esm")(?:\)*)#';
@@ -80,7 +81,7 @@ public function resolvePackages(array $packagesToRequire): array
throw new RuntimeException(\sprintf('Unable to find the latest version for package "%s" - try specifying the version manually.', $packageName));
}
- $pattern = $this->resolveUrlPattern($packageName, $filePath);
+ $pattern = $this->resolveUrlPattern($packageName, $filePath, $options->importMapType);
$requiredPackages[$i][1] = $this->httpClient->request('GET', \sprintf($pattern, $packageName, $version, $filePath));
$requiredPackages[$i][4] = $version;
@@ -108,7 +109,7 @@ public function resolvePackages(array $packagesToRequire): array
}
$contentType = $response->getHeaders()['content-type'][0] ?? '';
- $type = str_starts_with($contentType, 'text/css') ? ImportMapType::CSS : ImportMapType::JS;
+ $type = $options->importMapType ?? (str_starts_with($contentType, 'text/css') ? ImportMapType::CSS : ImportMapType::JS);
$resolvedPackages[$options->packageModuleSpecifier] = new ResolvedImportMapPackage($options, $version, $type);
$packagesToRequire = array_merge($packagesToRequire, $this->fetchPackageRequirementsFromImports($response->getContent()));
@@ -201,6 +202,7 @@ public function downloadPackages(array $importMapEntries, ?callable $progressCal
'content' => $this->makeImportsBare($response->getContent(), $dependencies, $extraFiles, $entry->type, $entry->getPackagePathString()),
'dependencies' => $dependencies,
'extraFiles' => [],
+ 'hasMainFile' => $entry->type->hasMainFile(),
];
if (0 !== \count($extraFiles)) {
@@ -317,6 +319,24 @@ private function makeImportsBare(string $content, array &$dependencies, array &$
return $content;
}
+ if (ImportMapType::FULL === $type) {
+ $data = json_decode($content);
+ $getFiles = function ($section, string $path) use(&$getFiles, &$extraFiles) {
+ foreach($section as $item) {
+ if($item->type == 'file') {
+ $extraFiles[] = $path . $item->name;
+ }
+ if($item->type == 'directory') {
+ $getFiles($item->files, $path . $item->name . '/' );
+ }
+ }
+ };
+
+ $getFiles($data->files, '/');
+
+ return $content;
+ }
+
preg_match_all(CssAssetUrlCompiler::ASSET_URL_PATTERN, $content, $matches);
foreach ($matches[1] as $path) {
if (str_starts_with($path, 'data:')) {
@@ -345,6 +365,10 @@ private function resolveUrlPattern(string $packageName, string $path, ?ImportMap
return self::URL_PATTERN_DIST_CSS;
}
+ if (ImportMapType::FULL === $type) {
+ return self::URL_PATTERN_FULL_DOWNLOAD;
+ }
+
return self::URL_PATTERN_DIST;
}
}
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/Resolver/PackageResolverInterface.php b/src/Symfony/Component/AssetMapper/ImportMap/Resolver/PackageResolverInterface.php
index 354fa9d151be7..afdef1f7237f3 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/Resolver/PackageResolverInterface.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/Resolver/PackageResolverInterface.php
@@ -37,7 +37,7 @@ public function resolvePackages(array $packagesToRequire): array;
*
* @param array $importMapEntries
*
- * @return array}>
+ * @return array, hasMainFile: bool}>
*/
public function downloadPackages(array $importMapEntries, ?callable $progressCallback = null): array;
}
diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php
index 6326036fc2ffd..45fb4eaa1124e 100644
--- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php
+++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/RemotePackageDownloaderTest.php
@@ -61,10 +61,10 @@ public function testDownloadPackagesDownloadsEverythingWithNoInstalled()
$progressCallback
)
->willReturn([
- 'foo' => ['content' => 'foo content', 'dependencies' => [], 'extraFiles' => ['/path/to/extra-file.woff' => 'extra file contents']],
- 'bar.js/file' => ['content' => 'bar content', 'dependencies' => [], 'extraFiles' => []],
- 'baz' => ['content' => 'baz content', 'dependencies' => ['foo'], 'extraFiles' => []],
- 'different_specifier' => ['content' => 'different content', 'dependencies' => [], 'extraFiles' => []],
+ 'foo' => ['content' => 'foo content', 'dependencies' => [], 'extraFiles' => ['/path/to/extra-file.woff' => 'extra file contents'], 'hasMainFile' => true],
+ 'bar.js/file' => ['content' => 'bar content', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
+ 'baz' => ['content' => 'baz content', 'dependencies' => ['foo'], 'extraFiles' => [], 'hasMainFile' => true],
+ 'different_specifier' => ['content' => 'different content', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
]);
$downloader = new RemotePackageDownloader(
@@ -131,9 +131,9 @@ public function testPackagesWithCorrectInstalledVersionSkipped()
$packageResolver->expects($this->once())
->method('downloadPackages')
->willReturn([
- 'bar.js/file' => ['content' => 'new bar content', 'dependencies' => [], 'extraFiles' => []],
- 'baz' => ['content' => 'new baz content', 'dependencies' => [], 'extraFiles' => []],
- 'has-missing-extra' => ['content' => 'new content', 'dependencies' => [], 'extraFiles' => ['/path/to/extra-file.woff' => 'extra file contents']],
+ 'bar.js/file' => ['content' => 'new bar content', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
+ 'baz' => ['content' => 'new baz content', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
+ 'has-missing-extra' => ['content' => 'new content', 'dependencies' => [], 'extraFiles' => ['/path/to/extra-file.woff' => 'extra file contents'], 'hasMainFile' => true],
]);
$downloader = new RemotePackageDownloader(
diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php
index ffb153fe54366..2da84991629be 100644
--- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php
+++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/Resolver/JsDelivrEsmResolverTest.php
@@ -305,7 +305,7 @@ public static function provideDownloadPackagesTests()
],
],
[
- 'lodash' => ['content' => 'lodash contents', 'dependencies' => [], 'extraFiles' => []],
+ 'lodash' => ['content' => 'lodash contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
],
];
@@ -318,7 +318,7 @@ public static function provideDownloadPackagesTests()
],
],
[
- 'lodash' => ['content' => 'lodash contents', 'dependencies' => [], 'extraFiles' => []],
+ 'lodash' => ['content' => 'lodash contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
],
];
@@ -331,7 +331,7 @@ public static function provideDownloadPackagesTests()
],
],
[
- 'lodash' => ['content' => 'chart.js contents', 'dependencies' => [], 'extraFiles' => []],
+ 'lodash' => ['content' => 'chart.js contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
],
];
@@ -344,7 +344,7 @@ public static function provideDownloadPackagesTests()
],
],
[
- 'lodash' => ['content' => 'bootstrap.css contents', 'dependencies' => [], 'extraFiles' => []],
+ 'lodash' => ['content' => 'bootstrap.css contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
],
];
@@ -369,9 +369,9 @@ public static function provideDownloadPackagesTests()
],
],
[
- 'lodash' => ['content' => 'lodash contents', 'dependencies' => [], 'extraFiles' => []],
- 'chart.js/auto' => ['content' => 'chart.js contents', 'dependencies' => [], 'extraFiles' => []],
- 'bootstrap/dist/bootstrap.css' => ['content' => 'bootstrap.css contents', 'dependencies' => [], 'extraFiles' => []],
+ 'lodash' => ['content' => 'lodash contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
+ 'chart.js/auto' => ['content' => 'chart.js contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
+ 'bootstrap/dist/bootstrap.css' => ['content' => 'bootstrap.css contents', 'dependencies' => [], 'extraFiles' => [], 'hasMainFile' => true],
],
];
@@ -390,6 +390,7 @@ public static function provideDownloadPackagesTests()
'content' => 'import{Color as t}from"@kurkle/color";function e(){}const i=(()=',
'dependencies' => ['@kurkle/color'],
'extraFiles' => [],
+ 'hasMainFile' => true,
],
],
];
@@ -409,6 +410,7 @@ public static function provideDownloadPackagesTests()
'content' => 'import e from"locutus/php/strings/sprintf";console.log()',
'dependencies' => ['locutus/php/strings/sprintf'],
'extraFiles' => [],
+ 'hasMainFile' => true,
],
],
];
@@ -429,6 +431,7 @@ public static function provideDownloadPackagesTests()
'content' => 'as Ticks,ta as TimeScale,ia as TimeSeriesScale,oo as Title,wo as Tooltip,Ci as _adapters,us as _detectPlatform,Ye as animator,Si as controllers,tn as default,St as defaults,Pn as elements,qi as layouts,ko as plugins,na as registerables,Ps as registry,sa as scales};',
'dependencies' => [],
'extraFiles' => [],
+ 'hasMainFile' => true,
],
],
];
@@ -453,6 +456,7 @@ public static function provideDownloadPackagesTests()
EOF,
'dependencies' => [],
'extraFiles' => [],
+ 'hasMainFile' => true,
],
],
];
@@ -471,6 +475,7 @@ public static function provideDownloadPackagesTests()
'content' => 'print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}',
'dependencies' => [],
'extraFiles' => [],
+ 'hasMainFile' => true,
],
],
];