Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

Commit b57f8ba

Browse filesBrowse the repository at this point in the historyBrowse files
Amoifrnicolas-grekas
authored andcommitted
[AssetMapper] Add an importmap_entries option to stop exposing the other entrypoints
1 parent 93fcc92 commit b57f8ba
Copy full SHA for b57f8ba

14 files changed

+296-12Lines changed: 296 additions & 12 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
8.2
55
---
66

7+
* Add the `framework.asset_mapper.importmap_entries` option to limit the rendered import map to the entries reachable from the rendered entrypoints
78
* Add the `framework.mailer.tracking` option to set default open/click tracking for every outgoing message
89
* Add the `doctrine.orm.entity` tag to auto-excluded `#[Entity]` and `#[MappedSuperclass]` classes to allow discovering them
910
* Add `framework.rate_limiter.builder` option
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,11 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $
11101110
->end()
11111111
->defaultValue('es-module-shims')
11121112
->end()
1113+
->enumNode('importmap_entries')
1114+
->info('Which entries end up in the rendered importmap: "all" of them, or only the ones "reachable" from the rendered entrypoints (their eager and lazy import chains) plus the polyfill.')
1115+
->values(['all', 'reachable'])
1116+
->defaultValue('all')
1117+
->end()
11131118
->arrayNode('importmap_script_attributes', 'importmap_script_attribute')
11141119
->info('Key-value pair of attributes to add to script tags output for the importmap.')
11151120
->normalizeKeys(false)
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,7 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde
15301530
$container
15311531
->getDefinition('asset_mapper.importmap.generator')
15321532
->replaceArgument(3, $config['importmap_integrity_algorithms'])
1533+
->setArgument(4, $config['importmap_entries'])
15331534
;
15341535

15351536
$container
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ public function testAssetMapperCanBeEnabled()
349349
'extensions' => [],
350350
'importmap_path' => '%kernel.project_dir%/importmap.php',
351351
'importmap_polyfill' => 'es-module-shims',
352+
'importmap_entries' => 'all',
352353
'vendor_dir' => '%kernel.project_dir%/assets/vendor',
353354
'minimum_release_age' => 0,
354355
'importmap_script_attributes' => [],
@@ -1218,6 +1219,7 @@ protected static function getBundleDefaultConfig()
12181219
'extensions' => [],
12191220
'importmap_path' => '%kernel.project_dir%/importmap.php',
12201221
'importmap_polyfill' => 'es-module-shims',
1222+
'importmap_entries' => 'all',
12211223
'vendor_dir' => '%kernel.project_dir%/assets/vendor',
12221224
'minimum_release_age' => 0,
12231225
'importmap_script_attributes' => [],
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3538,6 +3538,28 @@ public function testAssetMapperMinimumReleaseAge()
35383538
$this->assertSame(604800, $container->getDefinition('asset_mapper.importmap.update_checker')->getArgument(3));
35393539
}
35403540

