11
11
12
12
namespace Symfony \Component \AssetMapper \ImportMap \Resolver ;
13
13
14
+ use Symfony \Component \AssetMapper \Compiler \CssAssetUrlCompiler ;
14
15
use Symfony \Component \AssetMapper \Exception \RuntimeException ;
15
16
use Symfony \Component \AssetMapper \ImportMap \ImportMapEntry ;
16
17
use Symfony \Component \AssetMapper \ImportMap \ImportMapType ;
17
18
use Symfony \Component \AssetMapper \ImportMap \PackageRequireOptions ;
19
+ use Symfony \Component \Filesystem \Path ;
18
20
use Symfony \Component \HttpClient \HttpClient ;
19
21
use Symfony \Contracts \HttpClient \Exception \HttpExceptionInterface ;
20
22
use Symfony \Contracts \HttpClient \HttpClientInterface ;
@@ -157,12 +159,11 @@ public function resolvePackages(array $packagesToRequire): array
157
159
/**
158
160
* @param ImportMapEntry[] $importMapEntries
159
161
*
160
- * @return array<string, array{content: string, dependencies: string[]}>
162
+ * @return array<string, array{content: string, dependencies: string[], extraFiles: array<string, string> }>
161
163
*/
162
164
public function downloadPackages (array $ importMapEntries , callable $ progressCallback = null ): array
163
165
{
164
166
$ responses = [];
165
-
166
167
foreach ($ importMapEntries as $ package => $ entry ) {
167
168
if (!$ entry ->isRemotePackage ()) {
168
169
throw new \InvalidArgumentException (sprintf ('The entry "%s" is not a remote package. ' , $ entry ->importName ));
@@ -171,12 +172,13 @@ public function downloadPackages(array $importMapEntries, callable $progressCall
171
172
$ pattern = ImportMapType::CSS === $ entry ->type ? self ::URL_PATTERN_DIST_CSS : self ::URL_PATTERN_DIST ;
172
173
$ url = sprintf ($ pattern , $ entry ->getPackageName (), $ entry ->version , $ entry ->getPackagePathString ());
173
174
174
- $ responses [$ package ] = $ this ->httpClient ->request ('GET ' , $ url );
175
+ $ responses [$ package ] = [ $ this ->httpClient ->request ('GET ' , $ url ), $ entry ] ;
175
176
}
176
177
177
178
$ errors = [];
178
179
$ contents = [];
179
- foreach ($ responses as $ package => $ response ) {
180
+ $ extraFileResponses = [];
181
+ foreach ($ responses as $ package => [$ response , $ entry ]) {
180
182
if (200 !== $ response ->getStatusCode ()) {
181
183
$ errors [] = [$ package , $ response ];
182
184
continue ;
@@ -187,10 +189,21 @@ public function downloadPackages(array $importMapEntries, callable $progressCall
187
189
}
188
190
189
191
$ dependencies = [];
192
+ $ extraFiles = [];
193
+ /* @var ImportMapEntry $entry */
190
194
$ contents [$ package ] = [
191
- 'content ' => $ this ->makeImportsBare ($ response ->getContent (), $ dependencies ),
195
+ 'content ' => $ this ->makeImportsBare ($ response ->getContent (), $ dependencies, $ extraFiles , $ entry -> type , $ entry -> getPackagePathString () ),
192
196
'dependencies ' => $ dependencies ,
197
+ 'extraFiles ' => [],
193
198
];
199
+
200
+ if (0 !== \count ($ extraFiles )) {
201
+ $ extraFileResponses [$ package ] = [];
202
+ foreach ($ extraFiles as $ extraFile ) {
203
+ $ extraFileResponses [$ package ][] = [$ this ->httpClient ->request ('GET ' , sprintf (self ::URL_PATTERN_DIST_CSS , $ entry ->getPackageName (), $ entry ->version , $ extraFile )), $ extraFile , $ entry ->getPackageName (), $ entry ->version ];
204
+ }
205
+ }
206
+
194
207
if ($ progressCallback ) {
195
208
$ progressCallback ($ package , 'finished ' , $ response , \count ($ responses ));
196
209
}
@@ -205,6 +218,47 @@ public function downloadPackages(array $importMapEntries, callable $progressCall
205
218
throw new RuntimeException (sprintf ('Error %d downloading packages from jsDelivr for "%s". Check your package names. Response: ' , $ response ->getStatusCode (), $ packages ).$ response ->getContent (false ), 0 , $ e );
206
219
}
207
220
221
+ $ extraFileErrors = [];
222
+ download_extra_files:
223
+ $ packageFileResponses = $ extraFileResponses ;
224
+ $ extraFileResponses = [];
225
+ foreach ($ packageFileResponses as $ package => $ responses ) {
226
+ foreach ($ responses as [$ response , $ extraFile , $ packageName , $ version ]) {
227
+ if (200 !== $ response ->getStatusCode ()) {
228
+ $ extraFileErrors [] = [$ package , $ response ];
229
+ continue ;
230
+ }
231
+
232
+ $ extraFiles = [];
233
+
234
+ $ content = $ response ->getContent ();
235
+ if (str_ends_with ($ extraFile , '.css ' )) {
236
+ $ content = $ this ->makeImportsBare ($ content , $ dependencies , $ extraFiles , ImportMapType::CSS , $ extraFile );
237
+ }
238
+ $ contents [$ package ]['extraFiles ' ][$ extraFile ] = $ content ;
239
+
240
+ if (0 !== \count ($ extraFiles )) {
241
+ $ extraFileResponses [$ package ] = [];
242
+ foreach ($ extraFiles as $ newExtraFile ) {
243
+ $ extraFileResponses [$ package ][] = [$ this ->httpClient ->request ('GET ' , sprintf (self ::URL_PATTERN_DIST_CSS , $ packageName , $ version , $ newExtraFile )), $ newExtraFile , $ packageName , $ version ];
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ if ($ extraFileResponses ) {
250
+ goto download_extra_files;
251
+ }
252
+
253
+ try {
254
+ ($ extraFileErrors [0 ][1 ] ?? null )?->getHeaders();
255
+ } catch (HttpExceptionInterface $ e ) {
256
+ $ response = $ e ->getResponse ();
257
+ $ packages = implode ('", " ' , array_column ($ extraFileErrors , 0 ));
258
+
259
+ throw new RuntimeException (sprintf ('Error %d downloading extra imported files from jsDelivr for "%s". Response: ' , $ response ->getStatusCode (), $ packages ).$ response ->getContent (false ), 0 , $ e );
260
+ }
261
+
208
262
return $ contents ;
209
263
}
210
264
@@ -237,20 +291,37 @@ private function fetchPackageRequirementsFromImports(string $content): array
237
291
*
238
292
* Replaces those with normal import "package/name" statements.
239
293
*/
240
- private function makeImportsBare (string $ content , array &$ dependencies ): string
294
+ private function makeImportsBare (string $ content , array &$ dependencies, array & $ extraFiles , ImportMapType $ type , string $ sourceFilePath ): string
241
295
{
242
- $ content = preg_replace_callback (self ::IMPORT_REGEX , function ($ matches ) use (&$ dependencies ) {
243
- $ packageName = $ matches [2 ].$ matches [4 ]; // add the path if any
244
- $ dependencies [] = $ packageName ;
245
-
246
- // replace the "/npm/package@version/+esm" with "package@version"
247
- return str_replace ($ matches [1 ], sprintf ('"%s" ' , $ packageName ), $ matches [0 ]);
248
- }, $ content );
249
-
250
- // source maps are not also downloaded - so remove the sourceMappingURL
251
- // remove the final one only (in case sourceMappingURL is used in the code)
252
- if (false !== $ lastPos = strrpos ($ content , '//# sourceMappingURL= ' )) {
253
- $ content = substr ($ content , 0 , $ lastPos ).preg_replace ('{//# sourceMappingURL=.*$}m ' , '' , substr ($ content , $ lastPos ));
296
+ if (ImportMapType::JS === $ type ) {
297
+ $ content = preg_replace_callback (self ::IMPORT_REGEX , function ($ matches ) use (&$ dependencies ) {
298
+ $ packageName = $ matches [2 ].$ matches [4 ]; // add the path if any
299
+ $ dependencies [] = $ packageName ;
300
+
301
+ // replace the "/npm/package@version/+esm" with "package@version"
302
+ return str_replace ($ matches [1 ], sprintf ('"%s" ' , $ packageName ), $ matches [0 ]);
303
+ }, $ content );
304
+
305
+ // source maps are not also downloaded - so remove the sourceMappingURL
306
+ // remove the final one only (in case sourceMappingURL is used in the code)
307
+ if (false !== $ lastPos = strrpos ($ content , '//# sourceMappingURL= ' )) {
308
+ $ content = substr ($ content , 0 , $ lastPos ).preg_replace ('{//# sourceMappingURL=.*$}m ' , '' , substr ($ content , $ lastPos ));
309
+ }
310
+
311
+ return $ content ;
312
+ }
313
+
314
+ preg_match_all (CssAssetUrlCompiler::ASSET_URL_PATTERN , $ content , $ matches );
315
+ foreach ($ matches [1 ] as $ path ) {
316
+ if (str_starts_with ($ path , 'data: ' )) {
317
+ continue ;
318
+ }
319
+
320
+ if (str_starts_with ($ path , 'http:// ' ) || str_starts_with ($ path , 'https:// ' )) {
321
+ continue ;
322
+ }
323
+
324
+ $ extraFiles [] = Path::join (\dirname ($ sourceFilePath ), $ path );
254
325
}
255
326
256
327
return preg_replace ('{/\*# sourceMappingURL=[^ ]*+ \*/} ' , '' , $ content );
0 commit comments