With every major release, Drupal aims to introduce new features, improvements, and architectural changes. Drupal 10, the latest major upgrade, was released in December 2022 and came with many remarkable updates in features and dependencies. Every Drupal application is heavily dependent on its diverse array of contributed modules. These modules extend Drupal's core functionality, allowing developers to customize and tailor Drupal websites according to specific needs.

It's important to note that while some contributed modules will seamlessly align with Drupal 10 through compatible releases, others might necessitate additional attention and adaptation. In this article, we’ll discuss how you can utilize Drupal 9 modules in your Drupal 10 project and meet the standards and expectations set by it.

Moving the modules to Drupal 10


What's our strategy for transitioning to Drupal 10 while incorporating these modules? Utilizing the available patches is an option, but its feasibility depends on our project's version control workflow and the hosting platform in play.

If our remote repository includes all repositories such as modules/contrib/* and themes/contrib/*, and the hosting platform doesn't ignore these, patches can be applied directly. However, if these repositories are ignored, and given that Drupal projects now adhere to a composer-based structure, we might need to incorporate the patches within the composer.json file itself. If the module does not have any dependency library or module, we add it by simply ignoring the core_version_requirement key with drupal/lenient. But if the module has a dependency module and is not D10-compatible, you will need to implement the following steps as described below. 

Utilizing incompatible Drupal 9 modules with Drupal 10    

Incorporating incompatible Drupal 9 modules into a Drupal 10 setup necessitates several key steps within the composer.json configuration:

  1. Inclusion of Module Repository: Begin by adding the module’s repository to the composer.json file
  2. Module Key Update: Update the module’s key as required to ensure proper integration
  3. Patch Integration: Add the relevant patch under patches to address compatibility issues effectively
  4. Root Composer Dependency: Add the module’s dependency within the root composer.json 

Note: Kindly note that for the sake of this article, we will consistently reference the module "drupal/header_and_footer_scripts" as an illustrative example. As of the article’s creation, this module lacks a Drupal 10 release.

Inclusion of Module Repository in composer.json

As the composer is a dependency manager, it will check for compatibility with other modules’ composer.json file, Drupal version, PHP version, etc.

So, you will need to instruct the composer not to check the dependency and directly clone the directory from repositories.

In the root composer.json file, add the module’s repositories as follows:


"repositories": [  
   "header_and_footer_scripts": {  
          "type": "package",  
          "package": {  
              "name": "drupal-gitlab/header_and_footer_scripts",  
              "type": "drupal-module",  
              "version": "dev-3.0.x@dev",  
              "source": {  
                  "type": "git",  
                  "url": "https://git.drupalcode.org/project/header_and_footer_scripts.git",  
                  "reference": "3.0.x"  
              }  
          }  
      }  
  ]  
 

You can take the repository details from the project overview page here. Copy the URL.  
 

Here, I have used drupal-gitlab/module-name as the package name and the reference should be the existing version of the module from which the patch was created.

Module Key Update


Now, since we have used drupal-gitlab/header_and_footer_scripts as the package name (as mentioned above), let’s replace/add the same under the required key of composer.json so that composer takes it as a dependency and try to download the repositories into our codebase.

"require": {  
     “drupal-gitlab/header_and_footer_scripts: “dev-3.0.x@dev”  
}

Here, you should refer to the version mentioned as the package version in the repository.

Patch Integration


To use the patches you will require the “cweagans/composer-patches” so that you can apply the patch.


  "extra": {  
      "patches": {  
          "drupal-gitlab/header_and_footer_scripts": {  
              "Drupal 10 compatibility": "patches/header_and_footer_scripts_d10.patch"  
      }  
  },


You can apply the patch in two ways:

  1. From a patch file: 

    a. Download the patch file and place it in your project repository.
    b. Refer to the file as mentioned in the above code.
     
  2. From a URL:

    a. Get the patch URL from the drupal.org issues page.
    b. Replace the file path with the URL.
     

If a patch is not available, you can create one by verifying the deprecated methods and other problems using the “Upgrade Status” or “Rector” module.
 

Root Composer Dependency

 Certain modules may include a composer.json file within their repository. It is imperative to verify and, when necessary, incorporate these dependencies into our root composer.json. This precaution is essential, particularly since we have specified that dependency checks should be bypassed as part of our instructions.
 

Referring to the composer.json file of "header_and_footer_scripts" available here, it's important to note the following guidelines:
 

  • If the composer.json of a module lists dependencies under the require key, these should be added to the root composer.json of our project.
  • In cases where the listed items pertain to verification, such as PHP and Drupal version requirements, it is acceptable to disregard these during the process.
     

It is extremely important to note that the absence of the required dependencies could potentially lead to a complete site breakdown.
 

Putting it to the test

In a vanilla Drupal 10 instance, let’s implement the Drupal 10 upgrade for the same module in both ways: the traditional way and the way we displayed above, and see what happens!

Following the traditional approach:

{
"name": "drupal/drupal",
  "description": "Drupal is an open source content management platform powering millions of websites and applications.",
  "type": "project",
  "license": "GPL-2.0-or-later",
  "homepage": "https://www.drupal.org/project/drupal",
  "support": {
      "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
      "chat": "https://www.drupal.org/node/314178"
  },
  "require": {
      "composer/installers": "^2.0",
      "drupal/core": "self.version",
      "drupal/core-project-message": "self.version",
      "drupal/core-vendor-hardening": "self.version",
      "drupal/header_and_footer_scripts": "^3.0"
  },
  "require-dev": {
      "behat/mink": "^1.10",
      "behat/mink-browserkit-driver": "^2.1",
      "behat/mink-selenium2-driver": "^1.4",
      "colinodell/psr-testlogger": "^1.2",
      "composer/composer": "^2.4",
      "drupal/coder": "^8.3.10",
      "instaclick/php-webdriver": "^1.4.1",
      "justinrainbow/json-schema": "^5.2",
      "mglaman/phpstan-drupal": "^1.1.34",
      "mikey179/vfsstream": "^1.6.11",
      "phpspec/prophecy-phpunit": "^2",
      "phpstan/extension-installer": "^1.1",
      "phpstan/phpstan": "^1.10.1",
      "phpstan/phpstan-phpunit": "^1.3.11",
      "phpunit/phpunit": "^9.5",
      "symfony/browser-kit": "^6.3",
      "symfony/css-selector": "^6.3",
      "symfony/dom-crawler": "^6.3",
      "symfony/error-handler": "^6.3",
      "symfony/filesystem": "^6.3",
      "symfony/finder": "^6.3",
      "symfony/lock": "^6.3",
      "symfony/phpunit-bridge": "^6.3",
      "symfony/var-dumper": "^6.3"
  },
  "replace": {
      "symfony/polyfill-php72": "*",
      "symfony/polyfill-php73": "*",
      "symfony/polyfill-php74": "*",
      "symfony/polyfill-php80": "*",
      "symfony/polyfill-php81": "*"
  },
  "minimum-stability": "dev",
  "prefer-stable": true,
  "config": {
      "preferred-install": "dist",
      "sort-packages": true,
      "platform": {
          "php": "8.1.0"
      },
      "allow-plugins": {
          "composer/installers": true,
          "drupal/core-project-message": true,
          "drupal/core-vendor-hardening": true,
          "phpstan/extension-installer": true,
          "dealerdirect/phpcodesniffer-composer-installer": true
      }
  },
  "extra": {
      "_readme": [
          "By default Drupal loads the autoloader from ./vendor/autoload.php.",
          "To change the autoloader you can edit ./autoload.php.",
          "This file specifies the packages.drupal.org repository.",
          "You can read more about this composer repository at:",
          "https://www.drupal.org/node/2718229"
      ],
      "installer-paths": {
          "core": ["type:drupal-core"],
          "libraries/{$name}": ["type:drupal-library"],
          "modules/contrib/{$name}": ["type:drupal-module"],
          "profiles/contrib/{$name}": ["type:drupal-profile"],
          "themes/contrib/{$name}": ["type:drupal-theme"],
          "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
          "modules/custom/{$name}": ["type:drupal-custom-module"],
          "themes/custom/{$name}": ["type:drupal-custom-theme"]
      },
      "drupal-core-project-message": {
          "post-install-cmd-message": [
              "<bg=blue;fg=white>drupal/drupal</>: This package is meant for core development,",
              "               and not intended to be used for production sites.",
              "               See: https://www.drupal.org/node/3082474"
          ],
          "post-create-project-cmd-message": [
              "<bg=red;fg=white>drupal/drupal</>: This package is meant for core development,",
              "               and not intended to be used for production sites.",
              "               See: https://www.drupal.org/node/3082474"
          ]
      }
  },
  "autoload": {
      "psr-4": {
          "Drupal\\Core\\Composer\\": "core/lib/Drupal/Core/Composer",
          "Drupal\\Composer\\": "composer"
      }
  },
  "scripts": {
      "pre-install-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
      "pre-update-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
      "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
      "drupal-phpunit-upgrade-check": "Drupal\\Core\\Composer\\Composer::upgradePHPUnit",
      "drupal-phpunit-upgrade": [
          "@composer update phpunit/phpunit --with-dependencies --no-progress"
      ],
      "post-update-cmd": [
          "Drupal\\Composer\\Composer::generateMetapackages",
          "Drupal\\Composer\\Composer::generateComponentPackages"
      ],
      "phpcs": "phpcs --standard=core/phpcs.xml.dist --parallel=$(nproc) --",
      "phpcbf": "phpcbf --standard=core/phpcs.xml.dist --parallel=$(nproc) --"
  },
  "repositories": [
      {
          "type": "path",
          "url": "core"
      },
      {
          "type": "path",
          "url": "composer/Plugin/ProjectMessage"
      },
      {
          "type": "path",
          "url": "composer/Plugin/VendorHardening"
      },
      {
          "type": "composer",
          "url": "https://packages.drupal.org/8"
      }
  ]
}


You will notice an error while updating the dependencies “Your requirements could not be resolved to an installable set of packages.”

Following the previously discussed approach:

{
  "name": "drupal/drupal",
  "description": "Drupal is an open source content management platform powering millions of websites and applications.",
  "type": "project",
  "license": "GPL-2.0-or-later",
  "homepage": "https://www.drupal.org/project/drupal",
  "support": {
      "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
      "chat": "https://www.drupal.org/node/314178"
  },
  "require": {
      "composer/installers": "^2.0",
      "cweagans/composer-patches": "^1.7",
      "drupal/core": "self.version",
      "drupal/core-project-message": "self.version",
      "drupal/core-vendor-hardening": "self.version",
      "drupal-gitlab/header_and_footer_scripts": "dev-3.0.x@dev"
  },
  "require-dev": {
      "behat/mink": "^1.10",
      "behat/mink-browserkit-driver": "^2.1",
      "behat/mink-selenium2-driver": "^1.4",
      "colinodell/psr-testlogger": "^1.2",
      "composer/composer": "^2.4",
      "drupal/coder": "^8.3.10",
      "instaclick/php-webdriver": "^1.4.1",
      "justinrainbow/json-schema": "^5.2",
      "mglaman/phpstan-drupal": "^1.1.34",
      "mikey179/vfsstream": "^1.6.11",
      "phpspec/prophecy-phpunit": "^2",
      "phpstan/extension-installer": "^1.1",
      "phpstan/phpstan": "^1.10.1",
      "phpstan/phpstan-phpunit": "^1.3.11",
      "phpunit/phpunit": "^9.5",
      "symfony/browser-kit": "^6.3",
      "symfony/css-selector": "^6.3",
      "symfony/dom-crawler": "^6.3",
      "symfony/error-handler": "^6.3",
      "symfony/filesystem": "^6.3",
      "symfony/finder": "^6.3",
      "symfony/lock": "^6.3",
      "symfony/phpunit-bridge": "^6.3",
      "symfony/var-dumper": "^6.3"
  },
  "replace": {
      "symfony/polyfill-php72": "*",
      "symfony/polyfill-php73": "*",
      "symfony/polyfill-php74": "*",
      "symfony/polyfill-php80": "*",
      "symfony/polyfill-php81": "*"
  },
  "minimum-stability": "dev",
  "prefer-stable": true,
  "config": {
      "preferred-install": "dist",
      "sort-packages": true,
      "platform": {
          "php": "8.1.0"
      },
      "allow-plugins": {
          "composer/installers": true,
          "drupal/core-project-message": true,
          "drupal/core-vendor-hardening": true,
          "phpstan/extension-installer": true,
          "dealerdirect/phpcodesniffer-composer-installer": true,
          "cweagans/composer-patches": true
      }
  },
  "extra": {
      "patches": {
          "drupal-gitlab/header_and_footer_scripts": {
              "Drupal 10 compatibility": "patches/header_and_footer_scripts_d10.patch"
          }   
      },
      "_readme": [
          "By default Drupal loads the autoloader from ./vendor/autoload.php.",
          "To change the autoloader you can edit ./autoload.php.",
          "This file specifies the packages.drupal.org repository.",
          "You can read more about this composer repository at:",
          "https://www.drupal.org/node/2718229"
      ],
      "installer-paths": {
          "core": ["type:drupal-core"],
          "libraries/{$name}": ["type:drupal-library"],
          "modules/contrib/{$name}": ["type:drupal-module"],
          "profiles/contrib/{$name}": ["type:drupal-profile"],
          "themes/contrib/{$name}": ["type:drupal-theme"],
          "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
          "modules/custom/{$name}": ["type:drupal-custom-module"],
          "themes/custom/{$name}": ["type:drupal-custom-theme"]
      },
      "drupal-core-project-message": {
          "post-install-cmd-message": [
              "<bg=blue;fg=white>drupal/drupal</>: This package is meant for core development,",
              "               and not intended to be used for production sites.",
              "               See: https://www.drupal.org/node/3082474"
          ],
          "post-create-project-cmd-message": [
              "<bg=red;fg=white>drupal/drupal</>: This package is meant for core development,",
              "               and not intended to be used for production sites.",
              "               See: https://www.drupal.org/node/3082474"
          ]
      }
  },
  "autoload": {
      "psr-4": {
          "Drupal\\Core\\Composer\\": "core/lib/Drupal/Core/Composer",
          "Drupal\\Composer\\": "composer"
      }
  },
  "scripts": {
      "pre-install-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
      "pre-update-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
      "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
      "drupal-phpunit-upgrade-check": "Drupal\\Core\\Composer\\Composer::upgradePHPUnit",
      "drupal-phpunit-upgrade": [
          "@composer update phpunit/phpunit --with-dependencies --no-progress"
      ],
      "post-update-cmd": [
          "Drupal\\Composer\\Composer::generateMetapackages",
          "Drupal\\Composer\\Composer::generateComponentPackages"
      ],
      "phpcs": "phpcs --standard=core/phpcs.xml.dist --parallel=$(nproc) --",
      "phpcbf": "phpcbf --standard=core/phpcs.xml.dist --parallel=$(nproc) --"
  },
  "repositories": [
      {
          "type": "path",
          "url": "core"
      },
      {
          "type": "path",
          "url": "composer/Plugin/ProjectMessage"
      },
      {
          "type": "path",
          "url": "composer/Plugin/VendorHardening"
      },
      {
          "type": "composer",
          "url": "https://packages.drupal.org/8"
      },
      {
          "type": "package",
          "package": {
              "name": "drupal-gitlab/header_and_footer_scripts",
              "type": "drupal-module",
              "version": "dev-3.0.x@dev",
              "source": {
                  "type": "git",
                  "url": "https://git.drupalcode.org/project/header_and_footer_scripts.git",
                  "reference": "3.0.x"
              }
          }
      }
  ]
}
 

Once there is a Drupal 10 release of the module, remove the above code and add it as usual - “drupal/module-name”: “version”. 
 

Final Thoughts

Looking ahead, Drupal 10’s adoption of modern technologies and adherence to industry standards positions it to lead the charge in delivering cutting-edge digital experiences. Incorporating Drupal 9-compatible modules into your Drupal 10 project isn't just about code integration—it's about enabling your site to thrive in the dynamic digital ecosystem of tomorrow.

At Specbee, we're dedicated to realizing the full potential of Drupal for your digital projects. Our seasoned experts are well-versed in Drupal 10's advancements and possess the skills needed to seamlessly integrate modules, ensuring your website remains at the forefront of technology. Partner with us to drive your Drupal 10 project forward with confidence and innovation. 
 

Contact us

LET'S DISCUSS YOUR IDEAS. 
WE'D LOVE TO HEAR FROM YOU.

CONTACT US