إنتقل إلى المحتوى الرئيسي

تطوير Svelte وSvelteKit في Cursor: دليل شامل

تطوير Svelte في Cursor

Svelte هو إطار عمل UI قائم على المترجم يرسل الحد الأدنى من JavaScript إلى المتصفح، وSvelteKit هو إطار عمل التطبيق المتكامل الخاص به. قدّم Svelte 5 runes، نموذج تفاعلية جديد أكثر وضوحًا وقوة من تفاعلية let السابقة. Cursor، بأساسه المتوافق مع VS Code ومساعده الذكي المدمج، هو محرر ممتاز لعمل Svelte — يفهم ملفات .svelte، ويوفر IntelliSense غنيًا، ويمكنه توليد المكونات من مطالبات باللغة الطبيعية.

يغطي هذا الدليل سير عمل تطوير Svelte الكامل في Cursor: إعداد سلسلة الأدوات، إتقان Svelte 5 runes، بناء تطبيقات SvelteKit بتوجيه قائم على الملفات، إدارة الحالة المشتركة باستخدام stores، واستخدام الذكاء الاصطناعي لتوليد وإعادة هيكلة المكونات.

إعداد بيئة تطوير Svelte في Cursor​

المتطلبات الأساسية​

يتطلب Svelte 5 وSvelteKit إصدار Node.js 18 أو أحدث. تحقق من تثبيتك:

node --version
npm --version

إذا كنت بحاجة إلى إصدار أحدث، استخدم fnm أو nvm:

fnm install 20
fnm use 20
تلميح

