refactor: migrate hrefs to resolve()/asset() from $app/paths

Replace string-literal and template-literal hrefs across the codebase
with the modern SvelteKit 2.26+ resolve() and asset() APIs. Migration
makes route IDs explicit, type-checked against generated $app/types,
and base-path-aware. Two codemod scripts handle the bulk; remaining
ambiguous, query-bearing, and precomputed-href cases are converted
manually at the assignment sites.
This commit is contained in:
2026-04-29 22:14:29 +02:00
parent 70506e169a
commit e5d218820b
64 changed files with 669 additions and 161 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import Symbol from "./Symbol.svelte"
import ThemeToggle from "./ThemeToggle.svelte"
import type { Snippet } from 'svelte';
@@ -329,7 +330,7 @@ nav {
<div>
<nav class:no-links={!links}>
<a href="/" aria-label="Home" class="home-link" class:full={fullSymbol}><Symbol /></a>
<a href={resolve('/')} aria-label="Home" class="home-link" class:full={fullSymbol}><Symbol /></a>
{#if links}
<div class="links-wrapper">
{@render links()}
+4 -3
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import { onMount } from "svelte";
import { page } from '$app/stores';
import LogIn from '@lucide/svelte/icons/log-in';
@@ -153,10 +154,10 @@
<p>({user.nickname})</p>
<ul>
{#if user.groups?.includes('rezepte_users')}
<li><a href="/{recipeLang}/administration">Administration</a></li>
<li><a href={resolve('/[recipeLang=recipeLang]/administration', { recipeLang })}>Administration</a></li>
{/if}
<li><a href="https://sso.bocken.org/if/user/#/settings" >Einstellungen</a></li>
<li><a href="/logout?callbackUrl={encodeURIComponent(getLogoutCallbackUrl($page.url.pathname))}">Log Out</a></li>
<li><a href={`${resolve('/logout')}?callbackUrl=${encodeURIComponent(getLogoutCallbackUrl($page.url.pathname))}`}>Log Out</a></li>
</ul>
</div>
</div>
@@ -164,7 +165,7 @@
{:else}
<a
class="entry login-link"
href="/login?callbackUrl={encodeURIComponent($page.url.pathname + $page.url.search)}"
href={`${resolve('/login')}?callbackUrl=${encodeURIComponent($page.url.pathname + $page.url.search)}`}
aria-label={lang === 'de' ? 'Anmelden' : 'Login'}
title={lang === 'de' ? 'Anmelden' : 'Login'}
>
@@ -1,4 +1,5 @@
<script>
import { resolve } from '$app/paths';
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
@@ -243,7 +244,7 @@
</div>
{#if payment}
<EditButton href="/{root}/payments/edit/{paymentId}" />
<EditButton href={resolve('/[cospendRoot=cospendRoot]/payments/edit/[id]', { cospendRoot: root, id: paymentId })} />
{/if}
<style>
+3 -2
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import Shield from '@lucide/svelte/icons/shield';
import Flame from '@lucide/svelte/icons/flame';
@@ -18,7 +19,7 @@
<a
class="case-tab"
class:active={active === 'contra'}
href="/{faithLang}/{slug}/contra"
href={resolve('/[faithLang=faithLang]/[apologetikSlug=apologetikSlug]/contra', { faithLang, apologetikSlug: slug })}
>
<Shield class="ct-glyph" size={14} strokeWidth={2} aria-hidden="true" />
<span>{l.contra}</span>
@@ -26,7 +27,7 @@
<a
class="case-tab"
class:active={active === 'pro'}
href="/{faithLang}/{slug}/pro"
href={resolve('/[faithLang=faithLang]/[apologetikSlug=apologetikSlug]/pro', { faithLang, apologetikSlug: slug })}
>
<Flame class="ct-glyph" size={14} strokeWidth={2} aria-hidden="true" />
<span>{l.pro}</span>
@@ -1,4 +1,5 @@
<script>
import { resolve } from '$app/paths';
import { page } from '$app/stores';
import { getEnrichedExerciseById } from '$lib/data/exercisedb';
import { detectFitnessLang, fitnessSlugs } from '$lib/js/fitnessI18n';
@@ -14,7 +15,7 @@
{#if plain}
<span class="exercise-plain">{exercise.localName}</span>
{:else}
<a href="/fitness/{sl.exercises}/{exerciseId}" class="exercise-link">{exercise.localName}</a>
<a href={resolve('/fitness/[exercises=fitnessExercises]/[id]', { exercises: sl.exercises, id: exerciseId })} class="exercise-link">{exercise.localName}</a>
{/if}
{:else}
<span class="exercise-unknown">Unknown Exercise</span>
+2 -1
View File
@@ -1,4 +1,5 @@
<script>
import { resolve } from '$app/paths';
import { page } from '$app/stores';
import { browser } from '$app/environment';
import { untrack } from 'svelte';
@@ -477,7 +478,7 @@
<span class="fs-result-cal">{item.calories}<small> kcal</small></span>
</button>
{#if showDetailLinks && (item.source === 'bls' || item.source === 'usda' || item.source === 'off')}
<a class="fs-detail-link" href="/fitness/{s.nutrition}/food/{item.source}/{item.id}" aria-label="View details">
<a class="fs-detail-link" href={resolve('/fitness/[nutrition=fitnessNutrition]/food/[source]/[id]', { nutrition: s.nutrition, source: item.source, id: item.id })} aria-label="View details">
<ExternalLink size={13} />
</a>
{/if}
@@ -1,4 +1,5 @@
<script>
import { resolve } from '$app/paths';
import { page } from '$app/stores';
import { getExerciseById, getExerciseMetrics } from '$lib/data/exercises';
import Clock from '@lucide/svelte/icons/clock';
@@ -152,7 +153,7 @@
});
</script>
<a href="/fitness/{sl.history}/{session._id}" class="session-card">
<a href={resolve('/fitness/[history=fitnessHistory]/[id]', { history: sl.history, id: session._id })} class="session-card">
<div class="card-top">
<h3 class="session-name">{session.name}</h3>
<span class="session-date">{formatDate(session.startTime)} &middot; {formatTime(session.startTime)}</span>
+2 -1
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import "$lib/css/shake.css"
let { icon, ...restProps } = $props<{ icon: string, [key: string]: any }>();
</script>
@@ -26,4 +27,4 @@
}
</style>
<a href="/rezepte/icon/{icon}" {...restProps} >{icon}</a>
<a href={resolve('/[recipeLang=recipeLang]/icon/[icon]', { recipeLang: 'rezepte', icon })} {...restProps} >{icon}</a>
@@ -1,4 +1,5 @@
<script lang="ts">
import { resolve } from '$app/paths';
import type { TranslatedRecipeType } from '$types/types';
import TranslationFieldComparison from './TranslationFieldComparison.svelte';
import CreateIngredientList from '$lib/components/recipes/CreateIngredientList.svelte';
@@ -758,7 +759,7 @@ button:disabled {
{#each untranslatedBaseRecipes as baseRecipe}
<li>
<strong>{baseRecipe.name}</strong>
<a href="/de/edit/{baseRecipe.shortName}" target="_blank" rel="noopener noreferrer">
<a href={resolve('/[recipeLang=recipeLang]/edit/[name]', { recipeLang: 'de', name: baseRecipe.shortName })} target="_blank" rel="noopener noreferrer">
Open in new tab →
</a>
</li>