diff --git a/composer.json b/composer.json
index 4b484e137e983ce04ea39b6aa07e27abab9125d7..ed71c864f14920440731846e59f65564ccaa27eb 100644
--- a/composer.json
+++ b/composer.json
@@ -1,17 +1,18 @@
 {
-  "description": "Enables administrators to package configuration into modules",
-  "license": "GPL-2.0-or-later",
-  "minimum-stability": "dev",
-  "name": "drupal/features",
-  "require": {
-    "drupal/config_update": "^1.4"
-  },
-  "type": "drupal-module",
-  "extra": {
-    "drush": {
-      "services": {
-        "drush.services.yml": "^9 || ^10"
-      }
+    "name": "drupal/features",
+    "description": "Enables administrators to package configuration into modules",
+    "type": "drupal-module",
+    "license": "GPL-2.0-or-later",
+    "homepage": "https://www.drupal.org/project/features",
+    "require": {
+        "drupal/core": "^9.4 || ^10",
+        "drupal/config_update": "^1.4 || ^2"
+    },
+    "extra": {
+        "drush": {
+            "services": {
+                "drush.services.yml": "^10 || ^11"
+            }
+        }
     }
-  }
 }
diff --git a/drush/features.drush8.inc b/drush/features.drush8.inc
deleted file mode 100644
index 7e51c320a4f9658e4d0f83d297c9314fb20ea715..0000000000000000000000000000000000000000
--- a/drush/features.drush8.inc
+++ /dev/null
@@ -1,907 +0,0 @@
-<?php
-
-/**
- * @file
- * Features module drush integration.
- */
-
-use Drupal\features\FeaturesBundleInterface;
-use Drupal\features\FeaturesManagerInterface;
-use Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationWrite;
-use Drupal\Component\Diff\DiffFormatter;
-
-/**
- * Implements hook_drush_command().
- */
-function features_drush_command() {
-  $items = [];
-
-  $items['features-status'] = [
-    'description' => 'Display current Features settings.',
-    'aliases' => ['fs'],
-  ];
-
-  $items['features-list-packages'] = [
-    'description' => 'Display a list of all existing features and packages available to be generated.  If a package name is provided as an argument, then all of the configuration objects assigned to that package will be listed.',
-    'examples' => [
-      "drush features-list-packages" => 'Display a list of all existing featurea and packages available to be generated.',
-      "drush features-list-packages 'example_article'" => "Display a list of all configuration objects assigned to the 'example_article' package.",
-    ],
-    'arguments' => [
-      'package' => 'The package to list. Optional; if specified, lists all configuration objects assigned to that package. If no package is specified, lists all of the features.',
-    ],
-    'outputformat' => [
-      'default' => 'table',
-      'pipe-format' => 'list',
-      'field-labels' => [
-        'name' => 'Name',
-        'machine_name' => 'Machine name',
-        'status' => 'Status',
-        'version' => 'Version',
-        'state' => 'State',
-        'object' => 'Configuration object',
-      ],
-      'output-data-type' => 'format-table',
-    ],
-    'aliases' => ['fl'],
-  ];
-
-  $items['features-import-all'] = [
-    'description' => 'Import module config from all installed features.',
-    'examples' => [
-      "drush features-import-all" => 'Import module config from all installed features.',
-    ],
-    'aliases' => ['fra', 'fia', 'fim-all'],
-  ];
-
-  $items['features-export'] = [
-    'description' => "Export the configuration on your site into a custom module.",
-    'arguments' => [
-      'package' => 'A space delimited list of features to export.',
-    ],
-    'options' => [
-      'add-profile' => 'Package features into an install profile.',
-    ],
-    'examples' => [
-      "drush features-export" => 'Export all available packages.',
-      "drush features-export example_article example_page" => "Export the example_article and example_page packages.",
-      "drush features-export --add-profile" => "Export all available packages and add them to an install profile.",
-    ],
-    // Add previous "fu" alias for compatibility.
-    'aliases' => ['fex', 'fu', 'fua', 'fu-all'],
-  ];
-
-  $items['features-add'] = [
-    'description' => "Add a config item to a feature package.",
-    'arguments' => [
-      'feature' => 'Feature package to export and add config to.',
-      'components' => 'Patterns of config to add, see features-components for the format of patterns.',
-    ],
-    'aliases' => ['fa', 'fe'],
-  ];
-
-  $items['features-components'] = [
-    'description' => 'List features components.',
-    'arguments' => [
-      'patterns' => 'The features components type to list. Omit this argument to list all components.',
-    ],
-    'options' => [
-      'exported' => [
-        'description' => 'Show only components that have been exported.',
-      ],
-      'not-exported' => [
-        'description' => 'Show only components that have not been exported.',
-      ],
-    ],
-    'aliases' => ['fc'],
-  ];
-
-  $items['features-diff'] = [
-    'description' => "Show the difference between the active config and the default config stored in a feature package.",
-    'arguments' => [
-      'feature' => 'The feature in question.',
-    ],
-    'options' => [
-      'ctypes' => 'Comma separated list of component types to limit the output to. Defaults to all types.',
-      'lines' => 'Generate diffs with <n> lines of context instead of the usual two.',
-    ],
-    'aliases' => ['fd'],
-  ];
-
-  $items['features-import'] = [
-    'description' => "Import a module config into your site.",
-    'arguments' => [
-      'feature' => 'A space delimited list of features or feature:component pairs to import.',
-    ],
-    'options' => [
-      'force' => "Force import even if config is not overridden.",
-    ],
-    'examples' => [
-      'drush features-import foo:node.type.page foo:taxonomy.vocabulary.tags bar' => 'Import node and taxonomy config of feature "foo". Import all config of feature "bar".',
-    ],
-    'aliases' => ['fim', 'fr'],
-  ];
-
-  foreach ($items as $name => &$item) {
-    $item['options']['bundle'] = [
-      'description' => 'Use a specific bundle namespace.',
-    ];
-  }
-
-  return $items;
-}
-
-/**
- * Applies global options for Features drush commands.
- *
- * The option --name="bundle_name" sets the bundle namespace.
- *
- * @return \Drupal\features\FeaturesAssignerInterface
- */
-function _drush_features_options() {
-  /** @var \Drupal\features\FeaturesAssignerInterface $assigner */
-  $assigner = \Drupal::service('features_assigner');
-  $bundle_name = drush_get_option('bundle');
-  if (!empty($bundle_name)) {
-    $bundle = $assigner->applyBundle($bundle_name);
-    if ($bundle->getMachineName() != $bundle_name) {
-      drush_log(dt('Bundle @name not found. Using default.', ['@name' => $bundle_name]), 'warning');
-    }
-  }
-  else {
-    $assigner->assignConfigPackages();
-  }
-  return $assigner;
-}
-
-/**
- * Provides Drush command callback for features-status.
- */
-function drush_features_status() {
-  $args = func_get_args();
-  $assigner = _drush_features_options();
-
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  $current_bundle = $assigner->getBundle();
-  $export_settings = $manager->getExportSettings();
-  $methods = $assigner->getEnabledAssigners();
-  if ($current_bundle->isDefault()) {
-    drush_print(dt('Current bundle: none'));
-  }
-  else {
-    drush_print(dt('Current bundle: @name (@machine_name)',
-      [
-        '@name' => $current_bundle->getName(),
-        '@machine_name' => $current_bundle->getMachineName(),
-      ]));
-  }
-  drush_print(dt('Export folder: @folder', ['@folder' => $export_settings['folder']]));
-  $dt_args = ['@methods' => implode(', ', array_keys($methods))];
-  drush_print(dt('The following assignment methods are enabled:'));
-  drush_print(dt('  @methods', $dt_args));
-
-  if (!empty($args)) {
-    $config = $manager->getConfigCollection();
-    if (count($args) > 1) {
-      print_r(array_keys($config));
-    }
-    else {
-      print_r($config[$args[0]]);
-    }
-  }
-}
-
-/**
- * Drush command callback for features-list-packages.
- *
- * @param string $package_name
- *   (optional) The package name.
- *
- * @return array|bool
- */
-function drush_features_list_packages($package_name = '') {
-  $assigner = _drush_features_options();
-  $current_bundle = $assigner->getBundle();
-  $namespace = $current_bundle->isDefault() ? FeaturesBundleInterface::DEFAULT_BUNDLE : $current_bundle->getMachineName();
-
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  $packages = $manager->getPackages();
-
-  $packages = $manager->filterPackages($packages, $namespace);
-  $result = [];
-
-  // If no package was specified, list all packages.
-  if (empty($package_name)) {
-    drush_hide_output_fields(['object']);
-    foreach ($packages as $package) {
-      $overrides = $manager->detectOverrides($package);
-      $state = $package->getState();
-      if (!empty($overrides) && ($package->getStatus() != FeaturesManagerInterface::STATUS_NO_EXPORT)) {
-        $state = FeaturesManagerInterface::STATE_OVERRIDDEN;
-      }
-
-      $result[$package->getMachineName()] = [
-        'name' => $package->getName(),
-        'machine_name' => $package->getMachineName(),
-        'status' => $manager->statusLabel($package->getStatus()),
-        'version' => $package->getVersion(),
-        'state' => ($state != FeaturesManagerInterface::STATE_DEFAULT) ? $manager->stateLabel($state) : '',
-      ];
-    }
-    return $result;
-  }
-  // If a valid package was listed, list its configuration.
-  else {
-    foreach ($packages as $package) {
-      if ($package->getMachineName() == $package_name) {
-        drush_hide_output_fields([
-          'machine_name',
-          'name',
-          'status',
-          'version',
-          'state',
-        ]);
-        foreach ($package->getConfig() as $item_name) {
-          $result[$item_name] = [
-            'object' => $item_name,
-          ];
-        }
-        return $result;
-      }
-    }
-
-  }
-
-  // If no matching package found, return an error.
-  drush_log(dt('Package "@package" not found.', ['@package' => $package_name]), 'warning');
-  return FALSE;
-}
-
-/**
- * Drush command callback for features-import-all.
- */
-function drush_features_import_all() {
-  $assigner = _drush_features_options();
-  $current_bundle = $assigner->getBundle();
-  $namespace = $current_bundle->isDefault() ? FeaturesBundleInterface::DEFAULT_BUNDLE : $current_bundle->getMachineName();
-
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  $packages = $manager->getPackages();
-  $packages = $manager->filterPackages($packages, $namespace);
-  $overridden = [];
-
-  foreach ($packages as $package) {
-    $overrides = $manager->detectOverrides($package);
-    $missing = $manager->detectMissing($package);
-    if ((!empty($missing) || !empty($overrides)) && ($package->getStatus() == FeaturesManagerInterface::STATUS_INSTALLED)) {
-      $overridden[] = $package->getMachineName();
-    }
-  }
-
-  if (!empty($overridden)) {
-    call_user_func_array('drush_features_import', $overridden);
-  }
-  else {
-    drush_log(dt('Current state already matches active config, aborting.'), 'ok');
-  }
-}
-
-/**
- * Provides Drush command callback for features-export.
- */
-function drush_features_export($packages = NULL) {
-  $packages = func_get_args();
-  $assigner = _drush_features_options();
-
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  /** @var \Drupal\features\FeaturesGeneratorInterface $generator */
-  $generator = \Drupal::service('features_generator');
-
-  $current_bundle = $assigner->getBundle();
-
-  if (drush_get_option('add-profile')) {
-    if ($current_bundle->isDefault) {
-      return drush_set_error('', dt("Must specify a profile name with --name"));
-    }
-    $current_bundle->setIsProfile(TRUE);
-  }
-
-  $all_packages = $manager->getPackages();
-  foreach ($packages as $name) {
-    if (!isset($all_packages[$name])) {
-      return drush_set_error('', dt("The package @name does not exist.", ['@name' => $name]));
-    }
-  }
-
-  if (empty($packages)) {
-    $packages = $all_packages;
-    $dt_args = ['@modules' => implode(', ', array_keys($packages))];
-    drush_print(dt('The following extensions will be exported: @modules', $dt_args));
-    if (!drush_confirm(dt('Do you really want to continue?'))) {
-      return drush_user_abort('Aborting.');
-    }
-  }
-
-  // If any packages exist, confirm before overwriting.
-  if ($existing_packages = $manager->listPackageDirectories($packages, $current_bundle)) {
-    foreach ($existing_packages as $name => $directory) {
-      drush_print(dt("The extension @name already exists at @directory.", ['@name' => $name, '@directory' => $directory]));
-    }
-    // Apparently, format_plural is not always available.
-    if (count($existing_packages) == 1) {
-      $message = dt('Would you like to overwrite it?');
-    }
-    else {
-      $message = dt('Would you like to overwrite them?');
-    }
-    if (!drush_confirm($message)) {
-      return drush_user_abort();
-    }
-  }
-
-  // Use the write generation method.
-  $method_id = FeaturesGenerationWrite::METHOD_ID;
-  $result = $generator->generatePackages($method_id, $current_bundle, $packages);
-
-  foreach ($result as $message) {
-    $type = $message['success'] ? 'success' : 'error';
-    drush_log($message['message'], $message['variables'], $type);
-  }
-}
-
-/**
- * Adds a component to a features module.
- *
- * @param
- *   The selected components.
- */
-function drush_features_add() {
-  if ($args = func_get_args()) {
-    $assigner = _drush_features_options();
-
-    /** @var \Drupal\features\FeaturesManagerInterface $manager */
-    $manager = \Drupal::service('features.manager');
-    /** @var \Drupal\features\FeaturesGeneratorInterface $generator */
-    $generator = \Drupal::service('features_generator');
-
-    $current_bundle = $assigner->getBundle();
-
-    $module = array_shift($args);
-    if (empty($args)) {
-      return drush_set_error('', 'No components supplied.');
-    }
-    $components = _drush_features_component_list();
-    $options = [
-      'exported' => FALSE,
-    ];
-
-    $filtered_components = _drush_features_component_filter($components, $args, $options);
-    $items = $filtered_components['components'];
-
-    if (empty($items)) {
-      return drush_set_error('', 'No components to add.');
-    }
-
-    $packages = [$module];
-    // If any packages exist, confirm before overwriting.
-    if ($existing_packages = $manager->listPackageDirectories($packages)) {
-      foreach ($existing_packages as $name => $directory) {
-        drush_print(dt("The extension @name already exists at @directory.", ['@name' => $name, '@directory' => $directory]));
-      }
-      // Apparently, format_plural is not always available.
-      if (count($existing_packages) == 1) {
-        $message = dt('Would you like to overwrite it?');
-      }
-      else {
-        $message = dt('Would you like to overwrite them?');
-      }
-      if (!drush_confirm($message)) {
-        return drush_user_abort();
-      }
-    }
-    else {
-      $package = $manager->initPackage($module, NULL, '', 'module', $current_bundle);
-      list($full_name, $path) = $manager->getExportInfo($package, $current_bundle);
-      drush_print(dt('Will create a new extension @name in @directory', ['@name' => $full_name, '@directory' => $path]));
-      if (!drush_confirm(dt('Do you really want to continue?'))) {
-        drush_die('Aborting.');
-      }
-    }
-
-    $config = _drush_features_build_config($items);
-
-    $manager->assignConfigPackage($module, $config);
-
-    // Use the write generation method.
-    $method_id = FeaturesGenerationWrite::METHOD_ID;
-    $result = $generator->generatePackages($method_id, $current_bundle, $packages);
-
-    foreach ($result as $message) {
-      $type = $message['success'] ? 'success' : 'error';
-      drush_log($message['message'], $message['variables'], $type);
-    }
-  }
-  else {
-    return drush_set_error('', 'No feature name given.');
-  }
-}
-
-/**
- * Lists components, with pattern matching.
- */
-function drush_features_components() {
-  $args = func_get_args();
-  _drush_features_options();
-
-  $components = _drush_features_component_list();
-  ksort($components);
-  // If no args supplied, prompt with a list.
-  if (empty($args)) {
-    $types = array_keys($components);
-    array_unshift($types, 'all');
-    $choice = drush_choice($types, 'Enter a number to choose which component type to list.');
-    if ($choice === FALSE) {
-      return;
-    }
-
-    $args = ($choice == 0) ? ['*'] : [$types[$choice]];
-  }
-  $options = [
-    'provided by' => TRUE,
-  ];
-  if (drush_get_option(['exported', 'e'], NULL)) {
-    $options['not exported'] = FALSE;
-  }
-  elseif (drush_get_option(['not-exported', 'o'], NULL)) {
-    $options['exported'] = FALSE;
-  }
-
-  $filtered_components = _drush_features_component_filter($components, $args, $options);
-  if ($filtered_components) {
-    _drush_features_component_print($filtered_components);
-  }
-}
-
-/**
- * Lists the differences in the package config vs the active store.
- *
- * @param string $package
- *   The machine name of a package.
- */
-function drush_features_diff() {
-  if (!$args = func_get_args()) {
-    drush_print_table(drush_features_list_packages());
-    return;
-  }
-
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  /** @var \Drupal\features\FeaturesAssignerInterface $assigner */
-  $assigner = \Drupal::service('features_assigner');
-  $assigner->assignConfigPackages();
-
-  $module = $args[0];
-  $filter_ctypes = drush_get_option("ctypes");
-  if ($filter_ctypes) {
-    $filter_ctypes = explode(',', $filter_ctypes);
-  }
-
-  $feature = $manager->loadPackage($module, TRUE);
-  if (empty($feature)) {
-    drush_log(dt('No such feature is available: @module', ['@module' => $module]), 'error');
-    return;
-  }
-
-  $lines = drush_get_option('lines');
-  $lines = isset($lines) ? $lines : 2;
-
-  $formatter = new DiffFormatter();
-  $formatter->leading_context_lines = $lines;
-  $formatter->trailing_context_lines = $lines;
-  $formatter->show_header = FALSE;
-
-  if (drush_get_context('DRUSH_NOCOLOR')) {
-    $red = $green = "%s";
-  }
-  else {
-    $red = "\033[31;40m\033[1m%s\033[0m";
-    $green = "\033[0;32;40m\033[1m%s\033[0m";
-  }
-
-  $overrides = $manager->detectOverrides($feature);
-  $missing = $manager->reorderMissing($manager->detectMissing($feature));
-  $overrides = array_merge($overrides, $missing);
-
-  if (empty($overrides)) {
-    drush_print(dt('Active config matches stored config for @module.', ['@module' => $module]));
-  }
-  else {
-    /** @var \Drupal\config_update\ConfigDiffInterface $config_diff */
-    $config_diff = \Drupal::service('config_update.config_diff');
-    /** @var \Drupal\Core\Config\StorageInterface $active_storage */
-    $active_storage = \Drupal::service('config.storage');
-
-    // Print key for colors.
-    drush_print(dt('Legend: '));
-    drush_print(sprintf($red, dt('Code:    drush features-import will replace the active config with the displayed code.')));
-    drush_print(sprintf($green, dt('Active:  drush features-export will update the exported feature with the displayed active config')));
-
-    foreach ($overrides as $name) {
-      $message = '';
-      if (in_array($name, $missing)) {
-        $message = sprintf($red, t('(missing from active)'));
-        $extension = [];
-      }
-      else {
-        $active = $manager->getActiveStorage()->read($name);
-        $extension = $manager->getExtensionStorages()->read($name);
-        if (empty($extension)) {
-          $extension = [];
-          $message = sprintf($green, t('(not exported)'));
-        }
-        $diff = $config_diff->diff($extension, $active);
-        $rows = explode("\n", $formatter->format($diff));
-      }
-      drush_print();
-      drush_print(dt("Config @name @message", ['@name' => $name, '@message' => $message]));
-      if (!empty($extension)) {
-        foreach ($rows as $row) {
-          if (strpos($row, '>') === 0) {
-            drush_print(sprintf($green, $row));
-          }
-          elseif (strpos($row, '<') === 0) {
-            drush_print(sprintf($red, $row));
-          }
-          else {
-            drush_print($row);
-          }
-        }
-      }
-    }
-  }
-}
-
-/**
- * Imports module config into the active store.
- *
- * Same as the old "revert" functionality.
- */
-function drush_features_import() {
-  if ($args = func_get_args()) {
-    _drush_features_options();
-
-    // Determine if revert should be forced.
-    $force = drush_get_option('force');
-    // Determine if -y was supplied. If so, we can filter out needless output
-    // from this command.
-    $skip_confirmation = drush_get_context('DRUSH_AFFIRMATIVE');
-
-    /** @var \Drupal\features\FeaturesManagerInterface $manager */
-    $manager = \Drupal::service('features.manager');
-
-    // Parse list of arguments.
-    $modules = [];
-    foreach ($args as $arg) {
-      $arg = explode(':', $arg);
-      $module = array_shift($arg);
-      $component = array_shift($arg);
-
-      if (isset($module)) {
-        if (empty($component)) {
-          // If we received just a feature name, this means that we need all of
-          // its components.
-          $modules[$module] = TRUE;
-        }
-        elseif ($modules[$module] !== TRUE) {
-          if (!isset($modules[$module])) {
-            $modules[$module] = [];
-          }
-          $modules[$module][] = $component;
-        }
-      }
-    }
-
-    // Process modules.
-    foreach ($modules as $module => $components_needed) {
-
-      $dt_args['@module'] = $module;
-      /** @var \Drupal\features\Package $feature */
-      $feature = $manager->loadPackage($module, TRUE);
-      if (empty($feature)) {
-        drush_log(dt('No such feature is available: @module', $dt_args), 'error');
-        return;
-      }
-
-      if ($feature->getStatus() != FeaturesManagerInterface::STATUS_INSTALLED) {
-        drush_log(dt('No such feature is installed: @module', $dt_args), 'error');
-        return;
-      }
-
-      // Forcefully revert all components of a feature.
-      if ($force) {
-        $components = $feature->getConfigOrig();
-      }
-      // Only revert components that are detected to be Overridden.
-      else {
-        $components = $manager->detectOverrides($feature);
-        $missing = $manager->reorderMissing($manager->detectMissing($feature));
-        // Be sure to import missing components first.
-        $components = array_merge($missing, $components);
-      }
-
-      if (!empty($components_needed) && is_array($components_needed)) {
-        $components = array_intersect($components, $components_needed);
-      }
-
-      if (empty($components)) {
-        drush_log(dt('Current state already matches active config, aborting.'), 'ok');
-      }
-      else {
-        // Determine which config the user wants to import/revert.
-        $config_to_create = [];
-        foreach ($components as $component) {
-          $dt_args['@component'] = $component;
-          $confirmation_message = 'Do you really want to import @module : @component?';
-          if ($skip_confirmation || drush_confirm(dt($confirmation_message, $dt_args))) {
-            $config_to_create[$component] = '';
-          }
-        }
-
-        // Perform the import/revert.
-        $config_imported = $manager->createConfiguration($config_to_create);
-
-        // List the results.
-        foreach ($components as $component) {
-          $dt_args['@component'] = $component;
-          if (isset($config_imported['new'][$component])) {
-            drush_log(dt('Imported @module : @component.', $dt_args), 'ok');
-          }
-          elseif (isset($config_imported['updated'][$component])) {
-            drush_log(dt('Reverted @module : @component.', $dt_args), 'ok');
-          }
-          elseif (!isset($config_to_create[$component])) {
-            drush_log(dt('Skipping @module : @component.', $dt_args), 'ok');
-          }
-          else {
-            drush_log(dt('Error importing @module : @component.', $dt_args), 'error');
-          }
-        }
-      }
-    }
-  }
-  else {
-    drush_print_table(drush_features_list_packages());
-    return;
-  }
-}
-
-/**
- * Returns an array of full config names given a array[$type][$component].
- *
- * @param array $items
- *   The items to return data for.
- */
-function _drush_features_build_config(array $items) {
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  $result = [];
-  foreach ($items as $config_type => $item) {
-    foreach ($item as $item_name => $title) {
-      $result[] = $manager->getFullName($config_type, $item_name);
-    }
-  }
-  return $result;
-}
-
-/**
- * Returns a listing of all known components, indexed by source.
- */
-function _drush_features_component_list() {
-  $result = [];
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  $config = $manager->getConfigCollection();
-  foreach ($config as $item_name => $item) {
-    $result[$item->getType()][$item->getShortName()] = $item->getLabel();
-  }
-  return $result;
-}
-
-/**
- * Filters components by patterns.
- */
-function _drush_features_component_filter($all_components, $patterns = [], $options = []) {
-  $options += [
-    'exported' => TRUE,
-    'not exported' => TRUE,
-    'provided by' => FALSE,
-  ];
-  $pool = [];
-  // Maps exported components to feature modules.
-  $components_map = _drush_features_get_component_map();
-  // First filter on exported state.
-  foreach ($all_components as $source => $components) {
-    foreach ($components as $name => $title) {
-      $exported = count($components_map[$source][$name]) > 0;
-      if ($exported) {
-        if ($options['exported']) {
-          $pool[$source][$name] = $title;
-        }
-      }
-      else {
-        if ($options['not exported']) {
-          $pool[$source][$name] = $title;
-        }
-      }
-    }
-  }
-
-  $state_string = '';
-
-  if (!$options['exported']) {
-    $state_string = 'unexported';
-  }
-  elseif (!$options['not exported']) {
-    $state_string = 'exported';
-  }
-
-  $selected = [];
-  foreach ($patterns as $pattern) {
-    // Rewrite * to %. Let users use both as wildcard.
-    $pattern = strtr($pattern, ['*' => '%']);
-    $sources = [];
-    list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
-    // If source is empty, use a pattern.
-    if ($source_pattern == '') {
-      $source_pattern = '%';
-    }
-    if ($component_pattern == '') {
-      $component_pattern = '%';
-    }
-
-    $preg_source_pattern = strtr(preg_quote($source_pattern, '/'), ['%' => '.*']);
-    $preg_component_pattern = strtr(preg_quote($component_pattern, '/'), ['%' => '.*']);
-    // If it isn't a pattern, but a simple string, we don't anchor the
-    // pattern. This allows for abbreviating. Otherwise, we do, as this seems
-    // more natural for patterns.
-    if (strpos($source_pattern, '%') !== FALSE) {
-      $preg_source_pattern = '^' . $preg_source_pattern . '$';
-    }
-    if (strpos($component_pattern, '%') !== FALSE) {
-      $preg_component_pattern = '^' . $preg_component_pattern . '$';
-    }
-    $matches = [];
-
-    // Find the sources.
-    $all_sources = array_keys($pool);
-    $matches = preg_grep('/' . $preg_source_pattern . '/', $all_sources);
-    if (count($matches) > 0) {
-      // If we have multiple matches and the source string wasn't a
-      // pattern, check if one of the matches is equal to the pattern, and
-      // use that, or error out.
-      if (count($matches) > 1 and $preg_source_pattern[0] != '^') {
-        if (in_array($source_pattern, $matches)) {
-          $matches = [$source_pattern];
-        }
-        else {
-          return drush_set_error('', dt('Ambiguous source "@source", matches @matches', [
-            '@source' => $source_pattern,
-            '@matches' => implode(', ', $matches),
-          ]));
-        }
-      }
-      // Loose the indexes preg_grep preserved.
-      $sources = array_values($matches);
-    }
-    else {
-      return drush_set_error('', dt('No @state sources match "@source"', ['@state' => $state_string, '@source' => $source_pattern]));
-    }
-
-    // Now find the components.
-    foreach ($sources as $source) {
-      // Find the components.
-      $all_components = array_keys($pool[$source]);
-      // See if there's any matches.
-      $matches = preg_grep('/' . $preg_component_pattern . '/', $all_components);
-      if (count($matches) > 0) {
-        // If we have multiple matches and the components string wasn't a
-        // pattern, check if one of the matches is equal to the pattern, and
-        // use that, or error out.
-        if (count($matches) > 1 and $preg_component_pattern[0] != '^') {
-          if (in_array($component_pattern, $matches)) {
-            $matches = [$component_pattern];
-          }
-          else {
-            return drush_set_error('', dt('Ambiguous component "@component", matches @matches', [
-              '@component' => $component_pattern,
-              '@matches' => implode(', ', $matches),
-            ]));
-          }
-        }
-        if (!is_array($selected[$source])) {
-          $selected[$source] = [];
-        }
-        $selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
-      }
-      else {
-        // No matches. If the source was a pattern, just carry on, else
-        // error out. Allows for patterns like :*field*.
-        if ($preg_source_pattern[0] != '^') {
-          return drush_set_error('', dt('No @state @source components match "@component"', [
-            '@state' => $state_string,
-            '@component' => $component_pattern,
-            '@source' => $source,
-          ]));
-        }
-      }
-    }
-  }
-
-  // Lastly, provide feature module information on the selected components, if
-  // requested.
-  $provided_by = [];
-  if ($options['provided by'] && $options['exported']) {
-    foreach ($selected as $source => $components) {
-      foreach ($components as $name => $title) {
-        $exported = count($components_map[$source][$name]) > 0;
-        if ($exported) {
-          $provided_by[$source . ':' . $name] = implode(', ', $components_map[$source][$name]);
-        }
-      }
-    }
-  }
-
-  return [
-    'components' => $selected,
-    'sources' => $provided_by,
-  ];
-}
-
-/**
- * Provides a component to feature map (port of features_get_component_map).
- */
-function _drush_features_get_component_map() {
-  $result = [];
-  /** @var \Drupal\features\FeaturesManagerInterface $manager */
-  $manager = \Drupal::service('features.manager');
-  // Recalc full config list without running assignments.
-  $config = $manager->getConfigCollection();
-  $packages = $manager->getPackages();
-
-  foreach ($config as $item_name => $item) {
-    $type = $item->getType();
-    $short_name = $item->getShortName();
-    $name = $item->getName();
-    if (!isset($result[$type][$short_name])) {
-      $result[$type][$short_name] = [];
-    }
-    if (!empty($item->getPackage())) {
-      $package = $packages[$item->getPackage()];
-      $result[$type][$short_name][] = $package->getMachineName();
-    }
-  }
-
-  return $result;
-}
-
-/**
- * Prints a list of filtered components.
- */
-function _drush_features_component_print($filtered_components) {
-  $rows = [[dt('Available sources')]];
-  foreach ($filtered_components['components'] as $source => $components) {
-    foreach ($components as $name => $value) {
-      $row = [$source . ':' . $name];
-      if (isset($filtered_components['sources'][$source . ':' . $name])) {
-        $row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source . ':' . $name];
-      }
-      $rows[] = $row;
-    }
-  }
-
-  drush_print_table($rows, TRUE);
-}
diff --git a/features.info.yml b/features.info.yml
index e4472942ad007ccda79e51f90f45e30c4c0f8480..f2ded86201591097575adb8115a4259cb1daa9f1 100644
--- a/features.info.yml
+++ b/features.info.yml
@@ -2,7 +2,7 @@ name: 'Features'
 type: module
 description: 'Enables administrators to package configuration into modules.'
 package: Development
