@@ -21,16 +21,32 @@ const maybeMetaRedirect = (visit, {page}) => {
2121
2222window . addEventListener ( 'DOMContentLoaded' , emitExdocLoaded )
2323
24+ // Match links to local .html documentation pages, with or without a fragment.
25+ // Routing non-HTML files (downloads such as .mmd) through SWUP breaks the page
26+ // swap (#2182); the `.html#` branch keeps in-doc anchored links (function
27+ // references like file.html#list_dir/1, sidebar entries) on the SWUP path.
28+ export const LINK_SELECTOR = 'a[href]:not([href^="/"]):not([href^="http"]):is([href$=".html"], [href*=".html#"])'
29+
30+ // SWUP only swaps `#main`, so it must stay within a single ExDoc build: the
31+ // sidebar (navigation + version) is built from per-build <head> scripts SWUP
32+ // does not re-run. ExDoc writes every page of a build as a flat file in one
33+ // folder, so an in-build link is a bare filename and a slash in its path means
34+ // another folder/build (the Erlang/OTP docs flatten one build per application).
35+ // We test the path -- the part before the fragment -- not the whole href, since
36+ // anchors carry slashes too (the arity in file.html#list_dir/1).
37+ export const isWithinBuild = ( href ) => ! href . split ( '#' ) [ 0 ] . includes ( '/' )
38+
2439if ( ! isEmbedded && window . location . protocol !== 'file:' ) {
2540 new Swup ( {
2641 animationSelector : false ,
2742 containers : [ '#main' ] ,
28- ignoreVisit : ( url ) => {
43+ ignoreVisit : ( url , { el } = { } ) => {
2944 const path = url . split ( '#' ) [ 0 ]
3045 return path === window . location . pathname ||
31- path === window . location . pathname + '.html'
46+ path === window . location . pathname + '.html' ||
47+ ! isWithinBuild ( el ?. getAttribute ( 'href' ) ?? '' )
3248 } ,
33- linkSelector : 'a[href]:not([href^="/"]):not([href^="http"])[href$=".html"]' ,
49+ linkSelector : LINK_SELECTOR ,
3450 hooks : {
3551 'page:load' : maybeMetaRedirect ,
3652 'page:view' : emitExdocLoaded
0 commit comments