mirror of
https://github.com/squidfunk/mkdocs-material.git
synced 2025-02-17 10:48:34 +01:00
Fixed stay no page functionality when using mike's canonical versioning (#7559)
This commit is contained in:
parent
50a15becc8
commit
aeb9492c08
@ -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`.
|
- 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
|
See the ["Publishing a new version"](#publishing-a-new-version) section for
|
||||||
an example.
|
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
|
- the redirect happens via JavaScript and there is no way to know which page you
|
||||||
will be redirected to ahead of time.
|
will be redirected to ahead of time.
|
||||||
|
|
||||||
|
@ -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 ?? ""
|
||||||
|
}
|
@ -48,6 +48,8 @@ import {
|
|||||||
|
|
||||||
import { fetchSitemap } from "../sitemap"
|
import { fetchSitemap } from "../sitemap"
|
||||||
|
|
||||||
|
import { selectedVersionCorrespondingURL } from "./findurl"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Helper types
|
* Helper types
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
@ -122,22 +124,23 @@ export function setupVersionSelector(
|
|||||||
return EMPTY
|
return EMPTY
|
||||||
}
|
}
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
return of(url)
|
return of(new URL(url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return EMPTY
|
return EMPTY
|
||||||
}),
|
}),
|
||||||
switchMap(url => {
|
switchMap(selectedVersionBaseURL => {
|
||||||
return fetchSitemap(new URL(url))
|
return fetchSitemap(selectedVersionBaseURL).pipe(
|
||||||
.pipe(
|
map(
|
||||||
map(sitemap => {
|
sitemap =>
|
||||||
const location = getLocation()
|
selectedVersionCorrespondingURL({
|
||||||
const path = location.href.replace(config.base, url)
|
selectedVersionSitemap: sitemap,
|
||||||
return sitemap.has(path.split("#")[0])
|
selectedVersionBaseURL,
|
||||||
? new URL(path)
|
currentLocation: getLocation(),
|
||||||
: new URL(url)
|
currentBaseURL: config.base
|
||||||
})
|
}) ?? selectedVersionBaseURL,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user