summaryrefslogtreecommitdiff
path: root/tooling/lib/tooling/view_to_js_mappings.rb
blob: 76704a04469a09e22add06df1ef16430e06a79ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# frozen_string_literal: true

require_relative '../../../lib/gitlab_edition'

# Returns JS files that are related to the Rails views files that were changed in the MR.
module Tooling
  class ViewToJsMappings
    # The HTML attribute value pattern we're looking for to match an HTML file to a JS file.
    HTML_ATTRIBUTE_VALUE_REGEXP = /js-[-\w]+/.freeze

    # Search for Rails partials included in an HTML file
    RAILS_PARTIAL_INVOCATION_REGEXP = %r{(?:render|render_if_exist)(?: |\()(?:partial: ?)?['"]([\w/-]+)['"]}.freeze

    def initialize(view_base_folder: 'app/views', js_base_folder: 'app/assets/javascripts')
      @view_base_folders = folders_for_available_editions(view_base_folder)
      @js_base_folders   = folders_for_available_editions(js_base_folder)
    end

    def execute(changed_files)
      changed_view_files = view_files(changed_files)

      partials = changed_view_files.flat_map do |file|
        find_partials(file)
      end

      files_to_scan = changed_view_files + partials
      js_tags = files_to_scan.flat_map do |file|
        find_pattern_in_file(file, HTML_ATTRIBUTE_VALUE_REGEXP)
      end
      js_tags_regexp = Regexp.union(js_tags)

      @js_base_folders.flat_map do |js_base_folder|
        Dir["#{js_base_folder}/**/*.{js,vue}"].select do |js_file|
          file_content = File.read(js_file)
          js_tags_regexp.match?(file_content)
        end
      end
    end

    # Keep the files that are in the @view_base_folders folder
    def view_files(changed_files)
      changed_files.select do |filename|
        filename.start_with?(*@view_base_folders) &&
          File.exist?(filename)
      end
    end

    def folders_for_available_editions(base_folder)
      foss_prefix        = base_folder
      extension_prefixes = ::GitlabEdition.extensions.map { |prefix| "#{prefix}/#{foss_prefix}" }
      [foss_prefix, *extension_prefixes]
    end

    # Note: We only search for partials with depth 1. We don't do recursive search, as
    #       it is probably not necessary for a first iteration.
    def find_partials(file)
      partial_paths = find_pattern_in_file(file, RAILS_PARTIAL_INVOCATION_REGEXP)
      partial_paths.flat_map do |partial_path|
        view_file_folder        = File.dirname(file)
        partial_relative_folder = File.dirname(partial_path)

        dirname =
          if partial_relative_folder == '.' # The partial is in the same folder as the HTML file
            view_file_folder
          else
            File.join(view_file_folder, partial_relative_folder)
          end

        Dir["#{dirname}/_#{File.basename(partial_path)}.*"]
      end
    end

    def find_pattern_in_file(file, pattern)
      File.read(file).scan(pattern).flatten.uniq
    end
  end
end