Current section
Files
Jump to
Current section
Files
lib/phoenix_storybook/stories/doc.ex
defmodule PhoenixStorybook.Stories.Doc do
@moduledoc """
Functions to fetch component documentation and render it at HTML.
"""
alias PhoenixStorybook.Rendering.MarkdownRenderer
alias PhoenixStorybook.Stories.Doc
require Logger
defstruct [:header, :body]
@doc """
Fetch component documentation from component source and format it as HTML.
- For a live_component, fetches @moduledoc content
- For a function component, fetches @doc content of the relevant function
Output HTML is split in paragraphs and returned as a list of paragraphs.
"""
def fetch_doc_as_html(story, stripped? \\ true) do
case fetch_component_doc(story.storybook_type(), story) do
:error ->
nil
doc ->
case split_header(doc, stripped?) do
[] ->
nil
[header] ->
%Doc{header: MarkdownRenderer.markdown_to_html(header)}
[header, body] ->
%Doc{
header: MarkdownRenderer.markdown_to_html(header),
body: MarkdownRenderer.markdown_to_html(body)
}
end
end
end
def fetch_component_doc(:component, module) do
info = Function.info(module.function())
fetch_function_doc(info[:module], {info[:name], info[:arity]})
end
def fetch_component_doc(:live_component, module) do
fetch_module_doc(module.component())
end
defp fetch_function_doc(module, {fun, arity}) do
case Code.fetch_docs(module) do
{_, _, _, _, _, _, function_docs} ->
case find_function_doc(function_docs, fun, arity) do
map when is_map(map) -> map |> Map.values() |> Enum.at(0)
_ -> nil
end
_ ->
Logger.warning("could not fetch function docs from #{inspect(module)}")
:error
end
end
defp find_function_doc(docs, fun, arity) do
Enum.find_value(
docs,
%{},
fn
{{:function, item_fun, item_arity}, _, _, doc, _} ->
if fun == item_fun && arity == item_arity, do: doc, else: false
_ ->
false
end
)
end
defp fetch_module_doc(module) do
case Code.fetch_docs(module) do
{_, _, _, _, module_doc, _, _} ->
case module_doc do
map when is_map(map) -> map |> Map.values() |> Enum.at(0)
_ -> nil
end
_ ->
Logger.warning("could not fetch module doc from #{inspect(module)}")
:error
end
end
def strip_lv_attributes_doc(doc),
do: (" " <> doc) |> String.split("## Attributes\n\n", trim: true) |> hd()
def strip_lv_slots_doc(doc),
do: (" " <> doc) |> String.split("## Slots\n\n", trim: true) |> hd()
defp split_header(nil, _stripped?), do: []
defp split_header(doc, false), do: String.split(doc, "\n\n", parts: 2, trim: true)
defp split_header(doc, true) do
doc |> strip_lv_attributes_doc() |> strip_lv_slots_doc() |> split_header(false)
end
end