class Tapioca::Compilers::RequiresCompiler

Public Class Methods

new(sorbet_path) click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 12
def initialize(sorbet_path)
  @sorbet_path = sorbet_path
end

Public Instance Methods

compile() click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 17
def compile
  config = Spoom::Sorbet::Config.parse_file(@sorbet_path)
  files = collect_files(config)
  files.flat_map do |file|
    collect_requires(file).reject do |req|
      name_in_project?(files, req)
    end
  end.sort.uniq.map do |name|
    "require \"#{name}\"\n"
  end.join
end

Private Instance Methods

collect_files(config) click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 32
def collect_files(config)
  config.paths.flat_map do |path|
    path = (Pathname.new(@sorbet_path) / "../.." / path).cleanpath
    if path.directory?
      Dir.glob("#{path}/**/*.rb", File::FNM_EXTGLOB).reject do |file|
        relative_file_path = Pathname.new(file).relative_path_from(path)
        file_ignored_by_sorbet?(config, relative_file_path)
      end
    else
      [path.to_s]
    end
  end.sort.uniq
end
collect_requires(file_path) click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 47
def collect_requires(file_path)
  File.read(file_path).lines.map do |line|
    /^\s*require\s*(\(\s*)?['"](?<name>[^'"]+)['"](\s*\))?/.match(line) { |m| m["name"] }
  end.compact
end
file_ignored_by_sorbet?(config, file_path) click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 54
def file_ignored_by_sorbet?(config, file_path)
  file_path_parts = path_parts(file_path)

  config.ignore.any? do |ignore|
    # Sorbet --ignore matching method:
    # ---
    # Ignores input files that contain the given
    # string in their paths (relative to the input
    # path passed to Sorbet).
    #
    # Strings beginning with / match against the
    # prefix of these relative paths; others are
    # substring matchs.

    # Matches must be against whole folder and file
    # names, so `foo` matches `/foo/bar.rb` and
    # `/bar/foo/baz.rb` but not `/foo.rb` or
    # `/foo2/bar.rb`.
    ignore_parts = path_parts(Pathname.new(ignore))
    file_path_part_sequences = file_path_parts.each_cons(ignore_parts.size)
    # if ignore string begins with /, we only want the first sequence to match
    file_path_part_sequences = [file_path_part_sequences.first].to_enum if ignore.start_with?("/")

    # we need to match whole segments
    file_path_part_sequences.include?(ignore_parts)
  end
end
name_in_project?(files, name) click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 88
def name_in_project?(files, name)
  files.any? do |file|
    File.basename(file, ".rb") == name
  end
end
path_parts(path) click to toggle source
# File lib/tapioca/compilers/requires_compiler.rb, line 83
def path_parts(path)
  T.unsafe(path).descend.map { |part| part.basename.to_s }
end