4
4
5
5
use Composer \Composer ;
6
6
use Composer \EventDispatcher \EventSubscriberInterface ;
7
+ use Composer \IO \ConsoleIO ;
7
8
use Composer \IO \IOInterface ;
8
9
use Composer \Json \JsonFile ;
9
- use Composer \Package \CompletePackage ;
10
+ use Composer \Package \CompletePackageInterface ;
10
11
use Composer \Package \Package ;
11
12
use Composer \Package \PackageInterface ;
12
13
use Composer \Plugin \Capable ;
@@ -309,12 +310,14 @@ protected function removeUnusedLibraries(array $old_libraries) {
309
310
* The currently installed libraries.
310
311
*/
311
312
protected function downloadLibraries (array $ processed_libraries , array $ applied_drupal_libraries ) {
313
+ $ download_promises = [];
314
+ $ install_promises = [];
315
+
316
+ $ libraries_to_install = [];
312
317
foreach ($ processed_libraries as $ library_name => $ processed_library ) {
313
318
$ library_package = $ this ->getLibraryPackage ($ library_name , $ processed_library );
314
-
315
- $ ignore_patterns = $ processed_library ['ignore ' ];
316
- $ rename = $ processed_library ['rename ' ] ?? NULL ;
317
319
$ install_path = $ this ->installationManager ->getInstallPath ($ library_package );
320
+
318
321
if (
319
322
(
320
323
!isset ($ applied_drupal_libraries [$ library_name ]) ||
@@ -326,25 +329,103 @@ protected function downloadLibraries(array $processed_libraries, array $applied_
326
329
// - wasn't in the lock file.
327
330
// - doesn't match what is in the lock file.
328
331
// - doesn't exist on disk.
329
- $ this ->downloadPackage ($ library_package , $ install_path , $ ignore_patterns , $ rename );
332
+ $ download_result = $ this ->downloadManager ->download ($ library_package , $ install_path );
333
+ if ($ download_result instanceof PromiseInterface) {
334
+ // https://github.com/composer/composer/issues/9209
335
+ /* @see \Composer\Util\SyncHelper::downloadAndInstallPackageSync */
336
+ $ download_promises [] = $ download_result
337
+ // Prepare for install.
338
+ ->then (function () use ($ library_package , $ install_path ) {
339
+ return $ this ->downloadManager ->prepare ('install ' , $ library_package , $ install_path );
340
+ })
341
+ // Clean up after any download errors.
342
+ ->then (NULL , function ($ e ) use ($ library_package , $ install_path ) {
343
+ $ this ->composer ->getLoop ()
344
+ ->wait ([
345
+ $ this ->downloadManager ->cleanup ('install ' , $ library_package , $ install_path ),
346
+ ]);
347
+ throw $ e ;
348
+ });
349
+
350
+ // Install after the download resolves.
351
+ $ libraries_to_install [] = [
352
+ $ library_name ,
353
+ $ library_package ,
354
+ $ install_path ,
355
+ ];
356
+ }
357
+ else {
358
+ // Attempt to install synchronously.
359
+ $ install_result = $ this ->installPackage ($ library_package , $ install_path , $ processed_library );
360
+ if ($ install_result instanceof PromiseInterface) {
361
+ $ install_promises [] = $ install_result ;
362
+ }
363
+ }
330
364
}
331
365
}
366
+
367
+ if (count ($ download_promises )) {
368
+ // Wait on the download asynchronous promises to resolve.
369
+ $ this ->waitOnPromises ($ download_promises );
370
+ }
371
+
372
+ foreach ($ libraries_to_install as $ library_to_install ) {
373
+ [$ library_name , $ library_package , $ install_path ] = $ library_to_install ;
374
+ $ install_result = $ this ->installPackage ($ library_package , $ install_path , $ processed_libraries [$ library_name ]);
375
+ if ($ install_result instanceof PromiseInterface) {
376
+ $ install_promises [] = $ install_result ;
377
+ }
378
+ }
379
+
380
+ if (count ($ install_promises )) {
381
+ // Wait on the install promises to resolve.
382
+ $ this ->composer ->getLoop ()->wait ($ install_promises );
383
+ }
332
384
}
333
385
334
386
/**
335
- * Downloads a library package to disk.
387
+ * Wait synchronously for an array of promises to resolve.
388
+ *
389
+ * @param array $promises
390
+ * Promises to await.
391
+ */
392
+ protected function waitOnPromises (array $ promises ) {
393
+ $ progress = NULL ;
394
+ if ($ this ->io instanceof ConsoleIO && !$ this ->io ->isDebug () && count ($ promises ) > 1 && !getenv ('COMPOSER_DISABLE_PROGRESS_BAR ' )) {
395
+ // Disable progress bar by setting COMPOSER_DISABLE_PROGRESS_BAR=1 as we
396
+ // are unable to read composer's "--no-input" option easily from here
397
+ // without introducing extra complexity with the PluginEvents::COMMAND
398
+ // event.
399
+ $ progress = $ this ->io ->getProgressBar ();
400
+ }
401
+ $ this ->composer ->getLoop ()->wait ($ promises , $ progress );
402
+ if ($ progress ) {
403
+ $ progress ->clear ();
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Installs a library package to disk.
336
409
*
337
410
* @param \Composer\Package\Package $library_package
338
411
* The library package.
339
412
* @param string $install_path
340
413
* The package install path.
341
- * @param array $ignore_patterns
342
- * File patterns to ignore.
343
- * @param array|null $rename
344
- * Array mapping of files/folders to rename.
414
+ * @param array $processed_library
415
+ * The library definition.
416
+ *
417
+ * @return \React\Promise\PromiseInterface|void
418
+ * Returns a promise or void.
345
419
*/
346
- protected function downloadPackage (Package $ library_package , $ install_path , array $ ignore_patterns , array $ rename = NULL ) {
347
- $ process_download = function () use ($ install_path , $ ignore_patterns , $ rename ) {
420
+ protected function installPackage (Package $ library_package , $ install_path , array $ processed_library ) {
421
+ $ ignore_patterns = $ processed_library ['ignore ' ];
422
+ $ rename = $ processed_library ['rename ' ] ?? NULL ;
423
+
424
+ $ process_install = function () use ($ library_package , $ install_path , $ ignore_patterns , $ rename ) {
425
+ if ($ ignore_patterns || $ rename ) {
426
+ $ package_name = $ library_package ->getName ();
427
+ $ this ->io ->writeError (" - Processing <info> $ package_name</info> files... " );
428
+ }
348
429
349
430
// Delete files/folders according to the ignore pattern(s).
350
431
if ($ ignore_patterns ) {
@@ -411,17 +492,32 @@ function ($file) use ($patterns) {
411
492
}
412
493
};
413
494
414
- // Let composer download and unpack the library for us!
415
- $ promise = $ this ->downloadManager ->download ($ library_package , $ install_path );
495
+ if (version_compare (PluginInterface::PLUGIN_API_VERSION , '2.0 ' , '>= ' )) {
496
+ // Install the package after downloading (Composer 2 only).
497
+ $ promise = $ this ->downloadManager ->install ($ library_package , $ install_path );
416
498
417
- // Composer v2 might return a promise here.
418
- if ($ promise && $ promise instanceof PromiseInterface) {
419
- $ promise ->then ($ process_download );
420
- return ;
499
+ if (!($ promise instanceof PromiseInterface)) {
500
+ // Not a promise, create one that can be cleaned up after.
501
+ $ promise = \React \Promise \resolve ();
502
+ }
503
+
504
+ return $ promise
505
+ ->then ($ process_install )
506
+ // Clean up after the install.
507
+ ->then (function () use ($ library_package , $ install_path ) {
508
+ return $ this ->downloadManager ->cleanup ('install ' , $ library_package , $ install_path );
509
+ }, function ($ e ) use ($ library_package , $ install_path ) {
510
+ // Clean up after any errors.
511
+ $ this ->composer ->getLoop ()
512
+ ->wait ([
513
+ $ this ->downloadManager ->cleanup ('install ' , $ library_package , $ install_path ),
514
+ ]);
515
+ throw $ e ;
516
+ });
421
517
}
422
518
423
- // Execute as normal (composer v1, or v2 without async )
424
- $ process_download ();
519
+ // Execute as normal (Composer v1)
520
+ return $ process_install ();
425
521
}
426
522
427
523
/**
@@ -500,7 +596,7 @@ public function getInstalledJsonPath() {
500
596
'* '
501
597
);
502
598
503
- if (!$ installer_library_package || !$ installer_library_package instanceof CompletePackage ) {
599
+ if (!$ installer_library_package || !$ installer_library_package instanceof CompletePackageInterface ) {
504
600
// Could not resolve the package. The package is most likely being
505
601
// uninstalled.
506
602
return FALSE ;
0 commit comments