Current section

Files

Jump to
workspace lib workspace finder.ex
Raw

lib/workspace/finder.ex

defmodule Workspace.Finder do
@moduledoc false
# Module responsible for finding projects under a workspace root.
@default_ignored_paths [".git", "_build", ".elixir_ls"]
@doc """
Find all nested mix projects under the given path.
## Options
* `:ignore_paths` - list of paths to ignore
* `:ignore_projects` - list of projects to ignore
"""
@spec projects(path :: String.t(), opts :: keyword()) :: [Workspace.Project.t()]
def projects(workspace_path, opts) do
ignored_paths = Keyword.fetch!(opts, :ignore_paths) ++ @default_ignored_paths
projects =
workspace_path
|> nested_mix_projects(ignored_paths, workspace_path)
|> Enum.map(fn path -> Workspace.Project.new(path, workspace_path) end)
|> Enum.filter(&ignored_project?(&1, opts[:ignore_projects]))
projects
end
defp nested_mix_projects(path, ignore_paths, workspace_path) do
Workspace.Cli.debug("scanning projects under #{path}")
subdirs = subdirs(path, ignore_paths, workspace_path)
projects = Enum.filter(subdirs, &mix_project?/1)
Workspace.Cli.debug(" #{length(projects)} projects detected")
remaining = subdirs -- projects
Enum.reduce(remaining, projects, fn project, acc ->
acc ++ nested_mix_projects(project, ignore_paths, workspace_path)
end)
end
defp subdirs(path, ignore_paths, workspace_path) do
path
|> File.ls!()
|> Enum.map(fn file -> Path.join(path, file) end)
|> Enum.filter(fn path ->
File.dir?(path) and not ignored_path?(path, ignore_paths, workspace_path)
end)
end
defp mix_project?(path), do: File.exists?(Path.join(path, "mix.exs"))
defp ignored_project?(project, ignore_projects) do
cond do
project.module in ignore_projects ->
false
true ->
true
end
end
defp ignored_path?(mix_path, ignore_paths, workspace_path) do
ignore_paths
|> Enum.map(fn path -> workspace_path |> Path.join(path) |> Path.expand() end)
|> Enum.any?(fn path -> String.starts_with?(mix_path, path) end)
end
end