-core_version_requirement: ^8.8 || ^9
+core_version_requirement: ^9.4 || ^10
 dependencies:
   - drupal:config
   - config_update:config_update
diff --git a/features.post_update.php b/features.post_update.php
index 1ef317a7f2e7489325c721323d6ed5b4aa13d51a..307fa94683edcb495a89b0155fd5eec27e0dadf5 100644
--- a/features.post_update.php
+++ b/features.post_update.php
@@ -25,3 +25,10 @@ function features_post_update_features_assigner_args() {
 function features_post_update_prefixed_dependencies() {
   // Empty post-update hook.
 }
+
+/**
+ * Clear caches due to changes in features_assigner service arguments.
+ */
+function features_post_update_features_assigner_args2() {
+  // Empty post-update hook.
+}
diff --git a/features.services.yml b/features.services.yml
index c42aa80f6c0e2fcd7d5cc26d4a33a99145ef1b7a..2b9e1e275c00bee6c88b0129c143ca4ffcd18271 100644
--- a/features.services.yml
+++ b/features.services.yml
@@ -7,7 +7,7 @@ services:
     arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
   features_assigner:
     class: Drupal\features\FeaturesAssigner
-    arguments: ['@features.manager', '@plugin.manager.features_assignment_method', '@entity_type.manager', '@config.factory', '@config.storage', '%install_profile%']
+    arguments: ['@features.manager', '@plugin.manager.features_assignment_method', '@entity_type.manager', '@config.factory', '@config.storage', '%install_profile%', '@request_stack']
     calls:
       - [initFeaturesManager]
   features_generator:
@@ -17,7 +17,7 @@ services:
       - [initFeaturesManager]
   features.manager:
     class: Drupal\features\FeaturesManager
-    arguments: ['@app.root', '@entity_type.manager', '@config.factory', '@config.storage', '@config.manager', '@module_handler', '@features.config_update', '@extension.list.module']
+    arguments: ['%app.root%', '@entity_type.manager', '@config.factory', '@config.storage', '@config.manager', '@module_handler', '@features.config_update', '@extension.list.module', '@extension.path.resolver']
 
   features.config_update:
     class: Drupal\config_update\ConfigReverter
@@ -25,16 +25,25 @@ services:
 
   features.extension_storage:
     class: Drupal\features\FeaturesInstallStorage
-    arguments: ['@config.storage']
+    arguments: ['@config.storage', '@extension.path.resolver']
 
   features.extension_optional_storage:
     class: Drupal\features\FeaturesInstallStorage
-    arguments: ['@config.storage', 'config/optional']
+    arguments: ['@config.storage', '@extension.path.resolver', 'config/optional']
 
   features.config.installer:
     class: Drupal\features\FeaturesConfigInstaller
     decorates: config.installer
-    arguments: ['@features.config.installer.inner', '@features.manager', '@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher', '%install_profile%']
+    arguments:
+      - '@features.config.installer.inner'
+      - '@features.manager'
+      - '@config.factory'
+      - '@config.storage'
+      - '@config.typed'
+      - '@config.manager'
+      - '@event_dispatcher'
+      - '%install_profile%'
+      - '@extension.path.resolver'
     decoration_priority: 9
 
   logger.channel.features:
diff --git a/modules/features_ui/features_ui.info.yml b/modules/features_ui/features_ui.info.yml
index 2574aea4a805641b6c718b55f83e38fad0c8be6f..c5b9406493e7196dec7212736f9635762b342ed2 100644
--- a/modules/features_ui/features_ui.info.yml
+++ b/modules/features_ui/features_ui.info.yml
@@ -2,7 +2,7 @@ name: Features UI
 type: module
 description: 'Provides the user interface for Features.'
 package: Development
-core_version_requirement: ^8.8 || ^9
+core_version_requirement: ^9.4 || ^10
 configure: features.assignment
 dependencies:
   - features:features
diff --git a/modules/features_ui/src/Form/AssignmentBaseForm.php b/modules/features_ui/src/Form/AssignmentBaseForm.php
index 23f0b2aa23930a1c38cf3b8713483a31ff75f40b..0bfe709d1b3cf43a4c86b7accb4b44d8886ecb0d 100644
--- a/modules/features_ui/src/Form/AssignmentBaseForm.php
+++ b/modules/features_ui/src/Form/AssignmentBaseForm.php
@@ -27,7 +27,7 @@ class AssignmentBaseForm extends AssignmentFormBase {
 
     // Pass the last argument to limit the select to config entity types that
     // provide bundles for other entity types.
-    $this->setConfigTypeSelect($form, $settings['types']['config'], $this->t('base'), TRUE);
+    $this->setConfigTypeSelect($form, $settings['types']['config'], $this->t('base'));
     // Pass the last argument to limit the select to content entity types do
     // not have config entity provided bundles, thus avoiding duplication with
     // the config type select options.
diff --git a/modules/features_ui/src/Form/FeaturesEditForm.php b/modules/features_ui/src/Form/FeaturesEditForm.php
index 75cb3103c932c4c60fb4e084485bf96d45bf08f3..a2d71318cc87ddb31e8e3939c6284b7a8a09e4e7 100644
--- a/modules/features_ui/src/Form/FeaturesEditForm.php
+++ b/modules/features_ui/src/Form/FeaturesEditForm.php
@@ -152,7 +152,10 @@ class FeaturesEditForm extends FormBase {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state, $featurename = '') {
-    $session = $this->getRequest()->getSession();
+    if ($this->getRequest()->hasSession()) {
+      $session = $this->getRequest()->getSession();
+    }
+
     $trigger = $form_state->getTriggeringElement();
     if (isset($trigger['#name']) && $trigger['#name'] == 'package') {
       // Save current bundle name for later ajax callback.
diff --git a/modules/features_ui/tests/src/Functional/FeaturesBundleUiTest.php b/modules/features_ui/tests/src/Functional/FeaturesBundleUiTest.php
index 6a0efabca8f4d8ba8155c89bb1ad2904cd58de76..33351bf180e55ffa34a96f4f2c645895b3b24457 100644
--- a/modules/features_ui/tests/src/Functional/FeaturesBundleUiTest.php
+++ b/modules/features_ui/tests/src/Functional/FeaturesBundleUiTest.php
@@ -91,14 +91,14 @@ class FeaturesBundleUiTest extends BrowserTestBase {
 
     // Check initial form.
     $this->drupalGet('admin/config/development/features/bundle/_exclude/default');
-    $this->assertFieldChecked('edit-types-config-features-bundle', 'features_bundle is checked');
-    $this->assertNoFieldChecked('edit-types-config-system-simple', 'system_simple is not checked');
-    $this->assertNoFieldChecked('edit-types-config-user-role', 'user_role is not checked');
-    $this->assertFieldChecked('edit-curated', 'curated is checked');
-    $this->assertFieldChecked('edit-module-namespace', 'namespace is checked');
+    $this->assertSession()->checkboxChecked('edit-types-config-features-bundle');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-system-simple');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-user-role');
+    $this->assertSession()->checkboxChecked('edit-curated');
+    $this->assertSession()->checkboxChecked('edit-module-namespace');
 
     // Configure the form.
-    $this->drupalPostForm(NULL, [
+    $this->submitForm([
       'types[config][system_simple]' => TRUE,
       'types[config][user_role]' => FALSE,
       'curated' => TRUE,
@@ -107,11 +107,11 @@ class FeaturesBundleUiTest extends BrowserTestBase {
 
     // Check form results.
     $this->drupalGet('admin/config/development/features/bundle/_exclude/default');
-    $this->assertFieldChecked('edit-types-config-features-bundle', 'Saved, features_bundle is checked');
-    $this->assertFieldChecked('edit-types-config-system-simple', 'Saved, system_simple is checked');
-    $this->assertNoFieldChecked('edit-types-config-user-role', 'Saved, user_role is not checked');
-    $this->assertFieldChecked('edit-curated', 'Saved, curated is checked');
-    $this->assertNoFieldChecked('edit-module-namespace', 'Saved, namespace is not checked');
+    $this->assertSession()->checkboxChecked('edit-types-config-features-bundle');
+    $this->assertSession()->checkboxChecked('edit-types-config-system-simple');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-user-role');
+    $this->assertSession()->checkboxChecked('edit-curated');
+    $this->assertSession()->checkboxNotChecked('edit-module-namespace');
 
     // Check final values.
     $settings = $this->defaultBundle()->getAssignmentSettings('exclude');
@@ -143,19 +143,19 @@ class FeaturesBundleUiTest extends BrowserTestBase {
 
     // Can we visit the config page with no settings?
     $this->drupalGet('admin/config/development/features/bundle/_exclude/default');
-    $this->assertNoFieldChecked('edit-types-config-features-bundle', 'features_bundle is not checked');
-    $this->assertNoFieldChecked('edit-types-config-system-simple', 'system_simple is not checked');
-    $this->assertNoFieldChecked('edit-types-config-user-role', 'user_role is not checked');
-    $this->assertNoFieldChecked('edit-curated', 'curated is not checked');
-    $this->assertNoFieldChecked('edit-module-namespace', 'namespace is not checked');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-features-bundle');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-system-simple');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-user-role');
+    $this->assertSession()->checkboxNotChecked('edit-curated');
+    $this->assertSession()->checkboxNotChecked('edit-module-namespace');
 
     // Can we enable the method?
     $this->drupalGet('admin/config/development/features/bundle');
-    $this->assertNoFieldChecked('edit-enabled-exclude', 'Exclude disabled');
-    $this->drupalPostForm(NULL, [
+    $this->assertSession()->checkboxNotChecked('edit-enabled-exclude');
+    $this->submitForm([
       'enabled[exclude]' => TRUE,
     ], 'Save settings');
-    $this->assertFieldChecked('edit-enabled-exclude', 'Exclude enabled');
+    $this->assertSession()->checkboxChecked('edit-enabled-exclude');
 
     // Check new settings.
     $settings = $this->defaultBundle()->getAssignmentSettings('exclude');
@@ -168,9 +168,10 @@ class FeaturesBundleUiTest extends BrowserTestBase {
 
     // Can we run assignment with no settings?
     $this->drupalGet('admin/config/development/features');
+    $this->drupalGet('admin/config/development/features/bundle/_exclude/default');
 
     // Can we configure the method?
-    $this->drupalPostForm('admin/config/development/features/bundle/_exclude/default', [
+    $this->submitForm([
       'types[config][system_simple]' => TRUE,
       'types[config][user_role]' => FALSE,
       'curated' => TRUE,
@@ -179,11 +180,11 @@ class FeaturesBundleUiTest extends BrowserTestBase {
 
     // Check form results.
     $this->drupalGet('admin/config/development/features/bundle/_exclude/default');
-    $this->assertNoFieldChecked('edit-types-config-features-bundle', 'Saved, features_bundle is not checked');
-    $this->assertFieldChecked('edit-types-config-system-simple', 'Saved, system_simple is checked');
-    $this->assertNoFieldChecked('edit-types-config-user-role', 'Saved, user_role is not checked');
-    $this->assertFieldChecked('edit-curated', 'Saved, curated is checked');
-    $this->assertNoFieldChecked('edit-module-namespace', 'Saved, namespace is not checked');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-features-bundle');
+    $this->assertSession()->checkboxChecked('edit-types-config-system-simple');
+    $this->assertSession()->checkboxNotChecked('edit-types-config-user-role');
+    $this->assertSession()->checkboxChecked('edit-curated');
+    $this->assertSession()->checkboxNotChecked('edit-module-namespace');
 
     // Check final values.
     $settings = $this->defaultBundle()->getAssignmentSettings('exclude');
diff --git a/modules/features_ui/tests/src/Functional/FeaturesCreateUiTest.php b/modules/features_ui/tests/src/Functional/FeaturesCreateUiTest.php
index 27197b67f4d7d7858fab32e820d9bc321c41d927..3fc72335051aa74f2e40285200568ce8433730b2 100644
--- a/modules/features_ui/tests/src/Functional/FeaturesCreateUiTest.php
+++ b/modules/features_ui/tests/src/Functional/FeaturesCreateUiTest.php
@@ -45,7 +45,7 @@ class FeaturesCreateUiTest extends BrowserTestBase {
     $this->drupalPlaceBlock('local_actions_block');
     $this->drupalGet('admin/config/development/features');
     $this->clickLink('Create new feature');
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     $edit = [
       'name' => 'Test feature',
@@ -55,10 +55,10 @@ class FeaturesCreateUiTest extends BrowserTestBase {
       'system_simple[sources][selected][system.theme]' => TRUE,
       'system_simple[sources][selected][user.settings]' => TRUE,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Download Archive');
+    $this->submitForm($edit, 'Download Archive');
 
-    $this->assertResponse(200);
-    $archive = $this->getRawContent();
+    $this->assertSession()->statusCodeEquals(200);
+    $archive = $this->getSession()->getPage()->getContent();
     $filename = tempnam($this->tempFilesDirectory, 'feature');
     file_put_contents($filename, $archive);
 
@@ -89,12 +89,12 @@ class FeaturesCreateUiTest extends BrowserTestBase {
     // Ensure that the features listing renders the right content.
     $this->drupalGet('admin/config/development/features');
     $tds = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]/td');
-    $this->assertLink('Test feature');
+    $this->assertSession()->linkExists('Test feature');
     $this->assertEquals($feature_name, $tds[2]->getText());
     $description_column = $tds[3]->getText();
     $this->assertTrue(strpos($description_column, 'system.theme') !== FALSE);
     $this->assertTrue(strpos($description_column, 'user.settings') !== FALSE);
-    $this->assertRaw('Test description: <strong>giraffe</strong>');
+    $this->assertSession()->responseContains('Test description: <strong>giraffe</strong>');
     $this->assertEquals('Uninstalled', $tds[5]->getText());
     $this->assertEquals('', $tds[6]->getText());
 
@@ -104,7 +104,7 @@ class FeaturesCreateUiTest extends BrowserTestBase {
       'system_simple[included][system.theme]' => FALSE,
       'user_role[sources][selected][authenticated]' => TRUE,
     ];
-    $this->drupalPostForm(NULL, $edit, 'Write');
+    $this->submitForm($edit, 'Write');
     $info_filename = $module_path . '/' . $feature_name . '.info.yml';
 
     $parsed_info = Yaml::decode(file_get_contents($info_filename));
@@ -124,7 +124,8 @@ class FeaturesCreateUiTest extends BrowserTestBase {
     // Install new feature module.
     $edit = [];
     $edit['modules[' . $feature_name . '][enable]'] = TRUE;
-    $this->drupalPostForm('admin/modules', $edit, 'Install');
+    $this->drupalGet('admin/modules');
+    $this->submitForm($edit, 'Install');
 
     // Check that the feature is listed as installed.
     $this->drupalGet('admin/config/development/features');
@@ -143,9 +144,10 @@ class FeaturesCreateUiTest extends BrowserTestBase {
 
     $tds = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]/td');
     $this->assertTrue(strpos($tds[6]->getText(), 'Changed') !== FALSE);
+    $this->drupalGet('admin/modules/uninstall');
 
     // Uninstall the module.
-    $this->drupalPostForm('admin/modules/uninstall', ['uninstall[' . $feature_name . ']' => $feature_name], 'Uninstall');
+    $this->submitForm(['uninstall[' . $feature_name . ']' => $feature_name], 'Uninstall');
     $this->submitForm([], 'Uninstall');
 
     $this->drupalGet('admin/config/development/features');
@@ -155,12 +157,13 @@ class FeaturesCreateUiTest extends BrowserTestBase {
 
     $this->clickLink('Changed');
     $this->drupalGet('admin/config/development/features/diff/' . $feature_name);
-    $this->assertRaw('<td class="diff-context diff-deletedline">anonymous : <span class="diffchange">Giraffe</span></td>');
-    $this->assertRaw('<td class="diff-context diff-addedline">anonymous : <span class="diffchange">Anonymous</span></td>');
+    $this->assertSession()->responseContains('<td class="diff-context diff-deletedline">anonymous : <span class="diffchange">Giraffe</span></td>');
+    $this->assertSession()->responseContains('<td class="diff-context diff-addedline">anonymous : <span class="diffchange">Anonymous</span></td>');
 
     $edit = [];
     $edit['modules[' . $feature_name . '][enable]'] = TRUE;
-    $this->drupalPostForm('admin/modules', $edit, 'Install');
+    $this->drupalGet('admin/modules');
+    $this->submitForm($edit, 'Install');
 
     $this->drupalGet('admin/config/development/features');
     $tds = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]/td');
@@ -180,7 +183,7 @@ class FeaturesCreateUiTest extends BrowserTestBase {
     $this->assertTrue(strpos($tds[6]->getText(), 'Changed') !== FALSE);
 
     $this->clickLink('Test feature');
-    $this->drupalPostForm(NULL, [], 'Write');
+    $this->submitForm([], 'Write');
 
     $this->drupalGet('admin/config/development/features');
     $tds = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]/td');
diff --git a/src/FeaturesAssigner.php b/src/FeaturesAssigner.php
index 73bfae167dfb4da23597031cb57b773f42e2e229..6dfc7a08dd8f18652ccccfdf447d450b92ac88c8 100644
--- a/src/FeaturesAssigner.php
+++ b/src/FeaturesAssigner.php
@@ -5,9 +5,10 @@ namespace Drupal\features;
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\ExtensionInstallStorage;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Class responsible for performing package assignment.
@@ -78,6 +79,13 @@ class FeaturesAssigner implements FeaturesAssignerInterface {
    */
   protected $installProfile;
 
+  /**
+   * The current request.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
   /**
    * Constructs a new FeaturesAssigner object.
    *
@@ -94,13 +102,14 @@ class FeaturesAssigner implements FeaturesAssignerInterface {
    * @param string $install_profile
    *   The name of the currently active installation profile.
    */
-  public function __construct(FeaturesManagerInterface $features_manager, PluginManagerInterface $assigner_manager, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, $install_profile) {
+  public function __construct(FeaturesManagerInterface $features_manager, PluginManagerInterface $assigner_manager, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, $install_profile, RequestStack $request_stack) {
     $this->featuresManager = $features_manager;
     $this->assignerManager = $assigner_manager;
     $this->entityTypeManager = $entity_type_manager;
     $this->configFactory = $config_factory;
     $this->configStorage = $config_storage;
     $this->installProfile = $install_profile;
+    $this->request = $request_stack->getCurrentRequest();
     $this->bundles = $this->getBundleList();
     $this->currentBundle = $this->getBundle(FeaturesBundleInterface::DEFAULT_BUNDLE);
     // Ensure bundle information is fresh.
@@ -259,8 +268,8 @@ class FeaturesAssigner implements FeaturesAssignerInterface {
    */
   public function setCurrent(FeaturesBundleInterface $bundle) {
     $this->currentBundle = $bundle;
-    $session = \Drupal::request()->getSession();
-    if (isset($session)) {
+    if ($this->request->hasSession()) {
+      $session = $this->request->getSession();
       $session->set('features_current_bundle', $bundle->getMachineName());
     }
     return $bundle;
@@ -429,8 +438,8 @@ class FeaturesAssigner implements FeaturesAssignerInterface {
    */
   public function loadBundle($machine_name = NULL) {
     if (!isset($machine_name)) {
-      $session = \Drupal::request()->getSession();
-      if (isset($session)) {
+      if ($this->request->hasSession()) {
+        $session = $this->request->getSession();
         $machine_name = isset($session) ? $session->get('features_current_bundle', FeaturesBundleInterface::DEFAULT_BUNDLE) : FeaturesBundleInterface::DEFAULT_BUNDLE;
       }
     }
diff --git a/src/FeaturesBundleInterface.php b/src/FeaturesBundleInterface.php
index 83dfbf5a9bc034dbd43fd8e099477872fb266375..07cd609e2a63f6888fa29859148a2960f5726122 100644
--- a/src/FeaturesBundleInterface.php
+++ b/src/FeaturesBundleInterface.php
@@ -7,6 +7,8 @@ namespace Drupal\features;
  */
 interface FeaturesBundleInterface {
 
+  const CORE_VERSION_REQUIREMENT = '^9.4 || ^10';
+
   const DEFAULT_BUNDLE = 'default';
 
   /**
diff --git a/src/FeaturesConfigInstaller.php b/src/FeaturesConfigInstaller.php
index d79577e68d62485a3c666bd6b60e74bdec4f05fb..00c66932bde6e8c19c4dc415a4e71ffc7eae3dd9 100644
--- a/src/FeaturesConfigInstaller.php
+++ b/src/FeaturesConfigInstaller.php
@@ -2,12 +2,13 @@
 
 namespace Drupal\features;
 
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\ConfigInstaller;
 use Drupal\Core\Config\ConfigInstallerInterface;
-use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\ConfigManagerInterface;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Extension\ExtensionPathResolver;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -51,9 +52,11 @@ class FeaturesConfigInstaller extends ConfigInstaller {
    *   The event dispatcher.
    * @param string $install_profile
    *   The name of the currently active installation profile.
+   * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path_resolver
+   *   The extension path resolver.
    */
-  public function __construct(ConfigInstallerInterface $config_installer, FeaturesManagerInterface $features_manager, ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher, $install_profile) {
-    parent::__construct($config_factory, $active_storage, $typed_config, $config_manager, $event_dispatcher, $install_profile);
+  public function __construct(ConfigInstallerInterface $config_installer, FeaturesManagerInterface $features_manager, ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher, $install_profile, ExtensionPathResolver $extension_path_resolver) {
+    parent::__construct($config_factory, $active_storage, $typed_config, $config_manager, $event_dispatcher, $install_profile, $extension_path_resolver);
     $this->configInstaller = $config_installer;
     $this->featuresManager = $features_manager;
   }
diff --git a/src/FeaturesExtensionStorages.php b/src/FeaturesExtensionStorages.php
index 1030aad8f34fe1fc3dbca033e227a1bc0ddaf756..6f9ea120488278acffa3e43e0198a72d3c7585c8 100644
--- a/src/FeaturesExtensionStorages.php
+++ b/src/FeaturesExtensionStorages.php
@@ -5,6 +5,7 @@ namespace Drupal\features;
 use Drupal\Core\Config\InstallStorage;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Extension\Extension;
+use Drupal\Core\Extension\ExtensionPathResolver;
 
 /**
  * Wraps FeaturesInstallStorage to support multiple configuration directories.
@@ -18,6 +19,13 @@ class FeaturesExtensionStorages implements FeaturesExtensionStoragesInterface {
    */
   protected $configStorage;
 
+  /**
+   * Instance of the extension path resolver service.
+   *
+   * @var \Drupal\Core\Extension\ExtensionPathResolver
+   */
+  protected ExtensionPathResolver $extensionPathResolver;
+
   /**
    * The extension storages.
    *
@@ -37,9 +45,12 @@ class FeaturesExtensionStorages implements FeaturesExtensionStoragesInterface {
    *
    * @param \Drupal\Core\Config\StorageInterface $config_storage
    *   The configuration storage.
+   * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path_resolver
+   *   Instance of the extension path resolver service.
    */
-  public function __construct(StorageInterface $config_storage) {
+  public function __construct(StorageInterface $config_storage, ExtensionPathResolver $extension_path_resolver) {
     $this->configStorage = $config_storage;
+    $this->extensionPathResolver = $extension_path_resolver;
   }
 
   /**
@@ -53,7 +64,7 @@ class FeaturesExtensionStorages implements FeaturesExtensionStoragesInterface {
    * {@inheritdoc}
    */
   public function addStorage($directory = InstallStorage::CONFIG_INSTALL_DIRECTORY) {
-    $this->extensionStorages[$directory] = new FeaturesInstallStorage($this->configStorage, $directory);
+    $this->extensionStorages[$directory] = new FeaturesInstallStorage($this->configStorage, $this->extensionPathResolver, $directory);
     $this->reset();
   }
 
diff --git a/src/FeaturesInstallStorage.php b/src/FeaturesInstallStorage.php
index b7e90c00396c09a7a1b707300528cdb671891bf0..e1bd40a5ace65fbe502888576b028254389d6241 100644
--- a/src/FeaturesInstallStorage.php
+++ b/src/FeaturesInstallStorage.php
@@ -2,10 +2,11 @@
 
 namespace Drupal\features;
 
-use Drupal\Core\Site\Settings;
 use Drupal\Core\Config\ExtensionInstallStorage;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Extension\ExtensionPathResolver;
+use Drupal\Core\Site\Settings;
 
 /**
  * Storage to access configuration and schema in installed extensions.
@@ -19,6 +20,13 @@ use Drupal\Core\Extension\ExtensionDiscovery;
  */
 class FeaturesInstallStorage extends ExtensionInstallStorage {
 
+  /**
+   * Instance of the extension path resolver service.
+   *
+   * @var \Drupal\Core\Extension\ExtensionPathResolver
+   */
+  protected ExtensionPathResolver $extensionPathResolver;
+
   /**
    * Overrides \Drupal\Core\Config\ExtensionInstallStorage::__construct().
    *
@@ -27,6 +35,8 @@ class FeaturesInstallStorage extends ExtensionInstallStorage {
    * @param \Drupal\Core\Config\StorageInterface $config_storage
    *   The active configuration store where the list of enabled modules and
    *   themes is stored.
+   * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path_resolver
+   *   Instance of the extension path resolver service.
    * @param string $directory
    *   The directory to scan in each extension to scan for files. Defaults to
    *   'config/install'. This parameter will be mandatory in Drupal 9.0.0.
@@ -41,7 +51,8 @@ class FeaturesInstallStorage extends ExtensionInstallStorage {
    *   (optional) The current installation profile. This parameter will be
    *   mandatory in Drupal 9.0.0.
    */
-  public function __construct(StorageInterface $config_storage, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE, $profile = NULL) {
+  public function __construct(StorageInterface $config_storage, ExtensionPathResolver $extension_path_resolver, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE, $profile = NULL) {
+    $this->extensionPathResolver = $extension_path_resolver;
     // @todo: determine if we should be setting $include_profile to FALSE.
     parent::__construct($config_storage, $directory, $collection, FALSE, $profile);
   }
@@ -113,7 +124,7 @@ class FeaturesInstallStorage extends ExtensionInstallStorage {
           // file location so we can use drupal_get_path() on the active profile
           // during the module scan.
           // @todo Remove as part of https://www.drupal.org/node/2186491
-          drupal_get_filename('profile', $profile, $profile_list[$profile]->getPathname());
+          $this->extensionPathResolver->getPathname('profile', $profile);
         }
         // CHANGED START: Put Features modules first in list returned.
         // to allow features to override config provided by other extensions.
diff --git a/src/FeaturesManager.php b/src/FeaturesManager.php
index 644ab22e4c2178962668f657de0001934e23075c..b8b8dbffdf96316c9835a7cbfa1ab9e547f499d2 100644
--- a/src/FeaturesManager.php
+++ b/src/FeaturesManager.php
@@ -1,23 +1,23 @@
 <?php
 
 namespace Drupal\features;
-use Drupal\Core\Config\ImmutableConfig;
-use Drupal;
 use Drupal\Component\Serialization\Yaml;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\config_update\ConfigRevertInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Config\ImmutableConfig;
 use Drupal\Core\Config\InstallStorage;
 use Drupal\Core\Config\StorageInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Extension\Dependency;
 use Drupal\Core\Extension\Extension;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Extension\ExtensionPathResolver;
 use Drupal\Core\Extension\ModuleExtensionList;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\config_update\ConfigRevertInterface;
 
 /**
  * The FeaturesManager provides helper functions for building packages.
@@ -149,8 +149,10 @@ class FeaturesManager implements FeaturesManagerInterface {
    *   The config revert interface.
    * @param \Drupal\Core\Extension\ModuleExtensionList $module_extension_list
    *   The module extension list service.
+   * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path_resolver
+   *   Instance of the extension path resolver service.
    */
-  public function __construct($root, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, ConfigManagerInterface $config_manager, ModuleHandlerInterface $module_handler, ConfigRevertInterface $config_reverter, ModuleExtensionList $module_extension_list) {
+  public function __construct($root, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, ConfigManagerInterface $config_manager, ModuleHandlerInterface $module_handler, ConfigRevertInterface $config_reverter, ModuleExtensionList $module_extension_list, ExtensionPathResolver $extension_path_resolver) {
     $this->root = $root;
     $this->entityTypeManager = $entity_type_manager;
     $this->configStorage = $config_storage;
@@ -160,7 +162,7 @@ class FeaturesManager implements FeaturesManagerInterface {
     $this->configReverter = $config_reverter;
     $this->moduleExtensionList = $module_extension_list;
     $this->settings = $config_factory->getEditable('features.settings');
-    $this->extensionStorages = new FeaturesExtensionStoragesByDirectory($this->configStorage);
+    $this->extensionStorages = new FeaturesExtensionStoragesByDirectory($this->configStorage, $extension_path_resolver);
     $this->extensionStorages->addStorage(InstallStorage::CONFIG_INSTALL_DIRECTORY);
     $this->extensionStorages->addStorage(InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
     $this->packages = [];
@@ -788,7 +790,7 @@ class FeaturesManager implements FeaturesManagerInterface {
     /** @var \Drupal\features\Package[] $packages */
     foreach ($packages as $package) {
       foreach ($package->getConfig() as $item_name) {
-        if (!empty($config_collection[$item_name]->getData()['dependencies']['config'])) {
+        if (!empty($config_collection[$item_name]) && !empty($config_collection[$item_name]->getData()['dependencies']['config'])) {
           foreach ($config_collection[$item_name]->getData()['dependencies']['config'] as $dependency_name) {
             if (isset($config_collection[$dependency_name]) &&
               // For configuration in the
@@ -1030,6 +1032,9 @@ class FeaturesManager implements FeaturesManagerInterface {
     if ($package->getConfig()) {
       // Add configuration files.
       foreach ($package->getConfig() as $name) {
+        if (empty($config_collection[$name])) {
+          continue;
+        }
         $config = $config_collection[$name];
 
         $package->appendFile([
diff --git a/src/Package.php b/src/Package.php
index 8a747a994d4e19ead7e9a31c18b694b73fa96c6c..09ee4a7ca57740cdaa90242c7cc38166b0cf2025 100644
--- a/src/Package.php
+++ b/src/Package.php
@@ -41,13 +41,12 @@ class Package {
   protected $version = '';
 
   /**
-   * The package core version requirement..
+   * The package type.
    *
    * @var string
-   * @todo: Make coreVersionRequirement a property of the
-   *   FeaturesBundleInterface object. For now, hard-code it.
+   * @todo This could be fetched from the extension object.
    */
-  protected $coreVersionRequirement = '^8.9 || ^9';
+  protected $coreVersionRequirement = FeaturesBundleInterface::CORE_VERSION_REQUIREMENT;
 
   /**
    * The package type.
diff --git a/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php b/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php
index abcbe4a448725108ad6ac972c752156807630999..1d4b94ec06433af86b78aca21b771e0259c1201e 100644
--- a/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php
+++ b/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php
@@ -3,12 +3,12 @@
 namespace Drupal\features\Plugin\FeaturesGeneration;
 
 use Drupal\Core\Access\CsrfTokenGenerator;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\features\FeaturesGenerationMethodBase;
 use Drupal\Core\Archiver\ArchiveTar;
-use Drupal\Core\File\FileSystem;
+use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\features\FeaturesBundleInterface;
+use Drupal\features\FeaturesGenerationMethodBase;
 use Drupal\features\Package;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -41,7 +41,7 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
   /**
    * The file system service.
    *
-   * @var \Drupal\Core\File\FileSystem
+   * @var \Drupal\Core\File\FileSystemInterface
    */
   protected $fileSystem;
 
@@ -52,10 +52,10 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
    *   The app root.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token generator.
-   * @param \Drupal\Core\File\FileSystem $file_system
+   * @param \Drupal\Core\File\FileSystemInterface $file_system
    *   The file system service.
    */
-  public function __construct($root, CsrfTokenGenerator $csrf_token, FileSystem $file_system) {
+  public function __construct($root, CsrfTokenGenerator $csrf_token, FileSystemInterface $file_system) {
     $this->root = $root;
     $this->csrfToken = $csrf_token;
     $this->fileSystem = $file_system;
@@ -66,7 +66,7 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     return new static(
-      $container->get('app.root'),
+      $container->getParameter('app.root'),
       $container->get('csrf_token'),
       $container->get('file_system')
     );
diff --git a/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php b/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php
index 98d10ea279d4a58e7c5983803a3d5f23876a8366..aede6d42302da5ab15a21edf0826f3895f6b0329 100644
--- a/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php
+++ b/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php
@@ -58,7 +58,7 @@ class FeaturesGenerationWrite extends FeaturesGenerationMethodBase implements Co
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     return new static(
-      $container->get('app.root'),
+      $container->getParameter('app.root'),
       $container->get('file_system')
     );
   }
diff --git a/tests/modules/test_feature/test_feature.info.yml b/tests/modules/test_feature/test_feature.info.yml
index 4e847b63178e88930eadcd92d4a4e0f88c47af27..432a876d4df7340145c594df13bbc58796210354 100644
--- a/tests/modules/test_feature/test_feature.info.yml
+++ b/tests/modules/test_feature/test_feature.info.yml
@@ -1,7 +1,5 @@
-name: feature
+name: test_feature
 type: module
-core_version_requirement: ^8.8 || ^9
-package: Test
-# Force this to install after features.
+package: Testing
 dependencies:
   - features:features
diff --git a/tests/modules/test_feature_generation/src/Plugin/FeaturesGeneration/FeaturesGenerationArchiveAlter.php b/tests/modules/test_feature_generation/src/Plugin/FeaturesGeneration/FeaturesGenerationArchiveAlter.php
new file mode 100644
index 0000000000000000000000000000000000000000..4fac1278e2f84284809fd553f91fff6131524c74
--- /dev/null
+++ b/tests/modules/test_feature_generation/src/Plugin/FeaturesGeneration/FeaturesGenerationArchiveAlter.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\test_feature_generation\Plugin\FeaturesGeneration;
+
+use Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationArchive;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * FeaturesGenerationArchiveAlter.
+ */
+class FeaturesGenerationArchiveAlter extends FeaturesGenerationArchive {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      'vfs://drupal',
+      $container->get('csrf_token'),
+      $container->get('file_system')
+    );
+  }
+
+}
diff --git a/tests/modules/test_feature_generation/src/Plugin/FeaturesGeneration/FeaturesGenerationWriteAlter.php b/tests/modules/test_feature_generation/src/Plugin/FeaturesGeneration/FeaturesGenerationWriteAlter.php
new file mode 100644
index 0000000000000000000000000000000000000000..c752f58a416ef46c357d65ff4d3e9d3c2f0f131f
--- /dev/null
+++ b/tests/modules/test_feature_generation/src/Plugin/FeaturesGeneration/FeaturesGenerationWriteAlter.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\test_feature_generation\Plugin\FeaturesGeneration;
+
+use Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationWrite;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * FeaturesGenerationWriteAlter.
+ */
+class FeaturesGenerationWriteAlter extends FeaturesGenerationWrite {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      'vfs://drupal',
+      $container->get('file_system')
+    );
+  }
+
+}
diff --git a/tests/modules/test_feature_generation/test_feature_generation.info.yml b/tests/modules/test_feature_generation/test_feature_generation.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4a946259a5df8f3f3dc176c71676859bf69a2950
--- /dev/null
+++ b/tests/modules/test_feature_generation/test_feature_generation.info.yml
@@ -0,0 +1,5 @@
+name: Test feature generation
+type: module
+package: Testing
+dependencies:
+  - features:features
diff --git a/tests/modules/test_feature_generation/test_feature_generation.module b/tests/modules/test_feature_generation/test_feature_generation.module
new file mode 100644
index 0000000000000000000000000000000000000000..0b9297c046059b43eccec9dc3c27954ff0bdff30
--- /dev/null
+++ b/tests/modules/test_feature_generation/test_feature_generation.module
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Main hooks for Test feature generation module.
+ */
+
+use Drupal\test_feature_generation\Plugin\FeaturesGeneration\FeaturesGenerationArchiveAlter;
+use Drupal\test_feature_generation\Plugin\FeaturesGeneration\FeaturesGenerationWriteAlter;
+
+/**
+ * Implements hook_element_plugin_alter()
+ */
+function test_feature_generation_features_generation_info_alter(array &$definitions) {
+  $definitions['write']['class'] = FeaturesGenerationWriteAlter::class;
+  $definitions['archive']['class'] = FeaturesGenerationArchiveAlter::class;
+}
diff --git a/tests/modules/test_mybundle_core/test_mybundle_core.info.yml b/tests/modules/test_mybundle_core/test_mybundle_core.info.yml
index b8eb9cc850cacf193dcdd0de89c98f35fd646b6b..4c6e2d6b92a2954eda7aa6e14d7c17985371c20e 100644
--- a/tests/modules/test_mybundle_core/test_mybundle_core.info.yml
+++ b/tests/modules/test_mybundle_core/test_mybundle_core.info.yml
@@ -1,7 +1,5 @@
 name: Test Core
 type: module
-core_version_requirement: ^8.9 || ^9
-package: Test
-# Ensure features is enabled first
+package: Testing
 dependencies:
   - features:features
diff --git a/tests/src/Kernel/Entity/FeaturesBundleIntegrationTest.php b/tests/src/Kernel/Entity/FeaturesBundleIntegrationTest.php
index 38b20005f2350585831eb2833ae321176bef6221..c20234ec3630e27822d0f01410211365c6688762 100644
--- a/tests/src/Kernel/Entity/FeaturesBundleIntegrationTest.php
+++ b/tests/src/Kernel/Entity/FeaturesBundleIntegrationTest.php
@@ -15,7 +15,7 @@ class FeaturesBundleIntegrationTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['features'];
+  protected static $modules = ['features'];
 
   /**
    * {@inheritDoc}
diff --git a/tests/src/Kernel/FeaturesAssignTest.php b/tests/src/Kernel/FeaturesAssignTest.php
index 948432f663a21d89368867a4fdab2d6241698156..d53d7200eec9ffd0d660cd04875716270c1acccd 100644
--- a/tests/src/Kernel/FeaturesAssignTest.php
+++ b/tests/src/Kernel/FeaturesAssignTest.php
@@ -23,7 +23,7 @@ class FeaturesAssignTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['features', 'node', 'system', 'user', self::TEST_INSTALLED_PACKAGE];
+  protected static $modules = ['features', 'node', 'system', 'user', self::TEST_INSTALLED_PACKAGE];
 
   /**
    * The Feature Manager.
@@ -57,7 +57,7 @@ class FeaturesAssignTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected function setUp() {
+  protected function setUp(): void {
     parent::setUp();
 
     $this->installConfig('features');
diff --git a/tests/src/Kernel/FeaturesAssignerTest.php b/tests/src/Kernel/FeaturesAssignerTest.php
index a98c7776a4058181db4adeae63bbe165e038441c..7a2e2589411a7fe6332ddac9f4fa1bbdf6636e20 100644
--- a/tests/src/Kernel/FeaturesAssignerTest.php
+++ b/tests/src/Kernel/FeaturesAssignerTest.php
@@ -13,14 +13,14 @@ class FeaturesAssignerTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['system', 'config'];
+  protected static $modules = ['system', 'config'];
 
   protected $strictConfigSchema = FALSE;
 
   /**
    * {@inheritdoc}
    */
-  protected function setUp() {
+  protected function setUp(): void {
     parent::setUp();
     // We need system.site in order to run $this->configImporter->import().
     $this->installConfig('system');
diff --git a/tests/src/Kernel/FeaturesGenerateTest.php b/tests/src/Kernel/FeaturesGenerateTest.php
index 8e40b47606c4eac5e4c5dc638ca83b4fd99daa46..98b6d8d3d043c9602e4ac11234e837f81c0debfd 100644
--- a/tests/src/Kernel/FeaturesGenerateTest.php
+++ b/tests/src/Kernel/FeaturesGenerateTest.php
@@ -2,10 +2,11 @@
 
 namespace Drupal\Tests\features\Kernel;
 
-use Drupal\features\Entity\FeaturesBundle;
-use Drupal\KernelTests\KernelTestBase;
 use Drupal\Component\Serialization\Yaml;
 use Drupal\Core\Archiver\ArchiveTar;
+use Drupal\features\Entity\FeaturesBundle;
+use Drupal\features\FeaturesBundleInterface;
+use Drupal\KernelTests\KernelTestBase;
 use org\bovigo\vfs\vfsStream;
 
 /**
@@ -14,16 +15,18 @@ use org\bovigo\vfs\vfsStream;
 class FeaturesGenerateTest extends KernelTestBase {
 
   const PACKAGE_NAME = 'my_test_package';
+
   const BUNDLE_NAME = 'giraffe';
 
   /**
    * {@inheritdoc}
    */
-  public static $modules = [
+  protected static $modules = [
     'features',
     'node',
     'system',
     'user',
+    'test_feature_generation',
   ];
 
   /**
@@ -51,7 +54,7 @@ class FeaturesGenerateTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected function setUp() {
+  protected function setUp(): void {
     parent::setUp();
 
     $this->installConfig('features');
@@ -92,7 +95,7 @@ class FeaturesGenerateTest extends KernelTestBase {
     $expected_info = [
       "name" => "My test package",
       "type" => "module",
-      "core_version_requirement" => "^8.9 || ^9",
+      "core_version_requirement" => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT,
     ];
     $info = Yaml::decode($archive->extractInString(self::PACKAGE_NAME . '/' . self::PACKAGE_NAME . '.info.yml'));
     $this->assertEquals($expected_info, $info, 'Incorrect info file generated');
@@ -130,16 +133,11 @@ class FeaturesGenerateTest extends KernelTestBase {
   public function testExportWrite() {
     // Set a fake drupal root, so the testbot can also write into it.
     vfsStream::setup('drupal');
-    \Drupal::getContainer()->set('app.root', 'vfs://drupal');
     $this->featuresManager->setRoot('vfs://drupal');
-
     $package = $this->featuresManager->getPackage(self::PACKAGE_NAME);
     // Find out where package will be exported.
-    list($full_name, $path) = $this->featuresManager->getExportInfo($package, $this->assigner->getBundle());
+    [$full_name, $path] = $this->featuresManager->getExportInfo($package, $this->assigner->getBundle());
     $path = 'vfs://drupal/' . $path . '/' . $full_name;
-    if (file_exists($path)) {
-      file_unmanaged_delete_recursive($path);
-    }
     $this->assertFalse(file_exists($path), 'Package directory already exists.');
 
     $this->generator->generatePackages('write', $this->assigner->getBundle(), [self::PACKAGE_NAME]);
@@ -152,7 +150,7 @@ class FeaturesGenerateTest extends KernelTestBase {
     $expected_info = [
       "name" => "My test package",
       "type" => "module",
-      "core_version_requirement" => "^8.9 || ^9",
+      "core_version_requirement" => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT,
     ];
     $info = Yaml::decode(file_get_contents($info_file_uri));
     $this->assertEquals($expected_info, $info, 'Incorrect info file generated');
@@ -182,7 +180,7 @@ class FeaturesGenerateTest extends KernelTestBase {
     $expected_info = [
       "name" => "My test package",
       "type" => "module",
-      "core_version_requirement" => "^8.9 || ^9",
+      "core_version_requirement" => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT,
       "dependencies" => ["drupal:node", "drupal:user"],
       "mykey" => "test value",
     ];
@@ -213,7 +211,7 @@ class FeaturesGenerateTest extends KernelTestBase {
     $expected_info = [
       "name" => "My test package",
       "type" => "module",
-      "core_version_requirement" => "^8.9 || ^9",
+      "core_version_requirement" => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT,
       "dependencies" => ["drupal:node", "drupal:user"],
       "mykey" => "test value",
     ];
diff --git a/tests/src/Kernel/FeaturesManagerKernelTest.php b/tests/src/Kernel/FeaturesManagerKernelTest.php
index b81e23d0b3df16c55e5f0c854e647835dfe5f548..152723bf9d9afbf4ed4e4d403072fb342c1fd982 100644
--- a/tests/src/Kernel/FeaturesManagerKernelTest.php
+++ b/tests/src/Kernel/FeaturesManagerKernelTest.php
@@ -15,7 +15,7 @@ class FeaturesManagerKernelTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['system', 'config', 'features'];
+  protected static $modules = ['system', 'config', 'features'];
 
   protected $strictConfigSchema = FALSE;
 
@@ -36,7 +36,7 @@ class FeaturesManagerKernelTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected function setUp() {
+  protected function setUp(): void {
     parent::setUp();
 
     $this->installConfig('features');
diff --git a/tests/src/Unit/FeaturesBundleTest.php b/tests/src/Unit/FeaturesBundleTest.php
index 07ee1994d0fded79b8c7ee38c472fef89b67b0f7..1478e2be040d4b0ad089abd6a29cc99a49ce0b16 100644
--- a/tests/src/Unit/FeaturesBundleTest.php
+++ b/tests/src/Unit/FeaturesBundleTest.php
@@ -4,6 +4,7 @@ namespace Drupal\Tests\features\Unit;
 
 use Drupal\features\Entity\FeaturesBundle;
 use Drupal\Tests\UnitTestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
 
 /**
  * @coversDefaultClass Drupal\features\Entity\FeaturesBundle
@@ -11,10 +12,12 @@ use Drupal\Tests\UnitTestCase;
  */
 class FeaturesBundleTest extends UnitTestCase {
 
+  use ProphecyTrait;
+
   /**
    * {@inheritDoc}
    */
-  public function setUp() {
+  public function setUp(): void {
     parent::setUp();
 
     // Mock an assigner.
@@ -54,33 +57,33 @@ class FeaturesBundleTest extends UnitTestCase {
     ], 'features_bundle');
 
     // Get assignments and attributes.
-    $this->assertArrayEquals(
-      $bundle->getEnabledAssignments(),
+    $this->assertEquals(
       ['foo' => 'foo'],
+      $bundle->getEnabledAssignments(),
       'Can get enabled assignments'
     );
-    $this->assertArrayEquals(
-      $bundle->getAssignmentWeights(),
+    $this->assertEquals(
       ['foo' => 0, 'bar' => 1],
+      $bundle->getAssignmentWeights(),
       'Can get assignment weights'
     );
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings('foo'),
+    $this->assertEquals(
       $settings['foo'],
+      $bundle->getAssignmentSettings('foo'),
       'Can get assignment settings'
     );
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings(),
+    $this->assertEquals(
       $settings,
+      $bundle->getAssignmentSettings(),
       'Can get all assignment settings'
     );
 
     // Change settings.
     $settings['foo']['my_setting'] = 97;
     $bundle->setAssignmentSettings('foo', $settings['foo']);
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings('foo'),
+    $this->assertEquals(
       $settings['foo'],
+      $bundle->getAssignmentSettings('foo'),
       'Can change assignment settings'
     );
 
@@ -88,28 +91,28 @@ class FeaturesBundleTest extends UnitTestCase {
     $settings['foo']['weight'] = 1;
     $settings['bar']['weight'] = 0;
     $bundle->setAssignmentWeights(['foo' => 1, 'bar' => 0]);
-    $this->assertArrayEquals(
-      $bundle->getAssignmentWeights(),
+    $this->assertEquals(
       ['foo' => 1, 'bar' => 0],
+      $bundle->getAssignmentWeights(),
       'Can change assignment weights'
     );
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings(),
+    $this->assertEquals(
       $settings,
+      $bundle->getAssignmentSettings(),
       'Weight changes are reflected in settings'
     );
 
     // Enable existing assignment.
     $settings['bar']['enabled'] = TRUE;
     $bundle->setEnabledAssignments(['foo', 'bar']);
-    $this->assertArrayEquals(
-      $bundle->getEnabledAssignments(),
+    $this->assertEquals(
       ['foo' => 'foo', 'bar' => 'bar'],
+      $bundle->getEnabledAssignments(),
       'Can enable assignment'
     );
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings(),
+    $this->assertEquals(
       $settings,
+      $bundle->getAssignmentSettings(),
       'Enabled assignment status is reflected in settings'
     );
 
@@ -117,14 +120,14 @@ class FeaturesBundleTest extends UnitTestCase {
     $settings['foo']['enabled'] = FALSE;
     $settings['bar']['enabled'] = FALSE;
     $bundle->setEnabledAssignments([]);
-    $this->assertArrayEquals(
-      $bundle->getEnabledAssignments(),
+    $this->assertEquals(
       [],
+      $bundle->getEnabledAssignments(),
       'Can disable assignments'
     );
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings(),
+    $this->assertEquals(
       $settings,
+      $bundle->getAssignmentSettings(),
       'Disabled assignment status is reflected in settings'
     );
 
@@ -132,15 +135,15 @@ class FeaturesBundleTest extends UnitTestCase {
     $settings['foo']['enabled'] = TRUE;
     $settings['iggy'] = ['enabled' => TRUE, 'weight' => 0, 'new_setting' => 3];
     $bundle->setEnabledAssignments(['foo', 'iggy']);
-    $this->assertArrayEquals(
-      $bundle->getEnabledAssignments(),
+    $this->assertEquals(
       ['foo' => 'foo', 'iggy' => 'iggy'],
+      $bundle->getEnabledAssignments(),
       'Can enable new assignment'
     );
     $bundle->setAssignmentSettings('iggy', $settings['iggy']);
-    $this->assertArrayEquals(
-      $bundle->getAssignmentSettings(),
+    $this->assertEquals(
       $settings,
+      $bundle->getAssignmentSettings(),
       'New enabled assignment status is reflected in settings'
     );
 
diff --git a/tests/src/Unit/FeaturesManagerTest.php b/tests/src/Unit/FeaturesManagerTest.php
index 640d3ae7555e17f38b3a2802564422908c9c6b56..2205d3c25fe1fc93478f37ff85b6ad10cbe67649 100644
--- a/tests/src/Unit/FeaturesManagerTest.php
+++ b/tests/src/Unit/FeaturesManagerTest.php
@@ -4,19 +4,20 @@ namespace Drupal\Tests\features\Unit;
 
 use Drupal\Component\Serialization\Yaml;
 use Drupal\config_update\ConfigDiffInterface;
+use Drupal\config_update\ConfigRevertInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\ConfigManagerInterface;
 use Drupal\Core\Config\InstallStorage;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Extension\Extension;
+use Drupal\Core\Extension\ExtensionPathResolver;
 use Drupal\Core\Extension\ModuleExtensionList;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\config_update\ConfigRevertInterface;
+use Drupal\features\ConfigurationItem;
 use Drupal\features\Entity\FeaturesBundle;
 use Drupal\features\FeaturesAssignerInterface;
 use Drupal\features\FeaturesBundleInterface;
-use Drupal\features\ConfigurationItem;
 use Drupal\features\FeaturesExtensionStoragesInterface;
 use Drupal\features\FeaturesManager;
 use Drupal\features\FeaturesManagerInterface;
@@ -24,6 +25,7 @@ use Drupal\features\Package;
 use Drupal\Tests\UnitTestCase;
 use org\bovigo\vfs\vfsStream;
 use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
 
 /**
  * @coversDefaultClass Drupal\features\FeaturesManager
@@ -37,6 +39,7 @@ class FeaturesManagerTest extends UnitTestCase {
    *   The name of the install profile.
    */
   const PROFILE_NAME = 'my_profile';
+  use ProphecyTrait;
 
   /**
    * The feature manager interface.
@@ -48,61 +51,61 @@ class FeaturesManagerTest extends UnitTestCase {
   /**
    * The entity type manager object.
    *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $entityTypeManager;
 
   /**
    * The storage interface object.
    *
-   * @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Config\StorageInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $configStorage;
 
   /**
    * The config factory.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $configFactory;
 
   /**
    * The config manager.
    *
-   * @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $configManager;
 
   /**
    * The module handler.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $moduleHandler;
 
   /**
    * The extension.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $configReverter;
 
   /**
    * The module extension list mock.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
    */
   protected $moduleExtensionList;
 
   /**
    * {@inheritdoc}
    */
-  public function setUp() {
+  public function setUp(): void {
     parent::setUp();
 
     $container = new ContainerBuilder();
     $container->set('string_translation', $this->getStringTranslationStub());
-    $container->set('app.root', $this->root);
+    $container->setParameter('app.root', $this->root);
     // Since in Drupal 8.3 the "\Drupal::installProfile()" was introduced
     // then we have to spoof a value for the "install_profile" parameter
     // because it will be used by "ExtensionInstallStorage" class, which
@@ -126,6 +129,7 @@ class FeaturesManagerTest extends UnitTestCase {
     $this->configStorage = $this->createMock(StorageInterface::class);
     $this->configManager = $this->createMock(ConfigManagerInterface::class);
     $this->moduleHandler = $this->createMock(ModuleHandlerInterface::class);
+    $this->extensionPathResolver = $this->createMock(ExtensionPathResolver::class);
     // getModuleList should return an array of extension objects.
     // but we just need  isset($module_list[$provider]) for
     // ::getConfigDependency() and ::assignInterPackageDependencies().
@@ -158,19 +162,19 @@ class FeaturesManagerTest extends UnitTestCase {
     $this->moduleExtensionList->expects($this->any())
       ->method('getExtensionInfo')
       ->willReturn([]);
-    $this->featuresManager = new FeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $this->featuresManager = new FeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
   }
 
   protected function setupVfsWithTestFeature() {
     vfsStream::setup('drupal');
-    \Drupal::getContainer()->set('app.root', 'vfs://drupal');
+    \Drupal::getContainer()->setParameter('app.root', 'vfs://drupal');
     vfsStream::create([
       'modules' => [
         'test_feature' => [
           'test_feature.info.yml' => <<<EOT
 name: Test feature
 type: module
-core_version_requirement: "^8.8 || ^9"
+core_version_requirement: "^9.4 | ^10"
 description: test description
 EOT
           ,
@@ -231,7 +235,7 @@ EOT
     $this->assertEquals($packages, $this->featuresManager->getPackages());
     $this->assertEquals('bar', $this->featuresManager->getPackage('foo'));
     $this->featuresManager->reset();
-    $this->assertArrayEquals([], $this->featuresManager->getPackages());
+    $this->assertEquals([], $this->featuresManager->getPackages());
     $this->assertNull($this->featuresManager->getPackage('foo'));
   }
 
@@ -242,7 +246,7 @@ EOT
   public function testConfigCollection() {
     $config = ['config' => new ConfigurationItem('', [])];
     $this->featuresManager->setConfigCollection($config);
-    $this->assertArrayEquals($config, $this->featuresManager->getConfigCollection());
+    $this->assertEquals($config, $this->featuresManager->getConfigCollection());
   }
 
   /**
@@ -371,7 +375,7 @@ EOT
     $bundle->isDefault()->willReturn(TRUE);
     $assigner->getBundle()->willReturn($bundle->reveal());
     // Use the wrapper because we need ::drupalGetProfile().
-    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
     $features_manager->setAssigner($assigner->reveal());
 
     $features_manager->setConfigCollection($this->getAssignInterPackageDependenciesConfigCollection());
@@ -438,7 +442,7 @@ EOT
     $bundle->getMachineName()->willReturn('giraffe');
     $assigner->getBundle('giraffe')->willReturn($bundle->reveal());
     // Use the wrapper because we need ::drupalGetProfile().
-    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
     $features_manager->setAssigner($assigner->reveal());
     $features_manager->setConfigCollection($this->getAssignInterPackageDependenciesConfigCollection());
 
@@ -581,7 +585,7 @@ EOT
       'key2' => 'value0',
     ]);
 
-    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $config_storage->reveal(), $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $config_storage->reveal(), $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
     $features_manager->setExtensionStorages($extension_storage->reveal());
 
     $this->assertEquals(['test_overridden'], $features_manager->detectOverrides($package));
@@ -770,7 +774,7 @@ EOT
       'key' => 'value',
     ]);
 
-    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
     $features_manager->setExtensionStorages($extension_storage->reveal());
 
     $this->assertEmpty($features_manager->detectNew($package));
@@ -785,7 +789,7 @@ EOT
     $extension_storage = $this->prophesize(FeaturesExtensionStoragesInterface::class);
     $extension_storage->read('test_config')->willReturn(FALSE);
 
-    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
     $features_manager->setExtensionStorages($extension_storage->reveal());
 
     $this->assertEquals(['test_config'], $features_manager->detectNew($package));
@@ -811,8 +815,8 @@ EOT
     $data = [];
     $data['empty-info'] = [[], [], []];
     $data['override-info'] = [
-      ['name' => 'New name', 'core_version_requirement' => '^8.8 || ^9'],
-      ['name' => 'Old name', 'core_version_requirement' => '^8.8 || ^9'],
+      ['name' => 'New name', 'core_version_requirement' => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT],
+      ['name' => 'Old name', 'core_version_requirement' => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT],
       ['name' => 'New name'],
     ];
     $data['dependency-merging'] = [
@@ -831,7 +835,7 @@ EOT
   public function testInitPackageWithNewPackage() {
     $bundle = new FeaturesBundle(['machine_name' => 'test'], 'features_bundle');
 
-    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager($this->root, $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
     $features_manager->setAllModules([]);
 
     $package = $features_manager->initPackage('test_feature', 'test name', 'test description', 'module', $bundle);
@@ -854,7 +858,7 @@ EOT
   public function testInitPackageWithExistingPackage() {
     $bundle = new FeaturesBundle(['machine_name' => 'test'], 'features_bundle');
 
-    $features_manager = new TestFeaturesManager('vfs://drupal', $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $features_manager = new TestFeaturesManager('vfs://drupal', $this->entityTypeManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
 
     $this->setupVfsWithTestFeature();
     $extension = new Extension('vfs://drupal', 'module', 'modules/test_feature/test_feature.info.yml');
@@ -922,7 +926,7 @@ EOT
     $this->assertEquals(Yaml::encode([
       'name' => 'Test feature',
       'type' => 'module',
-      'core_version_requirement' => '^8.9 || ^9',
+      'core_version_requirement' => FeaturesBundleInterface::CORE_VERSION_REQUIREMENT,
     ]), $files['info']['string']);
     $this->assertEquals(Yaml::encode(TRUE), $files['features']['string']);
 
@@ -954,7 +958,7 @@ EOT
         ],
       ],
     ]);
-    $this->featuresManager = new FeaturesManager($this->root, $this->entityTypeManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $this->featuresManager = new FeaturesManager($this->root, $this->entityTypeManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
 
     $package = new Package('test_feature');
     $result = $this->featuresManager->getExportInfo($package);
@@ -973,7 +977,7 @@ EOT
         ],
       ],
     ]);
-    $this->featuresManager = new FeaturesManager($this->root, $this->entityTypeManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList);
+    $this->featuresManager = new FeaturesManager($this->root, $this->entityTypeManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler, $this->configReverter, $this->moduleExtensionList, $this->extensionPathResolver);
 
     $package = new Package('test_feature');
     $bundle = new FeaturesBundle(['machine_name' => 'test_bundle'], 'features_bundle');