Skip to content
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

Add an option to redirect trailing slash urls #74

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ Documenter = "1"
DocumenterCitations = "1"
IOCapture = "0.2"
NodeJS_20_jll = "20"
Test = "1"
julia = "1.6"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[weakdeps]
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"

[extensions]
DocumenterVitepressDocumenterCitationsExt = "DocumenterCitations"

[targets]
test = ["Test"]
100 changes: 87 additions & 13 deletions src/writer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"""
MarkdownVitepress(; repo, devbranch, devurl, kwargs...)

This is the main entry point for the Vitepress Markdown writer.
This is the main entry point for the Vitepress Markdown writer.

It is a config which can be passed to the `format` keyword argument in `Documenter.makedocs`, and causes it to emit a Vitepress site.

!!! tip "Quick start"
Expand Down Expand Up @@ -49,15 +49,22 @@
"""The path to which the Markdown files will be output. Defaults to `\$build/.documenter`."""
md_output_path::String = ".documenter"
"""
Determines whether to clean up the Markdown assets after build, i.e., whether to remove the contents of `md_output_path` after the Vitepress site is built.
Determines whether to clean up the Markdown assets after build, i.e., whether to remove the contents of `md_output_path` after the Vitepress site is built.
Options are:
- `nothing`: **Default**. Only remove the contents of `md_output_path` if the documentation will deploy, to save space.
- `true`: Removes the contents of `md_output_path` after the Vitepress site is built.
- `false`: Does not remove the contents of `md_output_path` after the Vitepress site is built.
"""
clean_md_output::Union{Nothing, Bool} = nothing
"""
DeployDecision from Documenter.jl. This is used to determine whether to deploy the documentation or not.
Whether to insert 200 redirects from https://example.com/page/ to https://example.com/page.

Defaults to `false`. This is useful for transitioning from Documenter.jl which uses
trailing slashes for its canonical urls by default.
"""
redirect_trailing_slash::Bool = false
"""
`DeployDecision` from Documenter.jl. This is used to determine whether to deploy the documentation or not.
Options are:
- `nothing`: **Default**. Automatically determine whether to deploy the documentation.
- `Documenter.DeployDecision`: Override the automatic decision and deploy based on the passed config.
Expand All @@ -66,8 +73,20 @@
`Documenter.deploy_folder(Documenter.auto_detect_deploy_system(); repo, devbranch, devurl, push_preview)`.
"""
deploy_decision::Union{Nothing, Documenter.DeployDecision} = nothing

"A list of assets, the same as what is provided to Documenter's HTMLWriter."
assets = nothing

# This inner constructor serves only to
function MarkdownVitepress(args...)
args[10] && !args[6] && throw(ArgumentError(
"""
MarkdownVitepress: `redirect_trailing_slash` can only be `true` if `build_vitepress` is also `true`,
because redirects are insterted after the site is built.
"""
))
new(args...)
end
end

# return the same file with the extension changed to .md
Expand All @@ -82,13 +101,13 @@
function docpath(file, builddir, mdfolder)
path = relpath(file, builddir)
filename = mdext(path)
return joinpath(builddir, mdfolder, filename)
return joinpath(builddir, mdfolder, filename)
end

"""
render(args...)

This is the main entry point and recursive function to render a Documenter document to
This is the main entry point and recursive function to render a Documenter document to
Markdown in the Vitepress flavour. It is called by `Documenter.build` and should not be
called directly.

Expand Down Expand Up @@ -161,7 +180,7 @@
cp(file, file_destpath; force = true)
end
end
end
end
if any(favicon_files)
for file in files[favicon_files]
file_relpath = relpath(file, joinpath(builddir, settings.md_output_path, "assets"))
Expand All @@ -184,6 +203,23 @@
end

mkpath(joinpath(builddir, "final_site"))

# We manually obtain the Documenter deploy configuration,
# so we can use it to set Vitepress's settings.
if isnothing(settings.deploy_decision)
# TODO: make it so that the user does not have to provide a repo url!
deploy_config = Documenter.auto_detect_deploy_system()
deploy_decision = Documenter.deploy_folder(
deploy_config;
repo = settings.repo, # this must be the full URL!
devbranch = settings.devbranch,
devurl = settings.devurl,
push_preview=true,
)
else
deploy_decision = settings.deploy_decision
end

if isfile(joinpath(builddir, settings.md_output_path, ".vitepress", "config.mts"))
touch(joinpath(builddir, settings.md_output_path, ".vitepress", "config.mts"))
end
Expand Down Expand Up @@ -224,7 +260,7 @@
rm(joinpath(dirname(builddir), "package-lock.json"))
end
end
# This is only useful if placed in the root of the `docs` folder, and we don't
# This is only useful if placed in the root of the `docs` folder, and we don't
# have any names which conflict with Jekyll (beginning with _ or .) in any case.
# touch(joinpath(builddir, "final_site", ".nojekyll"))