3541+
public function testAssetMapperImportmapEntries()
3542+
{
3543+
$container = $this->createContainerFromClosure(static function ($container) {
3544+
$container->loadFromExtension('framework', [
3545+
'http_method_override' => false,
3546+
'handle_all_throwables' => true,
3547+
'php_errors' => ['log' => true],
3548+
'assets' => null,
3549+
'asset_mapper' => [
3550+
'paths' => ['assets/'],
3551+
'importmap_entries' => 'reachable',
3552+
'importmap_polyfill' => 'my-polyfill',
3553+
],
3554+
]);
3555+
});
3556+
3557+
$definition = $container->getDefinition('asset_mapper.importmap.generator');
3558+
$this->assertSame('reachable', $definition->getArgument(4));
3559+
// the polyfill name is configured on the renderer only, and handed over at render time
3560+
$this->assertSame('my-polyfill', $container->getDefinition('asset_mapper.importmap.renderer')->getArgument(3));
3561+
}
3562+
35413563
public function testDefaultLock()
35423564
{
35433565
$container = $this->createContainerFromFile('lock');
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/schema.json‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/schema.json
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,21 @@
13931393
"default": "es-module-shims",
13941394
"description": "The importmap name that will be used to load the polyfill. Set to false to disable."
13951395
},
1396+
"importmap_entries": {
1397+
"anyOf": [
1398+
{
1399+
"enum": [
1400+
"all",
1401+
"reachable"
1402+
]
1403+
},
1404+
{
1405+
"$ref": "#/$defs/types/param"
1406+
}
1407+
],
1408+
"default": "all",
1409+
"description": "Which entries end up in the rendered importmap: \"all\" of them, or only the ones \"reachable\" from the rendered entrypoints (their eager and lazy import chains) plus the polyfill."
1410+
},
13961411
"importmap_script_attributes": {
13971412
"$ref": "#/$defs/types/object_null",
13981413
"additionalProperties": {
@@ -1532,6 +1547,7 @@
15321547
"extensions": [],
15331548
"importmap_path": "%kernel.project_dir%/importmap.php",
15341549
"importmap_polyfill": "es-module-shims",
1550+
"importmap_entries": "all",
15351551
"importmap_script_attributes": [],
15361552
"importmap_integrity_algorithms": [],
15371553
"vendor_dir": "%kernel.project_dir%/assets/vendor",
Collapse file

‎src/Symfony/Component/AssetMapper/CHANGELOG.md‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/AssetMapper/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
8.2
55
---
66

7+
* Add an `$entries` argument to `ImportMapGenerator` to restrict the import map to the entries reachable from the rendered entrypoints
78
* Defined stable alphabetical order of importmap entries
89
* Add support for adding integrity metadata to importmaps
910
* Add a `--no-esm` option to `importmap:require` and an `esm` option to importmap entries
Collapse file

‎src/Symfony/Component/AssetMapper/Command/AssetMapperCompileCommand.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/AssetMapper/Command/AssetMapperCompileCommand.php
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6868
$this->compiledConfigReader->removeConfig(AssetMapper::MANIFEST_FILE_NAME);
6969
$this->compiledConfigReader->removeConfig(ImportMapGenerator::IMPORT_MAP_CACHE_FILENAME);
7070
$entrypointFiles = [];
71+
$reachableEntrypointFiles = [];
72+
$limitedToReachableEntries = $this->importMapGenerator->isLimitedToReachableEntries();
7173
foreach ($this->importMapGenerator->getEntrypointNames() as $entrypointName) {
7274
$path = \sprintf(ImportMapGenerator::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entrypointName);
7375
$this->compiledConfigReader->removeConfig($path);
7476
$entrypointFiles[$entrypointName] = $path;
77+
78+
// always removed, so that turning the option off does not leave a stale file behind
79+
$reachablePath = \sprintf(ImportMapGenerator::ENTRYPOINT_REACHABLE_CACHE_FILENAME_PATTERN, $entrypointName);
80+
$this->compiledConfigReader->removeConfig($reachablePath);
81+
if ($limitedToReachableEntries) {
82+
$reachableEntrypointFiles[$entrypointName] = $reachablePath;
83+
}
7584
}
7685

7786
$manifest = $this->createManifestAndWriteFiles($io);
@@ -83,6 +92,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8392

8493
foreach ($entrypointFiles as $entrypointName => $path) {
8594
$this->compiledConfigReader->saveConfig($path, $this->importMapGenerator->findEagerEntrypointImports($entrypointName));
95+
if (isset($reachableEntrypointFiles[$entrypointName])) {
96+
$this->compiledConfigReader->saveConfig($reachableEntrypointFiles[$entrypointName], $this->importMapGenerator->findReachableEntrypointImports($entrypointName));
97+
}
8698
}
8799
$styledEntrypointNames = array_map(static fn (string $entrypointName) => \sprintf('<info>%s</>', $entrypointName), array_keys($entrypointFiles));
88100
$io->comment(\sprintf('Entrypoint metadata written for <comment>%d</> entrypoints (%s).', \count($entrypointFiles), implode(', ', $styledEntrypointNames)));
Collapse file

‎src/Symfony/Component/AssetMapper/ImportMap/ImportMapGenerator.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/AssetMapper/ImportMap/ImportMapGenerator.php
+57-5Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,38 @@ class ImportMapGenerator
2323
{
2424
public const IMPORT_MAP_CACHE_FILENAME = 'importmap.json';
2525
public const ENTRYPOINT_CACHE_FILENAME_PATTERN = 'entrypoint.%s.json';
26+
public const ENTRYPOINT_REACHABLE_CACHE_FILENAME_PATTERN = 'entrypoint.reachable.%s.json';
27+
28+
public const ENTRIES_ALL = 'all';
29+
public const ENTRIES_REACHABLE = 'reachable';
2630

2731
private const INTEGRITY_HASH_ALGORITHMS = ['sha256', 'sha384', 'sha512'];
2832

33+
/**
34+
* @param self::ENTRIES_* $entries
35+
*/
2936
public function __construct(
3037
private readonly AssetMapperInterface $assetMapper,
3138
private readonly CompiledAssetMapperConfigReader $compiledConfigReader,
3239
private readonly ImportMapConfigReader $importMapConfigReader,
3340
private readonly array $integrityHashAlgorithms = [],
41+
private readonly string $entries = self::ENTRIES_ALL,
3442
) {
3543
if ($unsupportedAlgorithms = array_diff($this->integrityHashAlgorithms, self::INTEGRITY_HASH_ALGORITHMS)) {
3644
throw new LogicException(\sprintf('Unsupported integrity hash algorithm "%s". Supported ones are "%s".', implode('", "', $unsupportedAlgorithms), implode('", "', self::INTEGRITY_HASH_ALGORITHMS)));
3745
}
46+
47+
if (!\in_array($this->entries, [self::ENTRIES_ALL, self::ENTRIES_REACHABLE], true)) {
48+
throw new LogicException(\sprintf('Unsupported import map entries "%s". Supported ones are "%s" and "%s".', $this->entries, self::ENTRIES_ALL, self::ENTRIES_REACHABLE));
49+
}
50+
}
51+
52+
/**
53+
* @internal
54+
*/
55+
public function isLimitedToReachableEntries(): bool
56+
{
57+
return self::ENTRIES_REACHABLE === $this->entries;
3858
}
3959

4060
/**
@@ -60,7 +80,7 @@ public function getEntrypointNames(): array
6080
*
6181
* @internal
6282
*/
63-
public function getImportMapData(array $entrypointNames): array
83+
public function getImportMapData(array $entrypointNames, ?string $polyfillImportName = null): array
6484
{
6585
$rawImportMapData = $this->getRawImportMapData();
6686
$finalImportMapData = [];
@@ -83,6 +103,17 @@ public function getImportMapData(array $entrypointNames): array
83103
}
84104
}
85105

106+
if (self::ENTRIES_REACHABLE === $this->entries) {
107+
$reachableImports = null === $polyfillImportName ? [] : [$polyfillImportName => true];
108+
foreach ($entrypointNames as $entrypointName) {
109+
foreach ($this->findReachableEntrypointImports($entrypointName) as $import) {
110+
$reachableImports[$import] = true;
111+
}
112+
}
113+
114+
$rawImportMapData = array_intersect_key($rawImportMapData, $reachableImports);
115+
}
116+
86117
return array_merge($finalImportMapData, $rawImportMapData);
87118
}
88119

@@ -135,6 +166,27 @@ public function findEagerEntrypointImports(string $entryName): array
135166
return $this->compiledConfigReader->loadConfig(\sprintf(self::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entryName));
136167
}
137168

