Skip to content

Commit b641188

Browse files
[AssetMapper] Add support for loading JSON using import statements
1 parent e59b73b commit b641188

File tree

6 files changed

+31
-23
lines changed

6 files changed

+31
-23
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ protected function configure(): void
4343
{
4444
$this
4545
->addArgument('name', InputArgument::OPTIONAL, 'An asset name (or a path) to search for (e.g. "app")')
46-
->addOption('ext', null, InputOption::VALUE_REQUIRED, 'Filter assets by extension (e.g. "css")', null, ['js', 'css', 'png'])
46+
->addOption('ext', null, InputOption::VALUE_REQUIRED, 'Filter assets by extension (e.g. "css")', null, ['js', 'css', 'json'])
4747
->addOption('full', null, null, 'Whether to show the full paths')
4848
->addOption('vendor', null, InputOption::VALUE_NEGATABLE, 'Only show assets from vendor packages')
4949
->setHelp(<<<'EOT'

src/Symfony/Component/AssetMapper/ImportMap/ImportMapConfigReader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function getEntries(): ImportMapEntries
4949
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)));
5050
}
5151

52-
$type = isset($data['type']) ? ImportMapType::tryFrom($data['type']) : ImportMapType::JS;
52+
$type = ImportMapType::tryFrom($data['type'] ?? 'js') ?? ImportMapType::JS;
5353
$isEntrypoint = $data['entrypoint'] ?? false;
5454

5555
if (isset($data['path'])) {

src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public function requirePackages(array $packagesToRequire, ImportMapEntries $impo
162162

163163
$newEntry = ImportMapEntry::createLocal(
164164
$requireOptions->importName,
165-
self::getImportMapTypeFromFilename($requireOptions->path),
165+
ImportMapType::tryFrom(pathinfo($path, \PATHINFO_EXTENSION)) ?? ImportMapType::JS,
166166
$path,
167167
$requireOptions->entrypoint,
168168
);
@@ -200,11 +200,6 @@ private function cleanupPackageFiles(ImportMapEntry $entry): void
200200
}
201201
}
202202

203-
private static function getImportMapTypeFromFilename(string $path): ImportMapType
204-
{
205-
return str_ends_with($path, '.css') ? ImportMapType::CSS : ImportMapType::JS;
206-
}
207-
208203
/**
209204
* Finds the MappedAsset allowing for a "logical path", relative or absolute filesystem path.
210205
*/

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class ImportMapRenderer
3131
private const DEFAULT_ES_MODULE_SHIMS_POLYFILL_URL = 'https://ga.jspm.io/npm:es-module-shims@1.10.0/dist/es-module-shims.js';
3232
private const DEFAULT_ES_MODULE_SHIMS_POLYFILL_INTEGRITY = 'sha384-ie1x72Xck445i0j4SlNJ5W5iGeL3Dpa0zD48MZopgWsjNB/lt60SuG1iduZGNnJn';
3333

