Skip to content

Commit 2c1a895

Browse files
authored
Add redirects config (#1952)
1 parent b79ed25 commit 2c1a895

File tree

4 files changed

+116
-8
lines changed

4 files changed

+116
-8
lines changed

Diff for: README.md

+43
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,49 @@ Similarly to the example above, if your Markdown includes Mermaid graph specific
448448
449449
For more details and configuration options, see the [Mermaid usage docs](https://mermaid-js.github.io/mermaid/#/usage).
450450
451+
## Changing documentation over time
452+
453+
As your project grows, your documentation may very likely change, even structurally. There are a few important things to consider in this regard:
454+
455+
- Links to your *extras* will break if you change or move file names.
456+
- Links to your *modules, and mix tasks* will change if you change their name.
457+
- Links to *functions* are actually links to modules with anchor links. If you change the function name, the link does
458+
not break but will leave users at the top of the module's documentation.
459+
460+
Because these docs are static files, the behavior of a missing page will depend on where they are hosted.
461+
In particular, [hexdocs.pm](https://hexdocs.pm) will show a 404 page.
462+
463+
You can improve the developer experience on everything but function names changing
464+
by using the `redirects` configuration. For example, if you changed the module `MyApp.MyModule`
465+
to `MyApp.My.Module` and the extra `get-started.md` to `quickstart.md`, you can
466+
setup the following redirects:
467+
468+
<!-- tabs-open -->
469+
470+
### Elixir
471+
472+
For this example, we've changed the module `MyApp.MyModule` to `MyApp.My.Module`, and the extra `get-started.md` to `quickstart.md`
473+
474+
```elixir
475+
redirects: %{
476+
"MyApp.MyModule" => "MyApp.My.Module",
477+
"get-started" => "quickstart"
478+
}
479+
```
480+
481+
### Erlang
482+
483+
For this example, we've changed the module `:my_module` to `:my_module2`, and the extra `get-started.md` to `quickstart.md`
484+
485+
```erlang
486+
{redirects, [
487+
{"my_module", "my_module2"},
488+
{"get-started", "quickstart"}
489+
]}.
490+
```
491+
492+
<!-- tabs-close -->
493+
451494
## Contributing
452495
453496
The easiest way to test changes to ExDoc is to locally rebuild the app and its own documentation:

Diff for: lib/ex_doc/config.ex

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ defmodule ExDoc.Config do
3939
package: nil,
4040
proglang: :elixir,
4141
project: nil,
42+
redirects: %{},
4243
retriever: ExDoc.Retriever,
4344
skip_undefined_reference_warnings_on:
4445
&__MODULE__.skip_undefined_reference_warnings_on/1,
@@ -78,6 +79,7 @@ defmodule ExDoc.Config do
7879
output: nil | Path.t(),
7980
package: :atom | nil,
8081
project: nil | String.t(),
82+
redirects: %{optional(String.t()) => String.t()} | [{String.t(), String.t()}],
8183
retriever: atom(),
8284
skip_undefined_reference_warnings_on: (String.t() -> boolean),
8385
skip_code_autolink_to: (String.t() -> boolean),

Diff for: lib/ex_doc/formatter/html.ex

+21-8
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ defmodule ExDoc.Formatter.HTML do
4747
generate_search(nodes_map, config) ++
4848
generate_not_found(nodes_map, config) ++
4949
generate_list(nodes_map.modules, nodes_map, config) ++
50-
generate_list(nodes_map.tasks, nodes_map, config) ++ generate_index(config)
50+
generate_list(nodes_map.tasks, nodes_map, config) ++
51+
generate_redirects(config, ".html")
5152

5253
generate_build(Enum.sort(all_files), build)
5354
config.output |> Path.join("index.html") |> Path.relative_to_cwd()
@@ -187,13 +188,6 @@ defmodule ExDoc.Formatter.HTML do
187188
File.write!(build, entries)
188189
end
189190

190-
defp generate_index(config) do
191-
index_file = "index.html"
192-
main_file = "#{config.main}.html"
193-
generate_redirect(index_file, config, main_file)
194-
[index_file]
195-
end
196-
197191
defp generate_not_found(nodes_map, config) do
198192
filename = "404.html"
199193
config = set_canonical_url(config, filename)
@@ -390,6 +384,25 @@ defmodule ExDoc.Formatter.HTML do
390384
|> Enum.sort_by(fn extra -> GroupMatcher.group_index(groups, extra.group) end)
391385
end
392386

387+
def generate_redirects(config, ext) do
388+
config.redirects
389+
|> Map.new()
390+
|> Map.put_new("index", config.main)
391+
|> Enum.map(fn {from, to} ->
392+
unless is_binary(from),
393+
do: raise("expected a string for the source of a redirect, got: #{inspect(from)}")
394+
395+
unless is_binary(to),
396+
do: raise("expected a string for the destination of a redirect, got: #{inspect(to)}")
397+
398+
source = from <> ext
399+
destination = to <> ext
400+
generate_redirect(source, config, destination)
401+
402+
source
403+
end)
404+
end
405+
393406
defp disambiguate_id(extra, discriminator) do
394407
Map.put(extra, :id, "#{extra.id}-#{discriminator}")
395408
end

Diff for: test/ex_doc/formatter/html_test.exs

+50
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,56 @@ defmodule ExDoc.Formatter.HTMLTest do
326326
end
327327
end
328328

329+
describe "generates redirects" do
330+
test "redirects are generated based on the configuration", %{tmp_dir: tmp_dir} = context do
331+
generate_docs(
332+
doc_config(context,
333+
extras: ["test/fixtures/LICENSE"],
334+
redirects: %{
335+
"/old-license" => "license"
336+
}
337+
)
338+
)
339+
340+
assert File.read!(tmp_dir <> "/html/old-license.html") == """
341+
<!DOCTYPE html>
342+
<html>
343+
<head>
344+
<meta charset="utf-8">
345+
<title>Elixir v1.0.1 — Documentation</title>
346+
<meta http-equiv="refresh" content="0; url=license.html">
347+
<meta name="generator" content="ExDoc v0.34.2">
348+
</head>
349+
<body></body>
350+
</html>
351+
"""
352+
end
353+
354+
test "redirects accept a list", %{tmp_dir: tmp_dir} = context do
355+
generate_docs(
356+
doc_config(context,
357+
extras: ["test/fixtures/LICENSE"],
358+
redirects: [
359+
{"/old-license", "license"}
360+
]
361+
)
362+
)
363+
364+
assert File.read!(tmp_dir <> "/html/old-license.html") == """
365+
<!DOCTYPE html>
366+
<html>
367+
<head>
368+
<meta charset="utf-8">
369+
<title>Elixir v1.0.1 — Documentation</title>
370+
<meta http-equiv="refresh" content="0; url=license.html">
371+
<meta name="generator" content="ExDoc v0.34.2">
372+
</head>
373+
<body></body>
374+
</html>
375+
"""
376+
end
377+
end
378+
329379
describe "generates extras" do
330380
@extras [
331381
"test/fixtures/LICENSE",

0 commit comments

Comments
 (0)