---
url: https://nsh.dev/ar/docs/dev-server-and-debugging
description: "شغّل تطبيقات Rust وTSX باستخدام nsh run وnsh dev، وصِل المحاكيات والمحاكيات الافتراضية والأجهزة الحقيقية، واستفد من إعادة البناء واستبدال الوحدات الفوري (HMR) والسجلات وطبقة عرض الأخطاء وتتبعات المكدس المرتبطة بالمصدر."
---

# خادم التطوير وتصحيح الأخطاء

أثناء التطوير، يبني NSH تطبيقك عند كل حفظ. يُبنى تطبيق Rust (يسمّي `nsh.json` الـ `"root"` الخاص به) في مشغّل خاص به يبدأه `nsh run`؛ وأي تغيير في الـ crate الخاص به يعيد بناءه ويعيد تشغيل التطبيق. أما تطبيق TSX فيُدفع إلى كل مضيف متصل، ويتحدّث في مكانه. يتولى ذلك أمران:

- `nsh run` يبني التطبيق **ويشغّله**: نافذة Mac Catalyst أو AppKit، أو نافذة GTK على Linux، أو نافذة Win32 على Windows، أو محاكي iOS، أو جهاز Android أو محاكيه، أو صفحة في المتصفح. وفي حالة TSX يبدأ أيضًا خادم التطوير الذي يتصل به مضيف التطوير.
- `nsh dev --server-only` يبدأ خادم التطوير لتطبيقات TSX، للمضيفات التي تشغّلها بنفسك: تطبيقك الخاص مع حزمة SDK للتطوير، أو جهاز على الشبكة، أو عدة مضيفات في وقت واحد.

```sh
# A Rust app, from its directory
nsh run                             # AppKit window (macOS); GTK window (Linux); Win32 window (Windows)
nsh run --target ios                # iOS simulator
nsh run --target android            # Android device or emulator

# A TSX app
nsh run App.tsx                     # AppKit window (macOS); GTK window (Linux); Win32 window (Windows)
nsh run App.tsx --target macos      # AppKit window (macOS)
nsh run App.tsx --target ios        # iOS simulator
nsh run App.tsx --target android    # Android device or emulator
nsh dev App.tsx --server-only       # server only
```

`nsh dev` يفتح أيضًا المضيف الأصلي افتراضيًا؛ وتستخدم المضيفات على الجوال `--target ios` أو `--target android`.

## `nsh run`

