Current section
Files
Jump to
Current section
Files
lib/mix/tasks/docs.repair_links.ex
defmodule Mix.Tasks.Docs.RepairLinks do
@moduledoc """
Mix task for repairing links in HTML files generated by `mix docs`.
"""
use Mix.Task
require Logger
@shortdoc "Repairs links in generated HTML files"
def run([]) do
Logger.error("Expected argument: path to ex_doc HTML output")
end
def run([path | other_args]) do
Logger.warn("Ignoring extra arguments: #{inspect other_args}")
do_run(path)
end
def do_run(doc_output_path) do
# Relative links for files in doc/ are okay; we only need to fix the
# links found in top-level (markdown) files.
for fname <- Path.wildcard("./*.md") do
html_path =
Path.join([
doc_output_path,
Path.basename(fname, ".md") <> ".html"
])
Logger.debug("processing #{html_path}")
content =
File.read!(html_path)
|> fix_links
File.write!(html_path, content)
end
end
defp fix_links(raw_html) do
anchor_links =
raw_html
|> Floki.find("a")
|> Floki.attribute("href")
img_links =
raw_html
|> Floki.find("img")
|> Floki.attribute("src")
bad_links =
(anchor_links ++ img_links)
|> Enum.reject(&String.starts_with?(&1, "http://"))
|> Enum.reject(&String.starts_with?(&1, "https://"))
|> Enum.reject(&String.starts_with?(&1, "#"))
new_html =
bad_links
|> Enum.reduce(raw_html, fn bad_link, acc ->
good_link = repair_link(bad_link)
Logger.debug("repaired link: #{bad_link} => #{good_link}")
acc
|> String.replace(~s(href="#{bad_link}"), ~s(href="#{good_link}"))
|> String.replace(~s(src="#{bad_link}"), ~s(src="#{good_link}"))
end)
new_html
end
defp repair_link("LICENSE") do
# not very proud of this.
"../LICENSE"
end
defp repair_link(link) do
toplevel_file? = not String.contains?(link, "/")
link =
case toplevel_file? do
true -> link
false -> Path.join("..", link)
end
link =
case Path.extname(link) do
".md" -> Path.basename(link, ".md") <> ".html"
_ -> link
end
link
end
end