@@ -7,20 +7,22 @@ import {
77} from "./preferences.svelte" ;
88import {
99 buildURL ,
10- canonicalizeMalformedVersionURL ,
1110 initializeRouting ,
1211 navigateToURL ,
1312 page ,
1413 replaceURLDebounced ,
14+ resolveVersionedPath ,
1515 type RouteTarget ,
1616} from "./routing.svelte" ;
1717import {
1818 buildsState ,
19+ type BuildsState ,
1920 initializeBuildsState ,
2021 resolveBuildVersion ,
2122} from "./builds.svelte" ;
2223import { initializeUILocale } from "./i18n/ui-locale" ;
2324import { DEFAULT_LOCALE } from "./constants" ;
25+ import { BASE_URL } from "./utils/env" ;
2426
2527type NavigationContext = {
2628 url : URL ;
@@ -157,6 +159,69 @@ export function changeMods(mods: string[]): void {
157159 ) ;
158160}
159161
162+ function stripBaseFromPathname ( pathname : string ) : string {
163+ const baseNoSlash = BASE_URL . endsWith ( "/" ) ? BASE_URL . slice ( 0 , - 1 ) : BASE_URL ;
164+
165+ if ( pathname === baseNoSlash ) return "/" ;
166+ if ( pathname . startsWith ( BASE_URL ) ) return pathname . slice ( BASE_URL . length - 1 ) ;
167+ if ( pathname . startsWith ( baseNoSlash + "/" ) )
168+ return pathname . slice ( baseNoSlash . length ) ;
169+ return pathname ;
170+ }
171+
172+ function hasVersionlessHomePath ( pathname : string ) : boolean {
173+ const path = stripBaseFromPathname ( pathname ) ;
174+ const cleanPath = path . startsWith ( "/" ) ? path . slice ( 1 ) : path ;
175+ return cleanPath . length === 0 ;
176+ }
177+
178+ function buildCanonicalBootstrapURL (
179+ urlInput : string ,
180+ builds : BuildsState ,
181+ ) : string {
182+ const currentURL = new URL ( urlInput , location . origin ) ;
183+
184+ const canonicalPath = resolveVersionedPath ( currentURL . pathname , builds ) ;
185+
186+ const canonicalMods =
187+ page . route . modsParam . length > 0
188+ ? page . route . modsParam
189+ : ( preferences . defaultMods ?? [ ] ) ;
190+
191+ const canonicalLocale = page . route . localeParam ;
192+
193+ const canonicalTileset = resolveTileset (
194+ preferences . preferredTileset ,
195+ page . route . tilesetParam ,
196+ ) ;
197+
198+ const builtCanonicalURL = new URL (
199+ buildURL (
200+ canonicalPath . versionSlug ,
201+ canonicalPath . target ,
202+ canonicalLocale ,
203+ canonicalTileset ,
204+ canonicalMods ,
205+ ) ,
206+ location . origin ,
207+ ) ;
208+
209+ // Preserve the root home route shape while still normalizing query-backed context.
210+ if (
211+ hasVersionlessHomePath ( currentURL . pathname ) &&
212+ canonicalPath . target . kind === "home"
213+ ) {
214+ builtCanonicalURL . pathname = currentURL . pathname ;
215+ }
216+
217+ builtCanonicalURL . hash = currentURL . hash ;
218+ return (
219+ builtCanonicalURL . pathname +
220+ builtCanonicalURL . search +
221+ builtCanonicalURL . hash
222+ ) ;
223+ }
224+
160225/**
161226 * Initializes the navigation prerequisites before the app shell mounts.
162227 *
@@ -167,29 +232,11 @@ export function changeMods(mods: string[]): void {
167232export async function bootstrapApplication ( ) : Promise < void > {
168233 initializeRouting ( ) ;
169234 initializePreferences ( ) ;
170-
171- if (
172- page . route . modsParam . length === 0 &&
173- preferences . defaultMods !== null &&
174- preferences . defaultMods . length > 0
175- ) {
176- const urlWithMods = buildURL (
177- page . route . versionSlug ,
178- page . route . target ,
179- page . route . localeParam ,
180- page . route . tilesetParam ,
181- preferences . defaultMods ,
182- ) ;
183- navigateToURL ( urlWithMods , "replace" ) ;
184- }
185-
186235 const versionState = await initializeBuildsState ( ) ;
236+ const canonicalURL = buildCanonicalBootstrapURL ( location . href , versionState ) ;
237+ const currentURL = location . pathname + location . search + location . hash ;
187238
188- const canonicalURL = canonicalizeMalformedVersionURL (
189- location . href ,
190- versionState ,
191- ) ;
192- if ( canonicalURL ) {
239+ if ( canonicalURL !== currentURL ) {
193240 navigateToURL ( canonicalURL , "replace" ) ;
194241 }
195242
0 commit comments