2.1الوحدات الأصلية (Native Modules) : fs وpath وos
الأساسي في 30 ثانية
- مدمجة دون تثبيت: تأتي fs وpath وos مع Node وتُستورد باسمها — اعتمادات أقل، أخفّ، وأكثر أمانًا.
- وحدة fs غير متزامنة افتراضيًا: استخدام fs.promises مع await لا يحجب حلقة الأحداث — بينما النسخة Sync تحجب الخادم عن الجميع.
- وحدة path للتنقل ووحدة os للمعلومات: تتولى path.join الفاصل الصحيح على كل نظام، وتعرض os المنصة والذاكرة — لا تجمع المسارات يدويًا أبدًا.
حالة مستمرة — ساليم يجرد التقارير في الجزائر
ساليم، مطوّر مبتدئ في الجزائر، عليه تجهيز فريقه بالأدوات: كل أسبوع، تصل عشرات التقارير النصية إلى مجلد مشترك، ويجب جردها (الاسم، الحجم، التاريخ) في ملف جرد. يكتب أول سكربت Node دون تثبيت أي شيء: تقرأ fs الملفات وتكتبها، وتبني path مسارات تعمل على نظامه Windows كما على خادم Linux الخاص بالفريق، وتعرض os المنصة في السجل. يعود هذا المشروع نفسه في 2.2 (الإشارة إلى التقدّم بالأحداث)، و2.3 (التقارير الكبيرة تُعالَج بالتدفق)، و2.4 (تقديم الجرد عبر HTTP).
الخيط الرابط للمستوى 2: يعود مشروع ساليم نفسه في الدرس والمخطط والسكربت وتحدي 48 ساعة — ويستمر في 2.2 و2.3 و2.4.
1. وحدات أصلية دون تثبيت أي شيء
يوفّر Node وحدات أصلية (Native Modules / built-in): مكتبات مدمجة، متاحة دون تثبيت أي شيء، تتيح الوصول إلى قدرات النظام — الملفات، الشبكة، نظام التشغيل. وهذا ما يجعل Node أكثر بكثير من مجرد منفّذ لجافاسكربت. تُستورد باسمها وحده (require('fs')، مع إمكانية البادئة الصريحة node: مثل require('node:fs')). الأكثر استخدامًا هي fs (نظام الملفات): قراءة الملفات وكتابتها وتعديلها وحذفها هي والمجلدات. والفائدة مزدوجة: تغطية الاحتياجات الشائعة دون اعتماد خارجي، وفهم أساس المكتبات (إذ يعتمد Express على الوحدة http). وقد تحقّق ساليم من ذلك: جرده لا يتطلب أي حزمة npm.
2. وحدة fs: غير المتزامن أولًا، وSync شبه أبدًا
نقطة حاسمة: توجد معظم دوال fs بنسختين — غير متزامنة (غير حاجبة، وهي المفضّلة: fs.readFile مع ردّ نداء، أو أفضل fs.promises.readFile مع await) ومتزامنة (حاجبة، بلاحقة Sync مثل fs.readFileSync). على الخادم، نستخدم غير المتزامنة حتى لا نحجب حلقة الأحداث: فاستدعاء readFileSync بطيء واحد يجمّد الخادم لجميع العملاء. ولا تُبرَّر المتزامنة إلا في حالات دقيقة — سكربتات صغيرة ظرفية، وتحميل الإعدادات عند الإقلاع. لذلك يكتب ساليم جرده بـfs.promises مع async/await: حديث، مقروء، وغير حاجب.
3. وحدة path للتنقل ووحدة os للنظام
تعالج الوحدة path المسارات بطريقة محمولة: فالفاصل هو / على macOS وLinux لكن \ على Windows، لذلك لا نركّب أي مسار يدويًا أبدًا. يستخدم path.join(__dirname, 'data', 'fichier.txt') الفاصل الصحيح حسب النظام؛ ويعطي path.resolve() مسارًا مطلقًا، بينما تستخرج path.basename() وpath.extname() وpath.dirname() أجزاءه. وتعرض الوحدة os النظام: os.platform() وos.cpus() (مفيدة للتجميع العنقودي، القسم 5-2) وos.totalmem() / os.freemem() وos.homedir() وos.tmpdir(). وتوجد وحدات أصلية أخرى (crypto وutil وurl…): لا حاجة لحفظها كلها، لكن يجب معرفة وجودها واستشارة التوثيق الرسمي.
🔎 مثال ملموس — 🔎 مثال ملموس — جرد ساليم بشكل غير متزامن
ثلاث وحدات أصلية مجمّعة، كما في سكربت ساليم:
const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
const dossier = path.join(__dirname, 'rapports');
const fichiers = await fs.readdir(dossier);
const inventaire = path.join(dossier, 'inventaire.txt');
await fs.writeFile(inventaire, fichiers.join('\n'));
console.log('Plateforme :', os.platform());
القراءة: لا حزمة مثبّتة، مسارات محمولة، كتابة غير حاجبة.
الدرس: استخدام fs.promises مع await على الخادم، والنسخة Sync محفوظة للسكربتات.
مفردات القسم
- الوحدات الأصلية (Native Modules / built-in)
- مكتبات مدمجة في Node، تُستورد باسمها دون تثبيت: fs وpath وos وhttp وcrypto وevents وstream…
مثال بسيط: يستورد ساليم fs وpath وos دون المساس بـnpm.
ما ليس كذلك: حزمة npm خارجية يجب تثبيتها وتحديثها.
رد فعل: احتياج شائع = التحقق من الوحدة الأصلية قبل التثبيت.
- وحدة fs (نظام الملفات)
- وحدة قراءة/كتابة الملفات والمجلدات، بنسخة غير متزامنة (مفضّلة، fs.promises مع await) أو متزامنة (Sync، حاجبة).
مثال بسيط: يجرد ساليم التقارير بـfs.readdir وfs.writeFile.
ما ليس كذلك: وحدة توجيه ويب (فذلك Express، المستوى 3).
رد فعل: ملفات على الخادم = fs.promises مع await.
- غير المتزامن مقابل المتزامن (fs)
- تترك غير المتزامنة (غير الحاجبة) Node يخدم طلبات أخرى أثناء العملية؛ بينما تحجب المتزامنة (Sync) الخيط الوحيد.
مثال بسيط: يُكتَب جرد ساليم دون تجميد الخادم.
ما ليس كذلك: صياغتان قابلتان للتبادل دون عواقب.
رد فعل: الخادم = غير متزامن؛ وSync = السكربتات والإعدادات عند الإقلاع.
- وحدة path
- وحدة معالجة محمولة للمسارات (join وresolve وbasename وextname وdirname) تتولى فاصل كل نظام.
مثال بسيط: يعمل path.join(__dirname, 'rapports') على Windows الخاص بساليم وعلى Linux الخاص بالفريق.
ما ليس كذلك: تجميع يدوي بشرطات مائلة ثابتة.
رد فعل: مسار يجب بناؤه = path.join.
- وحدة os
- وحدة الوصول إلى معلومات النظام: المنصة والمعالجات (os.cpus) والذاكرة والمجلدات القياسية (homedir وtmpdir).
مثال بسيط: يسجّل ساليم os.platform() وos.freemem() مع جرده.
ما ليس كذلك: وحدة قراءة ملفات.
رد فعل: معلومات الجهاز (التجميع العنقودي، التشخيص) = التفكير في os.
في التطبيق — تطبيق عملي — استخدام الوحدات الأصلية
- اقرأ ملفًا واكتبه بـfs.promises مع async/await (نسخة غير متزامنة وغير حاجبة)؛ وقارن مع fs.readFileSync (متزامنة وحاجبة).
- ابنِ مسارات محمولة بـpath.join(__dirname, 'dossier', 'fichier.txt') بدل تجميعها يدويًا.
- استخرج معلومات من مسار: path.basename وpath.extname وpath.dirname.
- اعرض معلومات النظام بوحدة os: os.platform() وos.cpus().length وos.freemem().
نص جاهز — تبرير الاعتماد على الأصلي فقط أمام المسؤول (30 ثانية)
«لجرد التقارير، لم أثبّت أي شيء: تقرأ fs الملفات وتكتبها بشكل غير متزامن بـfs.promises، وتبني path مسارات تعمل على Windows كما على Linux، وتسجّل os المنصة. صفر اعتماد، أي صفر تحديث يجب تتبّعه — وهي اللبنات نفسها التي بُني عليها Express.»
يُقال مع عرض السكربت يعمل على النظامين؛ والحجة المقنعة هي غياب الاعتماد.
نموذج نسخ ولصق — جرد مجلد بشكل غير متزامن
const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
async function inventaire(nomDossier) {
const dossier = path.join(__dirname, nomDossier);
const fichiers = await fs.readdir(dossier);
const lignes = [];
for (const f of fichiers) {
const chemin = path.join(dossier, f);
const stats = await fs.stat(chemin);
lignes.push(f + ' | ' + stats.size + ' octets | ' + path.extname(f));
}
const sortie = path.join(dossier, 'inventaire.txt');
await fs.writeFile(sortie, lignes.join('\n'));
console.log('Inventaire écrit sur', os.platform(), ':', sortie);
}
inventaire('rapports').catch(console.error);
سكربت شخصي سريع
أداة صغيرة ظرفية (إعادة تسمية ملفات أو فرزها): النسخة Sync مقبولة هنا، فالسكربت ينتهي فورًا.
فريق مختلط Windows وLinux
يحظر ساليم الشرطات المائلة الثابتة: كل مسار يمرّ عبر path.join ليعمل في كل مكان دون تعديل.
خادم في الإنتاج
استخدام fs.promises مع await في كل مكان: لا استدعاء Sync على مسار الطلبات، وتبقى حلقة الأحداث حرّة.
تشخيص الجهاز
قبل النشر، يضبط os.cpus().length وos.freemem() القياس (انظر التجميع العنقودي، القسم 5-2).
✓ ممارسات جيدة
- استورد الوحدات الأصلية باسمها (البادئة node: ممكنة) دون تثبيت أي شيء.
- استخدم fs.promises مع await على الخادم؛ واحفظ Sync للسكربتات والإعدادات.
- ابنِ كل مسار بـpath.join، ولا تجمّع أبدًا بالتسلسل.
✗ أخطاء شائعة
- استخدام النسخ Sync على خادم (حجب عن جميع العملاء).
- تجميع المسارات يدويًا (خلل تنقل بين Windows وLinux).
- تثبيت حزمة npm لما تفعله وحدة أصلية أصلًا.
النقاط الأساسية
- الوحدات الأصلية (Native Modules) مثل fs وpath وos وhttp وcrypto… مدمجة في Node (دون تثبيت) وتتيح الوصول إلى النظام؛ وتُستورد باسمها.
- وحدة fs (الملفات): فضّل غير المتزامن (fs.promises مع await، غير حاجب) على الخادم؛ فالمتزامن (Sync) يحجب حلقة الأحداث (محفوظ للسكربتات/الإعدادات).
- وحدة path: معالجة المسارات بطريقة محمولة (يتولى path.join الفاصل الصحيح / مقابل \) — لا تجمّع المسارات يدويًا أبدًا.
- وحدة os: معلومات النظام (المنصة، وos.cpus للتجميع العنقودي، والذاكرة). والوحدات الأصلية هي أساس المكتبات (يعتمد Express على http).
تحدي 48 ساعة — طبّق غدًا
اكتب سكربت الجرد من النموذج أعلاه على مجلد حقيقي (تقاريرك، وصادراتك): اجرد الاسم والحجم والامتداد بشكل غير متزامن، بمسارات path.join. واحظر أي استدعاء Sync من السكربت.
- أستطيع ذكر الوحدات الأصلية 3 ودور كل منها في جملة واحدة.
- أعرف كتابة سكربت fs.promises مع await دون Sync.
- أنتجت جردًا حقيقيًا بمسارات محمولة.
أسئلة شائعة
أي نسخة من fs أستخدم، النسخة غير المتزامنة أم المتزامنة، ومتى تكون المتزامنة مقبولة؟
على الخادم، غير المتزامنة دائمًا (fs.promises مع await): فـNode أحادي الخيط، والاستدعاء المتزامن (readFileSync…) يحجب هذا الخيط طوال العملية — فلا يردّ الخادم حينها على أي عميل آخر. وهذا بالضبط ما يسعى نموذج Node غير الحاجب إلى تجنّبه. ولا تُقبَل المتزامنة إلا في حالتين: السكربتات الصغيرة الظرفية (لا خادم، لا مستخدمون متزامنون) وتحميل الإعدادات عند الإقلاع (مرة واحدة، قبل الاستماع). قاعدة ساليم: Sync على مسار طلب = خطأ أداء.
لماذا أستخدم وحدة path بدل بناء المسارات يدويًا؟
لأن التجميع اليدوي ينكسر متى غيّرت النظام: فالفاصل هو / على macOS وLinux لكن \ على Windows، وسرعان ما ننتج فواصل مزدوجة أو أجزاء ملتصقة («ملف غير موجود»). يركّب path.join(__dirname, 'data', nom) بـالفاصل الصحيح حسب النظام ويعالج الحالات الحدّية؛ وينتج path.resolve() مسارًا مطلقًا، وتقطّع basename/extname/dirname بنظافة. ويعمل كود ساليم دون تعديل على نظامه Windows وعلى Linux الخاص بالفريق.
يقرأ ساليم تقارير على خادم يستخدمه الفريق كله. أي نسخة من fs؟