This is the up-to-date, user-friendly version of the post I wrote 2 years ago about the same topic.

⇩ Jump to the steps.

In this post you will be able to see how to add an incompatible Drupal module to a Drupal 10 project with Composer. At the time I’m writing (well, making up-to-date) this post, I’m not able to do this with Entity Migrate Export module: both the latest tag release (1.0.0-alpha10) and the latest branch release (1.0.x-dev) require Drupal core ^8.9 || ^9:

$ composer require drupal/eme
Using version ^1.0@alpha for drupal/eme
./composer.json has been updated
Running composer update drupal/eme
Gathering patches for root package.
No patches supplied.
Loading composer repositories with package information
Updating dependencies
Info from https://repo.packagist.org: #StandWithUkraine
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - drupal/eme[1.0.0-alpha1, ..., 1.0.0-alpha5] require drupal/core ^8.7.7 || ^9 -> found drupal/core[8.7.7, ..., 8.9.x-dev, 9.0.0-alpha1, ..., 9.5.x-dev] but the package is fixed to 10.0.x-dev (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
    - drupal/eme[1.0.0-alpha6, ..., 1.0.0-alpha10] require drupal/core ^8.9 || ^9 -> found drupal/core[8.9.0-beta1, ..., 8.9.x-dev, 9.0.0-alpha1, ..., 9.5.x-dev] but the package is fixed to 10.0.x-dev (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
    - Root composer.json requires drupal/eme ^1.0@alpha -> satisfiable by drupal/eme[1.0.0-alpha1, ..., 1.0.0-alpha10].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

Steps to follow

Find the corresponding package info JSON file

  1. We will be using the package info that’s already available for your composer. So, locate where your composer cache is, by executing composer config cache-dir:
    1
    2
    
    $ composer config cache-dir
    /Users/zoli/Library/Caches/composer
    
  2. The package info will be inside the cache directory’s repo subdirectory, in the corresponding repository subdirectory. On my development environment, in case of Drupal extensions, this is /Users/zoli/Library/Caches/composer/repo/https---packages.drupal.org-8/.

  3. For Entity Migrate Export, we will have two different JSON files: one for the usual tag-based releases (this is provider-drupal~eme.json), and an another one for the branch based releases (provider-drupal~eme~dev.json). Imho the best practice is to replace the latest release we found in the tag info JSON: so if the maintainer releases a newer (and already compatible) release, then we will also be informed through Drupal’s Module Update UI.

Add the right JSON object to your root composer.json as custom package

One rule here: your custom package entry must be added before the Drupal packagist repository.

  1. Open the package file, locate the latest release. This is the info you will be using:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    {
      "packages": {
        "drupal/eme": [
          {
            "keywords": ["Drupal"],
            "homepage": "https://www.drupal.org/project/eme",
            "version": "1.0.0-alpha10",
            "version_normalized": "1.0.0.0-alpha10",
            "license": "GPL-2.0-or-later",
            "authors": [],
            "support": {},
            "source": {},
            "dist": {},
            "type": "drupal-module",
            "uid": "eme-3219974",
            "name": "drupal/eme",
            "extra": {},
            "description": "Exports content entities into a migration module",
            "require": {
              "drupal/core": "^8.9 || ^9"
            },
            "require-dev": {
              "drupal/migrate_tools": "^5",
              "drupal/migrate_plus": "^5",
              "drush/drush": "^9 || ^10"
            }
          },
          {},
          {}
        ]
      },
      "minified": "composer/2.0",
      "last-modified": "Sat, 30 Apr 2022 16:30:38 GMT"
    }
    
  2. The section you will paste into the root composer.json is a single release info, an object which starts with the keywords key. Add this section to your root composer.json, above Drupal’s repository:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    {
      "name": "z.a.horvath/incubator",
      "type": "drupal-project",
      "repositories": {
        "custom repo key": {
          "type": "package",
          "package": {
            "keywords": ["Drupal"],
            "homepage": "https://www.drupal.org/project/eme",
            "version": "1.0.0-alpha10"
            
          }
        },
        "drupal": {
          "type": "composer",
          "url": "https://packages.drupal.org/8"
        }
      },
      
    }       
    

Fix the requirement’s version constraints which cause the conflict

Locate the require key in the package info, and modify the version constraints which cause conflicts:

1
2
3
"require": {
  "drupal/core": "^8.9 || ^9"
}

Let’s change the version constraint of drupal/core requirement, and allow Drupal 10 as well:

1
2
3
"require": {
  "drupal/core": "^8.9 || ^9 || ^10"
}

Summary

With the solution explained above we are able to download and use incompatible extensions in our Composer project. Of course these extensions might be really incompatible, but as soon as we can download them we can use cweagans/composer-patches to apply patches fixing the issues, or we can start writing the fix and share it with the community.