Skip to content

Commit 6c4ba45

Browse files
committed
Fixes to "staying on the same page functionality"
This is squidfunk#7350 with the tests removed, as requested in squidfunk#7350 (comment) . I do urge you to reconsider and merge something like squidfunk#7350 (including the tests I wrote) instead. Without tests, I suspect this code will break like its [previous version did]( squidfunk@ceacfe5) . Once the code does break, it might be difficult to fix without tests. Even if other parts of the theme work without tests, and even if you aren't in love with how I set up the infrastructure in squidfunk#7350 or squidfunk#7338, I think this logic is complex enough to need tests to be maintainable. Again, I wouldn't be able to check this code's correctness without the tests that aren't included in this PR. Of course, this is your project to maintain, so do what works best for you. I just hope this bugfix helps. --------------------- Fixes squidfunk#7226 Additionally, this allows using the version switcher even if the website is published at a different URL than the `site_url`. In particular, the version switcher should now work locally when serving the site with `mike serve`.
1 parent c6286de commit 6c4ba45

File tree

3 files changed

+117
-13
lines changed

3 files changed

+117
-13
lines changed

docs/setup/setting-up-versioning.md

-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ MkDocs implements this behavior by default, but there are a few caveats:
6464
- the [`site_url`][mkdocs.site_url] must be set correctly in `mkdocs.yml`.
6565
See the ["Publishing a new version"](#publishing-a-new-version) section for
6666
an example.
67-
- you must be viewing the site at that URL (and not locally, for example).
6867
- the redirect happens via JavaScript and there is no way to know which page you
6968
will be redirected to ahead of time.
7069

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Sitemap } from "../sitemap"
2+
3+
type CorrespondingURLParams = {
4+
selectedVersionSitemap: Sitemap
5+
selectedVersionBaseURL: URL
6+
currentLocation: URL
7+
currentBaseURL: string
8+
}
9+
10+
/**
11+
* Choose a URL to navigate to when the user chooses a version in the version
12+
* selector.
13+
*
14+
* @param selectedVersionSitemap - as obtained by fetchSitemap from `${selectedVersionBaseURL}/sitemap.xml`
15+
* @param selectedVersionBaseURL - usually `${currentBaseURL}/../selectedVersion`
16+
* @param currentLocation - current web browser location
17+
* @param currentBaseURL - as obtained from `config.base`
18+
* @returns the URL to navigate to or null if we can't be sure that the
19+
* corresponding page to the current page exists in the selected version
20+
*/
21+
export function selectedVersionCorrespondingURL(
22+
{selectedVersionSitemap,
23+
selectedVersionBaseURL,
24+
currentLocation,
25+
currentBaseURL}: CorrespondingURLParams
26+
): URL | undefined {
27+
const current_path = (new URL(currentBaseURL)).pathname
28+
const currentRelativePath = stripPrefix(currentLocation.pathname, current_path)
29+
if (currentRelativePath === undefined) {
30+
return
31+
}
32+
const sitemapCommonPrefix = shortestCommonPrefix(selectedVersionSitemap.keys())
33+
if (!selectedVersionSitemap.has(sitemapCommonPrefix)) {
34+
// We could also check that `commonSitemapPrefix` ends in the canonical version,
35+
// similarly to https://github.com/squidfunk/mkdocs-material/pull/7227. However,
36+
// I don't believe that Mike/MkDocs ever generate sitemaps where it would matter
37+
return
38+
}
39+
40+
const potentialSitemapURL = new URL(currentRelativePath, sitemapCommonPrefix)
41+
if (!selectedVersionSitemap.has(potentialSitemapURL.href)) {
42+
return
43+
}
44+
45+
const result = new URL(currentRelativePath, selectedVersionBaseURL)
46+
result.hash = currentLocation.hash
47+
result.search = currentLocation.search
48+
return result
49+
}
50+
51+
// Basic string manipulation
52+
53+
/**
54+
*
55+
* @param s - string
56+
* @param prefix - prefix to strip
57+
*
58+
* @returns either the string with the prefix stripped or undefined if the
59+
* string did not begin with the prefix.
60+
*/
61+
export function stripPrefix(s: string, prefix: string): string | undefined {
62+
if (s.startsWith(prefix)) {
63+
return s.slice(prefix.length)
64+
}
65+
return undefined
66+
}
67+
68+
/**
69+
*
70+
* @param s1 - first string
71+
* @param s2 - second string
72+
*
73+
* @returns - the length of the longest common prefix of the two strings.
74+
*/
75+
function commonPrefixLen(s1: string, s2: string): number {
76+
const max = Math.min(s1.length, s2.length)
77+
let result
78+
for (result = 0; result < max; ++result) {
79+
if (s1[result] !== s2[result]) {
80+
break
81+
}
82+
}
83+
return result
84+
}
85+
86+
/**
87+
*
88+
* @param strs - an iterable of strings
89+
*
90+
* @returns the longest common prefix of all the strings
91+
*/
92+
export function shortestCommonPrefix(strs: Iterable<string>): string {
93+
let result // Undefined if no iterations happened
94+
for (const s of strs) {
95+
if (result === undefined) {
96+
result = s
97+
} else {
98+
result = result.slice(0, commonPrefixLen(result, s))
99+
}
100+
}
101+
return result ?? ""
102+
}

src/templates/assets/javascripts/integrations/version/index.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ import {
4848

4949
import { fetchSitemap } from "../sitemap"
5050

51+
import { selectedVersionCorrespondingURL } from "./correspondingPage"
52+
5153
/* ----------------------------------------------------------------------------
5254
* Helper types
5355
* ------------------------------------------------------------------------- */
@@ -122,22 +124,23 @@ export function setupVersionSelector(
122124
return EMPTY
123125
}
124126
ev.preventDefault()
125-
return of(url)
127+
return of(new URL(url))
126128
}
127129
}
128130
return EMPTY
129131
}),
130-
switchMap(url => {
131-
return fetchSitemap(new URL(url))
132-
.pipe(
133-
map(sitemap => {
134-
const location = getLocation()
135-
const path = location.href.replace(config.base, url)
136-
return sitemap.has(path.split("#")[0])
137-
? new URL(path)
138-
: new URL(url)
139-
})
140-
)
132+
switchMap(selectedVersionBaseURL => {
133+
return fetchSitemap(selectedVersionBaseURL).pipe(
134+
map(
135+
sitemap =>
136+
selectedVersionCorrespondingURL({
137+
selectedVersionSitemap: sitemap,
138+
selectedVersionBaseURL,
139+
currentLocation: getLocation(),
140+
currentBaseURL: config.base
141+
}) ?? selectedVersionBaseURL,
142+
),
143+
)
141144
})
142145
)
143146
)

0 commit comments

Comments
 (0)