تتذكر الطرفية المدمجة في Cursor (Ctrl+`) دليل العمل الحالي، لذا يمكنك هيكلة مشروع وفتح الملفات فورًا دون تبديل السياق.

تثبيت إضافة Svelte​

يعتمد Cursor على إضافة Svelte الرسمية لدعم اللغة. ثبّتها من عرض الإضافات (Ctrl+Shift+X):

  1. ابحث عن "Svelte".
  2. ثبّت الإضافة التي ينشرها فريق Svelte.
  3. أعد تحميل Cursor إذا طُلب منك ذلك.

توفر الإضافة:

  • تمييز بناء الجملة لملفات .svelte.
  • التشخيصات وفحص الأنواع عبر svelte-language-server.
  • التنسيق من خلال Prettier مع حزمة prettier-plugin-svelte.
  • IntelliSense لاستيراد المكونات وprops.
warning

إذا استخدمت سابقًا إضافة Svelte مجتمعية، عطّلها قبل تثبيت الرسمية. الإضافات المتعارضة تسبب تشخيصات مكررة وتنسيقًا غير موثوق.

هيكلة مشروع SvelteKit​

استخدم CLI الرسمي sv لإنشاء تطبيق SvelteKit جديد:

npx sv create my-svelte-app

توفر المطالبة التفاعلية إعدادات مسبقة:

? Which template would you like? SvelteKit minimal
? Add type checking with TypeScript? Yes, using TypeScript syntax
? Add ESLint for code linting? Yes
? Add Prettier for code formatting? Yes
? Add Playwright for browser testing? No
? Add Vitest for unit testing? Yes

افتح المشروع في Cursor:

cd my-svelte-app
cursor .
npm install
npm run dev

يبدأ خادم التطوير على http://localhost:5173.

تلميح

يمكنك إضافة ميزات لاحقًا دون إعادة إنشاء المشروع. أمر sv add يثبّت تكاملات مثل Tailwind CSS أو Storybook أو Drizzle على تطبيق موجود:

npx sv add tailwindcss
npx sv add storybook

إتقان Svelte 5 Runes​

ما هي Runes؟​

Runes هي دوال خاصة يتعرف عليها المترجم تم تقديمها في Svelte 5. إنها تستبدل التفاعلية الضمنية لـ Svelte 4 بنموذج صريح وقابل للتنبؤ. runes الأساسية هي:

  • $state — يعلن عن حالة تفاعلية.
  • $derived — يعلن عن قيمة محسوبة من حالة تفاعلية أخرى.
  • $effect — يشغل آثارًا جانبية عند تغير التبعيات.
  • $props — يعلن عن props المكوّن.
  • $bindable — يحدد prop كقابلة للربط ثنائي الاتجاه.

الإعلان عن الحالة باستخدام $state​

<script lang="ts">
let count = $state(0);
let user = $state({ name: 'Ada', age: 36 });

function increment() {
count++;
}
</script>

<button onclick={increment}>
Count: {count}
</button>
<p>{user.name} is {user.age}</p>
تلميح

في Svelte 5، تستخدم معالجات الأحداث سمة onclick (أحرف صغيرة، بدون بادئة on:) بدلًا من on:click القديمة. الصياغة الجديدة أقرب إلى سمات DOM القياسية وتعمل بشكل أفضل مع TypeScript.

القيم المشتقة باستخدام $derived​

<script lang="ts">
let price = $state(100);
let quantity = $state(2);
let total = $derived(price * quantity);
let formatted = $derived(
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(total)
);
</script>

<p>Price: $ {price}</p>
<p>Quantity: {quantity}</p>
<p>Total: {formatted}</p>

الآثار الجانبية باستخدام $effect​

<script lang="ts">
let theme = $state<'light' | 'dark'>('light');

$effect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
});
</script>

<button onclick={() => (theme = theme === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
warning

يعمل $effect بعد تحديث DOM. لا تستخدمه لحساب قيم تؤثر على العرض — استخدم $derived بدلًا من ذلك. Effects للمزامنة مع الأنظمة الخارجية (APIs، localStorage، مكتبات الطرف الثالث).

props المكوّن باستخدام $props​

<script lang="ts">
interface Props {
title: string;
count?: number;
onIncrement?: (value: number) => void;
}

let { title, count = 0, onIncrement }: Props = $props();

function handleClick() {
count++;
onIncrement?.(count);
}
</script>

<h2>{title}</h2>
<button onclick={handleClick}>Clicked {count} times</button>

الربط ثنائي الاتجاه باستخدام $bindable​

<!-- TextInput.svelte -->
<script lang="ts">
interface Props {
value?: string;
}
let { value = $bindable('') }: Props = $props();
</script>

<input bind:value />

الاستخدام في مكوّن أب:

<script lang="ts">
import TextInput from './TextInput.svelte';
let name = $state('');
</script>

<TextInput bind:value={name} />
<p>Hello, {name}!</p>
تلميح

$bindable هو بديل Svelte 5 لنمط export let + bind:value. يجعل عقد الربط ثنائي الاتجاه صريحًا في توقيع نوع المكوّن الفرعي.

بناء تطبيقات SvelteKit​

التوجيه القائم على الملفات​

يستخدم SvelteKit التوجيه القائم على الملفات داخل دليل src/routes. كل ملف +page.svelte يصبح صفحة، وملفات +layout.svelte تغلف الصفحات المتداخلة.

src/routes/
├── +layout.svelte
├── +page.svelte → /
├── about/
│ └── +page.svelte → /about
├── blog/
│ ├── +page.svelte → /blog
│ └── [slug]/
│ └── +page.svelte → /blog/:slug
└── +layout.svelte

تحميل البيانات باستخدام +page.server.ts​

// src/routes/blog/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

interface Post {
slug: string;
title: string;
body: string;
}

export const load: PageServerLoad = async ({ params }) => {
const response = await fetch(`https://api.example.com/posts/${params.slug}`);
if (!response.ok) {
throw error(404, 'Post not found');
}
const post: Post = await response.json();
return { post };
};

استهلاك البيانات في الصفحة:

<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>

<article>
<h1>{data.post.title}</h1>
<p>{data.post.body}</p>
</article>
تلميح

وحدة $types يتم توليدها تلقائيًا بواسطة SvelteKit. إنها تمنحك دوال load وprops data مكتوبة بالكامل. يقرأ IntelliSense في Cursor هذه الأنواع تلقائيًا، لذا تحصل على الإكمال التلقائي لـ data.post.title.

