perf: optimize cold start by lazy-loading icons and parallelizing DB queries

- Replace barrel `import * as icons` in DynamicIcon with dynamic per-icon imports
- Eagerly connect Prisma client at startup to avoid first-request latency
- Parallelize 4 sequential DB queries in layout server load with Promise.all
This commit is contained in:
2026-04-10 19:04:35 +03:00
parent 7beca05eec
commit 1e3a04f4de
3 changed files with 53 additions and 73 deletions
+24 -10
View File
@@ -1,6 +1,4 @@
<script lang="ts">
import * as icons from 'lucide-svelte';
interface Props {
name: string | null;
size?: number;
@@ -9,17 +7,33 @@
let { name, size = 16, class: className = '' }: Props = $props();
// Convert kebab-case to PascalCase: "layout-dashboard" → "LayoutDashboard"
function toPascalCase(str: string): string {
// Convert PascalCase or kebab-case to kebab-case filename
// "LayoutDashboard" → "layout-dashboard", "layout-dashboard" stays as-is
function toKebabCase(str: string): string {
return str
.split('-')
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
.toLowerCase();
}
const iconComponent = $derived(
name ? ((icons as Record<string, unknown>)[toPascalCase(name)] as typeof import('svelte').SvelteComponent | undefined) ?? null : null
);
let iconComponent = $state<typeof import('svelte').SvelteComponent | null>(null);
$effect(() => {
const currentName = name;
iconComponent = null;
if (!currentName) return;
const kebab = toKebabCase(currentName);
import(/* @vite-ignore */ `lucide-svelte/icons/${kebab}`)
.then((mod) => {
// Only apply if name hasn't changed during load
if (name === currentName) {
iconComponent = mod.default;
}
})
.catch(() => {
// Icon not found
});
});
</script>
{#if iconComponent}