composer-installers-extender: Package type "library" is not supported

When I run composer require <package> with v2.0.0, it errors out:

$ composer require monolog/monolog
Using version ^2.1 for monolog/monolog
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals

Installation failed, reverting ./composer.json to its original content.

                                           
  [InvalidArgumentException]               
  Package type "library" is not supported  

Composer 1.10.13 oomphinc/composer-installers-extender:2.0.0

composer.json:

{
    "require": {
        "oomphinc/composer-installers-extender": "^2.0"
    },
    "extra": {
        "installer-types": ["library"]
    }
}

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 14
  • Comments: 31 (2 by maintainers)

Commits related to this issue

Most upvoted comments

I am not sure but I believe the solution is to use https://github.com/mnsami/composer-custom-directory-installer for type library.

I tracked down the change that caused this but the composer behavior is super weird and undocumented.

In 2.0 we have this code: https://github.com/oomphinc/composer-installers-extender/blob/8d3fe38a1723e0e91076920c8bb946b1696e28ca/src/Installers/CustomInstaller.php#L16-L18

Where in 1.0 the code looked like this: https://github.com/oomphinc/composer-installers-extender/blob/ca1c4b16b0905c81d1e77e608f36a2eff1a56f56/src/InstallerHelper.php#L7-L15

Now, that ends up interacting with this code in different ways as documented in the comment even though composers behavior seems completely broken. https://github.com/composer/installers/blob/c462a693445b8f6913e95c2ea774d60cf42f64ec/src/Composer/Installers/BaseInstaller.php#L65-L69

The key is these two lines

$packageType = substr($type, strlen($frameworkType) + 1);
if (!isset($locations[$packageType])) {
  throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type));
}

return $this->templatePath($locations[$packageType], $availableVars);

Basically what we see is that $type = 'framework' and $frameworkType = 'framework' and which mean substr actually fails and $packageType = FALSE. Then as the comment says, some type conversion happens and FALSE checks index 0, which is actually set.

Next is less important but templatePath returns false because strpos(FALSE, '{') always fails because false isn’t a string and that ends up returning the first argument(false) and that false get returned out of the method triggering a calling method check to use the default.

Now, it doesn’t actually make any sense to check the 0th index, relying on undocumented type conversion, relying on passing a boolean into strpos, and the undocumented return types(getInstallPath only claims to return strings which clearly isn’t the case). But all this seems to be expected in composer just not documented so I think we just return the old code?

Sorry for the ranting but a single comment along this chain in composer would have saved me several hours of headaches trying to sort this out so the composer team can deal if they end up reading this I think.

PR incoming.

@syzygy333 indeed! But you can’t control what the packages you want to require have as their types - at least not without overriding the package data as per https://github.com/oomphinc/composer-installers-extender/issues/26#issuecomment-730545360 , which then makes updating/maintaining those packages harder. So in that example of harvesthq/chosen - you can’t require that package without doing something awkward, unless this could be fixed in oomphinc/composer-installers-extender .

For some reason, the PR fixes the problem for me when running composer on PHP 7.4.5, but not when running the same composer in PHP 8.0.3. Literally the only difference is the php interpreter used, and the effect on PHP 8 is still “Package type library is not supported.”. I’m currently trying to investigate.

Update: From the earlier investigation by @neclimdul, I think I see what’s happening:

  • PHP 8 has changed substr() so that it returns "" (empty string) instead of FALSE for out-of-bounds indices.
  • PHP 8 has also changed non-strict comparison so that "" will not equal FALSE anymore.

(I’m not sure if the second affected array lookups, but whether or not $array[""] was different from $array[0] before, it certainly will be now.)

The combination of these two will mean that

$packageType = substr($type, strlen($frameworkType) + 1);
if (!isset($locations[$packageType])) {
  throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type));
}

with $type and $frameworkType set to the same value will now make $packageType equal "" instead of FALSE, and that $locations[$packageType] will then be undefined rather than evaluating to $locations[0].

I can’t begin to understand what composer’s intended logic is, but I suspect that we can get the effect we wanted by changing

 	function getLocations() { 
 		return array( false ); 
 	} 

to

 	function getLocations() { 
 		return array(0 => false , "" => false); 
 	} 

I hope.

Update: After installing the patch with the above modification, I can add “library” to the installer-types array without causing an error in PHP 8.

