/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/AssetMapper/ImportMap/ImportMapConfigReader.php
200 строк
8 KB
Ousama Ben Younes
[AssetMapper] Allow requiring raw ESM packages
05 авг 2026, 18:37
05 авг 2026, 18:37
68230e2
Код
Авторство
О чём код?
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\AssetMapper\ImportMap; use Symfony\Component\AssetMapper\Exception\RuntimeException; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Path; use Symfony\Component\VarExporter\VarExporter; /** * Reads/Writes the importmap.php file and returns the list of entries. * * @author Ryan Weaver <ryan@symfonycasts.com> */ class ImportMapConfigReader { private ImportMapEntries $rootImportMapEntries; private readonly Filesystem $filesystem; public function __construct( private readonly string $importMapConfigPath, private readonly RemotePackageStorage $remotePackageStorage, ) { $this->filesystem = new Filesystem(); } public function getEntries(): ImportMapEntries { if (isset($this->rootImportMapEntries)) { return $this->rootImportMapEntries; } $configPath = $this->importMapConfigPath; $importMapConfig = is_file($configPath) ? \Closure::bind(static fn () => include func_get_arg(0), null, null)($configPath) : []; if (!\is_array($importMapConfig)) { throw new RuntimeException(\sprintf('The "%s" file must return an array, got "%s".', $configPath, get_debug_type($importMapConfig))); } $entries = new ImportMapEntries(); foreach ($importMapConfig as $importName => $data) { $validKeys = ['path', 'version', 'type', 'entrypoint', 'package_specifier', 'esm']; if ($invalidKeys = array_diff(array_keys($data), $validKeys)) { throw new \InvalidArgumentException(\sprintf('The following keys are not valid for the importmap entry "%s": "%s". Valid keys are: "%s".', $importName, implode('", "', $invalidKeys), implode('", "', $validKeys))); } $type = ImportMapType::tryFrom($data['type'] ?? 'js') ?? throw new RuntimeException(\sprintf('The importmap entry "%s" has an invalid "type" value "%s". Valid values are: "%s".', $importName, $data['type'], implode('", "', array_column(ImportMapType::cases(), 'value')))); $isEntrypoint = $data['entrypoint'] ?? false; if (isset($data['path'])) { if (isset($data['version'])) { throw new RuntimeException(\sprintf('The importmap entry "%s" cannot have both a "path" and "version" option.', $importName)); } if (isset($data['package_specifier'])) { throw new RuntimeException(\sprintf('The importmap entry "%s" cannot have both a "path" and "package_specifier" option.', $importName)); } $entries->add(ImportMapEntry::createLocal($importName, $type, $data['path'], $isEntrypoint)); continue; } $version = $data['version'] ?? null; if (null === $version) { throw new RuntimeException(\sprintf('The importmap entry "%s" must have either a "path" or "version" option.', $importName)); } $packageModuleSpecifier = $data['package_specifier'] ?? $importName; $entries->add($this->createRemoteEntry($importName, $type, $version, $packageModuleSpecifier, $isEntrypoint, $data['esm'] ?? true)); } return $this->rootImportMapEntries = $entries; } public function writeEntries(ImportMapEntries $entries): void { $this->rootImportMapEntries = $entries; $importMapConfig = []; foreach ($entries as $entry) { $config = []; if ($entry->isRemotePackage()) { $config['version'] = $entry->version; if ($entry->packageModuleSpecifier !== $entry->importName) { $config['package_specifier'] = $entry->packageModuleSpecifier; } if (!$entry->useEsm) { $config['esm'] = false; } } else { $config['path'] = $entry->path; } if (ImportMapType::JS !== $entry->type) { $config['type'] = $entry->type->value; } if ($entry->isEntrypoint) { $config['entrypoint'] = true; } $importMapConfig[$entry->importName] = $config; } $map = class_exists(VarExporter::class) ? VarExporter::export($importMapConfig) : var_export($importMapConfig, true); $this->filesystem->dumpFile($this->importMapConfigPath, <<<EOF <?php /** * Returns the importmap for this application. * * - "path" is a path inside the asset mapper system. Use the * "debug:asset-map" command to see the full list of paths. * * - "entrypoint" (JavaScript only) set to true for any module that will * be used as an "entrypoint" (and passed to the importmap() Twig function). * * The "importmap:require" command can be used to add new entries to this file. * * @return array<string, array{ // Import name as key, description of the imported file as value * path: string, // Logical, relative or absolute path to the file * type?: 'js'|'css'|'json', // Type of the file, defaults to 'js' * entrypoint?: bool, // Whether the file is an entrypoint, for 'js' only * }|array{ * version: string, // Version of the remote package * package_specifier?: string, // Remote "package-name/path" specifier, defaults to the import name * type?: 'js'|'css'|'json', * entrypoint?: bool, * esm?: bool, // Whether jsDelivr's ESM build is used, defaults to true * }> */ return $map; EOF); } public function findRootImportMapEntry(string $moduleName): ?ImportMapEntry { $entries = $this->getEntries(); return $entries->has($moduleName) ? $entries->get($moduleName) : null; } /** * @param bool $useEsm Whether jsDelivr's ESM build is used instead of the raw package files */ public function createRemoteEntry(string $importName, ImportMapType $type, string $version, string $packageModuleSpecifier, bool $isEntrypoint /* , bool $useEsm = true */): ImportMapEntry { $useEsm = 5 < \func_num_args() ? func_get_arg(5) : true; $path = $this->remotePackageStorage->getDownloadPath($packageModuleSpecifier, $type); return ImportMapEntry::createRemote($importName, $type, $path, $version, $packageModuleSpecifier, $isEntrypoint, $useEsm); } /** * Converts the "path" string from an importmap entry to the filesystem path. * * The path may already be a filesystem path. But if it starts with ".", * then the path is relative and the root directory is prepended. */ public function convertPathToFilesystemPath(string $path): string { if (!str_starts_with($path, '.')) { return $path; } return Path::join($this->getRootDirectory(), $path); } /** * Converts a filesystem path to a relative path that can be used in the importmap. * * If no relative path could be created - e.g. because the path is not in * the same directory/subdirectory as the root importmap.php file - null is returned. */ public function convertFilesystemPathToPath(string $filesystemPath): ?string { $rootImportMapDir = realpath($this->getRootDirectory()); $filesystemPath = realpath($filesystemPath); if (!str_starts_with($filesystemPath, $rootImportMapDir)) { return null; } // remove the root directory, prepend "./" & normalize slashes return './'.str_replace('\\', '/', substr($filesystemPath, \strlen($rootImportMapDir) + 1)); } private function getRootDirectory(): string { return \dirname($this->importMapConfigPath); } }