Skip to content

Commit

Permalink
Fixed stay no page functionality when using mike's canonical versioni…
Browse files Browse the repository at this point in the history
…ng (#7559)
  • Loading branch information
ilyagr authored Sep 27, 2024
1 parent 50a15be commit aeb9492
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 13 deletions.
1 change: 0 additions & 1 deletion docs/setup/setting-up-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ MkDocs implements this behavior by default, but there are a few caveats:
- the [`site_url`][mkdocs.site_url] must be set correctly in `mkdocs.yml`.
See the ["Publishing a new version"](#publishing-a-new-version) section for
an example.
- you must be viewing the site at that URL (and not locally, for example).
- the redirect happens via JavaScript and there is no way to know which page you
will be redirected to ahead of time.

Expand Down
135 changes: 135 additions & 0 deletions src/templates/assets/javascripts/integrations/version/findurl/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Sitemap } from "../../sitemap"

/** See docstring for `selectedVersionCorrespondingURL` for the meaning of these fields. */
type CorrespondingURLParams = {
selectedVersionSitemap: Sitemap
selectedVersionBaseURL: URL
currentLocation: URL
currentBaseURL: string
}

/**
* Choose a URL to navigate to when the user chooses a version in the version
* selector.
*
* The parameters in `params` are named as follows, in order to make it clearer
* which parameter means what when invoking the function:
*
* - selectedVersionSitemap: Sitemap - as obtained by fetchSitemap from `${selectedVersionBaseURL}/sitemap.xml`
*
* - selectedVersionBaseURL: URL - usually `${currentBaseURL}/../selectedVersion`
*
* - currentLocation: URL - current web browser location
*
* - currentBaseURL: string - as obtained from `config.base`
*
* @param params - arguments with the meanings explained above.
* @returns the URL to navigate to or null if we can't be sure that the
* corresponding page to the current page exists in the selected version
*/
export function selectedVersionCorrespondingURL(
params: CorrespondingURLParams
): URL | undefined {
const {selectedVersionSitemap,
selectedVersionBaseURL,
currentLocation,
currentBaseURL} = params
const current_path = safeURLParse(currentBaseURL)?.pathname
if (current_path === undefined) {
return
}
const currentRelativePath = stripPrefix(currentLocation.pathname, current_path)
if (currentRelativePath === undefined) {
return
}
const sitemapCommonPrefix = shortestCommonPrefix(selectedVersionSitemap.keys())
if (!selectedVersionSitemap.has(sitemapCommonPrefix)) {
// We could also check that `commonSitemapPrefix` ends in the canonical version,
// similarly to https://github.com/squidfunk/mkdocs-material/pull/7227. However,
// I don't believe that Mike/MkDocs ever generate sitemaps where it would matter
return
}

const potentialSitemapURL = safeURLParse(currentRelativePath, sitemapCommonPrefix)
if (!potentialSitemapURL || !selectedVersionSitemap.has(potentialSitemapURL.href)) {
return
}

const result = safeURLParse(currentRelativePath, selectedVersionBaseURL)
if (!result) {
return
}
result.hash = currentLocation.hash
result.search = currentLocation.search
return result
}

/**
* A version of `new URL` that never throws. A polyfill for URL.parse() which is
* not yet ubuquitous.
*
* @param url - passed to `new URL` constructor
* @param base - passed to `new URL` constructor
*
* @returns `new URL(url, base)` or undefined if the URL is invalid.
*/
function safeURLParse(url: string|URL, base?: string|URL): URL | undefined {
try {
return new URL(url, base)
} catch {
return
}
}

// Basic string manipulation

/** Strip a given prefix from a function
*
* @param s - string
* @param prefix - prefix to strip
*
* @returns either the string with the prefix stripped or undefined if the
* string did not begin with the prefix.
*/
export function stripPrefix(s: string, prefix: string): string | undefined {
if (s.startsWith(prefix)) {
return s.slice(prefix.length)
}
return undefined
}

/** Find the length of the longest common prefix of two strings
*
* @param s1 - first string
* @param s2 - second string
*
* @returns - the length of the longest common prefix of the two strings.
*/
function commonPrefixLen(s1: string, s2: string): number {
const max = Math.min(s1.length, s2.length)
let result
for (result = 0; result < max; ++result) {
if (s1[result] !== s2[result]) {
break
}
}
return result
}

/** Find the longest common prefix of any number of strings
*
* @param strs - an iterable of strings
*
* @returns the longest common prefix of all the strings
*/
export function shortestCommonPrefix(strs: Iterable<string>): string {
let result // Undefined if no iterations happened
for (const s of strs) {
if (result === undefined) {
result = s
} else {
result = result.slice(0, commonPrefixLen(result, s))
}
}
return result ?? ""
}
27 changes: 15 additions & 12 deletions src/templates/assets/javascripts/integrations/version/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import {

import { fetchSitemap } from "../sitemap"

import { selectedVersionCorrespondingURL } from "./findurl"

/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -122,22 +124,23 @@ export function setupVersionSelector(
return EMPTY
}
ev.preventDefault()
return of(url)
return of(new URL(url))
}
}
return EMPTY
}),
switchMap(url => {
return fetchSitemap(new URL(url))
.pipe(
map(sitemap => {
const location = getLocation()
const path = location.href.replace(config.base, url)
return sitemap.has(path.split("#")[0])
? new URL(path)
: new URL(url)
})
)
switchMap(selectedVersionBaseURL => {
return fetchSitemap(selectedVersionBaseURL).pipe(
map(
sitemap =>
selectedVersionCorrespondingURL({
selectedVersionSitemap: sitemap,
selectedVersionBaseURL,
currentLocation: getLocation(),
currentBaseURL: config.base
}) ?? selectedVersionBaseURL,
),
)
})
)
)
Expand Down

0 comments on commit aeb9492

Please sign in to comment.