@neclimdul I agree that this approach is the simplest way forward.

@mstenta sorry for the misstag and congrats on the farmos release! @mstrelan ^^

@nicolas-girod thank you for the feedback. I ended up using composer 1 again and specifying

installer-types": [
        
        "wordpress-library"
    ],

as it was suggested previously. I think this is ok as a workaround for now 😃

I’ve found a workaround using Drupal Scaffold operations.

I got utterly fed-up of the Chosen library sometimes installing in the right place, and sometimes not, despite having followed the guides above and the work of several other people with the same problems.

As an example, out of the nine D9 sites I maintain - which all use the same composer.json file - Composer on five of the nine sites abjectly refused to put the Chosen library in the correct location, while on the other two sites it worked just fine.

A diff of the composer.jsons between working and non-working sites showed no obvious differences.

So, I’m using Drupal Scaffold as follows:

1: In the root of the repo at the same level as composer.json, I created a build-assets/ directory and added chosen.jquery.js and chosen.jquery.min.js, giving a directory structure of:

./composer.json
./build-assets/chosen.jquery.js
./build-assets/chosen.jquery.min.js

2: In composer.json, in the extra > drupal-scaffold > file-mapping section, add maps which will copy the two Chosen JS files into web/libraries/chosen/ after build:

{
  "extra": {
    "drupal-scaffold": {
      "file-mapping": {
        "[web-root]/libraries/chosen/chosen.jquery.js": "build-assets/chosen.jquery.js",
        "[web-root]/libraries/chosen/chosen.jquery.min.js": "build-assets/chosen.jquery.min.js"
      }
    }
  }
}

(Obviously the above sections in your composer.json will have other entries in them.)

To test, I ran rm composer.lock; composer install -vvvv and confirmed that the Chosen files appeared in the correct place, every time.

Hopefully this helps someone else! 😃

Alex

@syzygy333 no - that’s configuring where to put each package that you require, of those types. So if you require a package that declares itself to have the drupal-library type, then it will get placed within that web/libraries directory. (Great - that bit works as expected, yes 👍 ) But the point here is that if you require an external package which declares itself to be have the type library (or doesn’t explicitly declare its type), oomphinc/composer-installers-extender can’t currently support placing the package somewhere in the same way.

This works for me:

"extra": {
    "installer-types": [
        "npm-asset",
        "drupal-library"
    ],
    "installer-paths": {
        "web/core": [
            "type:drupal-core"
        ],
        "web/libraries/jquery.cycle": [
            "npm-asset/jquery-cycle"
        ],
        "web/libraries/{$name}": [
            "type:drupal-library",
            "type:npm-asset"
        ],
        "web/modules/contrib/{$name}": [
            "type:drupal-module"
        ],
        "web/profiles/contrib/{$name}": [
            "type:drupal-profile"
        ],
        "web/themes/contrib/{$name}": [
            "type:drupal-theme"
        ],
        "drush/contrib/{$name}": [
            "type:drupal-drush"
        ]
    },
},

It seems to be as simple as drupal-library or whatever-library instead of just library as the installer-type.

I’m having a similar issue with npm-asset type packages… but I think it’s different from this one. Would love to hear if this is working for anyone else! #32

As a workaround (at least for the chosen library) you can define a custom repository for the chosen library (which is no longer updated anyway) in your project composer.json and set it’s package:type to drupal-library. This seems to override the package:type in the chosen library source composer.json.

    "repositories": {
        "chosen": {
            "type": "package",
            "package": {
                "name": "harvesthq/chosen",
                "version": "1.8.7",
                "type": "drupal-library",
                "dist": {
                    "url": "https://github.com/harvesthq/chosen/releases/download/v1.8.7/chosen_v1.8.7.zip",
                    "type": "zip"
                }
            }
        },
    },

Then you can require as usual with:

    "require": {
        "harvesthq/chosen": "^1.8",
    },

And define the installer-paths like:

        "installer-paths": {
            "docroot/libraries/{$name}": [
                "type:drupal-library"
            ],
        },

Attempting to use installer-paths options for specific package name (harvesthq/chosen) or using the vendor prefix (vendor:harvesthq) seems to get ignored / overwrote by the package:type in the chosen library source composer.json.

I’m also seeing this - on a previous project with composers-installers-extender 1.1, this error didn’t happen

I downgraded to 1.1.2, and it’s no longer occurring