Skip to content

feat: add before_closing_*_tag mfa support #1670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,57 @@ ExDoc renders Markdown content for you, but you can extend it to render complex
```elixir
docs: [
# ...
before_closing_head_tag: &before_closing_head_tag/1,
before_closing_body_tag: &before_closing_body_tag/1
]

# ...

defp before_closing_head_tag(:html) do
"""
<!-- HTML injected at the end of the <head> element -->
"""
end

defp before_closing_head_tag(:epub), do: ""

defp before_closing_body_tag(:html) do
"""
<!-- HTML injected at the end of the <body> element -->
"""
end

defp before_closing_body_tag(_), do: ""
defp before_closing_body_tag(:epub), do: ""
```

You could use `MFA` as well:

```elixir
docs: [
# ...
before_closing_head_tag: {MyModule, :before_closing_head_tag, ["Demo"]},
before_closing_body_tag: {MyModule, :before_closing_body_tag, ["Demo"]}
]

# ...

defmodule MyModule do
def before_closing_head_tag(:html, name) do
# ...
end

def before_closing_head_tag(:epub, name) do
# ...
end

def before_closing_body_tag(:html, name) do
# ...
end

def before_closing_body_tag(:html, name) do
# ...
end
end
```

### Rendering Math
Expand Down
4 changes: 2 additions & 2 deletions lib/ex_doc/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ defmodule ExDoc.Config do
apps: [atom()],
assets: nil | String.t(),
authors: nil | [String.t()],
before_closing_body_tag: (atom() -> String.t()),
before_closing_head_tag: (atom() -> String.t()),
before_closing_body_tag: (atom() -> String.t()) | mfa(),
before_closing_head_tag: (atom() -> String.t()) | mfa(),
canonical: nil | String.t(),
cover: nil | Path.t(),
deps: [{ebin_path :: String.t(), doc_url :: String.t()}],
Expand Down
2 changes: 2 additions & 0 deletions lib/ex_doc/formatter/epub/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule ExDoc.Formatter.EPUB.Templates do

require EEx

import ExDoc.Utils, only: [before_closing_body_tag: 2, before_closing_head_tag: 2]

alias ExDoc.Formatter.HTML
alias ExDoc.Formatter.HTML.Templates, as: H

Expand Down
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/epub/templates/extra_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
</h1>
<%= content %>
<%# Extra content specified by the user (e.g. custom Javascript) %>
<%= config.before_closing_body_tag.(:epub) %>
<%= before_closing_body_tag(config, :epub) %>
</body>
</html>
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/epub/templates/head_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
href="<%= H.asset_rev "#{config.output}/OEBPS", "dist/epub-#{config.proglang}-*.css" %>" />
<script src="<%= H.asset_rev "#{config.output}/OEBPS", "dist/epub-*.js" %>"></script>
<%# Extra content specified by the user (e.g. custom CSS) %>
<%= config.before_closing_head_tag.(:epub) %>
<%= before_closing_head_tag(config, :epub) %>
</head>
<body class="content-inner">
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/epub/templates/module_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@
</section>
<% end %>
<%# Extra content specified by the user (e.g. custom Javascript) %>
<%= config.before_closing_body_tag.(:epub) %>
<%= before_closing_body_tag(config, :epub) %>
</body>
</html>
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/epub/templates/nav_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
</ol>
</nav>
<%# Extra content specified by the user (e.g. custom Javascript) %>
<%= config.before_closing_body_tag.(:epub) %>
<%= before_closing_body_tag(config, :epub) %>
</body>
</html>
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/epub/templates/title_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
<% end %>
</div>
<%# Extra content specified by the user (e.g. custom Javascript) %>
<%= config.before_closing_body_tag.(:epub) %>
<%= before_closing_body_tag(config, :epub) %>
</body>
</html>
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/html/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule ExDoc.Formatter.HTML.Templates do
@moduledoc false
require EEx

import ExDoc.Utils, only: [h: 1]
import ExDoc.Utils, only: [h: 1, before_closing_body_tag: 2, before_closing_head_tag: 2]

