chore: upgrade to svelte 5 and vite 8

Svelte 4.2 to 5.56, Vite 5 to 8, vite-plugin-svelte 3 to 7. The old tree
wouldn't resolve incrementally, because a stale vite-plugin-svelte-inspector
pinned the v3 peer, so this is a clean reinstall.

Clears all 7 npm audit advisories, including a high-severity Rollup path
traversal.

Reactivity moves to runes: $state, $derived, $effect. Event directives
become properties, and main.js uses mount().

Also drops inputElement, a bind:this target nothing ever read.
This commit is contained in:
Aculix Technologies
2026-08-15 23:59:28 +05:30
parent 50509a9cc1
commit 4857f1cc26
4 changed files with 270 additions and 1553 deletions
+238 -1519
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -12,9 +12,9 @@
"test:watch": "vitest" "test:watch": "vitest"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^7.3.0",
"svelte": "^4.2.0", "svelte": "^5.56.9",
"vite": "^5.0.0", "vite": "^8.2.1",
"vitest": "^4.1.10" "vitest": "^4.1.10"
}, },
"keywords": [ "keywords": [
+27 -28
View File
@@ -12,16 +12,15 @@
const storage = createStorage(); const storage = createStorage();
let tasks = []; let tasks = $state([]);
let newTask = ''; let newTask = $state('');
let darkMode = false; let darkMode = $state(false);
let inputElement; let isLoading = $state(true);
let isLoading = true;
let isInitialized = false; let isInitialized = false;
let todayKey = toKey(new Date()); let todayKey = $state(toKey(new Date()));
let selectedKey = todayKey; let selectedKey = $state(toKey(new Date()));
let draggedItem = null; let draggedItem = $state(null);
let draggedOverIndex = null; let draggedOverIndex = $state(null);
let midnightTimer = null; let midnightTimer = null;
/** Single write path, so persistence cannot drift out of step with the list. /** Single write path, so persistence cannot drift out of step with the list.
@@ -139,15 +138,16 @@
if (document.visibilityState === 'visible') runRollover(); if (document.visibilityState === 'visible') runRollover();
} }
$: currentDateDisplay = formatLong(selectedKey); const currentDateDisplay = $derived(formatLong(selectedKey));
$: buttonText = labelFor(selectedKey, todayKey); const buttonText = $derived(labelFor(selectedKey, todayKey));
$: remainingTasks = tasks.filter(task => !task.completed).length; const remainingTasks = $derived(tasks.filter(task => !task.completed).length);
$: completedTasks = tasks.filter(task => task.completed).length; const completedTasks = $derived(tasks.filter(task => task.completed).length);
$: if (darkMode !== undefined && isInitialized) { $effect(() => {
if (!isInitialized) return;
storage.saveTheme(darkMode ? 'dark' : 'light'); storage.saveTheme(darkMode ? 'dark' : 'light');
} });
function initLottie(node) { function initLottie(node) {
let instance = null; let instance = null;
@@ -207,7 +207,7 @@
}); });
</script> </script>
<svelte:window on:keydown={handleKeydown} /> <svelte:window onkeydown={handleKeydown} />
<div class="app" class:dark={darkMode}> <div class="app" class:dark={darkMode}>
{#if isLoading} {#if isLoading}
@@ -238,7 +238,7 @@
</div> </div>
<div class="header-actions"> <div class="header-actions">
<button class="today-btn" aria-label="Switch between Today and Tomorrow" on:click={switchDate}> <button class="today-btn" aria-label="Switch between Today and Tomorrow" onclick={switchDate}>
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" stroke="currentColor" stroke-width="2"/> <rect x="3" y="4" width="18" height="18" rx="2" ry="2" stroke="currentColor" stroke-width="2"/>
<line x1="16" y1="2" x2="16" y2="6" stroke="currentColor" stroke-width="2"/> <line x1="16" y1="2" x2="16" y2="6" stroke="currentColor" stroke-width="2"/>
@@ -250,7 +250,7 @@
<button <button
class="theme-toggle" class="theme-toggle"
on:click={toggleTheme} onclick={toggleTheme}
aria-label={darkMode ? 'Switch to light mode' : 'Switch to dark mode'} aria-label={darkMode ? 'Switch to light mode' : 'Switch to dark mode'}
> >
<svg class="theme-icon" class:rotated={darkMode} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="theme-icon" class:rotated={darkMode} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -282,12 +282,11 @@
<div class="task-input-container"> <div class="task-input-container">
<input <input
bind:this={inputElement}
bind:value={newTask} bind:value={newTask}
type="text" type="text"
placeholder="+ Add a task" placeholder="+ Add a task"
class="task-input" class="task-input"
on:keydown={handleKeydown} onkeydown={handleKeydown}
/> />
</div> </div>
@@ -296,7 +295,7 @@
{remainingTasks} {remainingTasks === 1 ? 'task' : 'tasks'} remaining {remainingTasks} {remainingTasks === 1 ? 'task' : 'tasks'} remaining
</span> </span>
{#if completedTasks > 0} {#if completedTasks > 0}
<button class="clear-completed" on:click={clearCompleted}> <button class="clear-completed" onclick={clearCompleted}>
Clear completed Clear completed
</button> </button>
{/if} {/if}
@@ -319,11 +318,11 @@
draggable="true" draggable="true"
in:fly={{ y: -10, duration: 300, delay: index * 30, easing: cubicOut }} in:fly={{ y: -10, duration: 300, delay: index * 30, easing: cubicOut }}
out:fly={{ x: 30, opacity: 0, duration: 250, delay: index * 20, easing: cubicOut }} out:fly={{ x: 30, opacity: 0, duration: 250, delay: index * 20, easing: cubicOut }}
on:dragstart={(e) => handleDragStart(e, index)} ondragstart={(e) => handleDragStart(e, index)}
on:dragover={(e) => handleDragOver(e, index)} ondragover={(e) => handleDragOver(e, index)}
on:dragend={handleDragEnd} ondragend={handleDragEnd}
on:dragleave={handleDragLeave} ondragleave={handleDragLeave}
on:keydown={(e) => handleTaskKeydown(e, task.id)} onkeydown={(e) => handleTaskKeydown(e, task.id)}
tabindex="0" tabindex="0"
role="button" role="button"
aria-label={task.completed ? `Completed: ${task.text}` : `Incomplete: ${task.text}`} aria-label={task.completed ? `Completed: ${task.text}` : `Incomplete: ${task.text}`}
@@ -331,7 +330,7 @@
<button <button
class="checkbox" class="checkbox"
class:checked={task.completed} class:checked={task.completed}
on:click={() => toggleTask(task.id)} onclick={() => toggleTask(task.id)}
aria-label={task.completed ? 'Mark as incomplete' : 'Mark as complete'} aria-label={task.completed ? 'Mark as incomplete' : 'Mark as complete'}
> >
{#if task.completed} {#if task.completed}
@@ -345,7 +344,7 @@
<button <button
class="delete-btn" class="delete-btn"
on:click={() => deleteTask(task.id)} onclick={() => deleteTask(task.id)}
aria-label="Delete task" aria-label="Delete task"
> >
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+2 -3
View File
@@ -1,7 +1,6 @@
import { mount } from 'svelte';
import App from './App.svelte'; import App from './App.svelte';
const app = new App({ export default mount(App, {
target: document.getElementById('app'), target: document.getElementById('app'),
}); });
export default app;