34+
private const LOADER_JSON = "export default (async()=>await(await fetch('%s')).json())()";
35+
private const LOADER_CSS = "document.head.appendChild(Object.assign(document.createElement('link'),{rel:'stylesheet',href:'%s'}))";
36+
3437
public function __construct(
3538
private readonly ImportMapGenerator $importMapGenerator,
3639
private readonly ?Packages $assetPackages = null,
@@ -48,7 +51,7 @@ public function render(string|array $entryPoint, array $attributes = []): string
4851
$importMapData = $this->importMapGenerator->getImportMapData($entryPoint);
4952
$importMap = [];
5053
$modulePreloads = [];
51-
$cssLinks = [];
54+
$webLinks = [];
5255
$polyfillPath = null;
5356
foreach ($importMapData as $importName => $data) {
5457
$path = $data['path'];
@@ -70,29 +73,34 @@ public function render(string|array $entryPoint, array $attributes = []): string
7073
}
7174

7275
$preload = $data['preload'] ?? false;
73-
if ('css' !== $data['type']) {
76+
if ('json' === $data['type']) {
77+
$importMap[$importName] = 'data:application/javascript,'.str_replace('%', '%25', \sprintf(self::LOADER_JSON, addslashes($path)));
78+
if ($preload) {
79+
$webLinks[$path] = 'fetch';
80+
}
81+
} elseif ('css' !== $data['type']) {
7482
$importMap[$importName] = $path;
7583
if ($preload) {
7684
$modulePreloads[] = $path;
7785
}
7886
} elseif ($preload) {
79-
$cssLinks[] = $path;
87+
$webLinks[$path] = 'style';
8088
// importmap entry is a noop
8189
$importMap[$importName] = 'data:application/javascript,';
8290
} else {
83-
$importMap[$importName] = 'data:application/javascript,'.rawurlencode(\sprintf('document.head.appendChild(Object.assign(document.createElement("link"),{rel:"stylesheet",href:"%s"}))', addslashes($path)));
91+
$importMap[$importName] = 'data:application/javascript,'.str_replace('%', '%25', \sprintf(self::LOADER_CSS, addslashes($path)));
8492
}
8593
}
8694

8795
$output = '';
88-
foreach ($cssLinks as $url) {
89-
$url = $this->escapeAttributeValue($url);
90-
91-
$output .= "\n<link rel=\"stylesheet\" href=\"$url\">";
96+
foreach ($webLinks as $url => $as) {
97+
if ('style' === $as) {
98+
$output .= "\n<link rel=\"stylesheet\" href=\"{$this->escapeAttributeValue($url)}\">";
99+
}
92100
}
93101

94102
if (class_exists(AddLinkHeaderListener::class) && $request = $this->requestStack?->getCurrentRequest()) {
95-
$this->addWebLinkPreloads($request, $cssLinks);
103+
$this->addWebLinkPreloads($request, $webLinks);
96104
}
97105

98106
$scriptAttributes = $attributes || $this->scriptAttributes ? ' '.$this->createAttributesString($attributes) : '';
@@ -186,12 +194,16 @@ private function createAttributesString(array $attributes, string $pattern = '%s
186194
return $attributeString;
187195
}
188196

189-
private function addWebLinkPreloads(Request $request, array $cssLinks): void
197+
private function addWebLinkPreloads(Request $request, array $links): void
190198
{
191-
$cssPreloadLinks = array_map(fn ($url) => (new Link('preload', $url))->withAttribute('as', 'style'), $cssLinks);
199+
foreach ($links as $url => $as) {
200+
$links[$url] = (new Link('preload', $url))
201+
->withAttribute('crossorigin', 'anonymous')
202+
->withAttribute('as', $as);
203+
}
192204

193205
if (null === $linkProvider = $request->attributes->get('_links')) {
194-
$request->attributes->set('_links', new GenericLinkProvider($cssPreloadLinks));
206+
$request->attributes->set('_links', new GenericLinkProvider($links));
195207

196208
return;
197209
}
@@ -200,7 +212,7 @@ private function addWebLinkPreloads(Request $request, array $cssLinks): void
200212
return;
201213
}
202214

203-
foreach ($cssPreloadLinks as $link) {
215+
foreach ($links as $link) {
204216
$linkProvider = $linkProvider->withLink($link);
205217
}
206218

src/Symfony/Component/AssetMapper/ImportMap/ImportMapType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ enum ImportMapType: string
1515
{
1616
case JS = 'js';
1717
case CSS = 'css';
18+
case JSON = 'json';
1819
}

src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapRendererTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public function testBasicRender()
9292
$this->assertStringContainsString('"app_css_preload": "data:application/javascript,', $html);
9393
$this->assertStringContainsString('<link rel="stylesheet" href="/subdirectory/assets/styles/app-preload-d1g35t.css">', $html);
9494
// non-preloaded CSS file
95-
$this->assertStringContainsString('"app_css_no_preload": "data:application/javascript,document.head.appendChild%28Object.assign%28document.createElement%28%22link%22%29%2C%7Brel%3A%22stylesheet%22%2Chref%3A%22%2Fsubdirectory%2Fassets%2Fstyles%2Fapp-nopreload-d1g35t.css%22%7D', $html);
95+
$this->assertStringContainsString('"app_css_no_preload": "data:application/javascript,document.head.appendChild(Object.assign(document.createElement(\'link\'),{rel:\'stylesheet\',href:\'/subdirectory/assets/styles/app-nopreload-d1g35t.css\'}))', $html);
9696
$this->assertStringNotContainsString('<link rel="stylesheet" href="/subdirectory/assets/styles/app-nopreload-d1g35t.css">', $html);
9797
// remote js
9898
$this->assertStringContainsString('"remote_js": "https://cdn.example.com/assets/remote-d1g35t.js"', $html);
@@ -207,7 +207,7 @@ public function testItAddsPreloadLinks()
207207
$this->assertInstanceOf(GenericLinkProvider::class, $linkProvider);
208208
$this->assertCount(1, $linkProvider->getLinks());
209209
$this->assertSame(['preload'], $linkProvider->getLinks()[0]->getRels());
210-
$this->assertSame(['as' => 'style'], $linkProvider->getLinks()[0]->getAttributes());
210+
$this->assertSame(['crossorigin' => 'anonymous', 'as' => 'style'], $linkProvider->getLinks()[0]->getAttributes());
211211
$this->assertSame('/assets/styles/app-preload-d1g35t.css', $linkProvider->getLinks()[0]->getHref());
212212
}
213213
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy