Skip to content

Commit 528eab1

Browse files
committed
update plugin
1 parent 7d698cc commit 528eab1

File tree

2 files changed

+64
-38
lines changed

2 files changed

+64
-38
lines changed

mkdocs_redirects/plugin.py

+62-37
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
Copyright 2019-2022 DataRobot, Inc. and its affiliates.
33
All rights reserved.
44
"""
5-
import logging
5+
from __future__ import annotations
6+
67
import os
78
import posixpath
9+
from typing import TypedDict
810

911
from mkdocs import utils
1012
from mkdocs.config import config_options
@@ -13,14 +15,15 @@
1315

1416
log = get_plugin_logger(__name__)
1517

18+
1619
def gen_anchor_redirects(anchor_list: list):
1720
"""
1821
Generate a dictionary of redirects for anchors.
1922
2023
:param anchor_list: A list of tuples containing old anchors and new links.
2124
:return: A string of JavaScript redirects for the anchors.
2225
"""
23-
js_redirects = ''
26+
js_redirects = ""
2427
for old_anchor, new_link in anchor_list:
2528
# Create a JavaScript redirect for each anchor
2629
js_redirects += f"""
@@ -59,7 +62,7 @@ def gen_anchor_redirects(anchor_list: list):
5962
"""
6063

6164

62-
def write_html(site_dir, old_path, new_path, anchor_list):
65+
def write_html(site_dir: str, old_path: str, new_path: str, anchor_list: list[tuple[str, str]]):
6366
"""Write an HTML file in the site_dir with a meta redirect to the new page"""
6467
# Determine all relevant paths
6568
old_path_abs = os.path.join(site_dir, old_path)
@@ -97,12 +100,38 @@ def get_html_path(path, use_directory_urls):
97100
return f.dest_path.replace(os.sep, "/")
98101

99102

103+
class RedirectEntry(TypedDict):
104+
hashes: list[tuple[str, str]]
105+
overall: str
106+
107+
108+
def build_redirect_entries(redirects: dict):
109+
redirect_entries: dict[str, RedirectEntry] = {}
110+
for page_old, page_new in redirects.items():
111+
page_old_without_hash, old_hash = _split_hash_fragment(str(page_old))
112+
if page_old_without_hash not in redirect_entries:
113+
redirect_entries[page_old_without_hash] = {"hashes": [], "overall": ""}
114+
if old_hash == "":
115+
redirect_entries[page_old_without_hash]["overall"] = page_new
116+
else:
117+
redirect_entries[page_old_without_hash]["hashes"].append((old_hash, page_new))
118+
119+
# If a page doesn't have an overall redirect, use the first hash redirect
120+
for page_old, redirect_map in redirect_entries.items():
121+
if redirect_map.get("overall", "") == "":
122+
redirect_entries[page_old]["overall"] = redirect_map["hashes"][0][1]
123+
124+
return redirect_entries
125+
126+
100127
class RedirectPlugin(BasePlugin):
101128
# Any options that this plugin supplies should go here.
102129
config_scheme = (
103130
("redirect_maps", config_options.Type(dict, default={})), # note the trailing comma
104131
)
105132

133+
redirect_entries: dict[str, RedirectEntry] = {}
134+
106135
# Build a list of redirects on file generation
107136
def on_files(self, files, config, **kwargs):
108137
self.redirects = self.config.get("redirect_maps", {})
@@ -111,59 +140,51 @@ def on_files(self, files, config, **kwargs):
111140
for page_old in self.redirects:
112141
page_old_without_hash, _ = _split_hash_fragment(str(page_old))
113142
if not utils.is_markdown_file(page_old_without_hash):
114-
log.warning("redirects plugin: '%s' is not a valid markdown file!", page_old_without_hash)
143+
log.warning(
144+
"redirects plugin: '%s' is not a valid markdown file!", page_old_without_hash
145+
)
115146

116147
# Build a dict of known document pages to validate against later
117148
self.doc_pages = {}
118149
for page in files.documentation_pages(): # object type: mkdocs.structure.files.File
119150
self.doc_pages[page.src_path.replace(os.sep, "/")] = page
120151

121152
# Create a dictionary to hold anchor maps for redirects
122-
redirect_maps = {}
123-
for page_old, page_new in self.redirects.items():
124-
page_old_without_hash, old_hash = _split_hash_fragment(str(page_old))
125-
if page_old_without_hash not in redirect_maps:
126-
redirect_maps[page_old_without_hash] = {'hashes': []}
127-
if old_hash == "":
128-
redirect_maps[page_old_without_hash]['overall'] = page_new
129-
else:
130-
redirect_maps[page_old_without_hash]['hashes'].append((old_hash, page_new))
131153

132-
# If a page doesn't have an overall redirect, use the first hash redirect
133-
for page_old, redirect_map in redirect_maps.items():
134-
if 'overall' not in redirect_map:
135-
redirect_maps[page_old]['overall'] = redirect_map['hashes'][0][1]
136-
137-
self.redirect_maps = redirect_maps
154+
self.redirect_entries = build_redirect_entries(self.redirects)
138155

139156
def on_page_content(self, html, page, config, files):
140157
use_directory_urls = config.get("use_directory_urls")
141158
page_old = page.file.src_uri
142-
if page_old not in self.redirect_maps:
159+
if page_old not in self.redirect_entries:
143160
return html
144161

145-
# Fixup redirect_maps to use the correct path
146-
for i in range(len(self.redirect_maps[page_old]['hashes'])):
147-
old_hash, new_link = self.redirect_maps[page_old]['hashes'][i]
162+
# Fixup redirect_entries to use the correct path
163+
for i in range(len(self.redirect_entries[page_old]["hashes"])):
164+
old_hash, new_link = self.redirect_entries[page_old]["hashes"][i]
148165
hash_redirect_without_hash, new_hash = _split_hash_fragment(str(new_link))
149166
if hash_redirect_without_hash in self.doc_pages:
150167
file = self.doc_pages[hash_redirect_without_hash]
151-
dest_hash_path = get_relative_html_path(page_old, file.url + new_hash, use_directory_urls)
152-
self.redirect_maps[page_old]['hashes'][i] = (old_hash, dest_hash_path)
168+
dest_hash_path = get_relative_html_path(
169+
page_old, file.url + new_hash, use_directory_urls
170+
)
171+
self.redirect_entries[page_old]["hashes"][i] = (old_hash, dest_hash_path)
153172

154-
for old_hash, new_link in self.redirect_maps[page_old]['hashes']:
173+
for old_hash, new_link in self.redirect_entries[page_old]["hashes"]:
155174
log.info(f"Injecting redirect for '{page_old}{old_hash}' to '{new_link}'")
156175

157-
js_redirects = JS_INJECT_EXISTS.format(redirects=gen_anchor_redirects(self.redirect_maps[page_old]['hashes']))
176+
js_redirects = JS_INJECT_EXISTS.format(
177+
redirects=gen_anchor_redirects(self.redirect_entries[page_old]["hashes"])
178+
)
158179
return js_redirects + html
159180

160181
# Create HTML files for redirects after site dir has been built
161182
def on_post_build(self, config, **kwargs):
162183
# Determine if 'use_directory_urls' is set
163184
use_directory_urls = config.get("use_directory_urls")
164-
for page_old, redirect_maps in self.redirect_maps.items():
185+
for page_old, redirect_entry in self.redirect_entries.items():
165186
# Need to remove hash fragment from new page to verify existence
166-
page_new = redirect_maps['overall']
187+
page_new = redirect_entry["overall"]
167188
page_old_without_hash, _ = _split_hash_fragment(str(page_old))
168189
page_new_without_hash, new_hash = _split_hash_fragment(str(page_new))
169190

@@ -174,37 +195,41 @@ def on_post_build(self, config, **kwargs):
174195
# If the redirect target is a valid internal page, we need to create a relative path
175196
elif page_new_without_hash in self.doc_pages:
176197
file = self.doc_pages[page_new_without_hash]
177-
dest_path = get_relative_html_path(page_old, file.url + new_hash, use_directory_urls)
198+
dest_path = get_relative_html_path(
199+
page_old, file.url + new_hash, use_directory_urls
200+
)
178201

179202
# If the redirect target isn't external or a valid internal page, throw an error
180203
# Note: we use 'warn' here specifically; mkdocs treats warnings specially when in strict mode
181204
else:
182205
log.warning("Redirect target '%s' does not exist!", page_new)
183206
continue
184207

185-
# Fixup redirect_maps to use the correct path
186-
for i in range(len(redirect_maps['hashes'])):
187-
old_hash, new_link = redirect_maps['hashes'][i]
208+
# Fixup redirect_entry to use the correct path
209+
for i in range(len(redirect_entry["hashes"])):
210+
old_hash, new_link = redirect_entry["hashes"][i]
188211
hash_redirect_without_hash, new_hash = _split_hash_fragment(str(new_link))
189212
if hash_redirect_without_hash in self.doc_pages:
190213
file = self.doc_pages[hash_redirect_without_hash]
191-
dest_hash_path = get_relative_html_path(page_old, file.url + new_hash, use_directory_urls)
192-
redirect_maps['hashes'][i] = (old_hash, dest_hash_path)
214+
dest_hash_path = get_relative_html_path(
215+
page_old, file.url + new_hash, use_directory_urls
216+
)
217+
redirect_entry["hashes"][i] = (old_hash, dest_hash_path)
193218

194219
if page_old_without_hash in self.doc_pages:
195220
# If the old page is a valid document page, it was injected in `on_page_content`.
196221
pass
197222
else:
198223
log.info(f"Creating redirect for '{page_old}' to '{dest_path}'")
199-
for old_hash, new_link in redirect_maps['hashes']:
224+
for old_hash, new_link in redirect_entry["hashes"]:
200225
log.info(f"Creating redirect for '{page_old}{old_hash}' to '{new_link}'")
201226
# Otherwise, create a new HTML file for the redirect
202227
dest_path = get_relative_html_path(page_old, dest_path, use_directory_urls)
203228
write_html(
204229
config["site_dir"],
205230
get_html_path(page_old, use_directory_urls),
206231
dest_path,
207-
redirect_maps['hashes'],
232+
redirect_entry["hashes"],
208233
)
209234

210235

tests/test_plugin.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@
2727
def run_redirect_test(monkeypatch, old_page, new_page, use_directory_urls):
2828
wrote = ()
2929

30-
def write_html(site_dir, old_path, new_path):
30+
def write_html(site_dir, old_path, new_path, anchor_list):
3131
nonlocal wrote
3232
wrote = (old_path, new_path)
3333

3434
monkeypatch.setattr(plugin, "write_html", write_html)
3535

3636
plg = plugin.RedirectPlugin()
3737
plg.redirects = {old_page: new_page}
38+
plg.redirect_entries = plugin.build_redirect_entries(plg.redirects)
3839
plg.doc_pages = {
3940
path: File(path, "docs", "site", use_directory_urls) for path in existing_pages
4041
}

0 commit comments

Comments
 (0)