إجراءات النموذج​

يتعامل SvelteKit مع عمليات إرسال النموذج مع إجراءات محددة في +page.server.ts:

// src/routes/login/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';

export const actions: Actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();
const email = formData.get('email');
const password = formData.get('password');

if (!email || !password) {
return fail(400, { message: 'Missing credentials' });
}

const token = await authenticate(String(email), String(password));
if (!token) {
return fail(401, { message: 'Invalid credentials' });
}

cookies.set('session', token, { path: '/' });
throw redirect(303, '/dashboard');
},
};

الصفحة المقابلة تستخدم إجراء enhance للتحسين التدريجي:

<!-- src/routes/login/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageData } from './$types';
let { form }: { form: PageData } = $props();
</script>

<form method="POST" use:enhance>
<label>Email <input name="email" type="email" /></label>
<label>Password <input name="password" type="password" /></label>
{#if form?.message}
<p class="error">{form.message}</p>
{/if}
<button type="submit">Log in</button>
</form>
warning

ملفات +page.server.ts من جهة الخادم تعمل على الخادم فقط. لا تستورد أبدًا واجهات API خاصة بالمتصفح (مثل localStorage) فيها، ولا تضع أسرارًا في +page.ts (الـ load الشامل)، الذي يمكن أن يعمل أيضًا في المتصفح.

التخطيطات ومجموعات التخطيط​

<!-- src/routes/+layout.svelte -->
<script lang="ts">
import type { Snippet } from 'svelte';
import Nav from '../components/Nav.svelte';

let { children }: { children: Snippet } = $props();
</script>

<div class="app">
<Nav />
<main>{@render children()}</main>
</div>

في Svelte 5، يحل snippet children محل صياغة <slot /> المهملة.

إدارة الحالة المشتركة باستخدام Stores​

أساسيات Svelte Stores​

حتى مع runes، تظل stores في Svelte مفيدة للحالة بين المكونات. store هو أي كائن لديه طريقة subscribe.

// lib/stores/user.ts
import { writable } from 'svelte/store';

interface User {
id: string;
name: string;
}

export const currentUser = writable<User | null>(null);

استهلاك store بالبادئة $:

<script lang="ts">
import { currentUser } from '$lib/stores/user';
</script>

{#if $currentUser}
<p>Welcome, {$currentUser.name}</p>
{:else}
<p>Please log in.</p>
{/if}

Stores القابلة للقراءة والمشتقة​

import { readable, derived } from 'svelte/store';

export const time = readable(new Date(), (set) => {
const interval = setInterval(() => set(new Date()), 1000);
return () => clearInterval(interval);
});

export const formattedTime = derived(time, ($time) =>
$time.toLocaleTimeString('en-US')
);

الجمع بين Runes وStores​

يمكنك استخدام stores داخل $effect لجسر الكود القديم مع runes:

<script lang="ts">
import { currentUser } from '$lib/stores/user';
import { get } from 'svelte/store';

let userSnapshot = $state(get(currentUser));

$effect(() => {
const unsubscribe = currentUser.subscribe((value) => {
userSnapshot = value;
});
return unsubscribe;
});
</script>
تلميح

للمشاريع الجديدة، يُفضل نمط حالة مشتركة قائم على runes باستخدام فئة بحقول $state وSvelte context API. هذا يتجنب boilerplate الخاص بـ store مع الحفاظ على التفاعلية:

// lib/state/counter.svelte.ts
export class CounterState {
count = $state(0);
increment() {
this.count++;
}
}

توليد مكونات Svelte بمساعدة الذكاء الاصطناعي​

توليد المكونات من المطالبات​

يتفوق ذكاء Cursor في إنتاج مكونات Svelte 5 بـ runes. افتح دردشة (Ctrl+Shift+P → "Cursor: New Chat") أو استخدم التعديلات المضمّنة (Ctrl+K) وصف ما تحتاجه:

Create a Svelte 5 component (script lang="ts") for an accessible accordion.
It should accept an items prop: array of { id, title, content }. Use runes
for state (which panel is open). Include keyboard navigation (arrow keys)
and scoped styles.

يولّد Cursor ملف .svelte كامل. راجع المخرجات وكرّر — اطلب إضافة prop allowMultiple أو انتقالات رسوم متحركة.

الترحيل من Svelte 4 إلى Svelte 5​

إذا كنت تحافظ على قاعدة شفرات قديمة، يمكن لـ Cursor ترحيل المكونات إلى صياغة runes. حدد الملف، اضغط Ctrl+K، وأدخل:

Migrate this Svelte 4 component to Svelte 5 runes syntax. Replace `let`
reactivity with $state, `$:` with $derived/$effect, `export let` with $props,
and `createEventDispatcher` with callback props. Preserve all behavior.
warning

ترحيل runes يغير توقيت التفاعلية بطرق خفية. بعد إعادة الهيكلة، شغّل مجموعة الاختبارات وتحقق يدويًا من أي مكوّن يستخدم خطافات دورة الحياة أو المراقبين، لأن $effect يعمل في ظروف مختلفة عن afterUpdate القديم.

توليد اختبارات من الطرف إلى الطرف​

اقران Playwright مع اختبارات يولّدها الذكاء الاصطناعي:

// tests/accordion.spec.ts
import { test, expect } from '@playwright/test';

test('accordion expands on click', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Section 1' }).click();
await expect(page.getByText('Content of section 1')).toBeVisible();
});

اطلب من Cursor: "Generate Playwright tests for Accordion.svelte covering expand/collapse and keyboard navigation."

تكوين Cursor لمشاريع Svelte​

إعدادات مساحة العمل​

أنشئ .vscode/settings.json لتوحيد التنسيق عبر فريقك:

{
"editor.formatOnSave": true,
"editor.defaultFormatter": "svelte.svelte-vscode",
"svelte.plugin.svelte.format.config.singleQuote": true,
"svelte.plugin.svelte.format.config.semi": true,
"svelte.plugin.svelte.note-new-transformation": false,
"typescript.tsdk": "node_modules/typescript/lib"
}

Prettier مع إضافة Svelte​

npm install -D prettier prettier-plugin-svelte

.prettierrc:

{
"singleQuote": true,
"semi": true,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": { "parser": "svelte" }
}
]
}
تلميح