# TODO: It should not depend on the parent module. Move required HTML functions to Utils.
# TODO: Add tests that assert on the returned structured, not on JSON
Expand Down
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/html/templates/footer_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@
</section>
</div>
<%# Extra content specified by the user (e.g. custom Javascript) %>
<%= config.before_closing_body_tag.(:html) %>
<%= before_closing_body_tag(config, :html) %>
</body>
</html>
2 changes: 1 addition & 1 deletion lib/ex_doc/formatter/html/templates/head_template.eex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<% end %>
<script async src="<%= asset_rev config.output, "dist/html-*.js" %>"></script>
<%# Extra content specified by the user (e.g. custom CSS) %>
<%= config.before_closing_head_tag.(:html) %>
<%= before_closing_head_tag(config, :html) %>
</head>
<body data-type="<%= sidebar_type(page.type) %>" class="page-<%= page.type %>">
<script>
Expand Down
22 changes: 22 additions & 0 deletions lib/ex_doc/utils.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
defmodule ExDoc.Utils do
@moduledoc false

@doc """
Runs the `before_closing_head_tag` callback.
"""
def before_closing_head_tag(%{before_closing_head_tag: {m, f, a}}, module) do
apply(m, f, [module | a])
end

def before_closing_head_tag(%{before_closing_head_tag: before_closing_head_tag}, module) do
before_closing_head_tag.(module)
end

@doc """
Runs the `before_closing_body_tag` callback.
"""
def before_closing_body_tag(%{before_closing_body_tag: {m, f, a}}, module) do
apply(m, f, [module | a])
end

def before_closing_body_tag(%{before_closing_body_tag: before_closing_body_tag}, module) do
before_closing_body_tag.(module)
end

@doc """
HTML escapes the given string.

Expand Down
22 changes: 22 additions & 0 deletions test/ex_doc/formatter/epub_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ defmodule ExDoc.Formatter.EPUBTest do
defp before_closing_head_tag(:epub), do: @before_closing_head_tag_content_epub
defp before_closing_body_tag(:epub), do: @before_closing_body_tag_content_epub

def before_closing_head_tag(:epub, name), do: "<meta name=#{name}>"
def before_closing_body_tag(:epub, name), do: "<p>#{name}</p>"

defp doc_config(%{tmp_dir: tmp_dir} = _context) do
[
app: :elixir,
Expand Down Expand Up @@ -188,6 +191,25 @@ defmodule ExDoc.Formatter.EPUBTest do
end
end

test "before_closing_*_tags required by the user are in the right place using MFA",
%{tmp_dir: tmp_dir} = context do
generate_docs_and_unzip(
context,
doc_config(context,
before_closing_head_tag: {__MODULE__, :before_closing_head_tag, ["Demo"]},
before_closing_body_tag: {__MODULE__, :before_closing_body_tag, ["Demo"]}
)
)

oebps_dir = tmp_dir <> "/epub/OEBPS"

for basename <- @example_basenames do
content = File.read!(Path.join(oebps_dir, basename))
assert content =~ ~r[<meta name=Demo>\s*</head>]
assert content =~ ~r[<p>Demo</p>\s*</body>]
end
end

test "assets required by the user end up in the right place", %{tmp_dir: tmp_dir} = context do
File.mkdir_p!("test/tmp/epub_assets/hello")
File.touch!("test/tmp/epub_assets/hello/world.png")
Expand Down
24 changes: 24 additions & 0 deletions test/ex_doc/formatter/html_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ defmodule ExDoc.Formatter.HTMLTest do
defp before_closing_head_tag(:html), do: @before_closing_head_tag_content_html
defp before_closing_body_tag(:html), do: @before_closing_body_tag_content_html

def before_closing_head_tag(:html, name), do: "<meta name=#{name}>"
def before_closing_body_tag(:html, name), do: "<p>#{name}</p>"

defp doc_config(%{tmp_dir: tmp_dir} = _context) do
[
apps: [:elixir],
Expand Down Expand Up @@ -548,6 +551,27 @@ defmodule ExDoc.Formatter.HTMLTest do
%{
tmp_dir: tmp_dir
} = context do
generate_docs(
doc_config(context,
before_closing_head_tag: {__MODULE__, :before_closing_head_tag, ["Demo"]},
before_closing_body_tag: {__MODULE__, :before_closing_body_tag, ["Demo"]},
extras: ["test/fixtures/README.md"]
)
)

content = File.read!(tmp_dir <> "/html/api-reference.html")
assert content =~ ~r[<meta name=Demo>\s*</head>]
assert content =~ ~r[<p>Demo</p>\s*</body>]

content = File.read!(tmp_dir <> "/html/readme.html")
assert content =~ ~r[<meta name=Demo>\s*</head>]
assert content =~ ~r[<p>Demo</p>\s*</body>]
end

test "before_closing_*_tags required by the user are placed in the right place using MFA",
%{
tmp_dir: tmp_dir
} = context do
generate_docs(
doc_config(context,
before_closing_head_tag: &before_closing_head_tag/1,
Expand Down