defmodule Mix.Tasks.Spark.CheatSheetsInSearch do
@shortdoc "Includes generated cheat sheets in the search bar"
@moduledoc @shortdoc
use Mix.Task
def run(opts) do
Mix.Task.run("compile")
{opts, _} =
OptionParser.parse!(opts,
switches: [strip_prefix: :string, check: :boolean, extensions: :string]
)
unless opts[:extensions] do
raise "Must supply a comma separated list of extensions to generate a .formatter.exs for"
end
extensions =
opts[:extensions]
|> String.split(",")
|> Enum.map(&Module.concat([&1]))
|> Enum.uniq()
with {:ok, search_data_file, search_data} <- search_data_file(),
{:ok, sidebar_items_file, sidebar_items} <- sidebar_items_file() do
{search_data, sidebar_items} =
Enum.reduce(extensions, {search_data, sidebar_items}, fn extension, acc ->
add_extension_to_search_data(extension, acc, opts)
end)
File.write!(search_data_file, "searchData=" <> Jason.encode!(search_data))
File.write!(sidebar_items_file, "sidebarNodes=" <> Jason.encode!(sidebar_items))
else
{:error, error} -> raise error
end
end
defp search_data_file do
"doc/dist/search_data-*.js"
|> Path.wildcard()
|> Enum.at(0)
|> case do
nil ->
{:error, "No search_data file found"}
file ->
case File.read!(file) do
"searchData=" <> contents ->
{:ok, file, Jason.decode!(contents)}
_ ->
{:error, "search data js file was malformed"}
end
end
end
defp sidebar_items_file do
"doc/dist/sidebar_items-*.js"
|> Path.wildcard()
|> Enum.at(0)
|> case do
nil ->
{:error, "No sidebar_items file found"}
file ->
case File.read!(file) do
"sidebarNodes=" <> contents ->
{:ok, file, Jason.decode!(contents)}
_ ->
{:error, "sidebar items js file was malformed"}
end
end
end
defp add_extension_to_search_data(extension, acc, opts) do
extension_name = Spark.Mix.Helpers.extension_name(extension, opts)
acc =
Enum.reduce(extension.sections(), acc, fn section, acc ->
add_section_to_search_data(extension_name, section, acc)
end)
Enum.reduce(
extension.dsl_patches(),
acc,
fn %Spark.Dsl.Patch.AddEntity{
section_path: section_path,
entity: entity
},
acc ->
add_entity_to_search_data(
extension_name,
entity,
acc,
section_path
)
end
)
end
defp add_section_to_search_data(
extension_name,
section,
{search_data, sidebar_items},
path \\ []
) do
search_data =
add_search_item(
search_data,
%{
"doc" => section.describe,
"ref" =>
"#{dsl_search_name(extension_name)}.html##{Enum.join(path ++ [section.name], "-")}",
"title" => "#{extension_name}.#{Enum.join(path ++ [section.name], ".")}",
"type" => "DSL"
}
)
search_data =
add_schema_to_search_data(
search_data,
extension_name,
section.schema,
path ++ [section.name]
)
acc =
Enum.reduce(
section.sections,
{search_data, sidebar_items},
&add_section_to_search_data(extension_name, &1, &2, path ++ [section.name])
)
Enum.reduce(
section.entities,
acc,
&add_entity_to_search_data(extension_name, &1, &2, path ++ [section.name])
)
end
defp add_entity_to_search_data(extension_name, entity, {search_data, sidebar_items}, path) do
search_data =
add_search_item(
search_data,
%{
"doc" => entity.describe,
"ref" =>
"#{dsl_search_name(extension_name)}.html##{Enum.join(path ++ [entity.name], "-")}",
"title" => "#{extension_name}.#{Enum.join(path ++ [entity.name], ".")}",
"type" => "DSL"
}
)
search_data =
add_schema_to_search_data(
search_data,
extension_name,
entity.schema,
path ++ [entity.name]
)
entity.entities
|> Enum.flat_map(&List.wrap(elem(&1, 1)))
|> Enum.reduce(
{search_data, sidebar_items},
&add_entity_to_search_data(extension_name, &1, &2, path ++ [entity.name])
)
end
defp add_schema_to_search_data(
search_data,
extension_name,
schema,
path
) do
Enum.reduce(schema || [], search_data, fn {key, config}, search_data ->
add_search_item(
search_data,
%{
"doc" => config[:doc] || "",
"ref" => "#{dsl_search_name(extension_name)}.html##{Enum.join(path ++ [key], "-")}",
"title" => "#{extension_name}.#{Enum.join(path ++ [key], ".")}",
"type" => "DSL"
}
)
end)
end
defp dsl_search_name(extension_name) do
("dsl-" <> extension_name) |> String.split(".") |> Enum.map_join("-", &String.downcase/1)
end
defp add_search_item(search_data, item) do
item = Map.update!(item, "title", &String.trim(&1 || ""))
Map.update!(search_data, "items", &Enum.uniq([item | &1]))
end
end