يقوم Prettier بتنسيق كتل <script> و<style>، لكنه يترك ترميز قالب Svelte دون مساسة عمدًا لتجنب كسر التوجيهات. لتنسيق القالب، اعتمد على المنسّق المدمج لإضافة Svelte.

تكوين ESLint​

// eslint.config.js
import js from '@eslint/js';
import svelte from 'eslint-plugin-svelte';
import tsParser from 'typescript-eslint';

export default [
js.configs.recommended,
...svelte.configs['flat/recommended'],
{
languageOptions: {
parserOptions: {
extraFileExtensions: ['.svelte'],
},
},
},
];

التصحيح والأداء​

Svelte DevTools​

ثبّت إضافة متصفح Svelte DevTools لفحص تدرج المكونات والحالة. بينما يستضيف Cursor المحرر، تعرض DevTools في المتصفح قيم وقت التشغيل الحرجة لتصحيح runes.

خرائط المصدر في الإنتاج​

يولّد SvelteKit خرائط مصدر افتراضيًا. أكد في svelte.config.js:

import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
},
};

export default config;
warning

عند النشر على منصات serverless، تحقق من أن adapter يتعامل مع خرائط المصدر بشكل صحيح. تتصرف adapters adapter-node وadapter-vercel بشكل مختلف — راجع وثائق adapter قبل إرسال خرائط المصدر إلى الإنتاج.

تحليل Bundle​

أضف rollup-plugin-visualizer لفحص حجم bundle:

// vite.config.ts
import { sveltekit } from '@sveltejs/vite-plugin-sveltekit';
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
plugins: [
sveltekit(),
visualizer({
filename: 'stats.html',
open: true,
gzipSize: true,
}),
],
});

أفضل الممارسات لـ Svelte في Cursor​