169+
return $this->findImports($this->getEntrypointAsset($entryName), false);
170+
}
171+
172+
/**
173+
* Given an importmap entry name, finds all the module imports in its chain, the lazy ones included.
174+
*
175+
* @internal
176+
*
177+
* @return array<string> The array of import names
178+
*/
179+
public function findReachableEntrypointImports(string $entryName): array
180+
{
181+
if ($this->compiledConfigReader->configExists(\sprintf(self::ENTRYPOINT_REACHABLE_CACHE_FILENAME_PATTERN, $entryName))) {
182+
return $this->compiledConfigReader->loadConfig(\sprintf(self::ENTRYPOINT_REACHABLE_CACHE_FILENAME_PATTERN, $entryName));
183+
}
184+
185+
return $this->findImports($this->getEntrypointAsset($entryName), true);
186+
}
187+
188+
private function getEntrypointAsset(string $entryName): MappedAsset
189+
{
138190
$rootImportEntries = $this->importMapConfigReader->getEntries();
139191
if (!$rootImportEntries->has($entryName)) {
140192
throw new \InvalidArgumentException(\sprintf('The entrypoint "%s" does not exist in "importmap.php".', $entryName));
@@ -153,7 +205,7 @@ public function findEagerEntrypointImports(string $entryName): array
153205
throw new \InvalidArgumentException(\sprintf('The path "%s" of the entrypoint "%s" mentioned in "importmap.php" cannot be found in any asset map paths.', $rootImportEntries->get($entryName)->path, $entryName));
154206
}
155207

156-
return $this->findEagerImports($asset);
208+
return $asset;
157209
}
158210

159211
/**
@@ -234,18 +286,18 @@ private function findAsset(string $path): ?MappedAsset
234286
}
235287

236288
/**
237-
* Finds recursively all the non-lazy modules imported by an asset.
289+
* Finds recursively all the modules imported by an asset, the lazy ones included or not.
238290
*
239291
* @return array<string> The array of deduplicated import names
240292
*/
241-
private function findEagerImports(MappedAsset $asset): array
293+
private function findImports(MappedAsset $asset, bool $includeLazy): array
242294
{
243295
$dependencies = [];
244296
$queue = [$asset];
245297

246298
while ($asset = array_shift($queue)) {
247299
foreach ($asset->getJavaScriptImports() as $javaScriptImport) {
248-
if ($javaScriptImport->isLazy) {
300+
if (!$includeLazy && $javaScriptImport->isLazy) {
249301
continue;
250302
}
251303
if (isset($dependencies[$javaScriptImport->importName])) {
Collapse file

‎src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public function render(string|array $entryPoint, array $attributes = []): string
4848
{
4949
$entryPoint = (array) $entryPoint;
5050

51-
$importMapData = $this->importMapGenerator->getImportMapData($entryPoint);
51+
$importMapData = $this->importMapGenerator->getImportMapData($entryPoint, $this->polyfillImportName ?: null);
5252
$importMap = [];
5353
$integrity = [];
5454
$modulePreloads = [];

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.