| الخيار | الوصف |
|---|---|
| `--target <catalyst \| ios \| android \| macos \| linux \| windows \| gpui \| web>` | مكان التشغيل. الافتراضي `macos` على macOS (AppKit)، و`linux` على Linux، و`windows` على Windows. يعمل تطبيق Rust على جميعها عدا `gpui`، الذي يضمّن جذره في تطبيق GPUI ([GPUI](https://nsh.dev/translations/ar/docs/gpui.md)). |
| `--device <name or UDID>` | محاكي iOS المراد استخدامه. الافتراضي: محاكٍ قيد التشغيل، وإلا فأحدث iPhone (يُشغَّل لك). |
| `--port <port>` | منفذ خادم التطوير. الافتراضي 8790؛ ويُستخدم أول منفذ حر تالٍ إذا كان مشغولًا. |
| `--release` | تقديم إصدارات release من TSX (مصغّرة، بلا معلومات تصحيح، ولا تحديثات فورية). |
| `--trace` | تسجيل زمن كل commit على المضيف (تطبيقات TSX). |

ما يفعله كل هدف:

- **`catalyst`** (macOS) يشغّل مضيف التطوير كتطبيق Mac. إيقاف `nsh run` (Ctrl-C، أو `kill`) يغلقه، وإغلاق النافذة ينهي `nsh run`.
- **`macos`** (macOS) يشغّل مضيف التطوير AppKit، وهو تطبيق سطح مكتب في نافذة بحجم 1200×800، بالطريقة نفسها؛ ويعيد Cmd+R تحميل أحدث بناء. يتطلب macOS 14 أو أحدث ([macOS](https://nsh.dev/translations/ar/docs/macos.md)).
- **`linux`** (Linux) يشغّل مضيف التطوير GTK في نافذة بحجم 390×844، بالطريقة نفسها: إيقاف `nsh run` يغلقه، وإغلاق النافذة ينهي `nsh run`. يتطلب GTK 4.14 أو أحدث ([Linux](https://nsh.dev/translations/ar/docs/linux.md)).
- **`windows`** (Windows) يشغّل مضيف التطوير Win32 في نافذة بحجم 1200×800، بالطريقة نفسها؛ ولا يبقى المضيف حيًا بعد `nsh run` حتى لو أُغلقت وحدة التحكم. يتطلب Windows 10 الإصدار 1809 أو أحدث ([Windows](https://nsh.dev/translations/ar/docs/windows.md)).
- **`ios`** يثبّت مضيف التطوير في المحاكي، ويشغّله موجّهًا إلى خادم التطوير، ويواصل المراقبة. يشارك المحاكي شبكة Mac، ولذلك يصل المضيف إلى الخادم عبر `127.0.0.1`. إيقاف `nsh run` (Ctrl-C) يغلق المضيف، وإلا انضم إلى الجلسة التالية. يتطلب Xcode.
- **`android`** يثبّت APK مضيف التطوير (مرة أخرى فقط عند تغيّر وقت التشغيل)، ويشغّل `adb reverse tcp:<port> tcp:<port>` ليصل `127.0.0.1` الخاص بالجهاز إلى الخادم، ثم يبدأ المضيف. يعمل مع محاكٍ أو جهاز USB. يتطلب Android SDK (`ANDROID_HOME`). وعندما ينتهي `nsh run` (بما في ذلك عبر Ctrl-C) يوقف المضيف ويزيل إعادة توجيه المنفذ تلك، فلا ينضم المضيف إلى الجلسة التالية.

وعندما ينتهي `nsh run`، أيًا كانت طريقة انتهائه، يوقف المضيف الذي بدأه: يُنهي تطبيق Catalyst أو يغلق مضيف macOS أو Linux أو Windows، أو ينهي مضيف التطوير في المحاكي، أو يفرض إيقافه على الجهاز.

في تطبيق Rust، يشغّل كل هدف المشغّل الخاص بالتطبيق، المبني مع الـ crate الخاص به: على أجهزة سطح المكتب بناء debug في مكانه مع إخراجه في الطرفية؛ وعلى iOS وAndroid حزمة منه تُثبَّت. أما في TSX فالمضيفات عامة: تشغّل أي تطبيق NSH يرسله الخادم، فلا شيء يُبنى لكل تطبيق (والتطبيق الذي له crate خاص به تُبنى له مضيفات معه).

## `nsh dev`

```sh
nsh dev App.tsx
```

```text
✓ built App.tsx (3.2 KB js, 9.8 KB bytecode, 21ms)
→ dev server ws://127.0.0.1:8790/ws
  devices on this network: ws://192.168.1.20:8790/ws
```

| الخيار | الوصف |
|---|---|
| `--port <port>` | منفذ خادم التطوير. الافتراضي 8790 (وأول منفذ حر تالٍ إذا كان مشغولًا). |
| `--release` | تقديم إصدارات release. |

صِل المضيفات بالعنوان المطبوع:

- **تطبيق iOS الخاص بك**، المربوط مع `NativeShellDev.xcframework`:
  `try app.connectDevServer(URL(string: "ws://192.168.1.20:8790/ws")!)`. راجع [التضمين في تطبيقات iOS](https://nsh.dev/translations/ar/docs/ios.md#development).
- **تطبيق Android الخاص بك**، مع `nsh-dev.aar`: `nsh.connectDevServer("ws://192.168.1.20:8790/ws")`، أو
  `adb reverse tcp:8790 tcp:8790` و`ws://127.0.0.1:8790/ws`. راجع [التضمين في تطبيقات Android](https://nsh.dev/translations/ar/docs/android.md#development).

يمكن توصيل أي عدد من المضيفات في وقت واحد؛ ويذهب كل بناء إليها جميعًا. والمضيف الذي يتصل لاحقًا يتلقى البناء الحالي فورًا. أما المضيف الذي لا يستطيع تشغيل التطبيق لأنه يفتقر إلى مجموعة UI يستوردها التطبيق (مضيف تطوير مبني لتطبيق آخر) فلا يتلقى بناءً: يعرض السبب، وتطبع الطرفية سطر `warn` مع الأمر الذي يثبّت مضيف التطبيق.

> [!NOTE]
> يحتاج جهاز على الشبكة المحلية إلى الوصول إلى حاسوبك عبر منفذ خادم التطوير: الشبكة نفسها، ودون جدار حماية بينهما.
> تُقدَّم الأصول (الصور والخطوط) عبر HTTP العادي من المنفذ نفسه.

<a id="rust-edits"></a>

## تعديلات Rust

Rust لغة مُصرَّفة: أي تغيير في crate التطبيق، أو في crate يعتمد عليه بمسار، يعيد بناء التطبيق، ويعيد `nsh run` تشغيله بالشيفرة الجديدة. وتبدأ حالة التطبيق من جديد؛ فاحتفظ بما يجب أن يبقى بعد إعادة التشغيل في [التخزين](https://nsh.dev/translations/ar/modules/nsh.md#storage). يُطبع خطأ البناء في الطرفية، ويعمل التطبيق مجددًا عند أول حفظ يُصرَّف بنجاح. وفي تطبيق يجمع Rust وTSX، تبقى تعديلات TSX تحديثات فورية (أدناه) وتعيد تعديلات Rust تشغيل التطبيق.

<a id="hot-module-replacement"></a>

## استبدال الوحدات الفوري (Hot module replacement)

تحتفظ إصدارات التطوير من TSX بوحدة واحدة لكل ملف مصدر. عند الحفظ، يعيد NSH البناء، ويقارن الوحدات بآخر بناء سليم، ويرسل **رقعة** تضم الوحدات التي تغيّرت فقط. يطبّقها المضيف في مكانها في أقل بكثير من ميلي ثانية؛ ويستغرق الأمر من حفظ الملف إلى رؤية التغيير نحو 120 ms.

ما يحدث لتطبيقك:

- **تُعاد معالجة المكوّنات التي عدّلتها فقط.** كل مكوّن من المستوى الأعلى (دالة تبدأ بحرف كبير وتُرجع JSX) يُغلَّف بوكيل ثابت. وعندما تتغير شيفرته (أو بقية شيفرة وحدته التي قد يستخدمها)، تُعاد معالجة نسخه بالشيفرة الجديدة؛ أما المكوّنات التي لم تتغير شيفرتها فتحتفظ بنسخها، ومعها إشاراتها (signals) وحالتها. عدّل نمط زر وستبقى الشاشة حيث كانت، بالنص المُدخل نفسه وموضع التمرير نفسه.
- **تعمل الوحدات التي تغيّرت مرة أخرى**، وكذلك الوحدات التي تستورد وحدة متغيّرة ولا تستطيع قبول التحديث بنفسها، وصولًا إلى نقطة الدخول. ولذلك تعمل شيفرة مستوى الوحدة مرة أخرى، باستثناء ما يحتفظ بحالته:
- **تحتفظ الإشارات ومكدسات التنقل من المستوى الأعلى بحالتها**: يحتفظ `const` الذي أنشأه `signal(…)` أو `createNavStack(…)` في أعلى الوحدة بقيمته (والمكدس بصفحاته) عبر التعديلات، ما دام مُهيّئه نفسه لم يتغير؛ غيّر المُهيّئ (`signal(0)` إلى `signal(10)`) لتبدأ من جديد.
- **تحتفظ كائنات `context()` من المستوى الأعلى بهويتها**، وتأخذ القيمة الافتراضية الجديدة، فيبقى المزوّدون والمستهلكون في وحدات مختلفة متطابقين.
- **تعديل نقطة الدخول يُبقي التطبيق مركّبًا.** إذا عمل `mount(App)` (أو `mount(() => <App />)`) مرة أخرى بالمكوّن نفسه، فإنه يحتفظ بما ركّبه: تُعاد معالجة المكوّنات التي عدّلتها فقط. وإذا عدّلت `App` نفسه أُعيدت معالجته مع ما يعرضه (وتبدأ حالة هذه المكوّنات من جديد)، لكن المتنقّل (navigator) المُنشأ في أعلى الوحدة يحتفظ بمساراته، فتبقى الشاشة المدفوعة مدفوعة. أما استدعاء `mount` تغيّر (مكوّن آخر، أو props) فيركّب التطبيق من جديد.
- **المكوّن الذي يرمي استثناءً** يبلّغ عن الخطأ ولا يعرض شيئًا حتى تصلحه؛ ويواصل بقية التطبيق العمل. ويتعافى عند أول حفظ سليم.

**يعيد المضيف تحميل التطبيق بالكامل** بدل الترقيع عندما:

- يستورد التغيير وحدة NSH لم يستوردها التطبيق العامل بعد (مثل أول `import … from "nsh"`)؛
- يتغير `capabilities` الخاص بالتطبيق في `nsh.json`؛
- لا يشغّل المضيف البناء الذي تستند إليه الرقعة (اتصل للتو، أو فشل تحميل سابق)؛
- تتعذّر إمكانية تطبيق الرقعة.

> [!TIP]
> الحالة التي يحتفظ بها المكوّن في إشاراته الخاصة تُعاد تهيئتها عندما يُعدَّل هذا المكوّن، أو مكوّن يعرضه. احتفظ بالحالة التي يجب أن تبقى بعد التعديلات في إشارات من المستوى الأعلى (أو في وحدة store)، وأنشئ المتنقّلات في أعلى الوحدة.

يقدّم `--release` إصدارات release دون تحديثات فورية: كل حفظ يعيد تحميل التطبيق. استخدمه لفحص البناء المصغّر. وأي شيء يُحمَّل من خادم تطوير، سواء `--release` أم لا، يتجاوز تحصين وقت التشغيل (ويبقى `eval` متاحًا)؛ راجع [العزل والصلاحيات](https://nsh.dev/translations/ar/docs/sandbox-and-capabilities.md#hardening).

<a id="logs"></a>

## السجلات

يسجّل تطبيق Rust باستخدام crate ‏`log` (`log::info!`، `log::warn!`، `log::error!`): يعرض `nsh run` الأسطر في طرفيته، مع إخراج التطبيق، على أجهزة سطح المكتب وفي محاكي iOS.

في TSX، تذهب `console.log` و`console.info` و`console.debug` و`console.warn` و`console.error` إلى السجل الأصلي مع الهدف `js`. وما دام مضيف متصلًا، تُرسل أسطر سجله إلى الطرفية التي تشغّل `nsh run` / `nsh dev`: أسطر `console.log` كما هي (مسبوقة باسم الجهاز عند اتصال عدة أجهزة)، والتحذيرات والأخطاء بعد اسم الجهاز:

```text
• iPhone 16 Pro connected, build 1
loading more after 1000
warn iPhone 16 Pro: deprecated option
error iPhone 16 Pro: uncaught exception: TypeError: cannot read property 'id' of undefined
          at Row (demos/Photos.tsx:18:22)
```

يُنسّق `console.log` القيم كما تفعل وحدة تحكم المتصفح: السلاسل النصية كما هي، والأخطاء مع مكدسها، والدوال باسمها، والكائنات والمصفوفات بصيغة JSON. وعندما لا يكون المضيف متصلًا، تذهب السجلات إلى سجل المنصة (وحدة تحكم Xcode / السجل الموحّد على iOS، و`adb logcat` على Android).

ويُسجَّل الوعد (promise) المرفوض الذي ما يزال بلا معالج بعد انتهاء العمل الذي رفضه (أي بعد تشغيل microtasks الخاصة به) على أنه خطأ أيضًا، كما يبلّغ عنه المتصفح: دالة `async` ترمي استثناءً بينما لا ينتظرها أحد، أو `Promise.reject(…)` لا يلتقطه أحد، أو استدعاء إلى Rust يفشل دون أن ينتظره شيء. يذهب السطر إلى حيث يذهب `console.error` (الطرفية، أو سجل المنصة، أو وحدة تحكم المتصفح على الويب)، ومكدسه يشير إلى مصادرك:

```text
error iPhone 16 Pro: unhandled rejection: Error: HTTP 404
          at loadProfile (App.tsx:12:11)
```

المعالج الذي يُرفق لاحقًا في الدورة نفسها (`const p = load(); …; p.catch(…)`) يُحتسب معالجةً للرفض. استخدم await مع الوعد أو أرفق به `.catch` حيث يهمّ الفشل.

أما بقية الطرفية فسطر واحد لكل خطوة (`✓` اكتمل، `→` مكان الاتصال، `•` ما فعله جهاز)؛ ولا يُطبع إخراج الأدوات التي يشغّلها NSH (cargo والمضيفات نفسها) إلا عند فشل إحداها. ويطبع `nsh run -v` (أو `NSH_VERBOSE=1`) كل شيء: كل سطر سجل مع مستواه ومصدره، وإخراج cargo والمضيفات نفسها.

ويضيف `--trace` كلفة كل commit:

```text
perf commit: 10 ops, 151µs (list js 2µs), 8 mount items, 27 nodes
```

راجع [الاختبار](https://nsh.dev/translations/ar/docs/testing-and-trace.md) لمعرفة ما تعنيه هذه الأرقام.

<a id="the-error-overlay"></a>

## شاشة الأخطاء

تُظهر هذه الأخطاء طبقة حمراء فوق التطبيق على المضيف، مع الرسالة والمكدس:

- فشل في البناء (خطأ في الصياغة، أو عنصر غير معروف، أو استيراد مفقود)، مع إطار شيفرة يشير إلى السطر؛
- خطأ أثناء تقييم الحزمة (bundle)؛
- استثناء غير ملتقط في معالج حدث أو مؤقّت أو effect.

اضغط على الطبقة لإغلاقها. وتختفي أيضًا عند أول إعادة تحميل ناجحة. وتُطبع أخطاء البناء في الطرفية كذلك:

```text
error building App.tsx failed
App.tsx

  x <Buton> names nothing in scope
   ,-[3:11]
 2 | function App() {
 3 |   return <Buton />;
   :           ^^^^^
 4 | }
```

يزيل المجمّع (bundler) أنواع TypeScript دون فحصها؛ شغّل [`nsh check`](https://nsh.dev/translations/ar/docs/cli.md#nsh-check) (المصرّف الأصلي لـ TypeScript مع كتابات `tsconfig.json` `nsh init`؛ ولا حاجة إلى Node) أو اعتمد على محررك لأخطاء الأنواع.

في إصدارات release (تطبيق مُضمِّن مع SDK الخاص بـ release، أو تطبيق مُحزَّم)، تُسجَّل الأخطاء وتُمرَّر إلى معالج الأخطاء في التطبيق المضيف (`NshAppView.onError`، `NshView.setErrorListener`) ولكن لا تُعرض فوق التطبيق.

<a id="source-maps-and-stack-traces"></a>

## خرائط المصدر ومكدسات الاستدعاء

ينتج التجميع خريطة مصدر (source map) تركّب كل خطوة (TypeScript ← JSX ← الحزمة)، وتُرسل مع كل بناء. وقبل طباعة مكدس الاستدعاء أو عرضه، يعيد وقت التشغيل كتابة مواضع الحزمة إلى مصادرك، فتشير المكدسات إلى `App.tsx:2:28` لا إلى الحزمة:

```text
error Mac Catalyst: uncaught exception: Error: tap boom
          at <anonymous> (App.tsx:2:58)
```

يكتب `nsh build` الخريطة باسم `bundle.js.map`؛ والتطبيق المُضمِّن الذي يحمّل حزمة من مجلد يقرؤها من جوار الحزمة، وتحملها [الحزم الموقّعة](https://nsh.dev/translations/ar/docs/signed-packages.md) أيضًا، ما لم تُحفظ بجوار الحزمة وتُطبَّق باستخدام `nsh symbolicate` ([أخطاء الحزم المنشورة](https://nsh.dev/translations/ar/docs/signed-packages.md#errors-from-published-packages)).

<a id="debugging-tips"></a>

## نصائح لتصحيح الأخطاء

- **لا شيء يتحدّث بعد تعديل**: تحقق من الطرفية بحثًا عن خطأ في البناء؛ وتعرضه الطبقة الحمراء على المضيف أيضًا.
- **يحتفظ التطبيق بحالة قديمة كنت تتوقع إعادة تهيئتها**: يحتفظ الاستبدال الفوري عمدًا بحالة المكوّنات غير المتغيّرة والإشارات والمتنقّلات من المستوى الأعلى. أعد التشغيل بـ Ctrl-C ثم `nsh run`، أو غيّر مُهيّئ الحالة.
- **يفشل طلب في بناء مُحزَّم أو مُضمَّن فقط**: تحقق من [صلاحيات](https://nsh.dev/translations/ar/docs/sandbox-and-capabilities.md) التطبيق؛ إذ تُسجَّل حالات الرفض مع المفتاح الذي ينبغي إضافته.
- **شيء بطيء**: أعد إنتاجه دون واجهة باستخدام [`nsh test --trace`](https://nsh.dev/translations/ar/docs/testing-and-trace.md)، الذي يطبع كل عملية على عرض (view) أصلي، أو شغّل تطبيق TSX باستخدام `--trace`.
- **أخطاء الأنواع في المحرر**: يُبقي `nsh run` و`nsh dev` الملف `.nsh/types` محدّثًا؛ راجع [بنية المشروع](https://nsh.dev/translations/ar/docs/project-structure.md).

<a id="see-also"></a>

## انظر أيضًا

- [CLI](https://nsh.dev/translations/ar/docs/cli.md)
- [الاختبار](https://nsh.dev/translations/ar/docs/testing-and-trace.md)
- [التضمين في تطبيقات iOS](https://nsh.dev/translations/ar/docs/ios.md)، [التضمين في تطبيقات Android](https://nsh.dev/translations/ar/docs/android.md)