هيكل المشروع​

src/
├── lib/
│ ├── components/
│ │ ├── Accordion.svelte
│ │ └── TextInput.svelte
│ ├── stores/
│ │ └── user.ts
│ ├── state/
│ │ └── counter.svelte.ts
│ └── utils/
├── routes/
│ ├── +layout.svelte
│ └── +page.svelte
└── app.html

اصطلاحات تسمية ملفات Runes​

يقدم Svelte 5 امتداد .svelte.ts (و.svelte.js) للوحدات التي تستخدم runes خارج المكونات. سمّ ملفات الحالة المشتركة وفقًا لذلك:

// src/lib/state/cart.svelte.ts
export class CartState {
items = $state<{ id: number; qty: number }[]>([]);

get total() {
return this.items.reduce((sum, i) => sum + i.qty, 0);
}

add(id: number, qty = 1) {
this.items.push({ id, qty });
}
}
تلميح

اللاحقة .svelte.ts تخبر مترجم Svelte بمعالجة runes داخل وحدات TypeScript العادية. بدونها، يُعامل $state كدالة غير معرفة ويفشل البناء.

إمكانية الوصول افتراضيًا​

مترجم Svelte يحذر من alt المفقود وaria-* وارتباطات التسمية. عامل هذه التحذيرات كأخطاء في CI:

// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const config = {
preprocess: vitePreprocess(),
kit: { adapter: adapter() },
compilerOptions: {
accessors: false,
},
};

export default config;

الأخطاء الشائعة وكيفية تجنبها​

استخدام صياغة الأحداث القديمة​

يُهمّش Svelte 5 صياغة on:click لصالح onclick. خلط الاثنين يعمل خلال الانتقال لكن ينتج تحذيرات. وحّد الصياغة الجديدة:

<!-- Svelte 4 (مُهمَش) -->
<button on:click={handler}>Click</button>

<!-- Svelte 5 (موصى به) -->
<button onclick={handler}>Click</button>

تعديل Props​

Props للقراءة فقط افتراضيًا. استخدم $bindable للربط ثنائي الاتجاه أو أصدر callbacks:

<script lang="ts">
interface Props {
value: string;
onChange?: (v: string) => void;
}
let { value, onChange }: Props = $props();

function handleInput(e: Event) {
const target = e.target as HTMLInputElement;
onChange?.(target.value);
}
</script>

<input value={value} oninput={handleInput} />

الإفراط في استخدام $effect​

يجب أن تتزامن Effects مع الأنظمة الخارجية، وليس حساب قيم العرض. إذا وجدت نفسك تكتب $effect لتحديث متغير مستخدم في القالب، بدّل إلى $derived:

<script lang="ts">
let a = $state(1);
let b = $state(2);

// خطأ — effect لقيمة مشتقة
// let sum = $state(0);
// $effect(() => { sum = a + b; });

// صحيح — مشتق
let sum = $derived(a + b);
</script>
warning

Effects التي تعتمد على نفسها يمكن أن تنشئ حلقات لا نهائية. يكتشف مترجم Svelte بعض الحالات، لكن تحقق دائمًا من أن تبعيات effect الخاصة بك محدودة ومنتهية.

الخلاصة​

Cursor هو بيئة قوية لتطوير Svelte وSvelteKit. إضافة Svelte الرسمية توفر تأليفًا آمنًا للأنواع وتنسيقًا، وSvelte 5 runes تجلب تفاعلية صريحة وقابلة للتنبؤ، وتوجيه SvelteKit القائم على الملفات مع دوال load مكتوبة يسرّع العمل المتكامل. من خلال اقتران هذه الأدوات مع مساعد Cursor الذكي لتوليد المكونات وإعادة الهيكلة وهيكلة الاختبارات، يمكنك بناء تطبيقات Svelte حديثة بكفاءة. ابدأ بـ sv create، وثبّت تكوين Prettier وESLint، ودع runes والذكاء الاصطناعي يتعاملان مع الهيكلة المتكررة بينما تركز على تسليم تجربة مستخدم مصقولة.