diff --git a/lib/ex_doc/config.ex b/lib/ex_doc/config.ex index 082526e03..b20debced 100644 --- a/lib/ex_doc/config.ex +++ b/lib/ex_doc/config.ex @@ -14,7 +14,7 @@ defmodule ExDoc.Config do defstruct annotations_for_docs: &__MODULE__.annotations_for_docs/1, api_reference: true, apps: [], - assets: nil, + assets: %{}, authors: nil, before_closing_body_tag: &__MODULE__.before_closing_body_tag/1, before_closing_footer_tag: &__MODULE__.before_closing_footer_tag/1, @@ -54,7 +54,7 @@ defmodule ExDoc.Config do annotations_for_docs: (map() -> list()), api_reference: boolean(), apps: [atom()], - assets: nil | String.t(), + assets: %{binary() => binary()}, authors: nil | [String.t()], before_closing_body_tag: (atom() -> String.t()) | mfa() | map(), before_closing_footer_tag: (atom() -> String.t()) | mfa() | map(), diff --git a/lib/ex_doc/formatter/epub.ex b/lib/ex_doc/formatter/epub.ex index 6c14b693f..0f9136198 100644 --- a/lib/ex_doc/formatter/epub.ex +++ b/lib/ex_doc/formatter/epub.ex @@ -2,6 +2,7 @@ defmodule ExDoc.Formatter.EPUB do @moduledoc false @mimetype "application/epub+zip" + @assets_dir "OEBPS/assets" alias __MODULE__.{Assets, Templates} alias ExDoc.Formatter.HTML @@ -30,10 +31,9 @@ defmodule ExDoc.Formatter.EPUB do config = %{config | extras: extras} - assets_dir = "OEBPS/assets" - static_files = HTML.generate_assets(config, assets_dir, default_assets(config)) - HTML.generate_logo(assets_dir, config) - HTML.generate_cover(assets_dir, config) + static_files = HTML.generate_assets("OEBPS", default_assets(config), config) + HTML.generate_logo(@assets_dir, config) + HTML.generate_cover(@assets_dir, config) uuid = "urn:uuid:#{uuid4()}" datetime = format_datetime() diff --git a/lib/ex_doc/formatter/html.ex b/lib/ex_doc/formatter/html.ex index 105fb826b..a127c49ef 100644 --- a/lib/ex_doc/formatter/html.ex +++ b/lib/ex_doc/formatter/html.ex @@ -22,7 +22,7 @@ defmodule ExDoc.Formatter.HTML do extras = build_extras(config, ".html") # Generate search early on without api reference in extras - static_files = generate_assets(config, @assets_dir, default_assets(config)) + static_files = generate_assets(".", default_assets(config), config) search_data = generate_search_data(project_nodes, extras, config) # TODO: Move this categorization to the language @@ -284,37 +284,41 @@ defmodule ExDoc.Formatter.HTML do @doc """ Generate assets from configs with the given default assets. """ - def generate_assets(config, assets_dir, defaults) do - write_default_assets(config, defaults) ++ copy_assets(config, assets_dir) - end - - defp copy_assets(config, assets_dir) do - if path = config.assets do - path - |> Path.join("**/*") - |> Path.wildcard() - |> Enum.map(fn source -> - filename = Path.join(assets_dir, Path.relative_to(source, path)) - target = Path.join(config.output, filename) - File.mkdir(Path.dirname(target)) - File.copy(source, target) - filename - end) - else - [] - end - end + def generate_assets(namespace, defaults, %{output: output, assets: assets}) do + namespaced_assets = + if is_map(assets) do + Enum.map(assets, fn {source, target} -> {source, Path.join(namespace, target)} end) + else + IO.warn( + "giving a binary to :assets is deprecated, please give a map from source to target instead" + ) - defp write_default_assets(config, sources) do - Enum.flat_map(sources, fn {files, dir} -> - target_dir = Path.join(config.output, dir) + [{assets, Path.join(namespace, "assets")}] + end + + Enum.flat_map(defaults ++ namespaced_assets, fn {dir_or_files, relative_target_dir} -> + target_dir = Path.join(output, relative_target_dir) File.mkdir_p!(target_dir) - Enum.map(files, fn {name, content} -> - target = Path.join(target_dir, name) - File.write(target, content) - Path.relative_to(target, config.output) - end) + cond do + is_list(dir_or_files) -> + Enum.map(dir_or_files, fn {name, content} -> + target = Path.join(target_dir, name) + File.write(target, content) + Path.relative_to(target, output) + end) + + is_binary(dir_or_files) and File.dir?(dir_or_files) -> + dir_or_files + |> File.cp_r!(target_dir) + |> Enum.map(&Path.relative_to(&1, output)) + + is_binary(dir_or_files) -> + [] + + true -> + raise ":assets must be a map of source directories to target directories" + end end) end diff --git a/lib/mix/tasks/docs.ex b/lib/mix/tasks/docs.ex index 47069ff6f..250d037fb 100644 --- a/lib/mix/tasks/docs.ex +++ b/lib/mix/tasks/docs.ex @@ -69,9 +69,8 @@ defmodule Mix.Tasks.Docs do * `:api_reference` - Whether to generate `api-reference.html`; default: `true`. If this is set to false, `:main` must also be set. - * `:assets` - Path to a directory that will be copied as is to the "assets" - directory in the output path. Its entries may be referenced in your docs - under "assets/ASSET.EXTENSION"; defaults to no assets directory. + * `:assets` - A map of source => target directories that will be copied as is to + the output path. It defaults to an empty map. * `:authors` - List of authors for the generated docs or epub. diff --git a/test/ex_doc/formatter/epub_test.exs b/test/ex_doc/formatter/epub_test.exs index c1f02225c..2bfdc3845 100644 --- a/test/ex_doc/formatter/epub_test.exs +++ b/test/ex_doc/formatter/epub_test.exs @@ -74,7 +74,7 @@ defmodule ExDoc.Formatter.EPUBTest do assert_raise( RuntimeError, ~s{asset with extension ".pdf" is not supported by EPUB format}, - fn -> generate_docs(doc_config(context, assets: "test/tmp/epub_assets")) end + fn -> generate_docs(doc_config(context, assets: %{"test/tmp/epub_assets" => "assets"})) end ) after File.rm_rf!("test/tmp/epub_assets") @@ -236,7 +236,7 @@ defmodule ExDoc.Formatter.EPUBTest do generate_docs_and_unzip( context, doc_config(context, - assets: "test/tmp/epub_assets", + assets: %{"test/tmp/epub_assets" => "assets"}, logo: "test/fixtures/elixir.png", cover: "test/fixtures/elixir.png" ) diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index 09fa0bc2e..9bb3bb307 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -28,7 +28,7 @@ defmodule ExDoc.Formatter.HTMLTest do project: "Elixir", version: "1.0.1", formatter: "html", - assets: "test/tmp/html_assets", + assets: %{"test/tmp/html_assets" => "assets"}, output: tmp_dir <> "/html", source_beam: "test/tmp/beam", source_url: "https://github.com/elixir-lang/elixir", @@ -745,7 +745,10 @@ defmodule ExDoc.Formatter.HTMLTest do File.touch!("test/tmp/html_assets/hello/world") generate_docs( - doc_config(context, assets: "test/tmp/html_assets", logo: "test/fixtures/elixir.png") + doc_config(context, + assets: %{"test/tmp/html_assets" => "assets"}, + logo: "test/fixtures/elixir.png" + ) ) assert File.regular?(tmp_dir <> "/html/assets/logo.png")