perf: analytics stratejisi, radix tree-shaking, DevPanel, motion/mini (Faz 2)
- Rybbit Script beforeInteractive -> afterInteractive: analitik artık hydration'dan önce kritik yolda çalışmıyor (yalnızca preload hint'i kalıyor) - next.config: experimental.optimizePackageImports ["radix-ui"] — 36 alt paketli kök barrel modül grafiğini şişiriyordu (VPS build OOM geçmişi) - DevPanel: dev-panel-mount.tsx ile prod'da import() erişilemez dalda; dev kodu + dev action proxy'leri prod chunk'lardan çıktı (rota başına ~21 KB) - motion/mini: use-pixel-heat + sihirbaz-cagri-karti yalnızca duration/ease kullanıyor; WAAPI tabanlı mini animate tam motoru bundle'dan çıkarır Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,12 +22,13 @@ const nextConfig: NextConfig = {
|
|||||||
"openai",
|
"openai",
|
||||||
"resend",
|
"resend",
|
||||||
],
|
],
|
||||||
|
experimental: {
|
||||||
|
// radix-ui kök barrel'ı 36 alt paketi çeker ve Next'in varsayılan
|
||||||
|
// optimize listesinde yoktur; named import'lar doğrudan alt modüle
|
||||||
|
// çevrilir (dev boot / build modül grafiği küçülür).
|
||||||
|
optimizePackageImports: ["radix-ui"],
|
||||||
...(kaynakKisitli
|
...(kaynakKisitli
|
||||||
? {
|
? {
|
||||||
// cacheComponents açıkken prerender fazında source map üretimi varsayılan
|
|
||||||
// açık; 1900+ statik sayfada ciddi bellek maliyeti var.
|
|
||||||
enablePrerenderSourceMaps: false,
|
|
||||||
experimental: {
|
|
||||||
// Turbopack'ın Rust tarafı NODE_OPTIONS heap sınırını görmez; bellek
|
// Turbopack'ın Rust tarafı NODE_OPTIONS heap sınırını görmez; bellek
|
||||||
// hedefi byte cinsinden ayrıca verilir (1.5 GB).
|
// hedefi byte cinsinden ayrıca verilir (1.5 GB).
|
||||||
turbopackMemoryLimit: 1_610_612_736,
|
turbopackMemoryLimit: 1_610_612_736,
|
||||||
@@ -35,7 +36,14 @@ const nextConfig: NextConfig = {
|
|||||||
// worker × 4 sayfa. Her worker ayrı bir Node süreci/heap'idir.
|
// worker × 4 sayfa. Her worker ayrı bir Node süreci/heap'idir.
|
||||||
cpus: 1,
|
cpus: 1,
|
||||||
staticGenerationMaxConcurrency: 4,
|
staticGenerationMaxConcurrency: 4,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
|
...(kaynakKisitli
|
||||||
|
? {
|
||||||
|
// cacheComponents açıkken prerender fazında source map üretimi varsayılan
|
||||||
|
// açık; 1900+ statik sayfada ciddi bellek maliyeti var.
|
||||||
|
enablePrerenderSourceMaps: false,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
async redirects() {
|
async redirects() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { KaydetBannerLazy } from "@/features/liste/components/kaydet-banner-lazy
|
|||||||
import { TercihProfiliKapisiLazy } from "@/features/liste/components/tercih-profili-kapisi-lazy";
|
import { TercihProfiliKapisiLazy } from "@/features/liste/components/tercih-profili-kapisi-lazy";
|
||||||
import { HeaderGate } from "@/components/header-gate";
|
import { HeaderGate } from "@/components/header-gate";
|
||||||
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
|
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
|
||||||
import { DevPanel } from "@/components/dev/dev-panel";
|
import { DevPanelMount } from "@/components/dev/dev-panel-mount";
|
||||||
import { ScrollFlow } from "@/components/scroll-flow";
|
import { ScrollFlow } from "@/components/scroll-flow";
|
||||||
import { JsonLd, organizationJsonLd, webSiteJsonLd, SITE_URL } from "@/lib/seo";
|
import { JsonLd, organizationJsonLd, webSiteJsonLd, SITE_URL } from "@/lib/seo";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
@@ -85,19 +85,6 @@ export default function RootLayout({
|
|||||||
// içerikle büyümeli, viewport tabanı body'deki min-h-dvh'den gelir.
|
// içerikle büyümeli, viewport tabanı body'deki min-h-dvh'den gelir.
|
||||||
className={`${calSans.variable} ${clarityCity.variable} ${geistMono.variable} ${bricolage.variable} antialiased`}
|
className={`${calSans.variable} ${clarityCity.variable} ${geistMono.variable} ${bricolage.variable} antialiased`}
|
||||||
>
|
>
|
||||||
{/* Rybbit analitik — beforeInteractive her zaman <head> içine defer olarak
|
|
||||||
enjekte edilir. Yalnızca prod build'de: Rybbit localhost'u da takip
|
|
||||||
ettiği için dev trafiği prod verisini kirletmesin (DevPanel ile aynı
|
|
||||||
kapı). mask-patterns: /sonuc URL'i ham başarı sıralamasını,
|
|
||||||
/odeme/sonuc sipariş kimliğini taşıyor — path tek satırda toplanır. */}
|
|
||||||
{process.env.NODE_ENV === "production" ? (
|
|
||||||
<Script
|
|
||||||
src="https://rybbit.kolaytercih.com/api/script.js"
|
|
||||||
data-site-id="f2cb042c657e"
|
|
||||||
data-mask-patterns='["/sonuc", "/odeme/sonuc"]'
|
|
||||||
strategy="beforeInteractive"
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<body className="min-h-dvh flex flex-col bg-slate-50">
|
<body className="min-h-dvh flex flex-col bg-slate-50">
|
||||||
<HeaderGate>
|
<HeaderGate>
|
||||||
{/* Site geneli progressive blur — üst ve alt */}
|
{/* Site geneli progressive blur — üst ve alt */}
|
||||||
@@ -130,10 +117,23 @@ export default function RootLayout({
|
|||||||
{/* İlk "+" için sıra + sihirbaz kapısı */}
|
{/* İlk "+" için sıra + sihirbaz kapısı */}
|
||||||
<TercihProfiliKapisiLazy />
|
<TercihProfiliKapisiLazy />
|
||||||
<Toaster position="top-center" />
|
<Toaster position="top-center" />
|
||||||
{/* Dev süperadmin paneli — prod build'de hiç render edilmez */}
|
{/* Dev süperadmin paneli — prod build'de modül grafiğine bile girmez */}
|
||||||
{process.env.NODE_ENV !== "production" ? <DevPanel /> : null}
|
{process.env.NODE_ENV !== "production" ? <DevPanelMount /> : null}
|
||||||
<JsonLd data={organizationJsonLd()} />
|
<JsonLd data={organizationJsonLd()} />
|
||||||
<JsonLd data={webSiteJsonLd()} />
|
<JsonLd data={webSiteJsonLd()} />
|
||||||
|
{/* Rybbit analitik — afterInteractive: hydration sonrası client'ta
|
||||||
|
enjekte edilir, LCP/kritik yolla yarışmaz. Yalnızca prod build'de:
|
||||||
|
Rybbit localhost'u da takip ettiği için dev trafiği prod verisini
|
||||||
|
kirletmesin (DevPanel ile aynı kapı). mask-patterns: /sonuc URL'i
|
||||||
|
ham başarı sıralamasını, /odeme/sonuc sipariş kimliğini taşıyor. */}
|
||||||
|
{process.env.NODE_ENV === "production" ? (
|
||||||
|
<Script
|
||||||
|
src="https://rybbit.kolaytercih.com/api/script.js"
|
||||||
|
data-site-id="f2cb042c657e"
|
||||||
|
data-mask-patterns='["/sonuc", "/odeme/sonuc"]'
|
||||||
|
strategy="afterInteractive"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
18
src/components/dev/dev-panel-mount.tsx
Normal file
18
src/components/dev/dev-panel-mount.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
// NODE_ENV build sırasında statik yerine konur: prod build'de import()
|
||||||
|
// erişilemez dalda kalır ve dev-panel (+ dev server-action proxy'leri)
|
||||||
|
// modül grafiğine hiç girmez — layout'taki statik import ~92 KB'lık
|
||||||
|
// paylaşılan chunk'a dev kodu taşıyordu. Dev'de panel dynamic yüklenir.
|
||||||
|
const DevPanelIc =
|
||||||
|
process.env.NODE_ENV !== "production"
|
||||||
|
? dynamic(() => import("./dev-panel").then((m) => m.DevPanel), {
|
||||||
|
ssr: false,
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
export function DevPanelMount() {
|
||||||
|
return DevPanelIc ? <DevPanelIc /> : null;
|
||||||
|
}
|
||||||
@@ -16,7 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, type RefObject } from "react";
|
import { useEffect, type RefObject } from "react";
|
||||||
import { animate, type AnimationPlaybackControls } from "motion";
|
// WAAPI tabanlı mini animate yeterli (yalnızca duration/ease kullanılıyor);
|
||||||
|
// tam motor client bundle'ına girmez. Tip mini'den export edilmiyor —
|
||||||
|
// type-only import build'de silinir, maliyeti yok.
|
||||||
|
import { animate } from "motion/mini";
|
||||||
|
import type { AnimationPlaybackControls } from "motion";
|
||||||
|
|
||||||
const FINE_POINTER_QUERY = "(hover: hover) and (pointer: fine)";
|
const FINE_POINTER_QUERY = "(hover: hover) and (pointer: fine)";
|
||||||
const HOT_COLOR = "#f97316"; // orange-500
|
const HOT_COLOR = "#f97316"; // orange-500
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
import { animate } from "motion";
|
import { animate } from "motion/mini";
|
||||||
import { useReducedMotion } from "motion/react";
|
import { useReducedMotion } from "motion/react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { PixelField } from "@/components/pixel-decor";
|
import { PixelField } from "@/components/pixel-decor";
|
||||||
|
|||||||
Reference in New Issue
Block a user