public function deriveProductTree(SKU $sku) {
    static $cache = [];

    if (isset($cache[$sku->language()->getId()], $cache[$sku->language()->getId()][$sku->id()])) {
      return $cache[$sku->language()->getId()][$sku->id()];
    }

    $tree = [
      'parent' => $sku,
      'products' => self::getChildren($sku),
      'combinations' => [],
    ];

    $configurables = unserialize(
      $sku->get('field_configurable_attributes')->getString()
    );

    $tree['configurables'] = [];
    foreach ($configurables as $configurable) {
      $tree['configurables'][$configurable['code']] = $configurable;
    }

    $configurable_codes = array_keys($tree['configurables']);

    foreach ($tree['products'] ?? [] as $sku_code => $sku_entity) {
      $attributes = $sku_entity->get('attributes')->getValue();
      $attributes = array_column($attributes, 'value', 'key');
      foreach ($configurable_codes as $code) {
        $value = $attributes[$code] ?? '';

        if (empty($value)) {
          continue;
        }

        $tree['combinations']['by_sku'][$sku_code][$code] = $value;
      }
    }

    /** @var \Drupal\acm_sku\CartFormHelper $helper */
    $helper = \Drupal::service('acm_sku.cart_form_helper');

    $configurable_weights = $helper->getConfigurableAttributeWeights(
      $sku->get('attribute_set')->getString()
    );

    // Sort configurables based on the config.
    uasort($tree['configurables'], function ($a, $b) use ($configurable_weights) {
      return $configurable_weights[$a['code']] - $configurable_weights[$b['code']];
    });

    $tree['options'] = Configurable::recursiveConfigurableTree(
      $tree,
      $tree['configurables']
    );

    $cache[$sku->language()->getId()][$sku->id()] = $tree;

    return $tree;
  }