Expand All @@ -244,6 +280,44 @@
@info "DocumenterVitepress: Markdown output cleaned up. Folder looks like: $(readdir(doc.user.build))"
end

if settings.redirect_trailing_slash
@info "DocumenterVitepress: inserting javascript 200 redirects from https://example.com/page/ to https://example.com/page because `redirect_trailing_slash` is true."
for (root, dirs, files) in walkdir(builddir)
for file in files
name, ext = splitext(file)
if ext === ".html" && name ∉ ("404", "index")
dir = joinpath(root, name)
if !isdir(dir)
mkdir(dir)
println(((settings.deploy_url, root, builddir, name)))
url = "https://"*normpath(joinpath(settings.deploy_url, relpath(root, builddir), name))
println(url)
open(joinpath(dir, "index.html"), "w") do io
write(io, """
<!DOCTYPE html>
<script>
const u = new URL(window.location.href);
window.location.replace(u.origin + u.pathname.slice(0,-1) + u.search + u.hash);
</script>
<a href="..$name">Redirecting to ..$name</a>
<meta http-equiv="refresh" content="0; URL=../$name">
<link rel="canonical" href="../$name">""")
# The script is equivalent to
# `<meta http-equiv="refresh" content="0; URL=../$name">`
# but keeps fragments. If Javascript fails for whatever
# reason, the meta http-equiv will proc, dropping fragments
# If that, too fails, there's an ordinary, human readable
# relative link.
#
# This uses a relative canonical link which is bad form, but
# oh well. We don't have access to the full URL until deploy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may have access to the deploy URL through settings.deploy_url and deploy_decision, so in that situation we could add that in if it's better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A recall trying that and finding that field to be empty.

# time.
end
end
end
end
end
end
else
@info """
DocumenterVitepress: did not build Vitepress site because `build_vitepress` was set to `false`.
Expand Down Expand Up @@ -339,7 +413,7 @@
if url !== nothing
# This is how Documenter does it:
# push!(ret.nodes, a[".docs-sourcelink", :target=>"_blank", :href=>url]("source"))
# so clearly we should be inserting some form of HTML tag here,
# so clearly we should be inserting some form of HTML tag here,
# and defining its rendering in CSS?
# TODO: switch to Documenter style here
println(io, "\n", "[source]($url)", "\n")
Expand Down Expand Up @@ -407,9 +481,9 @@
for thing in code_blocks
# reset the buffer and push the old code block
if thing.language != current_language
# Remove this if statement if you want to
# Remove this if statement if you want to
# include empty code blocks in the output.
if isempty(thing.code)
if isempty(thing.code)
current_string *= "\n\n"
continue
end
Expand Down Expand Up @@ -699,7 +773,7 @@
# Code blocks
function render(io::IO, mime::MIME"text/plain", node::Documenter.MarkdownAST.Node, code::MarkdownAST.CodeBlock, page, doc; kwargs...)
if startswith(code.info, "@")
@warn """

Check warning on line 776 in src/writer.jl

View workflow job for this annotation

GitHub Actions / build

DocumenterVitepress: un-expanded `@doctest` block encountered on page src/code_example.md. The first few lines of code in this node are: ``` julia> 1 + 1 2 ```
DocumenterVitepress: un-expanded `$(code.info)` block encountered on page $(page.source).
The first few lines of code in this node are:
```
Expand Down Expand Up @@ -768,7 +842,7 @@
# Main.@infiltrate
print(io, "\$", math.math, "\$")
end
# Display math
# Display math
function render(io::IO, mime::MIME"text/plain", node::Documenter.MarkdownAST.Node, math::MarkdownAST.DisplayMath, page, doc; kwargs...)
# Main.@infiltrate
println(io)
Expand Down Expand Up @@ -813,7 +887,7 @@
end
end
# We create this IOBuffer in order to render to it.
iob = IOBuffer()
iob = IOBuffer()
# This will eventually hold the rendered table cells as Strings.
cell_strings = Vector{Vector{String}}()
current_row_vec = String[]
Expand Down
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Test, DocumenterVitepress

@test DocumenterVitepress.MarkdownVitepress(; repo = "...", devbranch = "...", devurl = "...") isa DocumenterVitepress.MarkdownVitepress
@test_throws ArgumentError DocumenterVitepress.MarkdownVitepress(; repo = "...", devbranch = "...", devurl = "...",
build_vitepress = false, redirect_trailing_slash = true)
Loading