class ViewComponent::Compiler

def template_errors

def template_errors
  @_template_errors ||= begin
    errors = []
    errors << "Couldn't find a template file or inline render method for #{@component}." if @templates.empty?
    # We currently allow components to have both an inline call method and a template for a variant, with the
    # inline call method overriding the template. We should aim to change this in v4 to instead
    # raise an error.
    @templates.reject(&:inline_call?)
      .map { |template| [template.variant, template.format] }
      .tally
      .select { |_, count| count > 1 }
      .each do |tally|
      variant, this_format = tally.first
      variant_string = " for variant `#{variant}`" if variant.present?
      errors << "More than one #{this_format.upcase} template found#{variant_string} for #{@component}. "
    end
    default_template_types = @templates.each_with_object(Set.new) do |template, memo|
      next if template.variant
      memo << :template_file if !template.inline_call?
      memo << :inline_render if template.inline_call? && template.defined_on_self?
      memo
    end
    if default_template_types.length > 1
      errors <<
        "Template file and inline render method found for #{@component}. " \
        "There can only be a template file or inline render method per component."
    end
    # If a template has inline calls, they can conflict with template files the component may use
    # to render. This attempts to catch and raise that issue before run time. For example,
    # `def render_mobile` would conflict with a sidecar template of `component.html+mobile.erb`
    duplicate_template_file_and_inline_call_variants =
      @templates.reject(&:inline_call?).map(&:variant) &
      @templates.select { _1.inline_call? && _1.defined_on_self? }.map(&:variant)
    unless duplicate_template_file_and_inline_call_variants.empty?
      count = duplicate_template_file_and_inline_call_variants.count
      errors <<
        "Template #{"file".pluralize(count)} and inline render #{"method".pluralize(count)} " \
        "found for #{"variant".pluralize(count)} " \
        "#{duplicate_template_file_and_inline_call_variants.map { |v| "'#{v}'" }.to_sentence} " \
        "in #{@component}. There can only be a template file or inline render method per variant."
    end
    @templates.select(&:variant).each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |template, memo|
      memo[template.normalized_variant_name] << template.variant
      memo
    end.each do |_, variant_names|
      next unless variant_names.length > 1
      errors << "Colliding templates #{variant_names.sort.map { |v| "'#{v}'" }.to_sentence} found in #{@component}."
    end
    errors
  end
end