module GovukPublishingComponents class AuditComponents attr_reader :data def initialize(path) # paths to key file locations @templates_path = "app/views/govuk_publishing_components/components" @stylesheets_path = "app/assets/stylesheets/govuk_publishing_components/components" @print_stylesheets_path = "app/assets/stylesheets/govuk_publishing_components/components/print" @javascripts_path = "app/assets/javascripts/govuk_publishing_components/components" @tests_path = "spec/components" @javascript_tests_path = "spec/javascripts/components" @helpers_path = "lib/govuk_publishing_components/presenters" @all_templates = Dir["#{path}/#{@templates_path}/*.erb"].sort @templates_full_path = "#{path}/#{@templates_path}/" @component_numbers = { template: 0, stylesheet: 0, print_stylesheet: 0, javascript: 0, test: 0, javascript_test: 0, helper: 0, } @data = { gem_found: false, } @data = compile_data(path) if Dir.exist?(path) end private def compile_data(path) { gem_found: true, component_code: clean_files(@all_templates, @templates_full_path).sort, components_containing_components: find_all_partials_in(@all_templates), component_file_details: get_component_file_details(path), component_numbers: @component_numbers, } end def get_component_file_details(path) component_templates = clean_files(@all_templates, @templates_full_path).sort details = [] component_templates.each do |component| component_detail = { name: component, link: get_component_link(component), } file_details = [ { type: "template", file: "#{[path, @templates_path].join('/')}/_#{component.gsub(' ', '_')}.html.erb", }, { type: "stylesheet", file: "#{[path, @stylesheets_path].join('/')}/_#{component.gsub(' ', '-')}.scss", }, { type: "print_stylesheet", file: "#{[path, @stylesheets_path].join('/')}/_#{component.gsub(' ', '-')}.scss", }, { type: "javascript", file: "#{[path, @javascripts_path].join('/')}/#{component.gsub(' ', '-')}.js", }, { type: "test", file: "#{[path, @tests_path].join('/')}/#{component.gsub(' ', '_')}_spec.rb", }, { type: "javascript_test", file: "#{[path, @javascript_tests_path].join('/')}/#{component.gsub(' ', '-')}-spec.js", }, { type: "helper", file: "#{[path, @helpers_path].join('/')}/#{component.gsub(' ', '_')}_helper.rb", }, ] file_details.each do |detail| component_detail = component_detail.merge(get_component_asset_detail(detail, component)) end details << component_detail end details end def get_component_asset_detail(detail, component) details = {} type = detail[:type] file = detail[:file] details["#{type}_exists".to_sym] = false if File.file?(file) # we don't have separate print stylesheets anymore # so check the main stylesheet for print styles if type == "print_stylesheet" unless File.foreach(file).grep(/(media-type: print)/).empty? details["#{type}_exists".to_sym] = true @component_numbers[type.to_sym] += 1 end else @component_numbers[type.to_sym] += 1 details["#{type}_exists".to_sym] = true details["#{type}_lines".to_sym] = count_lines_in(file) details["#{type}_link".to_sym] = get_asset_link(type, component) end end details end def count_lines_in(file) File.read(file).each_line.count end def clean_files(files, replace) files.map { |file| clean_file_name(file.gsub(replace, "")) }.sort end def clean_file_name(name) name.tr("/_-", " ") .gsub(".html.erb", "") .gsub(".erb", "") .gsub(".scss", "") .gsub(".js", "") .gsub("spec", "") .gsub(".rb", "") .gsub("helper", "") .strip end def get_component_name_from_full_path(path) path.gsub(/.*\K\/_/, "/") .gsub(@templates_full_path, "") .gsub(".html.erb", "") .gsub(".erb", "") .tr('\"\'', "") end # create link to component guide page for a given component e.g. 'component name' def get_component_link(component) "/component-guide/#{component.gsub(' ', '_')}" end def find_all_partials_in(templates) components = [] templates.each do |template| partials_found = true components_to_search = [template] components_found = [] while partials_found extra_components = find_partials_in(components_to_search) if extra_components.any? components_found << extra_components components_to_search = extra_components else partials_found = false components_found = components_found.flatten.reject { |c| c.include?("/") } if components_found.any? component_name = clean_file_name(get_component_name_from_full_path(template)) components << { component: component_name, link: get_component_link(component_name), sub_components: components_found.uniq.sort.map { |name| clean_file_name(name) }, } end end end end components.sort_by { |c| c[:component] } end def find_partials_in(components) extra_components = [] components.each do |component| extra_components << components_within_component(component) end extra_components.flatten.uniq.sort end def components_within_component(file) file = get_component_name_from_full_path(file) file = "#{@templates_full_path}#{file}.html.erb".sub(/.*\K\//, "/_") data = File.read(file) match = data.scan(/["']{1}(govuk_publishing_components\/components\/[\/a-z_-]+["']{1})/) match.flatten.uniq.map(&:to_s).sort.map do |m| m.gsub("govuk_publishing_components/components/", "").tr('\"\'', "") end end def get_asset_link(a_thing, component) url = "https://github.com/alphagov" repo = "govuk_publishing_components" blob = "blob/main" link = nil link = "#{url}/#{repo}/#{blob}/#{@templates_path}/_#{component.gsub(' ', '_')}.html.erb" if a_thing == "template" link = "#{url}/#{repo}/#{blob}/#{@stylesheets_path}/_#{component.gsub(' ', '-')}.scss" if a_thing == "stylesheet" link = "#{url}/#{repo}/#{blob}/#{@print_stylesheets_path}/_#{component.gsub(' ', '-')}.scss" if a_thing == "print_stylesheet" link = "#{url}/#{repo}/#{blob}/#{@javascripts_path}/#{component.gsub(' ', '-')}.js" if a_thing == "javascript" link = "#{url}/#{repo}/#{blob}/#{@tests_path}/#{component.gsub(' ', '_')}_spec.rb" if a_thing == "test" link = "#{url}/#{repo}/#{blob}/#{@javascript_tests_path}/#{component.gsub(' ', '-')}-spec.js" if a_thing == "javascript_test" link = "#{url}/#{repo}/#{blob}/#{@helpers_path}/#{component.gsub(' ', '_')}_helper.rb" if a_thing == "helper" link end end end