<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x633;&#x64A;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x633;&#x64A;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644;</description><language>ar</language><item><title>&#x62F;&#x644;&#x64A;&#x644;&#x643; &#x625;&#x644;&#x649; &#x645;&#x62D;&#x631;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/workflow/%D9%85%D8%AD%D8%B1%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_11/----.png.901b74f90f7b2868185286c071292961.png" /></p>
<p>
	يُدرك المبرمج أيًا كانت خبرته أن أي مساعدة مهما كانت صغيرة في تحسين تجربته أثناء كتابة الشيفرات أمر على قدر كبير من الأهمية لما فيه من اختصار لوقت كتابة الشيفرة وزيادة في الإنتاجية وتقليل الأخطاء التي قد يقع فيها. لهذا يميل المبرمجون المحترفون إلى العمل ضمن بيئات عمل متكاملة تضم محرر شيفرة متطور، وأدوات لتصريف أو تفسير الشيفرة، وأدوات لتنفيذها ومراقبتها وإجراء الاختبارات المختلفة عليها.
</p>

<p>
	نستعرض في مقالنا دور محررات الشيفرات البرمجية الاحترافية في تأمين بيئة برمجية مريحة والميزات التي تقدمها لتوفير وقت وجهد المبرمجين وزيادة إنتاجيتهم، كما سنلقي نظرة على أكثر محررات الشيفرة البرمجية انتشارًا.
</p>

<h2 id="">
	ما هي محررات الشيفرات البرمجية
</h2>

<p>
	تُعرَّف محررات الشيفرة بأنها برمجيات تُستخدم لكتابة شيفرة خاصة بلغة برمجة محددة أو شيفرات عدة لغات برمجية مقدمة دعمًا للمبرمج وفق سويات مختلفة تتعلق بسهولة متابعة الشيفرة والإكمال التلقائي للتعابير البرمجية والتنبيه إلى أخطاء الصياغة ما قبل التصريف أو التفسير.
</p>

<p>
	تُعد الأكثرية الساحقة من <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> لغات مكتوبة؛ أي ينبغي على المبرمج كتابة جميع التعليمات على شكل عبارات نصية متلاحقة وفق صياغة صحيحة. وبالطبع فإن الشيفرات المكتوبة هي نصوص تمامًا كأي نص آخر، ويجري تحريرها كتحرير أي نص آخر، ففي النهاية قد تخطأ في كتابة بعض التعليمات وتريد التراجع أو تريد نسخ جزء من الشيفرة إلى مكان آخر وهكذا.
</p>

<p>
	وطالما أن الشيفرات هي في الأصل تعابير نصية مكتوبة فلماذا لا أستطيع استخدام محررات النصوص التقليدية مثل المفكرة أو مايكروسوفت وورد أو مستندات جوجل؟ في الواقع تستطيع ذلك بكل سهولة، لكن لنتخيل الحالة التي تكتب فيها برنامجًا مكوُنًا من 20 سطرًا برمجيًا على سبيل المثال،ولاحظ أن عليك تذكر كل تعليمات الشيفرة التي عليك وضعها في سياقها الصحيح. بعد أن تنهي كتابة برنامجك عليك نقله إلى المفسر الذي سيحلل الشيفرة ويفسرها ثم يضعها موضع التنفيذ، وقد تتفاجأ بكم من الأخطاء يشير إليها المفسر وتبدأ رحلة التقصي عنها لمعالجتها بالعودة إلى محررك النصي وتفقد مواطن الخطأ. لنفترض الآن أن أخطاءك كتابية كأن تكون قد بدلت حرفًا بآخر في التعليمات أو أغفلت حرفًا أو نقطة أو فاصلة (لم أقل أنك ارتكبت أخطاءً قواعدية Syntax errors أو في الدلالة Semantic errors فهذه قصة أخرى)، عليك هنا البحث عن هذه الأخطاء ضمن 20 سطرًا سيستغرق الأمر بعض الوقت، لكن تخيّل أن تبحث عن الأخطاء ضمن 1000 سطر أو أكثر! كارثة، أليس كذلك؟
</p>

<p>
	لهذا السبب ظهرت فكرة وجود محررات مخصصة للشيفرة تتميز بقدر كافٍ من الذكاء لتمييز أخطاءٍ كالتي أسلفنا ذكرها مباشرة أثناء كتابة الشيفرة وبالتالي سيكون المبرمج قادرًا على تصحيحها في موضوعها بأقل جهد. إضافة إلى تلك الأخطاء تساعدك محررات الشيفرة العصرية في الإشارة إلى خرقك لقواعد اللغة التي تبرمج فيها وحتى الإشارة إلى الأخطاء الدلالية وهي أخطاء منطقية قد لا ينتبه إليها المبرمجون المتقدمون إلا عند تنفيذ الشيفرة وغيرها الكثير من النقاط التي تجعل محررات الشيفرة أداة لا غنى عنها.
</p>

<p>
	وأيًا كان مستواك في <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>، ستلمس فرقًا واضحًا عند استخدامك محررات الشيفرة سواء أكنت تتعلم البرمجة حديثًا أم كنت على قدر جيد من الكفاءة، إذ ستحصل على شيفرة صحيحة بأقل جهد ممكن وأقل وقت.
</p>

<h2 id="-1">
	ميزات محررات الشيفرات البرمجية
</h2>

<p>
	تقدم محررات الشيفرات البرمجية مجموعة من المزايا التي قد تجدها جميعًا وقد تجد بعضها، وسنتحدث عن الأكثر عمومًا ثم تليها الميزات الأقل شيوعًا.
</p>

<h3 id="1">
	1. تمييز التعليمات أثناء كتابتها
</h3>

<p>
	تُعد هذه الميزة أساسيةً لأي محرر شيفرة، إذ يتعرف المحرر على هذه الكلمة هي كلمة أو تعليمة خاصة بلغة البرمجة المستخدمة ويعرضها بشكل مختلف كأن تظهر بخط سميك أو مائل أو أن تُعرض بلون مختلف. كما تميّز المحررات أنواع البيانات المختلفة وتعرضها بألوان مختلفة. فقد تعرض الأعداد بلون معين والقيم النصية (التي توضع عادة بين إشارة أو إشارتي تنصيص) بلون آخر وهكذا…
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138777" href="https://academy.hsoub.com/uploads/monthly_2023_11/01_keywords_reg.png.5982e3090e8b96bd50a91941addd6e09.png" rel=""><img alt="01 keywords reg" class="ipsImage ipsImage_thumbnailed" data-fileid="138777" data-unique="zxmf0fnp7" src="https://academy.hsoub.com/uploads/monthly_2023_11/01_keywords_reg.png.5982e3090e8b96bd50a91941addd6e09.png"> </a>
</p>

<p>
	تعرض لقطة الشاشة السابقة أحد محررات الشيفرة الذي يمّيز الكلمات المفتاحية والأنواع والتعليمات بألوان وتنسيقات كتابية مختلفة. وهكذا يمكن للمبرمج أن يعلم مثلًا أنه ارتكب خطأً في كتابة تعليمة ما إذا ظهرت بخط عادي.
</p>

<h3 id="2">
	2. ترقيم الأسطر
</h3>

<p>
	تُرقّم معظم المحررات البرمجية أسطر الشيفرة بما في ذلك الأسطر الفارغة وأسطر التعليقات وهذا أمر ضروري كي تعرف مكان الخطأ وتنتقل إليه مباشرة. كما يمكن أن تشير بعض المحررات إلى موقع الخطأ ضمن السطر الواحد من خلال رقم العمود، وهو رقم يطابق ترتيب الحروف انطلاقًا من بداية السطر.
</p>

<h3 id="3">
	3. تجميع الشيفرة وترتيبها
</h3>

<p>
	تستطيع بعض محررات الشيفرة فهم هيكلية الشيفرة التي تكتبها من خلال تمييز بداية الدالة أو الإجرائية ونهايتها أو بداية <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AA%D8%AE%D8%A7%D8%B0-%D8%A7%D9%84%D9%82%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%83%D8%B1%D8%A7%D8%B1%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1892/" rel="">الكتل الشرطية</a> ونهايتها أو بداية ونهاية الأصناف وهكذا. وبالتالي يمكن لهذه المحررات تصنيف هذه الأقسام المختلفة من الشيفرة ضمن قوائم خاصة كأن تكون هناك قوائم خاصة بأسماء الدوال التي تشير إليها الشيفرة وقوائم تضم الأصناف التي كتبت شيفرتها وقوائم بالمصفوفات إلى ما هنالك.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138776" href="https://academy.hsoub.com/uploads/monthly_2023_11/02_code_arrange.png.562e09898cd6875fa4d9f9e2f177c78f.png" rel=""><img alt="02 code arrange" class="ipsImage ipsImage_thumbnailed" data-fileid="138776" data-unique="vo26wg2k8" src="https://academy.hsoub.com/uploads/monthly_2023_11/02_code_arrange.png.562e09898cd6875fa4d9f9e2f177c78f.png"> </a>
</p>

<p>
	إضافة إلى ذلك تعرض بعض المحررات إشارة <code>-</code> أو إشارة <code>&lt;</code> عند بداية كل قسم تميّزه، وبالنقر على هذه الإشارة (تحول شكل الأيقونة إلى <code>+</code> أو <code>&gt;</code>) يعرض المحرر شيفرة هذا القسم أو يخفيها للتقليل من حجم الشيفرة المعروض ضمن الصفحة وبالتالي التركيز فقط على القسم الذي يعمل عليه المبرمج.
</p>

<h3 id="4">
	4. التحقق من أخطاء الصياغة (قواعد اللغة)
</h3>

<p>
	تتعرف بعض محررات الشيفرة إلى الأخطاء في الصياغة أو الأخطاء التي تخالف قواعد اللغة وتشير إليها من خلال وضع خط تحتها أو إحاطتها بمربع أو وضع إشارة ملونة ضمن هامش الصفحة إلى جوار السطر الذي ارتُكب فيه الخطأ القواعدي.
</p>

<p>
	لهذه الميزة فائدة عظيمة للمبرمجين لإن الإشارة إلى هذا النوع من الأخطاء سيريح المستخدم من التحقق دائمًا أن صياغته للتعليمات صحيحة دون نسيان أية أقواس أو إشارات أو رموز نهاية السطر. إضافة إلى ذلك تشير بعض المحررات إلى استخدام قيم خاطئة لنوع محدد من البيانات كإسناد قيمة نصية إلى متغير من النوع الصحيح مثلًا أو التحويل الخاطئ أو غير المسموح لنوع إلى نوع آخر وغيرها من الأخطاء التي تنتج عن الأنواع.
</p>

<p style="text-align: center;">
	<img alt="02_syntax_error.png" class="ipsImage ipsImage_thumbnailed" data-fileid="138775" data-ratio="34.83" data-unique="vknzjuby4" width="623" src="https://academy.hsoub.com/uploads/monthly_2023_11/02_syntax_error.png.d5a9fc52360a2753a3893e16782a76f4.png">
</p>

<h3 id="5">
	5. الإكمال التلقائي للشيفرة
</h3>

<p>
	لبعض المحررات القدرة على توقع التعليمات التي تريد كتابتها بمجرد كتابة بعض الأحرف؛ فتراها تعرض قائمة من التعليمات أو الأصناف أو الأعضاء أو البنى أو العمليات التي تبدأ بنفس الأحرف وقد تكون إحداها غايتك مما يسرع في كتابة الشيفرة ويساعدك في نفس الوقت على اختيار التعليمة مكتوبةً بطريقة صحيحة، وكذلك في معرفة الخيارات المتاحة لإكمال هذه التعليمة في حال لم تتذكرها كلها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138774" href="https://academy.hsoub.com/uploads/monthly_2023_11/04_auto_complete.png.133e6f163f76f177ebd25eb269fa12cd.png" rel=""><img alt="04 auto complete" class="ipsImage ipsImage_thumbnailed" data-fileid="138774" data-unique="22hcwqkdg" src="https://academy.hsoub.com/uploads/monthly_2023_11/04_auto_complete.thumb.png.6e70622600300cb807460d801b91ac41.png"> </a>
</p>

<p>
	لبعض محررات الشيفرة أيضًا أسلوب ترميزي لتمييز الأنواع والدوال والأغراض عند إظهار قوائم الإكمال التلقائي، إذ يسبق كل خيار أيقونة صغيرة تشير إلى نوعه وطبيعته متغيرًا كان أم دالة أم عضوًا في كائن وهكذا يمكنك اختيار ما هو مطلوب إن تشابهت التسميات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138773" href="https://academy.hsoub.com/uploads/monthly_2023_11/05_auto_list_type.png.430eb6a5808abdd7b8ad5bedc6cdf536.png" rel=""><img alt="05 auto list type" class="ipsImage ipsImage_thumbnailed" data-fileid="138773" data-unique="i1r3aov4u" src="https://academy.hsoub.com/uploads/monthly_2023_11/05_auto_list_type.png.430eb6a5808abdd7b8ad5bedc6cdf536.png"> </a>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		يقتصر الإكمال التلقائي في بعض المحررات على إنهاء الأقواس أو إشارات التنصيص تلقائيًا فعند كتابة <code>(</code> مثلًا يظهر القوس المقابل <code>)</code> تلقائيًا.
	</p>
</blockquote>

<h3 id="6">
	6. إظهار معلومات عن استخدام الشيفرة ونصائح لحل الأخطاء
</h3>

<p>
	تعرض بعض المحررات معلومات عن التعليمات التي تكتبها سواء كانت متغيرات أم أصناف أو دوال بمجرد تمرير مؤشر الفأرة فوقها أو حتى أثناء ظهور هذه التعليمات ضمن قوائم الإكمال التلقائي. تتعلق هذه المعلومات بنوع الصنف والأعضاء التي يأخذها ونوع الدالة والمعاملات والقيم التي تعيدها وعدد معاملاتها ونوع كل معامل. وبهذه الطريقة قد تجد دالة أو تعليمة أنسب من التي تفكّر في استخدامها لربما لم تتعرف عليها مسبقًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138772" href="https://academy.hsoub.com/uploads/monthly_2023_11/06_get_sugg.png.f6c227a864fb426f876a70767db0ffe2.png" rel=""><img alt="06 get sugg" class="ipsImage ipsImage_thumbnailed" data-fileid="138772" data-unique="8zvf8zhx2" src="https://academy.hsoub.com/uploads/monthly_2023_11/06_get_sugg.png.f6c227a864fb426f876a70767db0ffe2.png"> </a>
</p>

<p>
	كما تشير بعض المحررات إلى ضرورة تغيير صياغة بعض التعليمات لتتوافق مع معايير أحدث للغة، وقد تكون صياغتك صحيحة لا لبس بها. كما قد تنصحك باستخدام دوال بدلًا من دوال لأنها ستسبدل قريبًا ولن تكون متاحة لاحقًا. وتقدم بعض المحررات مبدأ الإصلاح السريع للشيفرة "Quick Fix"، إذ تقدم لك مجموعة من الحلول الجاهزة للخطأ الذي ارتكبته كي تسهّل عليك التصحيح، وتظهر عادة إشارة مصباح في الأماكن التي تُقترح فيها إصلاحات سريعة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138771" href="https://academy.hsoub.com/uploads/monthly_2023_11/07_quick_fix.png.fedb78fbcc70824bb016e04ee3526fd7.png" rel=""><img alt="07 quick fix" class="ipsImage ipsImage_thumbnailed" data-fileid="138771" data-unique="0mqagykd1" src="https://academy.hsoub.com/uploads/monthly_2023_11/07_quick_fix.png.fedb78fbcc70824bb016e04ee3526fd7.png"> </a>
</p>

<h3 id="7">
	7. تخصيص الإكمال التلقائي
</h3>

<p>
	تقدم بعض المحررات إمكانية تخصيص الإكمال التلقائي من خلال ملفات إعداد خاصة وهذا أمر متقدم نوعًا ما. تتيح لك هذه الميزة مثلًا ظهور خيارات إكمال تلقائي محددة ضمن صنف معين أو ضمن كتلة معينة من الشيفرة. فلو فرضنا أنك ضمن حلقة <code>for</code> وكان عداد الحلقة بالاسم <code>index</code> فسيظهر لك الخيار index كأول خيار ضمن قوائم الإكمال التلقائي عندما تكتب الشيفرة داخل هذه الكتلة. كما يمكنك تخصيص مفاتيح محددة من لوحة المفاتيح لإدراج أفضل تكملة ممكنة لما تكتبه وفقًا للسياق مثل المفتاح Tab.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		تختلف المحررات التي تدعم التخصيص وفق آلية عملها فتجد أنها تجربة مفيدة أحيانًا في بعض المحررات وسيئة في بعضها لهذا لا يعتمد المبرمجون المتقدمون عليها.
	</p>
</blockquote>

<h2 id="-2">
	محررات الشيفرات البرمجية وتنفيذ الشيفرة
</h2>

<p>
	هل يستطيع محرر الشيفرة البرمجية تنفيذ هذه الشيفرة؟
</p>

<p>
	لا يمكن لمحررات الشيفرة من ناحية المبدأ أن تنفذ الشيفرات المكتوبة فهي محررات وفقًا لتسميتها وليست مصرّفات Compilers أو مفسّرات interpreters. وكما أشرنا سابقًا، تقتصر وظيفة المحررات على تسهيل كتابة الشيفرة وتسريعها ومراقبة أخطاء الصياغة.
</p>

<p>
	لكن كتابة الشيفرة على محرر ثم نقله إلى مصرّف ومن ثم إعداد النسخ التنفيذية للبرنامج على مجمّع مدير الحزم package manager أمر مربك لا يعطي راحة في العمل، لهذا يفضل معظم المبرمجين المحترفين الذي يعملون على مشاريع برمجية كاملة استخدام بيئات العمل المتكاملة Integrated Development Environment واختصارًا IDE التي تضم جميع أدوات العمل من المحررات إلى المصرّفات إلى آلات تحزيم ونشر البرمجيات.
</p>

<p>
	إذًا ما الحاجة لمحررات الشيفرة إن كانت بيئات العمل المتكاملة حلًا مثاليًا؟
</p>

<p>
	في الواقع لا تُعد بيئات العمل المتكاملة الحل المثالي دائمًا لأسباب عدة أهمها:
</p>

<ul>
	<li>
		لا تحتاج الكثير من اللغات المحمولة الرائجة إلى مصرّفات خاصة مثل HTML وجافاسكربت.
	</li>
	<li>
		الحجم الكبير لبيئات العمل المتكاملة ووجود ميزات كثيرة لا حاجة لها في العديد من الحالات.
	</li>
	<li>
		تسبب البطء في الأجهزة وخاصة ذات المواصفات المنخفضة وحتى المتوسطة.
	</li>
	<li>
		قد تكون مهمة المبرمج كتابة أجزاء محددة أو وظائف محددة فلا حاجة لبيئة عمل في هذه الحالة.
	</li>
</ul>

<p>
	ظهر الحل في وجود محررات شيفرة برمجية قابلة للتوسع أي أنها تدعم تحرير عدد كبير من لغات البرمجة من ناحية تمييزها لكنها تقبل في نفس الوقت موسّعات خاصة Extensions خاصة بلغة محددة تدعم هذه المحررات بميزات إضافية من ناحية التعرف على الشيفرة والإكمال التلقائي كما تؤمن آلية لتصريف وتنفيذ الشيفرة. وهكذا تختار موسّعًا ذو حجم وإمكانات معينة تناسب أعمالك ثم تثبيته على محرر الشيفرة. نذكر من هذه المحررات فيجوال ستديو كود Visual studio code من مايكروسوفت الشهير وهو محرر مجاني ومفتوح المصدر وقابل للتوسع.
</p>

<h2 id="-3">
	أشهر محررات الشيفرة المجانية
</h2>

<p>
	نستعرض تاليًا مجموعة من أكثر محررات الشيفرة شعبية
</p>

<h3 id="vscode">
	المحرر فيجوال ستديو كود VS Code
</h3>

<ul>
	<li>
		<strong>اللغات المدعومة أصلًا</strong>: جافاسكربت و JSON و HTML و Node.js و TypeScript.
	</li>
	<li>
		<strong>المنصات المدعومة</strong>: ويندوز، لينكس، ماك.
	</li>
	<li>
		<strong>اللغات المدعومة عن طريق الموّسعات</strong>: معظم اللغات البرمجية مثل ++C و بايثون وجافا و #C و GO.
	</li>
	<li>
		<strong>يعمل كمحرر نصي</strong>: نعم.
	</li>
	<li>
		<strong>السعر</strong>: مجاني وتوجد نسخة بيئة تطوير متكاملة IDE مدفوعة.
	</li>
	<li>
		<strong>مفتوح المصدر</strong>: نعم
	</li>
</ul>

<p>
	يُعد <a href="https://code.visualstudio.com/download" rel="external nofollow">فيجوال ستديو كود</a> محرر شيفرة ومحرر نصي متقدم ومجاني ومفتوح المصدر ويحتل حتى الآن المرتبة الأولى كأكثر المحررات شعبية. ويتميز بأنه محرر خفيف وقوي ويدعم الكثير من اللغات وإطارات العمل من خلال تثبيت الموّسعات المختلفة التي تستطيع إيجادها على <a href="https://marketplace.visualstudio.com/vscode" rel="external nofollow">المتجر الخاص على الإنترنت</a>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138770" href="https://academy.hsoub.com/uploads/monthly_2023_11/08_vsc.png.bc969e5b289cd4c677483a36b227d588.png" rel=""><img alt="08 vsc" class="ipsImage ipsImage_thumbnailed" data-fileid="138770" data-unique="alwrqqg3p" src="https://academy.hsoub.com/uploads/monthly_2023_11/08_vsc.thumb.png.5eabb3bf7fd2f32965549557ab2581e7.png"> </a>
</p>

<p>
	يتمتع فيجوال استوديو بجميع ميزات محررات الشيفرة العصرية التي فصّلناها سابقًا من خلال تقنية <a href="https://code.visualstudio.com/docs/editor/intellisense" rel="external nofollow">IntelliSense</a> التي تزود المستخدم بإمكانات التعرف على صياغة اللغة والإشارة إلى أخطائها والإكمال التلقائي للشيفرة.
</p>

<p>
	يقدم لك فيجوال ستوديو معلومات مرجعية وتعريفات بالأصناف والدوال والمتغيرات بمجرد أن تظلل العبارة البرمجية ومن ثم النقر بالزر اليميني عليها ثم اختيار المعلومة المطلوبة.
</p>

<p>
	يؤخذ على هذا المحرر النقاط التالية:
</p>

<ul>
	<li>
		قد تُضطر إلى تثبيت بعض الموّسعات بنفسك وليس من خلال المحرر نفسه، ومن ثم ضبط إعداداتها وهذا أمر مربك في أحيان كثيرة.
	</li>
	<li>
		توافق ضعيف مع ماك أو إس وقد يكون بطيئًا أحيانًا على لينكس.
	</li>
	<li>
		طريقة تعامله مع الأخطاء الناتجة عن ضبط الإعدادات والتي قد تدفعك إلى التخلي عن المحرر ككل.
	</li>
</ul>

<h3 id="sublimetext">
	المحرر Sublime Text
</h3>

<p>
	يُعد هذه المحرر من المحررات القوية ومتعددة المنصات ويدعم عددًا كبيرًا من الإضافات Plugins التي تزيد من إمكاناته وتدعم تفاعله مع لغات برمجة جديدة. يحقق هذه المحرر معايير محررات الشيفرة العصرية ويمتاز بقدرته على فتح ملفات شيفرة تصل إلى أكثر من 200000 سطر برمجي بكل سهولة.
</p>

<ul>
	<li>
		<strong>اللغات المدعومة</strong>: ++C وبايثون و PHP و Rails وغيرها الكثير من خلال حزم الإضافات.
	</li>
	<li>
		<strong>المنصات المدعومة</strong>: ويندوز، لينكس، ماك.
	</li>
	<li>
		<strong>يعمل كمحرر نصي</strong>: نعم.
	</li>
	<li>
		<strong>السعر</strong>: مدفوع مع وجود نسخة مجانية محدودة.
	</li>
	<li>
		<strong>مفتوح المصدر</strong>: لا.
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138769" href="https://academy.hsoub.com/uploads/monthly_2023_11/09_sublime_text.png.111420eef09fcc4b91f60e1ae43f0f94.png" rel=""><img alt="09 sublime text" class="ipsImage ipsImage_thumbnailed" data-fileid="138769" data-unique="goyivjppn" src="https://academy.hsoub.com/uploads/monthly_2023_11/09_sublime_text.thumb.png.e3685f4807d300b0bddc4aa16fd2e758.png"> </a>
</p>

<p>
	ومن الميزات الخاصة التي يتمتع بها <a href="https://www.sublimetext.com/download" rel="external nofollow">Sublime text</a> نجد:
</p>

<ul>
	<li>
		كم واسع من تعليمات التحرير كالإزاحة وتنسيق الفقرات والحفظ التلقائي والبحث بمجرد الكتابة.
	</li>
	<li>
		تعيين اختصارات للأوامر المختلفة.
	</li>
	<li>
		التدقيق الإملائي.
	</li>
	<li>
		ميزة "الانتقال إلى أي شيء Goto Anything" للانتقال السريع إلى أي ملف أو رمز أو سطر أو كائن.
	</li>
	<li>
		التحرير المتزامن: وهي تقنية تسمح بالكتابة في عدة ملفات أو أقسام في نفس الوقت.
	</li>
	<li>
		يتكامل مع برنامج Sublime Merge الذي يُستخدم كعميل للاتصال مع <a href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%A7-%D9%87%D9%88-git%D8%9F-r1592/" rel="">Git</a>.
	</li>
</ul>

<p>
	لهذا المحرر سلبيات عدة نذكر منها:
</p>

<ul>
	<li>
		النسخة المجانية محدودة وتعرض دائمًا نافذة تحثك على شراء النسخة المدفوعة.
	</li>
	<li>
		صعوبة في تثبيت الكثير من الإضافات وخاصة للمبتدئين.
	</li>
	<li>
		تختفي بعض الاقتراحات وخيارات الإكمال التلقائي أثناء كتابة الشيفرة ولا يمكن رؤيتها.
	</li>
</ul>

<h3 id="notepad">
	المحرر ++Notepad
</h3>

<p>
	يتميز هذا المحرر بسرعته الكبيرة فهو محرر خفيف ولا يعتمد على الكثير من الموارد وبالتالي سيكون المحرر المثالي للمستخدمين الذين يمتلكون حواسيب ضعيفة.
</p>

<ul>
	<li>
		<strong>اللغات المدعومة</strong>: أكثر من 70 لغة بما فيها ++C وبايثون و PHP وHTML و CSS و swift
	</li>
	<li>
		<strong>المنصات المدعومة</strong>: ويندوز، لينكس.
	</li>
	<li>
		<strong>يعمل كمحرر نصي</strong>: نعم.
	</li>
	<li>
		<strong>السعر</strong>: مجاني.
	</li>
	<li>
		<strong>مفتوح المصدر</strong>: فقط لنسخة لينكس.
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138768" href="https://academy.hsoub.com/uploads/monthly_2023_11/10_nodepp.png.3fa4743077611ce0a55681813daedc54.png" rel=""><img alt="10 nodepp" class="ipsImage ipsImage_thumbnailed" data-fileid="138768" data-unique="5yt6e9ncx" src="https://academy.hsoub.com/uploads/monthly_2023_11/10_nodepp.png.3fa4743077611ce0a55681813daedc54.png"> </a>
</p>

<p>
	يمتاز <a href="https://notepad-plus-plus.org/downloads/" rel="external nofollow">++Nodepad</a> المحرر بأنه:
</p>

<ul>
	<li>
		فعّال وبسيط ويؤمن ميزات مثل تمييز الصياغة اللغوية وتمييز النصوص والدوال والأقواس.
	</li>
	<li>
		يؤمن ميزة الإكمال التلقائي.
	</li>
	<li>
		قادر على تنظيم الشيفرة وتجميعها ضمن كتل يمكن عرضها وإخفاؤها.
	</li>
	<li>
		يزوّد هذا المحرر المستخدمين بخارطة للمستند للوصول إلى أية نقطة من الشيفرة في حال كان الملف ضخمًا. وهكذا تستطيع الانتقال بسرعة بين أي منطقتين من الملف دون الحاجة إلى التمرير إلى الأعلى والأسفل مرارًا.
	</li>
	<li>
		مجاني ومترجم إلى أكثر من 80 لغة منها العربية.
	</li>
</ul>

<p>
	يؤخذ على البرنامج مايلي:
</p>

<ul>
	<li>
		واجهة المستخدم غير عصرية وتوحي بأنه برنامج قديم.
	</li>
	<li>
		لا يضم متصفح ملفات.
	</li>
	<li>
		صعوبة عرض عدة ملفات بطريقة مريحة مما ينعكس سلبًا عند العمل على عدة مشاريع.
	</li>
	<li>
		محدودية الميزات التي يقدمها موازنة بغيره.
	</li>
	<li>
		لا يوجد دعم رسمي لنسخة نظام لينكس، علمًا أنه بالإمكان تحميلها من متجر أوبونتو Ubuntu software center.
	</li>
</ul>

<h3 id="bluefish">
	المحرر Bluefish
</h3>

<p>
	<a href="http://bluefish.openoffice.nl/" rel="external nofollow">بلوفيش</a> محرر شيفرة قوي ومخصص للمستخدمين المتمرسين من مبرمجين ومطورين ومصممين لمواقع الويب. وهو برنامج مفتوح المصدر متعدد المنصات يتميز بخفة وزنه وواجهته الرسومية السريعة.
</p>

<ul>
	<li>
		<strong>اللغات المدعومة</strong>: ++C وبايثون و بيرل وPHP وHTML و CSS و Ruby وجافاسكربت و SQL وغيرها الكثير.
	</li>
	<li>
		<strong>المنصات المدعومة</strong>: ويندوز، لينكس، ماك، FreeBSD، Solaris.
	</li>
	<li>
		<strong>يعمل كمحرر نصي</strong>: نعم.
	</li>
	<li>
		<strong>السعر</strong>: مجاني.
	</li>
	<li>
		<strong>مفتوح المصدر</strong>: نعم.
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138767" href="https://academy.hsoub.com/uploads/monthly_2023_11/11_bluefish.png.20ff3d2e5051ff5f8ec36e74598f00f1.png" rel=""><img alt="11 bluefish" class="ipsImage ipsImage_thumbnailed" data-fileid="138767" data-unique="u1v5x8tom" src="https://academy.hsoub.com/uploads/monthly_2023_11/11_bluefish.png.20ff3d2e5051ff5f8ec36e74598f00f1.png"> </a>
</p>

<p>
	لمحرر بلوفيش ميزات عديدة أهمها:
</p>

<ul>
	<li>
		يضم شريط اختصارات سريعة يمكن أن يضع فيه المستخدم اختصارات للأداوات الأكثر استخدامًا.
	</li>
	<li>
		يمكن تخصيص واجهة المستخدم من ناحية اللون والشكل بما يناسب لغة البرمجة.
	</li>
	<li>
		يستطيع تمييز الصياغة اللغوية للغات البرمجة المدعومة ويدعم الإكمال التلقائي وإغلاق وسوم اللغات التوصيفية والتنسيقية مثل وسوم HTML و CSS.
	</li>
	<li>
		يمكن فتح عدد كبير من الملفات ضمن واجهة المستخدم والتعامل معها.
	</li>
	<li>
		معلومات مرجعية عن الشيفرات المكتوبة.
	</li>
	<li>
		الاستعادة التلقائية للملفات عند حدوث خطأ.
	</li>
	<li>
		قابل للتوسع عن طريق الإضافات والسكربتات.
	</li>
</ul>

<p>
	لا يؤخذ على هذا المحرر الكثير لكن يواجه بعض الانتقادات مثل:
</p>

<ul>
	<li>
		صعوبة فتح ملفات شيفرة ضخمة الحجم فقد يتوقف أو لا يحفظ التغييرات.
	</li>
	<li>
		لا يدعم الاختيارات المتعددة أو البحث ضمنها أي لا يمكن اختيار أجزاء منفصلة من الشيفرة والتعامل معها.
	</li>
</ul>

<h3 id="emacs">
	المحرر Emacs
</h3>

<p>
	يُعد من أفضل محررات الشيفرات البرمجية إن كنت تبحث عن بيئة عمل قابلة للتخصيص بشكل كبير. إذ يأتي مع شريط قوائم وشريط أدوات وقوائم منسدلة يمكن أن تظهر أو تخفي أيًا منها، كما يمكنك إضافة نوافذ فرعية لعرض ملفات وفضاءات عمل خاصة والتحكم بالألوان ومظهر واجهة المستخدم.
</p>

<ul>
	<li>
		<strong>اللغات المدعومة</strong>: ++C وبايثون و Lisp وغيرها الكثير.
	</li>
	<li>
		<strong>المنصات المدعومة</strong>: ويندوز، لينكس، ماك، FreeBSD.
	</li>
	<li>
		<strong>يعمل كمحرر نصي</strong>: نعم.
	</li>
	<li>
		<strong>السعر</strong>: مجاني.
	</li>
	<li>
		<strong>مفتوح المصدر</strong>: نعم.
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="138766" href="https://academy.hsoub.com/uploads/monthly_2023_11/12_emacs.png.1fae5cee7cd3acc6a570b4f48d01ad96.png" rel=""><img alt="12 emacs" class="ipsImage ipsImage_thumbnailed" data-fileid="138766" data-unique="ha8d3id3r" src="https://academy.hsoub.com/uploads/monthly_2023_11/12_emacs.png.1fae5cee7cd3acc6a570b4f48d01ad96.png"> </a>
</p>

<p>
	<a href="https://www.gnu.org/software/emacs/download.html" rel="external nofollow">للمحرر Emacs</a> الميزات التالية:
</p>

<ul>
	<li>
		يتميز هذا المحرر بقدرته على تصريف وتنفيذ الشيفرة واختبار البرامج وتنفيذ الأوامر التنفيذية shell commands.
	</li>
	<li>
		خيارات متقدمة للبحث والاستبدال.
	</li>
	<li>
		يعمل من خلال واجهة رسومية أو من خلال الطرفية.
	</li>
	<li>
		يميز الصياغة اللغوية ويدعم الإكمال التلقائي من خلال نمط الإكمال التلقائي إذ يدعم المحرر عدة أنماط للعمل.
	</li>
	<li>
		يضم مستكشف ملفات.
	</li>
</ul>

<p>
	يؤخذ على هذا المحرر:
</p>

<ul>
	<li>
		صعوبة التعامل معه وخاصة للمبتدئين.
	</li>
	<li>
		لا بد من العودة دائمًا إلى التوثيق الخاص به لتذكر تعليمات الضبط والإعداد.
	</li>
	<li>
		صعوبة التعامل مع نمط الإكمال التلقائي.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: يمكنك أيضًا استخدام محرات شيفرة تعمل من خلال الإنترنت نذكر منها Codepen و CodeSandbox و Glitch و Repl.it.
</p>

<h2 id="-4">
	خاتمة
</h2>

<p>
	لمحررات الشيفرات البرمجية دور كبير في تحسين بيئة عمل المبرمجين أثناء كتابة الشيفرة وزيادة إنتاجيتهم؛ كتمييز صياغة لغة البرمجة التي يكتب شيفرتها والتنبيه إلى وجود أخطاء فيها والإكمال التلقائي للشيفرة وعرض اقتراحات لحلها وتسهيل عمليات التحرير الأساسية من نسخ ولصق وعمليات البحث والاستبدال هي مفاتيح رئيسية لتسهيل عمل المبرمج وتوفير وقته وجهده. لهذا إن كنت مقبلًا على تعلم البرمجة وأردت محرر شيفرات يناسب احتياجاتك، سيساعدك هذا المقال على توضيح النقاط الأساسية التي ينبغي أن تبحث عنها في محرر الشيفرات البرمجية ويعرض لك عددًا من المحررات الممتازة التي تتمتع بمعظم ميزات محررات الشيفرة العصرية إضافة إلى كونها مجانية وتعمل على أكثر المنصات شيوعًا.
</p>

<h2 id="-5">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AD%D8%B1%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r666/" rel="">محررات الشيفرة البرمجية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%B1%D8%B1-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">ما هو أفضل محرر أكواد بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D9%84%D9%88%D8%A8-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%AA%D8%AD%D9%82%D9%8A%D9%82-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D9%82%D8%B1%D8%A7%D8%A1%D8%AA%D9%87%D8%A7-r1307/" rel="">أسلوب كتابة الشيفرات البرمجية وتحقيق سهولة قراءتها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2176</guid><pubDate>Fri, 24 Nov 2023 13:05:01 +0000</pubDate></item><item><title>&#x628;&#x64A;&#x626;&#x627;&#x62A; &#x627;&#x644;&#x62A;&#x637;&#x648;&#x64A;&#x631; IDE &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/workflow/%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-ide-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1772/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/63662bffe3fd8_-----IDE---Python.png.bff947f54528f9b0f9f8fe2e4b50ad7b.png" /></p>
<p>
	تعد <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">لغة البرمجة بايثون Python</a> من أكثر اللغات انتشارًا لسهولة تعلمها ولدعمها الكثير من المشاريع مثل مواقع الويب وأدوات سطح المكتب والبرمجيات الخاصة بالمؤسسات وحتى السكربتات الصغيرة المُساعدة على أتمتة المهام الدورية، واستُخدمَت بايثون لكتابة جميع مشاريع البرامج الشهيرة أو أجزاء منها مثل dnf/yum و OpenStack و OpenShot و <a href="https://academy.hsoub.com/design/3d/blender/" rel="">Blender</a> و Caliber وحتى وكيل BitTorrent الأصلي.
</p>

<p>
	تطوير تطبيقات بايثون يكون عبر محررات وبيئات تطوير متكاملة وهي كثيرة للغاية، إذ يفضل البعض استخدام محرر نصي بسيط مثل إيماكس <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A5%D9%8A%D9%85%D8%A7%D9%83%D8%B3-emacs-r1761/" rel="">Emacs</a> أو <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-r1769/" rel="">Vim</a> أو Gedit، وجميع هذه المحررات قابلة لتوسيع إمكانياتها مثل ميزة إبراز صيغة الجملة syntax highlighting والإكمال التلقائي، أما بالنسبة للمستخدم المحترف الذي يعمل في مشاريع كبيرة معقدة فيفضّل استخدام بيئة تطوير متكاملة IDE بدلًا من الجمع بين استخدام محرر نصي بسيط و<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر terminal</a>.
</p>

<p>
	تعد بيئة التطوير المتكاملة عبارة عن محرر نصوص مدعّم بميزات إضافية وتتخصص أحيانًا بلغة برمجة واحدة أو لغتين فقط وتساعد المبرمج على تتبع المشروع ككل حيث تقوم معظم هذه البيئات بالاحتفاظ بذاكرة تخزين مؤقت لأسماء الأصناف والتوابع والمتغيرات بحيث يمكن إكمالها تلقائيًا بسرعة وتمتلك بعضها أدوات اكتشاف الأخطاء وتصحيحها وتكون مدعمة بتصحيح المنطق الفوري instant logic الذي يساهم في تسريع أداء المواقع والتطبيقات وزيادة الأمان.
</p>

<p>
	نُشر استبيان يتضمن السؤال التالي:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		"ما هي بيئة التطوير المتكاملة IDE المفضلة لديك لبرمجة مشاريع بلغة بايثون؟"
	</p>
</blockquote>

<p>
	عدد الإجابات وصل إلى 40095 إجابة وتظهر التفاصيل كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111493" href="https://academy.hsoub.com/uploads/monthly_2022_11/63662c252815f_.PNG.797e33bfca657eaaec7f6634e593fe95.PNG" rel="" data-fileext="PNG"><img alt="نتائج الإجابات على الاستبيان حول بايثون" class="ipsImage ipsImage_thumbnailed" data-fileid="111493" data-unique="0ignsop3i" style="width: 650px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/63662c26b67e3_.thumb.PNG.48e1df484d918e93347a248b2df66efc.PNG"></a>
</p>

<p>
	يذكر المقال خمس بيئات ويشرح تفاصيلها لمساعدتك على اختيار الأنسب لك، وجميع هذه الخيارات متعددة المنصات cross-platform لذا يمكن استخدامها على <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a> الذي تختاره.
</p>

<h2>
	بيئة PyCharm
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111489" href="https://academy.hsoub.com/uploads/monthly_2022_11/PyCharm.PNG.eaf73f73130148b76756997eeae228cd.PNG" rel="" data-fileext="PNG"><img alt="بيئة PyCharm" class="ipsImage ipsImage_thumbnailed" data-fileid="111489" data-unique="ie4yuaa1l" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/PyCharm.thumb.PNG.53ef143a6b661c7ea8fa6fdbc5978798.PNG"></a>
</p>

<p>
	يعتبر <a href="https://www.jetbrains.com/pycharm/" rel="external nofollow">PyCharm</a>   <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%B1%D8%B1-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">محرر بايثون</a> بسيط وصغير الحجم نسبيًا وشائع الاستخدام، يتوفر منه إصدارين أحدهما مجاني و<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوح المصدر</a> وهو <a href="https://www.jetbrains.com/pycharm/download/#section=linux" rel="external nofollow">الإصدار المجتمعي</a> المتاح بموجب ترخيص <a href="http://www.apache.org/licenses/LICENSE-2.0.html" rel="external nofollow">Apache 2.0</a> والآخر مدفوع.
</p>

<p>
	يمتلك pycharm جميع خصائص بيئة التطوير المتكاملة وهي:
</p>

<ul>
	<li>
		فحص الشيفرة البرمجية
	</li>
	<li>
		أدوات تغيير هيكلية الشيفرة البرمجية
	</li>
	<li>
		منظومة اختبار وحدات مدمجة integrated unit testing
	</li>
	<li>
		نظام تحكم بالإصدار مدمج integrated version control
	</li>
	<li>
		أدوات المراقبة والتحكم بالمشروع
	</li>
	<li>
		إبراز صيغة الجملة والإكمال التلقائي
	</li>
</ul>

<p>
	أما قصور PyCharm تتمثل في احتكار العديد من المزايا المتقدمة للإصدار مغلق المصدر وعدم توفيرها في الإصدار مفتوح المصدر، بالإضافة إلى نموذج <a href="https://en.wikipedia.org/wiki/Open-core_model" rel="external nofollow">النواة المفتوح Open core</a> الذي يعتمده.
</p>

<h2>
	بيئة Eric
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111488" href="https://academy.hsoub.com/uploads/monthly_2022_11/Eric.PNG.ba049692b7461d3b021bcde59dbeeee0.PNG" rel="" data-fileext="PNG"><img alt="بيئة Eric" class="ipsImage ipsImage_thumbnailed" data-fileid="111488" data-unique="zsz5xov5l" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/Eric.thumb.PNG.ded91981dfa72d34f1bb06ea258de34b.PNG"></a>
</p>

<p>
	إريك Eric بيئة تطوير متكاملة لتحرير شيفرات بايثون، وسُميّت باسم الممثل <a href="https://www.imdb.com/name/nm0001385/" rel="external nofollow">Eric Idle</a> في برنامج Monty Python.
</p>

<p>
	كُتبَت إريك بلغة بايثون باستخدام إطار العمل Qt، ومتاحة بموجب <a href="https://www.gnu.org/licenses/gpl-3.0.en.html" rel="external nofollow">الإصدار الثالث من GPL</a>.
</p>

<p>
	تستخدم بيئة إريك <a href="https://www.scintilla.org/" rel="external nofollow">Scintilla</a> وهو مكوِّن لتحرير الشيفرة المصدرية يُستخدم في العديد من برامج تحرير النصوص والعديد من بيئات التطوير المتكاملة، وهو متاح أيضًا كمحرر <a href="https://www.scintilla.org/SciTEDownload.html" rel="external nofollow">SciTE </a> مستقل.
</p>

<p>
	تتميز بيئة إريك بما يلي:
</p>

<ul>
	<li>
		صغيرة الحجم
	</li>
	<li>
		إبراز الأقواس المتقابلة
	</li>
	<li>
		إكمال الشيفرة
	</li>
	<li>
		متصفح الصنف class browser
	</li>
	<li>
		اختبار الوحدة المدمجة
	</li>
	<li>
		دالة مدمجة مع بيئة التطوير لقائمة المهام
	</li>
	<li>
		دالة لمعاينة نموذج Qt: تُساعد المستخدم عند تطوير واجهة رسومية بالاعتماد على Qt لتطبيق ما
	</li>
</ul>

<p>
	جميع هذه الميزات تجعلها خيارًا مفضلًا للكثيرين ومع ذلك تبقى بيئة صعبة التعلم بالنسبة للمبتدئين في مجال بيئات التطوير المتكاملة وتحتاج إلى وقت كبير لإتقانها ولكن هذا الوقت يستحق العناء خاصةً أن استخدامها يُحسّن من جودة التعليمات البرمجية، وإن ألِفَ المستخدم استعمالها فلن يصعب عليه الانتقال إلى بيئات أخرى لما فيها من تشابه في الاستخدام مع بعضها.
</p>

<h2>
	بيئة Pyzo
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111490" href="https://academy.hsoub.com/uploads/monthly_2022_11/Pyzo.PNG.cb60cfd4a6f68be3f5f14023af4901e2.PNG" rel="" data-fileext="PNG"><img alt="بيئة Pyzo" class="ipsImage ipsImage_thumbnailed" data-fileid="111490" data-unique="e04txk00j" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/Pyzo.thumb.PNG.f4b8ada4121c97c1462d0a8be899478a.PNG"></a>
</p>

<p>
	يُعدّ <a href="https://pyzo.org/install_linux.html" rel="external nofollow">Pyzo</a> محرر بايثون يمتلك العديد من المزايا مثل سجل ومصحح أخطاء ومتعقب مسافة بادئة ومتتبع للتعليمات البرمجية، كما أنه مزود بمستعرض ملفات مدمج ومستعرض للمشروع project overview.
</p>

<p>
	يعتمد Pyzo على Qt مما يكسب بيئة العمل الخاصة به مرونة كبيرة حيث يسمح بإجراء الكثير من التعديلات حسب تفضيلات المستخدم مثل نقل اللوحات panels لتناسب سير العمل أو لمحاكاة تصميم بيئة تطوير متكاملة أخرى، كما يمكن إظهار وإخفاء لوحات الأدوات بناءً على المعلومات ذات الاستخدام المتكرر مما يساعد المستخدم على التركيز، بالإضافة إلى إمكانية تعيين مجموعة مفاتيح معينة لكل إجراء.
</p>

<p>
	لا يعد pyzo بيئة تطوير متكاملة مثل اكلبس Eclipse لعدم امتلاكه بنية مزودة بالملحقات plugin architecture ولكن يعده البعض بديلاً لماتلاب <a href="https://pyzo.org/python_vs_matlab.html" rel="external nofollow">MATLAB</a> كونه يناسب مشاريع الحوسبة العلمية مثل الرياضيات والعلوم المتقدمة، ويعد باختصار بيئة تطوير متكاملة بسيطة ومفتوح المصدر وبالتالي قابل للتخصيص بطبيعته مما يجعله مناسبًا لأي مشروع بايثون.
</p>

<h2>
	بيئة Spyder
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111491" href="https://academy.hsoub.com/uploads/monthly_2022_11/Spyder.PNG.51fc2b483f398e60d1d17d85e46b15c6.PNG" rel="" data-fileext="PNG"><img alt="بيئة Spyder" class="ipsImage ipsImage_thumbnailed" data-fileid="111491" data-unique="f6qqapdi8" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/Spyder.thumb.PNG.044968c6e20453345653cdab51d72b47.PNG"></a>
</p>

<p>
	تتشابه بيئة <a href="https://www.spyder-ide.org/" rel="external nofollow">Spyder</a> مع Pyzo في الجمهور المستهدف فهو موجّه لعلماء البيانات ولكن يختلف مع المحرر Pyzo بكونه بيئة متكاملة.
</p>

<p>
	تتصف Spyder بالتعقيد لكونها بيئة كبيرة وغنية بالميزات وتمتلك لوحات لعرض البيانات المُدخلة وخرج الشيفرة البرمجية والسجلات والتأريخ history وغير ذلك الكثير.
</p>

<p>
	يمكن تنزيلها لوحده أو كجزء من حزمة مع توزيعة <a href="https://www.anaconda.com/products/distribution" rel="external nofollow">أناكوندا Anaconda</a> وهي مجموعة أدوات برمجة شائعة يستخدمها مبرمجو لغتي <a href="https://wiki.hsoub.com/Python" rel="external">بايثون Python</a> و R.
</p>

<h2>
	Eclipse مع PyDev
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111487" href="https://academy.hsoub.com/uploads/monthly_2022_11/Eclipse.PNG.a589374e95a4c97901070832d648b60e.PNG" rel="" data-fileext="PNG"><img alt=" بيئة Eclipse مع PyDev" class="ipsImage ipsImage_thumbnailed" data-fileid="111487" data-unique="lyfqjhwiz" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_11/Eclipse.thumb.PNG.281f83d0da1aaa2c5d78105a8cae561a.PNG"></a>
</p>

<p>
	غالبًا ما يتبادر <a href="https://www.eclipse.org/" rel="external nofollow">Eclipse</a> إلى الذهن عند ذِكر بيئات التطوير المتكاملة مفتوحة المصدر، إذ يقف خلفه مجتمع ضخم من المطورين وعدد لا يحصى من الملحقات المتاحة مما يمكن المستخدم من تخصيصه لتلبية جميع احتياجاته، ولكن تعد هذه الوفرة في حد ذاتها سلبية لبعض المستخدمين فهو يبدو لهم ذو إمكانيات فائضة عن الحاجة مما يجعله معقدًا.
</p>

<p>
	يناسب Eclipse المبتدئين لأن تعلم أداة واحدة سيمكّنهم من تنفيذ معظم المشاريع وبكثير من لغات البرمجة، كما يناسب مبرمجي بايثون الجدد الذين يمتلكون خبرة سابقة في البرمجة وخاصة <a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%A7-%D9%87%D9%8A%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r1515/" rel="">لغة جافا Java</a> حيث ستكون بيئة التطوير المتكاملة مألوفة بالنسبة لهم.
</p>

<p>
	يضيف <a href="https://www.pydev.org/" rel="external nofollow">PyDev</a> عددًا كبيرًا من الميزات إلى إيكلبس Eclipse مثل تلوين الجملة بناءً على لغة البرمجة المستخدمة وإكمال الشيفرة البرمجية ويدمج مصحح أخطاء بايثون بالإضافة إلى أدوات إعادة هيكلة للشيفرة وغيرها الكثير.
</p>

<p>
	يمكن الاستفادة من PyDev عند استخدام إطار عمل الويب جانغو <a href="https://academy.hsoub.com/programming/python/django/" rel="">Django</a> بلغة بايثون الشهير لإنشاء مشاريع جانغو جديدة وتنفيذ إجراءات جانغو باستخدام مفاتيح الاختصار hotkeys واستخدام تكوين تشغيل منفصل خاص بجانغو.
</p>

<p>
	يتوفر كل من Eclipse و PyDev بموجب <a href="https://www.eclipse.org/legal/epl-v10.html" rel="external nofollow">ترخيص Eclipse العام</a>.
</p>

<h2>
	خيارات أخرى
</h2>

<p>
	يتوفر الكثير من خيارات برامج تحرير شيفرات بايثون مفتوحة المصدر وبيئات التطوير المتكاملة، وفيما يلي البعض منها:
</p>

<ul>
	<li>
		<a href="http://pythontoolkit.sourceforge.net/contact.html" rel="external nofollow">أدوات بايثون Python Tool Kit</a> والتي تعرف اختصارًا باسم PTK و <a href="https://github.com/pyscripter/pyscripter" rel="external nofollow">PyScripter</a> و <a href="https://wiki.python.org/moin/LeoEditor" rel="external nofollow">LeoEditor</a> وهي أدوات لتحرير شيفرة بايثون، ويوجد IDLE وهو بيئة تطوير IDE لبايثون.
	</li>
	<li>
		برامج تحرير شيفرات بايثون البرمجية للأغراض العامة مع دعم بايثون من خلال الملحقات مثل <a href="https://www.geany.org/" rel="external nofollow">Geany</a> و <a href="https://atom.io/" rel="external nofollow">Atom</a> و <a href="https://brackets.io/" rel="external nofollow">Brackets</a>.
	</li>
	<li>
		بيئات تطوير متكاملة مفتوحة المصدر للأغراض العامة يمكن تحويلها كما في Eclipse إلى برامج تحرير بايثون باستخدام ملحقات محددة وبعض التوسعات، ومن الأمثلة عليها: <a href="https://opensource.com/netbeans" rel="external nofollow">Netbeans</a> و <a href="https://code.visualstudio.com/" rel="external nofollow">VS Code</a> أو النسخة الحرة منه <a href="https://github.com/VSCodium/vscodium" rel="external nofollow">VS Codium</a>.
	</li>
	<li>
		قد تمتلك معظم برامج تحرير التعليمات البرمجية القابلة للتوسيع دعم بايثون، فمثلًا يمكن تحويل محرر النصوص إيماكس Emacs إلى بيئة تطوير بايثون متكاملة Python IDE باستخدام بعض الحزم وإعدادات الضبط التي يجب استخدامها، كما يحتوي محرر KDE Kate على مزايا مثل إبراز صيغة الجملة والتحكم بإظهار أو إخفاء الشيفرة البرمجية code collapsing ودعم قراءة كافة ملفات المشروع بمجرد فتح مجلد المشروع الرئيسي، بالإضافة إلى امتدادات أخرى مُساعدة.
	</li>
</ul>

<p>
	يمكن الاطلاع على قائمة أشمل مما ذُكر في هذا المقال حول خيارات تحرير شيفرات بايثون من خلال صفحة <a href="https://wiki.python.org/moin/IntegratedDevelopmentEnvironments" rel="external nofollow">بيئات التطوير المتكاملة</a> وصفحة <a href="https://wiki.python.org/moin/PythonEditors" rel="external nofollow">المحررات</a> من توثيق بايثون الرسمي.
</p>

<h2>
	خاتمة
</h2>

<p>
	يجب الانتباه إلى أنه لا يمكن قول أن محرر أو بيئة تطوير محددة أفضل من غيرها لتطوير مشاريع بايثون، حيث أن الخيارات كثيرة بكثر حاجات المستخدمين المختلفة، ويجب أن يكون التركيز منصبًا حول الخيار الذي يحل المشكلة التي يواجهها المستخدم أو اختيار المناسب لخبرته، فبالنسبة للمبتدئين يعتبر اختيار بيئة تطوير متكاملة أو محرر نصوص بسيط صغير الحجم أفضل، أما إذا رغب المستخدم بمحرر يساعده على كتابة شيفرة أكثر وضوحًا مع أخطاء أقل فسيساعده اختيار محرر متقدم غني بالميزات على تحقيق مراده.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/resources/python/ides" rel="external nofollow">Top 5 open source Python IDEs</a> لصاحبه Jason Baker.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/" rel="">مدخل إلى بيئة التطوير المتكاملة IDE</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1772</guid><pubDate>Tue, 29 Nov 2022 16:01:00 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x645;&#x62D;&#x631;&#x631; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x641;&#x64A;&#x645; Vim</title><link>https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-r1769/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/Vim.png.e676323a8664a7350c918ac3f8fdf5a9.png" /></p>

<p>
	في السبعينيات، كانت تُعالج الملفات النصية بطريقة بدائية جدًا لا تشبه أي شكل من أشكال محررات النصوص اليوم مثل <a href="https://academy.hsoub.com/apps/productivity/liberoffice/" rel="">تطبيقات ليبر أوفيس</a> وذلك باستخدام أوامر مثل <code>ed</code> التي تبحث على سطر معين وتدرج بعده نصًا آخر أو تزله بشكل مشابه لقائمة البحث والاستبدال حاليًا ولكن بأسلوب أقرب إلى آلة كاتبة متطورة قليلًا، إلى أن جاء بيل جوي Bill Joy وصمم أول محرر مرئي عُرف باسم Vi اختصارًا لعبارة visual editor الذي اكتسب شهرةً واسعةً لكونه تطبيقًا مريحًا جدًا أتاح عرض الملفات بالكامل على الشاشة وتحريرها مباشرة.
</p>

<p>
	طُوِّرَت لاحقًا نسخة من Vi باسم <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71/" rel="">Vim</a> اختصارًا لعبارة Vi Improved أي تطبيق Vi المُحسَّن، ولهذا السبب يُعرف فيم Vim باسم Vi أيضًا، وقد بقيت الأوامر الخاصة بفيم في بعض أنظمة بوزيكس POSIX تحمل اسم vi، ويعتبر فيم شائع الاستخدام أكثر من في Vi.
</p>

<h2>
	محرر النصوص فيم Vim
</h2>

<p>
	فيم Vim هو محرر نصوص خاص <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">بنظام التشغيل</a> يونيكس Unix ومضمّن في كل من أنظمة التشغيل <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس Linux</a> وماك mac ونظام BSD، يشتهر بالسرعة والكفاءة في الأداء لكونه تطبيقًا صغيرًا يمكن تشغيله من خلال سطر الأوامر أو مع واجهة رسومية، ولكن الخيار الأول هو الغالب لأنه يسمح بالتحكم الكامل به بواسطة لوحة المفاتيح دون الحاجة إلى القوائم أو الفأرة، فمثلًا لإدراج نص في ملف تضغط على المحرف <strong>I</strong> وتكتب المطلوب، أما للتنقل أو إصدار أوامر مثل حفظ Save وحذف محرف Backspace والانتقال إلى بداية الجملة Home والانتقال إلى نهاية الجملة End وغيرها العديد من الأوامر تضغط على <strong>Esc</strong> في لوحة المفاتيح ثم تضغط على أي مفتاح أو مجموعة مفاتيح تتوافق مع الإجراء الذي تريد تنفيذه.
</p>

<p>
	تختلف طريقة استخدام المحرر فيم عن استخدام المحررات الحديثة، ولكنها الطريقة التي يستخدمها مديري أنظمة يونيكس Unix في جميع أنحاء العالم لتحرير ملفات ضبط إعدادات النظام وسجلات التغييرات changelogs والسكربتات.
</p>

<h2>
	تثبيت فيم Vim
</h2>

<p>
	تأتي بعض الأنظمة مزودة مسبقًا بمحرر Vi ويتوجب عليك تثبيت Vim بشكل منفصل، وللتحقق مما إذا كان Vim مثبتًا لديك استخدم الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7293_8" style="">
<span class="pln">which vim</span></pre>

<p>
	إذا لم تحصل على أي شيء فلن يكون Vim مثبتًا ويمكنك تثبيته من <a href="https://www.vim.org/" rel="external nofollow">الصفحة الرئيسية</a> للموقع الخاص به.
</p>

<p>
	تقدم العديد من التوزيعات بعض البُنى المختلفة لفيم مما يعني وجود بنية توفر الحد الأدنى من الإمكانيات التي تحاكي Vi القديم مع تضمين ما يكفي من ميزات وبنية محسّنة توفر تجربة استخدام جيدة وحديثة.
</p>

<p>
	أفضل طريقة لتحديد ما هو متاح هو البحث في مدير الحزم الخاص بك عن أي حزمة تحتوي على السلسلة "vim" ثم تثبيت الحزمة التي تحتوي على معظم الميزات مثل حزمة <strong>vim-Enhanced</strong> على فيدورا Fedora.
</p>

<h2>
	حالات استخدام المحرر فيم Vim
</h2>

<p>
	فيم هو المحرر الاحتياطي الافتراضي في جميع أنظمة بوزيكس POSIX، سواء كنت قد قمت للتو بتثبيت نظام تشغيل جديد أو قمت بالإقلاع في بيئة ذات حد أدنى من الإمكانيات بهدف إصلاح نظام أو إجراء شيء ما وحينها إن لم تتمكن من استخدام أي محرر آخر فمن المؤكد أن فيم سيكون أفضل خيار للاستخدام آنذاك، وطبعًا يمكن في جميع هذه الحالات استخدام برامج تحرير صغيرة أخرى مثل جنو نانو GNU Nano أو Jove إلا أن فيم Vim يعتبر محررًا مناسبًا لأي نظام حاسوب مهما كان.
</p>

<p>
	تمتد فعالية المحرر فيم لتشمل <a href="https://academy.hsoub.com/design/user-experience/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-%d8%aa%d8%ac%d8%b1%d8%a8%d8%a9-%d8%a7%d9%84%d9%85%d8%b3%d8%aa%d8%ae%d8%af%d9%85-user-experience-r149/" rel="">تجربة الاستخدام</a> أيضًا وليس فقط الأداء، حيث لا تعتمد واجهة فيم المحلية القائمة على سطر الأوامر على القوائم أو الأجهزة الطرفية الممتازة أو حتى المفاتيح الإضافية مثل <strong>Ctrl</strong> أو <strong>Alt</strong> بل يستخدم فيم المفاتيح الشائعة لأي لوحة مفاتيح بغض النظر عن اللغة أو النُسق أو الجهاز، كما يمكن إعادة تعيين تلك غير الشائعة بسهولة تامة.
</p>

<h2>
	كيفية استخدام فيم
</h2>

<p>
	يمكن استخدام محرر النصوص فيم في كل من الأجهزة المحمولة وأجهزة الحواسيب.
</p>

<h3>
	استخدام فيم في الأجهزة المحمولة
</h3>

<p>
	إذا شغلت <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر أوامر</a> على هاتفك المحمول فيعد فيم خيارًا رائعًا للتحرير باستثناء مشكلة واحدة وهي عدم وجود مفتاح <strong>Esc</strong> وآنذاك يتوفر حلين:
</p>

<ul>
<li>
		تنزيل لوحات مفاتيح بديلة متاحة ولكنها ستضيف مفاتيح إضافية على حساب حجم لوحة المفاتيح.
	</li>
	<li>
		ضبط فيم ليعين تسلسل مفاتيح يقابل الضغط عليها الضغط على مفتاح <strong>Esc</strong>، ولتحقيق ذلك أضف أمر <strong>imap</strong> التالي إلى ملف الضبط <strong>vimrc.</strong> الخاص ببيئتك، فيستخدم المثال التالي فاصلتين كتسلسل يقابل المفتاح <strong>Esc</strong>، ولكن يمكنك استخدام أي شيء لا تستخدمه عادةً في الكتابة كتتابع سريع، مثل <strong>qq</strong> أو <strong>yy</strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7293_12" style="">
<span class="pln">imap ,, </span><span class="tag">&lt;Esc&gt;</span></pre>

<h3>
	استخدام فيم في أجهزة الحاسوب
</h3>

<p>
	على الرغم من امتلاك المحرر فيم الكثير من الإمكانيات إلا أنه لا يوجد سوى عدد قليل من عناصر التحكم التي يجب معرفتها من أجل استخدام فيم، لذا قام مطوروه بإنشاء شرح مخصص <a href="http://www2.geog.ucl.ac.uk/~plewis/teaching/unix/vimtutor" rel="external nofollow">vimtutor</a> وهو عبارة عن جولة بسيطة وتفاعلية تعلمك أساسيات استخدام فيم، ومنها:
</p>

<ul>
<li>
		لتشغيل فيم: إما من خلال سطر الأوامر بكتابة <strong>vim</strong> أو على سطح المكتب عن طريق تشغيل <strong>gvim</strong> وهو برنامج فيم المزود بواجهة رسومية.
	</li>
	<li>
		للدخول في وضع إدراج النص اكتب على <strong>I</strong>، وفي هذا الوضع لا توجد أوامر بل يمكن فقط كتابة نص في المستند.
	</li>
	<li>
		للدخول إلى الوضع العادي الذي تُستخدم فيه الأوامر اضغط على <strong>Esc</strong>.
	</li>
	<li>
		يمكن في الوضع العادي تحريك المؤشر باستخدام <strong>h</strong> لليسار و <strong>j</strong> لأسفل و <strong>k</strong> لأعلى و <strong>l</strong> لليمين، قد يكون من المفيد تذكر أن j تحرك المؤشر للأسفل من خلال مساواتها بصريًا بالسهم.
	</li>
	<li>
		للخروج من Vim، اكتب <strong>wq:</strong> إذا كنت تريد حفظ عملك أو <strong>!q:</strong> لتجاهل التغييرات غير المحفوظة.
	</li>
</ul>
<p>
	يمكن الاطلاع على أوامر فيم الأخرى غير المذكورة وهي ليست أساسية ولكن تساعد على زيادة فعالية العمل وراحة المستخدم.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111484" href="https://academy.hsoub.com/uploads/monthly_2022_11/63661535971c6_.PNG.578e29caa54035fc44fa3e76805ae3e3.PNG" rel=""><img alt="استخدام فيم.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="111484" data-unique="eekm84buh" src="https://academy.hsoub.com/uploads/monthly_2022_11/636615368000b_.thumb.PNG.e39b343164ca284c6c0e64ff5fca5064.PNG"></a>
</p>

<p>
	عندما تشغل فيم لأول مرة تقوم بإنشاء وفتح ملف نصي مؤقت وفارغ تمامًا كما تفعل عند فتح أي محرر نصوص آخر، ولكن على عكس برامج تحرير النصوص الأخرى لا يمكنك كتابة أي شيء في الملف في البداية لأنك في الوضع العادي normal mode والذي يستخدم لإصدار أوامر لفيم، أما للدخول في وضع الكتابة فيجب الضغط على الحرف <strong>I</strong> في وضع الأوامر أو الوضع العادي الذي يعني الدخول في وضع الكتابة أو الإدراج وحينها يمكنك الكتابة وإدراج النصوص.
</p>

<p>
	تعتبر هذه الأوضاع أدوات تبديل يتم تشغيلها أو إيقاف تشغيلها لتغيير حالة بعض الإجراءات، فمثلًا عندما تريد إرجاع كلمة تضغط على المفتاح <strong>Esc</strong> للدخول إلى الوضع العادي ثم تضغط على الحرف <strong>b</strong> لتحريك المؤشر إلى اليسار بمقدار كلمة واحدة.
</p>

<p>
	يعد هذا التنقل المستمر بين الوضع العادي ووضع الإدراج سلبية فيم الوحيدة وخاصةً بالنسبة للمستخدمين الجدد ولكنها مع ذلك غير معقدة إذ يتطلب استخدامه فترة من الوقت للاعتياد عليه، فهو يشبه إلى حد ما الضغط على مفتاح <strong>Caps Lock</strong> بدلاً من مفتاح <strong>Shift</strong> للحصول على سلسلة من الأحرف الكبيرة أو الضغط مع الاستمرار على لوحة مفاتيح الهاتف للحصول على محرف بديل للمحرف الظاهر على الزر.
</p>

<h2>
	ملحقات فيم Vim plugins
</h2>

<p>
	من أبرز ميزات فيم هي قابليته للتوسيع أي يمكن إضافة ميزات إليه من خلال الملحقات مثل أنظمة الألوان البسيطة و<a href="https://github.com/preservim/nerdtree" rel="external nofollow">مديري الملفات file managers</a> وغيرها وهذه الملحقات كثيرة ويمكن اختيار المفيدة منها أو جميعها من خلال موقع <a href="https://vimawesome.com/" rel="external nofollow">Vim Awesome</a> المخصص لملحقات فيم، وبعد اختيار الملحقات المطلوبة يمكن تثبيتها يدويًا أو باستخدام مدير حزم Vim مثل <a href="https://github.com/junegunn/vim-plug" rel="external nofollow">Vim-plug</a>.
</p>

<p>
	تعد الملحقات عامل جذب يجذب المستخدمين الجدد لاستخدام المحرر فيم حيث تساعدهم على استخدام فيم بالشكل المطلوب تمامًا وبسهولة استخدام كبيرة دون تعقيد.
</p>

<h2>
	تعلم فيم واحترف استخدامه
</h2>

<p>
	ستشعر في الفترة الأولى من استخدام فيم وكأن شخصًا ما أعاد ترتيب مفاتيح لوحة المفاتيح بدون علمك وسرق منك الفأرة، بالإضافة لبعض الضغط نتيجة التركيز على حفظ وتذكر الأوامر المهمة ولهذا السبب توجد بعض المصادر التي تساعدك على البدء وتكوين أساس متين مثل سلسلة "<a href="https://academy.hsoub.com/tags/%D9%85%D8%AF%D8%AE%D9%84%20%D8%A5%D9%84%D9%89%20%D9%85%D8%AD%D8%B1%D9%91%D8%B1%20vim/" rel="">مدخل إلى محرر Vim</a>" المكونة من ثلاث مقالات وهي:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71/" rel="">تعرف على أساسيات Vim - الجزء الأول</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r76/" rel="">تعرف على أساسيات Vim - الجزء الثاني</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A5%D8%B6%D8%A7%D9%81%D8%A7%D8%AA-vim-r77/" rel="">إدارة إضافات vim</a>
	</li>
</ul>
<p>
	كما تتوفر مقالات أخرى مثل مقال <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-vim-%D9%84%D8%B1%D9%81%D8%B9-%D8%A5%D9%86%D8%AA%D8%A7%D8%AC%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-r1618/" rel="">مميزات محرر النصوص Vim لرفع إنتاجية العمل</a> ومقال <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/" rel="">مقارنة بين محرر النصوص فيم Vim ونانو Nano</a> ننصحك بالرجوع إليها.
</p>

<p>
	يتطلب تعلم فيم واحترافه بعض التدريب وبعد فترة بسيطة سيصبح استخدامه سهلًا للغاية وستلاحظ أنك شارد الذهن وتستخدم زر <strong>Esc</strong> في جميع تطبيقاتك المفضلة دون انتباه.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/resources/what-vim" rel="external nofollow">What is Vim?‎</a>.
</p>
]]></description><guid isPermaLink="false">1769</guid><pubDate>Sat, 05 Nov 2022 07:58:51 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x645;&#x62D;&#x631;&#x631; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x625;&#x64A;&#x645;&#x627;&#x643;&#x633; Emacs</title><link>https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A5%D9%8A%D9%85%D8%A7%D9%83%D8%B3-emacs-r1761/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_11/6362479c78382_-----Emacs.png.c78897dd5153aa2bd88f4bba6dc895a4.png" /></p>

<p>
	صُمِّم محرر النصوص إيماكس Emacs لأنظمة تشغيل <a href="https://ar.wikipedia.org/wiki/%D8%A8%D9%88%D8%B2%D9%8A%D9%83%D8%B3" rel="external nofollow">بوزيكس POSIX</a> وهو متاح لكل من <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس Linux</a> وماك mac وويندوز Windows ونظام بي إس دي BSD، تتباين الآراء حوله فهو المحرر المفضل للكثيرين بسبب امتلاكه أوامر فعالة تطورت باستمرار لمدة 40 عامًا والتي تكسبه العديد من المزايا مثل إضافة الملحقات وتعديل إعدادات الضبط وتنفيذ الإجراءات الشائعة والمعقدة، بينما يعتبره آخرون محرر نصوص معقّد وغامض لأنه محرر قديم جرى تطويره قبل وجود مصطلحات الحاسوب الحديثة فهو يستخدم مصطلحات مثل "زُر visit" بدلًا من "افتح opening" ومصطلح "اكتب write" بدلًا من "احفظ save".
</p>

<p>
	لا يمكن اتخاذ قرار حوله بهذه السرعة، لذا يساعدك هذا المقال بالاطلاع على كل تفاصيله وخصائصه.
</p>

<h2>
	مشروع جنو إيماكس GNU Emacs
</h2>

<p>
	في الفترة الأولى للحوسبة لم تكن معالجات الكلمات موجودة على الإطلاق وكانت محررات النصوص بالكاد موجودة، حيث أن محرر النصوص هو في الأساس معالج كلمات بدون أي مزايا مثل تنسيق وتصميم الصفحة أو تغيير نوع الخط وكانت بدائية لدرجة أنها لا تفتح حتى مستند نصي كامل مرة واحدة.
</p>

<p>
	كان محرر النصوص عبارة عن أمر برمجي يؤدي وظائف محددة مثل توليد كلمات وإضافتها إلى ملف نصي أو البحث عن كلمات معينة في الملف واستبدالها بكلمات أخرى أو إزالة أسطر من ملف، أي يلزم وقت طويل لتنفيذ عدة أوامر وبذلك يصبح تحرير المستندات الكبيرة عملية مملة، لذلك بدأ الأشخاص في تطوير ماكرو macros وهي تعليمة جامعة لأداء المهام الشائعة ذات الصلة، حيث أصدر د. ريتشارد ستالمان Richard Stallman في عام 1983 مجموعة من الماكرو الخاصة به تحت اسم ماكرو التحرير أو emacs للاختصار.
</p>

<p>
	عندما بدأ د. ستالمان مشروع جنو أصبح Emacs أحد أكثر تطبيقاته نجاحًا وامتلك إيماكس عدة إصدارات ولكن <a href="https://www.gnu.org/software/emacs/" rel="external nofollow">GNU Emacs</a> هي النسخة الأكثر شيوعًا.
</p>

<h2>
	خصائص المحرر Emacs
</h2>

<p>
	يمتلك إيماكس العديد من الخصائص التي تجعله خيارًا مفضلَا للكثيرين من مختلف التخصصات والاهتمامات، من أهمها أنه مجاني وغيرها من الخصائص نذكرها تاليًا.
</p>

<h3>
	قلة استهلاك موارد النظام
</h3>

<p>
	إيماكس صغير الحجم بالمقارنة مع محررات البرمجة الحديثة التي تستخدم محركات متصفح الويب وخوادم <a href="https://academy.hsoub.com/programming/javascript/%d9%85%d8%a7-%d9%87%d9%8a-%d8%ac%d8%a7%d9%81%d8%a7-%d8%b3%d9%83%d8%b1%d9%8a%d8%a8%d8%aa-%d8%9f-r524/" rel="">جافا سكريبت</a> كواجهة خلفية لها، ويعد محرر بسيط مكتوب بلغتي سي C وليسب Lisp لذا لا يكاد يستهلك شيئًا من موارد النظام مع أنه يفعل أكثر بكثير من مجرد تحرير النص مما يجعله أداة مهمة.
</p>

<h3>
	الاعتماد على مجموعات لوحة المفاتيح
</h3>

<p>
	يستخدم إيماكس مجموعات لوحة مفاتيح تختلف اختلافًا جوهريًا عن الطريقة التي تستخدمها المحررات الحديثة، وهذا لأنه مصمم ليكون مرنًا في كيفية استخدامه وخاصةً ضمن الأجهزة التي لا تحتوي على لوحات المفاتيح التقليدية وعبر الشبكات التي قد لا تنقل مفاتيح التعديل مثل <strong>Ctrl</strong> و <strong>Alt</strong> بشكل صحيح.
</p>

<p>
	تعتمد هذه المجموعات على مفتاحي <strong>Ctrl</strong> و <strong>Alt</strong>، يتم تمثيل مفتاح <strong>Ctrl</strong> في التوثيق على أنه <strong>C</strong> ومفتاح <strong>Alt</strong> على أنه M (لأنه قبل أن يسمى Alt كان يطلق عليه اسم "Meta").
</p>

<p>
	لاستخدام مفتاح آخر مع مفتاح Ctrl، تُكتب التعليمة بالصيغة <code>C-x</code> (بمعنى Ctrl + X) أو بالصيغة <code>C-c</code> (بمعنى Ctrl + C)، وينطبق الأمر نفسه على مفتاح Alt فإن التعليمة <code>M-x</code>تعني الضغط على مفتاحي Alt-X.
</p>

<p>
	تكون بعض مجموعات لوحة المفاتيح عبارة عن تسلسل من مجموعات أبسط، فمثلًا لفتح ملف نكتب <code>C-x</code> للدخول إلى وضع الأوامر ثم <code>C-f</code> للعثور على الملف المطلوب فتحه، ولتقسيم نافذة التحرير أفقيًا (تسمى المخزن المؤقت buffer في Emacs)، نضغط على <code>C-x</code> ثم الرقم 2.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="111153" href="https://academy.hsoub.com/uploads/monthly_2022_11/6362479c46bb4_.PNG.ee838caa7d4c560cebb374610d3e5806.PNG" rel=""><img alt="ارتباطات لوحة المفاتيح.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="111153" data-unique="t1irb13zp" src="https://academy.hsoub.com/uploads/monthly_2022_11/6362479ee4580_.thumb.PNG.cc0a43df3030903da44e699ac9fe7239.PNG"></a>
</p>

<p>
	يرتبط كل إجراء تقريبًا في إيماكس بدالة في Lisp تنفذّ هذه الإجراء، وإذا كان هناك تابع تريد استدعائه ولم يتم ربطه بعد فيمكنك فقط كتابة اسم التابع في مخزن الأوامر المؤقت command buffer تمامًا كما تفعل مع تشغيل تطبيق في سطر أوامر لينكس، على سبيل المثال لتنشيط وضع <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">بايثون Python</a> تضغط على ما يلي: <code>M-x</code> ثم تختار <code>python-mode</code> ثم تضغط <code>Return</code>.
</p>

<p>
	إذا لم يمتلك جهازك مفتاح Alt فيمكنك استبداله بمفتاح Esc لكنه سيعمل بشكل مختلف عنه حيث يجب الضغط على Esc وتحريره ثم الضغط على المفتاح المرتبط به وتحريره، فمثلًا لكتابة الاختصار M-x يجب أولًا الضغط على Esc وتحريره ثم الضغط على X وتحريره، وحتى إذا لم يمتلك جهازك مفتاح Alt أو Esc فيمكنك إعادة تعيين مفاتيح أخرى حسب الحاجة.
</p>

<p>
	تبعد خاصية مجموعات لوحات المفاتيح بعض المستخدمين عن إيماكس، لأن كل عملية فيه هي دالة إجرائية يمكن استدعائها بمزيج من اختصارات لوحة المفاتيح بالتالي يحتاج المستخدم دومًا إلى تذكر هذه الاختصارات مما يجعله صعب التعلم فحتى عند تذكر الجزء الأول من مجموعة لوحة المفاتيح لا يزال هناك الجزء الثاني الذي يجب تذكره وهو ما يشبه إلى حد ما تعلم العزف على البيانو، من ناحية التركيز على المفاتيح وحركات اليد والأصابع، أي يتطلب إتقان إيماكس بعض التدريب وبمجرد التعود عليها تتداخل أوامر المحرر مع عملية نقل الأفكار إلى كلمات ويصبح التفاعل سلسًا ويرتبط إنشاء المحتوى بتحريره سواء كان المحتوى تعليمات برمجية أو مستندات إبداعية وشخصية.
</p>

<h3>
	المرونة وقابلية التخصيص
</h3>

<p>
	يمتلك إيماكس خيارين للتشغيل تبعًا لرغبة المستخدم، حيث يمكن تشغيله في واجهة المستخدم الرسومية وهو ما يُسهّل تعلم البرنامج، أو داخل نافذة <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> وهو الخيار الأنسب لمديري الأنظمة ومطوري الويب أو أي شخص يحتاج إلى تحرير النص عن بُعد، كما يمكن استخدام إيماكس كَوكيل client أي يمكن تشغيله في الخلفية ثم الاتصال به من نافذة أخرى أو من جهاز آخر.
</p>

<h3>
	قابلية للنقل
</h3>

<p>
	يُكتب إيماكس بشيفرة قائمة على الوحدات modular code لذا يُبنى في صيغة مبسطة مع عدد قليل من الاعتماديات وهذا ما يجعل نقله إلى أنظمة أخرى سهلًا وبالتالي لا يوجد نظام حاسوب سواء كان قديمًا أو جديدًا، شائعًا أو متخصصًا إلا ويمكنه تشغيل إيماكس أو على الأقل نسخة منه ذات الحد الأدنى من الإمكانيات، حيث أن جميع إصدارات إيماكس قابلة للنقل بسهولة ولا تتطلب أكثر من مكتبة C وواجهة سطر الأوامر.
</p>

<h3>
	متعدد الأوضاع modes
</h3>

<p>
	يمتلك إيماكس مجموعة من الأوضاع وهي عبارة عن ملحقات plugins تغيّر أداء إيماكس حسب عمل المستخدم فيه، ومن هذه الأوضاع:
</p>

<ul>
<li>
		<a href="https://orgmode.org/" rel="external nofollow">وضع Org</a> لتنظيم الأمور اليومية وإدارة المفكرة
	</li>
	<li>
		وضع Deft لتدوين الملاحظات
	</li>
	<li>
		<a href="https://github.com/rnkn/fountain-mode" rel="external nofollow">وضع Fountain</a> لكتابة النصوص السينمائية
	</li>
	<li>
		وضع Muse لكتابة الأوراق والمقالات العلمية
	</li>
	<li>
		<a href="https://www.gnu.org/software/emacs/manual/html_mono/ses.html" rel="external nofollow">وضع SES</a> لجداول البيانات
	</li>
	<li>
		<a href="https://www.emacswiki.org/emacs/PythonProgrammingInEmacs" rel="external nofollow">وضع Python</a> لكتابة شيفرات برمجية بلغة بايثون
	</li>
	<li>
		<a href="https://www.emacswiki.org/emacs/ShMode" rel="external nofollow">وضع سكربت صدفة Shell</a> يرمز له Sh، لتحرير Bash
	</li>
	<li>
		<a href="https://www.gnu.org/software/emacs/manual/html_mono/nxml-mode.html" rel="external nofollow">وضع nXML</a> لتحرير XML
	</li>
</ul>
<p>
	بالإضافة إلى الأوضاع، هناك تطبيقات مخصصة لإيماكس مثل:
</p>

<ul>
<li>
		صدفة لتشغيل الأوامر على النظام الذي يشغل إيماكس
	</li>
	<li>
		مدير ملفات قائم على النصوص لتصفح الملفات
	</li>
	<li>
		عارض صور ومشغل وسائط متعددة
	</li>
	<li>
		واجهة رسوم توضيحية بدائية للمساعدة في رسم مخططات انسيابية flowcharts
	</li>
	<li>
		واجهة Git
	</li>
	<li>
		وكيل بريد إلكتروني
	</li>
</ul>
<p>
	لذا مهما كانت حاجة المستخدم سيجد وضع أو تطبيق أو حزمة لتلبيتها، كما يوفر لعبة Tetris لتمضية وقت الملل.
</p>

<h3>
	مفتوح المصدر
</h3>

<p>
	أكثر التطبيقات القابلة للبرمجة اليوم هي جداول البيانات المكتبية office spreadsheets كونها لا تتطلب خبرة في البرمجة على الإطلاق بل تعتمد على الصيغ والماكرو مما يساعد الكثير من الأشخاص على تصميم تطبيقات معقدة وفعالة، وبالطبع هناك حدود لإمكانيات جداول البيانات لذا إن كانت فكرة تطبيقك لا تتناسب تمامًا مع بنية الجداول فقد تعمل بشكل أفضل كنص عادي، وهذا ما حدث مع إيماكس فهو محرر نصوص عن طريق الخطأ فقط.
</p>

<p>
	بدأ إيماكس كمجموعة من الماكرو المفيدة التي تهدف إلى تحرير النص، إلا أن بنية التطبيق بدأت بشيفرة برمجية بلغة سي C التي تنتج مترجم Lisp، مما يجعل إيماكس طريقة عزل لحماية شيفرة ليسب Lisp أي إذا تعلمت <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/" rel="external nofollow">Elisp</a> وهو اختصار Emacs Lisp، يمكنك تعديل التطبيق الذي تقوم بتشغيله أثناء تشغيله ولا يتطلب تنزيل الشيفرة المصدرية أو تصريفها بعد كل تغيير، ويمكن إنشاء أي بيئة على الفور حسب رغبة المستخدم بمهارة ودقة.
</p>

<p>
	يتيح إيماكس الاطلاع على الشيفرة التي تؤثر على بيئة العمل وتعديلها أو تغييرها وتكون الشيفرة موثقة، والبناء فوق العمل الذي أنشأه الآخرون، والتحكم في الأدوات وتخصيصها.
</p>

<h2>
	ابدأ باستخدام إيماكس
</h2>

<p>
	تختلف مصادر تثبيت إيماكس تبعًا <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">لنظام التشغيل</a> المُستخدَم، لتثبيته على نظام لينكس Linux أو نظام بي إس دي BSD يجب الذهاب إلى مستودع التوزيع عبر مدير الحزم وتنزيله منها، أما على أنظمة ماك Mac وويندوز Windows، يمكن تنزيله وتثبيته من <a href="https://www.gnu.org/software/emacs/" rel="external nofollow">موقع GNU Emacs</a>.
</p>

<p>
	نجد أن إيماكس كغيره من المحررات يمتلك إيجابيات وبعض السلبيات، وحتى مع وجود هذه السلبيات وبمجرد تعلم أساسياته ستمتلك محررًا قويًا وفعالًا وقابلًا للتعديل وتوسيع إمكانياته بشكل كبير دومًا، لذا نرى أنه خيار يستحق النظر والتجربة.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/resources/what-emacs" rel="external nofollow">What is Emacs?‎</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%AF%D9%81%D8%A9-shell-scripts-r252/" rel="">مدخل إلى كتابة سكربتات الصدفة Shell Scripts</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-vim-%D9%84%D8%B1%D9%81%D8%B9-%D8%A5%D9%86%D8%AA%D8%A7%D8%AC%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-r1618/" rel="">مميزات محرر النصوص Vim لرفع إنتاجية العمل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A7%D9%84%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-r1436/" rel="">الأدوات المستخدمة في بناء مواقع ويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D8%AF%D9%81%D8%A9-%D8%A8%D8%A7%D8%B4-bash-r606/" rel="">مدخل إلى صدفة باش Bash</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1761</guid><pubDate>Wed, 02 Nov 2022 11:00:42 +0000</pubDate></item><item><title>&#x645;&#x645;&#x64A;&#x632;&#x627;&#x62A; &#x645;&#x62D;&#x631;&#x631; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; Vim &#x644;&#x631;&#x641;&#x639; &#x625;&#x646;&#x62A;&#x627;&#x62C;&#x64A;&#x629; &#x627;&#x644;&#x639;&#x645;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-vim-%D9%84%D8%B1%D9%81%D8%B9-%D8%A5%D9%86%D8%AA%D8%A7%D8%AC%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-r1618/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62bade679d058_-----vim---.png.a18042f0cd2e1eb5ec974c77cc8e1a4a.png" /></p>

<p>
	<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71/" rel="">محرر النصوص Vim</a> (ويلفظ فيم) هو المحرر الأكثر استخدامًا اليوم فهو متوفر في معظم الأجهزة على حساب المحررات الأخرى مثل نانو Nano وإيماكس Emacs و Vscodium ولا تكاد تخلو منه أي جلسة اتصال <a href="https://academy.hsoub.com/devops/security/ssh/%D8%A3%D9%86%D9%81%D8%A7%D9%82-ssh%D8%8C-%D9%85%D8%A7%D9%87%D9%8A%D8%AA%D9%87%D8%A7-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D9%87%D8%A7-r76/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></a>.
</p>

<p>
	وسنعرض في هذا المقال المخصص للأشخاص المتمكنين من أساسيات العمل على المحرر بعضًا من مزاياه المتقدمة والمفيدة التي تخفى حتى على من استخدامه لسنوات، ويفترض هذا المقال معرفة مسبقة بمحرر vim أما إن كنت حديث عهد بهذا المحرر ولا تعرف عنه الكثير، فننصحك بالرجوع أولًا إلى مقال <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71" rel="">تعرف على أساسيات Vim</a>.
</p>

<h2>
	إضافة الإشارات المرجعية
</h2>

<p>
	يوفر لك المحرر فيم Vim إمكانية إضافة الإشارات المرجعية bookmarks على مقاطع النص لتمييزها وتسهيل التنقل بينها وهذه العملية مفيدة جدًا في حالة النصوص الكبيرة.
</p>

<p>
	إليك طريقة الاستخدام؛ لوضع إشارة مرجعية اكتب m (الحرف الأول من mark) ويليه اسم الإشارة المرجعية.
</p>

<p>
	لنفترض مثلًا أن لديك مقطعًا في النص يتحدث عن الأسماء names وتريد تمييزه بوضع إشارة مرجعية اسمها n فعليك إذن أن تكتب mn، وإن أردت العودة للمقطع الخاص بالأسماء من أي موضع آخر في النص يكفيك كتابة `n لتنتقل إلى موقع الإشارة المرجعية n تمامًا، أو كتابة 'n لتنتقل إلى بداية السطر الذي يحتوي هذه الإشارة المرجعية.
</p>

<p>
	في حال رغبت بحذف الإشارة المرجعية n استخدم الأمر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_3103_10" style="">
<span class="pun">:</span><span class="pln">delmarks n</span></pre>

<p>
	ولاستعراض كافة الإشارات المرجعية استخدم الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_3103_12" style="">
<span class="pun">:</span><span class="pln">marks </span></pre>

<p>
	مع ضرورة الإشارة إلى أن استخدام الأحرف الإنكليزية الصغيرة في تسمية الإشارات المرجعية يعني أن هذه الإشارات محلية تستخدم ضمن المستند الحالي فقط، أما تسميتها بالأحرف الكبيرة تعني أن الإشارة عامة ويمكن الاستدلال بها ضمن عدة مستندات نصية أي أن استخدامه ليس محصورًا بالمستند نفسه.
</p>

<h2>
	تعريف الاختصارات الخاصة بالمستخدم
</h2>

<p>
	يتيح لك هذا المحرر إمكانية إنشاء اختصارات للكلمات والعبارات الطويلة والتي قد تكون كتابتها مزعجة وبالأخص إن تكررت أكثر من مرة ضمن النص.
</p>

<p>
	عرف الاختصارات باستخدام الأمر ab: يليه الاختصار ومن ثم العبارة المراد اختصارها، فمثلًا إن أردت إنشاء اختصار للعبارة Acme Painted Fake Roadways, Incs إلى شيء مثل apfr فيمكنك فعل ذلك وفق مايلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_14" style="">
<span class="pun">:</span><span class="pln">ab apfr </span><span class="typ">Acme</span><span class="pln"> </span><span class="typ">Painted</span><span class="pln"> </span><span class="typ">Fake</span><span class="pln"> </span><span class="typ">Roadways</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Inc</span></pre>

<p>
	والآن في كل مرة تكتب فيها الاختصار apfr متبوعًا بفراغ يبدله المحرر تلقائيًا إلى العبارة كاملة دون أن تحتاج لكتابتها.
</p>

<p>
	أما إن رغبت بإزالة أي اختصار، استخدم الأمر uab: متبوعًا بالاختصار مثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_16" style="">
<span class="pun">:</span><span class="pln">uab apfr</span></pre>

<p>
	وتجدر الإشارة إلى أن الاختصارات التي تعرفها ضمن جلسة العمل تعتبر مؤقتة وتزول بمجرد إغلاق الجلسة.
</p>

<h2>
	الإكمال التلقائي للكلمات أثناء الكتابة
</h2>

<p>
	في الواقع يجهل العديد من المستخدمين قدرة هذا المحرر على الإكمال التلقائي للكلمات المكتوبة سابقًا ضمن النص.
</p>

<p>
	لنفترض أنك تكتب نصًا يتضمن كلمة مثل Antarctica عدة مرات فبعد كتابتها لأول مرة ضمن النص يمكنك لاحقًا كتابة الحروف الأولى منها فقط مثلًا ant والضغط على Ctrl+P وسيظهر لك المحرر كافة الكلمات التي كتبتها سابقًا في النص وتبدأ بحروف ant وعندها استخدم زر tab للتنقل بين الاحتمالات واختر الكلمة التي تريدها وسيقوم المحرر استكمالها تلقائيًا مع العلم أن زيادة عدد الحروف المكتوبة سيضيق لائحة الكلمات المحتملة أكثر.
</p>

<h2>
	تحديد نطاق
</h2>

<p>
	تتيح هذه الميزة للمستخدم تحديد نطاق معين من الأسطر وتطبيق جملة من الإجراءات عليه بشكل موحد وفق مايلي.
</p>

<p>
	لتحديد نطاق معين تبدأ كتابة الأمر برقم أول سطر في النطاق المطلوب تحديده متبوعًا بفاصلة يليها رقم أخر سطر في النطاق وهذا السطر يكون مضمنًا (فتطبق عليه الإجراءات)، كما يمكنك استخدام رمز النقطة للإشارة إلى السطر الحالي، ورمز $ للإشارة إلى السطر الأخير، أما % تعني كامل المستند.
</p>

<p>
	نورد بعض الأمثلة (مع التأكيد على أن كتابة هذه التعليمات تتم والمحرر في نمط الأوامر):
</p>

<ol>
<li>
		حذف الأسطر من 2 إلى 10:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_18" style="">
<span class="pun">:</span><span class="lit">2</span><span class="pun">,</span><span class="lit">10d</span></pre>

<ol start="2">
<li>
		حذف الأسطر من 25 حتى نهاية المستند:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_20" style="">
<span class="pun">:</span><span class="lit">25</span><span class="pun">,</span><span class="pln">$d</span></pre>

<ol start="3">
<li>
		حذف كافة الأسطر:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_22" style="">
<span class="pun">:%</span><span class="pln">d</span></pre>

<ol start="4">
<li>
		نسخ الأسطر من 5 إلى 10 ولصقها بعد السطر 15:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_24" style="">
<span class="pun">:</span><span class="lit">5</span><span class="pun">,</span><span class="lit">10t</span><span class="pln"> </span><span class="lit">15</span></pre>

<ol start="5">
<li>
		قص الأسطر من 5 إلى 10 ولصقها بعد السطر 15:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3103_26" style="">
<span class="pun">:</span><span class="lit">5</span><span class="pun">,</span><span class="lit">10m</span><span class="pln"> </span><span class="lit">15</span></pre>

<h2>
	رفع الانتاجية عبر Vim
</h2>

<p>
	مع هذا المحرر لدينا دائمًا أشياء جديدة ومفيدة نتعلمها فهو غني للغاية ومليء بالميزات الجديرة بالاستكشاف والتي تخفى عن مستخدمين خبروه لسنوات.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/article/22/3/vim-features-productivity" rel="external nofollow">‎4 Vim features to use to improve productivity</a> لصاحبه Hunter Coleman.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71/" rel="">تعرف على أساسيات Vim - الجزء الأول</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r76/" rel="">تعرف على أساسيات Vim - الجزء الثاني</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A5%D8%B6%D8%A7%D9%81%D8%A7%D8%AA-vim-r77/" rel="">إدارة إضافات vim</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1618</guid><pubDate>Tue, 28 Jun 2022 11:20:18 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x628;&#x64A;&#x646; &#x645;&#x62F;&#x64A;&#x631; &#x627;&#x644;&#x62D;&#x632;&#x645; NPM &#x648; Yarn</title><link>https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%88-yarn-r1597/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/6297466e859c0_----Yarn--NPM.jpg.f3e9612e6249a40aa4f5ebc06ca2d0fa.jpg" /></p>

<p>
	يعد مجال البرمجة عمومًا وتطوير الويب خصوصًا من أكثر المجالات نموًا في السنوات القليلة الماضية، فنشهد شهريًا صدور تقنية جديدة سواء كانت مكتبة أو إطار عمل ومع صدور تقنية جديدة تختفي مقابلها تقنية أخرى ولتأخذ محلها في النظام البيئي للبرمجة عمومًا ولبرمجة الويب خصوصًا، الأمر الذي يجعل المبرمج يحتار في اختيار ما يتعلمه وحتى إدارته وخصيصًا إذا كان مبتدئًا في مجال البرمجة.
</p>

<p>
	فأصبح لدينا مثلًا مكتبات (خصوصًا مكتبات وأطر عمل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> الكثيرة والمتنوعة) والتي تعتمد بدورها على مكتبات أخرى وتلك الأخيرة تعتمد على مكتبات وهلم جرًا، فإن احتجت في مشروع تطوير موقع مثلًا إلى مكتبة تحوي ميزة ما، فعملية تنزيل مكتبة أو إطار العمل يدويًا عملية صعبة ومعقدة وهنا يتطلب الأمر أداة تدير كل تلك الأمور وهنا جاءت فكرة مدير الحزم.
</p>

<p>
	سنغطي في هذا المقال الفكرة الكامنة وراء مدير الحزم وسنستعرضُ جوانبًا مختلفة من أشهر مديري حزم للغة جافاسكربت في وقتنا الحالي وهما Yarn و NPM لنرى كيف ستوفر الوقت والجهد وتناسب احتياجاتك المشاريع الحديثة. هذا المنشور ليس مجرد دليل حول مدير الحزم Yarn و <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">NPM</a> ولكنه يهدف إلى توفير هيكل للمساعدة في الحكم على مدير الحزم بشكل عام، وفي حالة طرح مدير حزم جديد في الشهر المقبل فإنك ستعرف بالضبط ما هي الأمور التي يجب النظر إليها!
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: نسعى لتجنب التحيز بين مديري الحزم ولكن بالتأكيد هناك بعض المشاكل يحلها مدير حزم بطريقة أفضل من الآخر وهذا أمر طبيعي.
		</p>
	</div>
</blockquote>

<p>
	نفترض في هذا المقال بأن لديك معرفة أساسية بتقنيات تطوير الويب (مثل لغة <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>) وكيفية عمل المواقع بشكل عام.
</p>

<h2>
	تاريخ ونشأة مدير الحزم
</h2>

<p>
	منذ زمن ليس ببعيد كانت رحلة مطور الويب وتحديدًا مطور الواجهات الأمامية سهلة نسبيًا إذ كل ما سيحتاج تعلمه هو HTML و CSS وجافاسكربت وفي حال أراد زيادة الاحترافية والجودة في عمله فيمكنه الاطلاع على <a href="https://wiki.hsoub.com/Bootstrap" rel="external">Bootstrap</a> مثلًا أو بعض الحركات والخدع بلغة جافاسكربت، ولكن الأمر اختلف بصورة كبيرة فاليوم نتحدث عن مدير الحزم (مثل Npm) ومحول الشيفرة البرمجية (مثل Babel) ومجمع الحزم (مثل <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">Webpack</a>) بل حتى لغات جديدة (مثل <a href="https://academy.hsoub.com/programming/javascript/typescript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-typescript-r1214/" rel="">لغة TypeScript</a>) والكثير من التقنيات الأخرى الجديدة. الأمر الذي يجعل المبتدئين ينفرون من كل هذا التعقيد، ولكن مهلًا هل صناعة صفحة ويب تحتاج بالضرورة لهذه الدرجة من التعقيد؟
</p>

<p>
	في الحقيقة نعم محاولة إرضاء مستخدمي القرن الواحد والعشرين وتحديدًا الجيل زد يحتاج منا مواكب مستوى الجودة الذي تعودوا عليه من استخدامهم لتطبيقات الشركات الكبرى مثل فيسبوك وانستغرام ..إلخ ولكن الأمر ليس صعبًا إذا فهما المشاكل التي استدعت وجود مثل هكذا حلول.
</p>

<p>
	سنوضح أهمية مدير الحزم من خلال المثال التالي: كانت الطريقة القديمة في كتابة الشيفرة البرمجية هي كتابة الهيكل العام للصفحة من خلال لغة HTML وفي حال أردنا تضمين تنسيقات ولغة جافاسكربت ليكون الموقع تفاعليًا يمكننا تنفيذ الأمر بطريقتين إما شيفرة سطرية أي وضع التعليمات البرمجية للغة جافاسكربت <a href="https://academy.hsoub.com/programming/css/%D8%A7%D9%84%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%84%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%81%D9%8A-css-r1054/" rel="">وتنسيقات CSS</a> في نفس الصفحة لتكون على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4341_13" style="">
<span class="pun">&lt;</span><span class="pln">html</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">head</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">meta charset</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">title</span><span class="pun">&gt;</span><span class="typ">Inline</span><span class="pln"> </span><span class="typ">Example</span><span class="pun">&lt;/</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">head</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">body</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">The</span><span class="pln"> </span><span class="typ">Answer</span><span class="pln"> is
          </span><span class="pun">&lt;</span><span class="pln">span id</span><span class="pun">=</span><span class="str">"answer"</span><span class="pun">&gt;&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">

        </span><span class="pun">&lt;</span><span class="pln">script type</span><span class="pun">=</span><span class="str">"text/javascript"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="kwd">function</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          </span><span class="kwd">function</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> iteratee</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
              length </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
              memo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">
            </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> length</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
              memo </span><span class="pun">=</span><span class="pln"> iteratee</span><span class="pun">(</span><span class="pln">memo</span><span class="pun">,</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">])</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> memo</span><span class="pun">;</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          </span><span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">){</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> add</span><span class="pun">);</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          </span><span class="com">/* Main Function */</span><span class="pln">
          </span><span class="kwd">var</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pln"> </span><span class="pun">];</span><span class="pln">
          </span><span class="kwd">var</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln">
          document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"answer"</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> answer</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">body</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">html</span><span class="pun">&gt;</span></pre>

<p>
	بالرغم من أن هذه الطريقة جيدة نسبيًا إذ كل الشيفرة موجودة أمامنا إلا أن هذه هي الطريقة المثالية لبناء شيفرة برمجية غير قابلة للصيانة وذلك بسبب عدم إمكانية إعادة استخدام الشيفرة البرمجية في صفحات الويب الأخرى في حال احتجنا لها وعدم وجود ثبات في التبعية بين الشيفرة البرمجية أي إذا اعتمدت دالة معينة في وظيفتها على نتيجة دالة أخرى فيجب علينا حينها الانتباه بأن تكون محققة قبلها والحرص على عدم تغيير ترتيب تواجد الدوال في الشيفرة البرمجية. وبالإضافة إلى التضارب في المتغيرات العامة Global Variables خصيصًا عند ازدياد ضخامة المشروع.
</p>

<p>
	سرعان ما اكتشف المطورون حتمية استخدام ملفات خارجية منفصلة وتجزيئ الشيفرة البرمجية إلى أجزاء صغيرة وتحميلها تباعًا في ملف واحد يعد بمثابة المدخل يسمى عادة index.html. وبهذه الطريقة يمكننا استدعاء مكتبات منفصلة. هذا الأمر حل لنا بعضا من المشاكل التي كنا نواجهها في الطريقة القديمة وسمح لنا بإعادة استخدام الشيفرة البرمجية أكثر من مرة ومن أي مكان نريده، وبذلك نكون تخلصنا من فكرة نسخ الشيفرة البرمجية من مكان ما ولصقها في مكان آخر متى ما أردنا استخدام هذه الشيفرة.
</p>

<p>
	سيكون شكل ملف index.html هكذا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4341_15" style="">
<span class="tag">&lt;html&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">External File Example</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="com">&lt;!-- الملف الرئيسي الّذي سيستدعي ملفات جافاسكربت --&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">
      The Answer is
      </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"answer"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./add.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./reduce.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./sum.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./main.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	لكن مهلًا هل هذا كل ما سنحتاجه لإضافة ملفات خارجية لموقعنا؟ في الحقيقة بالرغم من حل جزء من المشكلة ظهرت لدينا مشكلة أخرى وهي في حال ظهر تحديث جديد لمكتبة ما هل ستبحث عن جميع الأسطر التي تحتوي على التضمين للمكتبة التي تريدها ونعوضها بالنسخة الجديدة هذه العملية اليدوية مرهقة جدًا أليس كذلك؟
</p>

<h2>
	ظهور مدير الحزم Package Manager
</h2>

<p>
	عندما ضجر مجتمع المطورين من هذه المشاكل التي تستهلك الوقت والجهد حاولوا حل هذه المشكلة وهنا ظهر لنا الحل السحري لإدارة المكتبات الخارجية وهو <strong>مدير الحزم Package Manager</strong>: (ويشار إليه أيضًا <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">بنظام إدارة الحزم</a>) وهو مجموعة من أدوات البرامج التي تعمل على أتمتة عملية تثبيت الحزم وترقيتها وتكوينها وإزالتها بطريقة متسقة.
</p>

<p>
	عندما نتعامل مع مدير الحزم سنواجه مصطلحين جديدين وهما <strong>الحزمة Package</strong> : وهي أرشيف يحتوي الملفات المطلوب تثبيتها ومعها بيانات توصيفية (أو تسمى أحيانًا ميتا Meta) مثل رقم الإصدار والمزايا الموجودة فيها ومتطلبات التثبيت (التبعيات الخاصة بها) والعلاقات بين الحزم مثل التوافقية والإحلال والتعارض وماذا يُشغّل قبل وبعد التثبيت (من شيفرات برمجية وسكربتات) …إلخ.
</p>

<p>
	بمجرد تثبيت الحزمة في مشروعك ستصبح اسم الحزمة <strong>تبعيّة Dependency</strong>: وهي جزء من برنامج معين تابع لجهة خارجية من المحتمل أن يكون كتبه شخص آخر ويعمل بشكل مثالي على حل مشكلة واحدة نيابة عنك. يمكن أن يحتوي مشروع الويب عدد من التبعيات ويمكن أن تتضمن تبعيات المشروع تبعيات فرعية لم تثبتها صراحةً في مشروعك. مع أن التعريفين متشابهان بطريقة ما إلا أننا تعمدنا ذلك للتوضيح وجهتي النظر التي يمكننا استنباطها من رحلة مبرمج الويب.
</p>

<p>
	وعمومًا يمكن لمدير الحزم أن يساعدنا على:
</p>

<ul>
<li>
		معرفة ما هي الحزم المثبتة.
	</li>
	<li>
		معرفة ما هو الإصدار المثبتة من حزمة معينة.
	</li>
	<li>
		تثبيت حزمة غير موجودة عندك (البحث عن مصدر موثوق لها وتنزيلها من الإنترنت ثم تثبيتها) أو تحديثها أو حذفها.
	</li>
	<li>
		تشغيل الحزم دون تنزيل (الميزة متاحة في مدير الحزم npm باستخدام الأمر npx).
	</li>
	<li>
		مشارك الشيفرة البرمجية مع أي مستخدم npm في أي مكان.
	</li>
	<li>
		تقييد الشيفرة البرمجية على مطورين معينين.
	</li>
	<li>
		إنشاء حساب شركات لتنسيق وصيانة الحزم والتشفير والمطورين.
	</li>
	<li>
		تشكيل فرق مطورين افتراضي باستخدام حساب الشركات.
	</li>
	<li>
		إدارة إصدارات متعددة من تبعيات التعليمات البرمجية والتعليمات البرمجية.
	</li>
	<li>
		تحديث التطبيقات بسهولة عند تحديث الكود الأساسي.
	</li>
	<li>
		ابحث عن مطورين آخرين يعملون على مشاكل ومشاريع مماثلة.
	</li>
</ul>
<p>
	من الأمثلة البسيطة على التبعية المفيدة التي يمكن أن تحتاجها في مشروعك هي مكتبة لحساب التواريخ النسبية كنص يمكن للبشر قراءته بسهولة. بالطبع يمكنك كتابة الشيفرة البرمجية بنفسك، ولكن هناك فرصة قوية أن يكون شخص آخر حل هذه المشكلة بالفعل - فلماذا نضيع الوقت في إعادة اختراع العجلة؟ علاوة على ذلك، من المحتمل أن ما تكون التبعية مختبرة وموثوقة في العديد من المواقف المختلفة، ومتوافقة مع جميع أنواع المستعرضات القديمة أو الحديثة مما يجعلها أكثر قوة من الحل الخاص بك.
</p>

<p>
	لا يقتصر وجود مدير الحزم على مشاريع الويب فحسب وإنما يوجد في أنظمة الحاسوب مثل <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس</a> وتكون مهمته مشابهة إذ يعمل على إدارة البرمجيات المُثبتة على جهاز الحاسوب؛ ويتعقب البرامج المثبتة، ويسمح للمستخدم بتثبيت البرامج الجديدة بسهولة أو ترقية البرامج إلى إصدارات أحدث أو إزالة البرامج المثبتة مشابه جدًا لبرنامج Google Play على الهواتف المحمولة إلا أن الأخير مزود بشاشة رسومية سهلة الاستخدام. بحلول منتصف العشرينات من القرن العشرين أصبح مدير الحزم يتعامل مع نظام التشغيل Windows أيضًا، ويستخدم مدراء الحزم مع مختلف لغات البرمجة مثل: لغة البرمجة <a href="https://wiki.hsoub.com/Python" rel="external">بايثون Python</a> ولغة <a href="https://wiki.hsoub.com/Ruby" rel="external">Ruby</a>، ويطلق على مدير الحزم أيضًا مدير تنصيب النظام System Install Manager.
</p>

<p>
	في <a href="https://academy.hsoub.com/devops/linux/%d8%a7%d9%84%d8%af%d9%84%d9%8a%d9%84-%d8%a7%d9%84%d9%86%d9%87%d8%a7%d8%a6%d9%8a-%d9%84%d8%a7%d8%ae%d8%aa%d9%8a%d8%a7%d8%b1-%d8%aa%d9%88%d8%b2%d9%8a%d8%b9%d8%a9-%d9%84%d9%8a%d9%86%d9%83%d8%b3-r48/" rel="">توزيعات لينكس</a> المختلفة لا تُتناقل ملفات الحزم (عبر وحدات التخزين القابلة للإزالة كالفلاشة أو القرص المضغوط مثلًا مثل برامج ويندوز) بل توضع هذه الملفات في مستودعات على الإنترنت وعندما يطلب المستخدم حزمة معينة تجلب الحزمة تلقائيًا من المستودع. يتحكم المستخدم في المستودعات التي يريد أن يثق بها وغالبًا في التوزيعات الكبيرة مثل فيدورا وديبيان تحتوي مستودعات التوزيعة الرسمية عشرات الآلاف من الحزم البرمجية.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: قبل استخدام مكتبة أو حزمة أو إطار عمل مفتوح المصدر تأكد من مراجعة نوعية ترخيصه لأن بعض التراخيص توفر قيودًا محدودة على إعادة الاستخدام في البرامج الاحتكارية مثل ترخيص MIT، وبعض التراخيص لا تسمح باستخدامها في البرامج الاحتكارية، وعمومًا يجب الانتباه إلى هذه النقطة لتجنب الآثار المترتبة على الاستخدام الخاطئ.
		</p>
	</div>
</blockquote>

<h2>
	أنواع مدراء الحزم
</h2>

<p>
	هنالك الكثير من مدراء الحزم بحسب بيئة التطوير أو اللغة المستخدمة ومن أشهر مدراء الحزم نذكر:
</p>

<ol>
<li>
		<strong><a href="https://academy.hsoub.com/programming/php/%D9%85%D8%A7-%D9%87%D9%88-composer-%D9%88%D9%84%D9%85%D8%A7%D8%B0%D8%A7-%D9%8A%D8%AC%D8%A8-%D8%B9%D9%84%D9%89-%D9%83%D9%84-%D9%85%D8%B7%D9%88%D8%B1-php-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-r10/#:~:text=%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%20Composer&amp;text=%D9%8A%D9%8F%D9%85%D9%83%D9%86%20%D8%A5%D9%8A%D8%AC%D8%A7%D8%AF%20%D9%87%D8%B0%D9%87%20%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A7%D8%AA%20%D9%88%D8%A2%D9%84%D9%8A%D8%A7%D8%AA,https://packagist.org/.&amp;text=%D9%81%D9%8A%20%D8%AD%D8%A7%D9%84%20%D9%85%D8%A7%20%D8%A5%D8%B0%D8%A7%20%D9%84%D9%85,%D8%A5%D9%86%D8%B4%D8%A7%D8%A4%D9%87%20%D8%AF%D8%A7%D8%AE%D9%84%20%D9%85%D9%8F%D8%AC%D9%84%D8%AF%20%D8%A7%D9%84%D9%85%D8%B4%D8%B1%D9%88%D8%B9%20%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D9%8A." rel="">Composer</a></strong>: يستخدم في المشاريع المبنية على <a href="https://wiki.hsoub.com/PHP" rel="external">لغة البرمجة PHP</a>.
	</li>
	<li>
		<strong>Gems</strong>: هو مخصص للغة البرمجة Ruby ويستخدم مع إطار العمل Rails.
	</li>
	<li>
		<strong>npm</strong>: هو مدير حزم مخصص لبيئة Node.js المعتمدة على لغة البرمجة جافاسكربت، ويمكن استخدام هذا المدير في أي مشروع برمجي على الويب.
	</li>
	<li>
		<strong>yarn</strong>: هو مدير حزم طورته شركة فيسبوك عام 2016 لبيئة <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a> التي تعمل بلفة جافاسكربت ويسعى هذا المدير لتوفر السرعة والاتساق والاستقرار والأمان بطريقة أفضل من npm بالرغم من أنه مبني على نفس الشيفرة البرمجية الخاصة ب npm.
	</li>
	<li>
		<strong>pip</strong>: وهو مدير حزم يستخدم في البرمجيات التي تعتمد على <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">لغة البرمجة بايثون</a> (مثل: <a href="https://academy.hsoub.com/programming/python/django/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-django%C2%A0-r353/" rel="">Django</a> و <a href="https://academy.hsoub.com/programming/python/flask/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%84%D8%A8-r344/" rel="">Flask</a>).
	</li>
	<li>
		<strong>Bower</strong> هو مدير حزم للواجهات الأمامية مصمم خصيصًا للمكتبات مثل <a href="https://wiki.hsoub.com/jQuery" rel="external">jquery</a> و <a href="https://academy.hsoub.com/programming/javascript/angular/%D9%85%D8%A7-%D9%87%D9%8A-angular%D8%9F-r1379/" rel="">angular</a> و bootstrap وما إلى ذلك.
	</li>
</ol>
<p>
	يتألف أي مدير حوم من ثلاثة مكونات وهي:
</p>

<ol>
<li>
		<strong>الموقع الإلكتروني</strong>: يستخدم موقع الويب لاكتشاف الحزم وإعداد ملفات التعريف وإدارة الجوانب الأخرى لتجربة مدير الحزم، فعلى سبيل المثال يمكنك إعداد مؤسسات لإدارة الوصول إلى الحزم العامة أو الخاصة.
	</li>
	<li>
		<strong>السجل</strong>: وهو عن قاعدة بيانات عامة كبيرة تحتوي على الحزم والمعلومات الوصفية المرتبطة بها.
	</li>
	<li>
		<strong>واجهة سطر أوامر CLI، تسمى سجل npm</strong>: وهي الطريقة التي يتفاعل بها المطورين مع مدير الحزمة.
	</li>
</ol>
<p>
	سنناقش في هذا المقال الفرق بين مدير الحزم NPM و Yarn لنساعد المبرمج المبتدئ على اتخاذ قراره.
</p>

<h3>
	نبذة وتاريخ موجز عن مديري الحزم NPM و Yarn
</h3>

<p>
	مدير الحزم NPM (وهو اختصارًا لـ Node Package Manager) وهو مدير حزم للغة جافاسكربت. إنه المدير الافتراضي لبيئة Node.js التي تعمل بلغة جافاسكربت. ظهر لأول مرة في عام 2010 وسرعان ما انتشر بين المطورين نظرًا للحلول التي قدمها.
</p>

<p>
	مدير الحزم Yarn (وهو اختصارًا لـ Yet Another Resource Negotiator) وهو مدير مشابه لمدير npm. طورته شركة فيسبوك وهو <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوح المصدر</a> والهدف من تطويره (في ذلك الوقت) هو إصلاح مشكلات الأداء والأمان التي يعاني منها npm.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				وجه المقارنة
			</th>
			<th>
				مدير الحزم npm
			</th>
			<th>
				مدير الحزم Yarn
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				تاريخ صدوره
			</td>
			<td>
				2010
			</td>
			<td>
				2016
			</td>
		</tr>
<tr>
<td>
				الموقع الرسمي
			</td>
			<td>
				<a href="https://github.com/npm/cli" rel="external nofollow">npm</a>
			</td>
			<td>
				<a href="https://github.com/yarnpkg/yarn" rel="external nofollow">yarn</a>
			</td>
		</tr>
<tr>
<td>
				الإصدار الحالي
			</td>
			<td>
				v8.3.2
			</td>
			<td>
				v3.1.1
			</td>
		</tr>
<tr>
<td>
				فريق التطوير
			</td>
			<td>
				جمهور المطورين فقط
			</td>
			<td>
				شركة فيسبوك وجمهور المطورين
			</td>
		</tr>
</tbody>
</table>
<p>
	لنفهم تطور كل مدير لنسافر بسرعة عبر الزمن لرؤية الصورة الكبيرة:
</p>

<ul>
<li>
		2010: صدور مدير الحزم npm بدعم Node.
	</li>
	<li>
		2016: صدور مدير الحزم Yarn. أظهر أداء أفضل بكثير من مدير الحزم npm. كما أنه ينشئ ملف yarn.lock الذي يجعل مشاركة ونسخ المشاريع المتماثل والتبعيات أسهل بكثير وأدق.
	</li>
	<li>
		2017: صدور npm 5 والذي أصبح ينشئ تلقائيًا ملف package-lock.json ردًا على إصدار مدير الحزم yarn لملف yarn.lock.
	</li>
	<li>
		2018: صدور npm 6 بأمان محسّن وأصبح الآن يتحقق npm من الثغرات الأمنية قبل تثبيت التبعيات.
	</li>
	<li>
		2020: صدور تحديثات لمدير الحزم Yarn 2 و npm 7. تأتي كلتا الحزمتين مع ميزات جديدة رائعة كما سنرى لاحقًا في هذا البرنامج التعليمي.
	</li>
	<li>
		2021: صدور تحديثات Yarn 3 و npm 8 بتحسينات ومزايا مختلفة.
	</li>
</ul>
<p>
	يعتمد مديرا الحزم على بيئة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">Node.js</a> وهي بيئة تشغيل مفتوحة المصدر تعمل عبر مختلف أنظمة التشغيل الأساسية لتنفيذ الشيفرة البرمجية للغة جافاسكربت خارج المتصفح. تعد بيئة Node.js حلًا مثاليًا لبناء خدمات خلفية قابلة للتطوير بشكل كبير وكثيفة البيانات وفي الوقت الحقيقي تعمل على تشغيل تطبيقات العملاء مثل <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهات البرمجية APIs</a> ومواقع بث الفيديو عبر الإنترنت، وتطبيقات الصفحة الواحدة، وتطبيقات الدردشة عبر الإنترنت، وما إلى ذلك.
</p>

<p>
	تستخدم العديد من الشركات الشهيرة بيئة Node.js لتطوير تطبيقاتها (مثل Netflix و Uber وغيرها الكثير غيرهما). أحد أسباب شعبية بيئة Node.js هو توفر حزم ومكتبات أطر عمل متنوعة ومفتوحة المصدر مساندة لعمل بيئة Node.js ومن أشهرها Express.js و Lodash و AsyncJS و Meteor و Sails وغيرها. ويمكن استخدام هذه الحزم في مشاريع التي تعتمد على لغة جافاسكربت وأطر العمل الخاصة بها.
</p>

<h3>
	مقارنة بين npm vs yarn
</h3>

<p>
	في وقتنا الحاضر يتسارع مدراء الحزم في السباق، ولكن لا تزال هناك العديد من الاختلافات التي تساعد في تحديد أيهما سنختار سنستعرض أهم الجوانب التي يجب النظر إليها عند اعتمادك لأي مدير حزم.
</p>

<h3>
	المجتمع والتطوير
</h3>

<p>
	لننظر في المجتمع لتقييم تطوير كل من npm و yarn لنلقي نظرة على مجتمعاتها خصيصًا فيما يتعلق بالإحصاءات الموجودة في مستودعات GitHub (نؤكد أن هذه الأرقام حتى تاريخ كتابة المقال).
</p>

<p>
	المجتمع الأكبر للمطورين يمكن أن يساعدك في الحصول على المساعدة بصورة أسرع عند مواجهة أية مشكلة في التنفيذ، وبالرغم من أن Yarn أحدث بالمقارنة مع npm إلا أنه يلحق بالركب بسرعة من حيث الشعبية. على سبيل المثال إذا قارنا بين عدد التنزيلات بين npm و Yarn في بين السنوات 2017 وحتى 2022 يمكننا أن نرى بأن npm هو الفائز الواضح هنا، ولكن إن نظرنا إلى الأرقام في مستودعات الحزم سنجد أن الأمر معكوس لصالح yarn.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100377" href="https://academy.hsoub.com/uploads/monthly_2022_06/downloads.png.157381eb8e48fdc2099672559556954c.png" rel=""><img alt="downloads.png" class="ipsImage ipsImage_thumbnailed" data-fileid="100377" data-unique="f9sc1qgfl" src="https://academy.hsoub.com/uploads/monthly_2022_06/downloads.thumb.png.3922d96cac071845520e9de814018051.png" style="width: 600px; height: auto;"></a>
</p>

<table>
<thead><tr>
<th>
				وجه المقارنة
			</th>
			<th>
				مدير الحزم npm
			</th>
			<th>
				مدير الحزم Yarn
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				عدد المراقبين
			</td>
			<td>
				173 ألف شخص
			</td>
			<td>
				572 ألف شخص
			</td>
		</tr>
<tr>
<td>
				عدد النجوم
			</td>
			<td>
				5.4 ألف نجمة
			</td>
			<td>
				40.4 ألف نجمة
			</td>
		</tr>
<tr>
<td>
				عدد التفريعات
			</td>
			<td>
				1.5 ألف تفريعة
			</td>
			<td>
				2.8 ألف تفريعة
			</td>
		</tr>
<tr>
<td>
				عدد المساهمين
			</td>
			<td>
				731 مساهم
			</td>
			<td>
				521 مساهم
			</td>
		</tr>
</tbody>
</table>
<h3>
	الأداء والسرعة
</h3>

<p>
	يتمثل أحد الاختلافات الرئيسية بين NPM و Yarn في كيفية تعاملهما مع عملية تثبيت الحزم. إذ يعمد Yarn على تثبيت الحزم بالتوازي أي يجلب حزم متعددة ويثبتها في وقت واحد. فإذا كنت تثبت خمس حزم، واثنتان تستغرقان وقتًا طويلًا للتثبيت، فسيُثبت Yarn الحزم جنبًا إلى جنب. من ناحية أخرى تثبت NPM كل الحزم بطريقة تسلسلية أي كل حزمة بمفردها. في الحقيقة إن التثبيت المتوازي هو أحد الأسباب التي تجعل Yarn يتفوق على NPM في سباق الأداء.
</p>

<p>
	يوجد في كلاهما ميزة على ذاكرة تخزين مؤقت غير متصلة بالإنترنت وتدار بطريقة فعالة من خلال عملية تخزين كل حزمة مؤقتًا وحفظها على القرص، ويمكنك بعد ذلك تثبيت نفس الحزمة في مجلد مختلف من خلال ذاكرة التخزين المؤقت حتى عندما تكون غير متصل بالإنترنت، ويسمى هذا المفهوم بعمليات التثبيت الصفرية Zero installs، وبالرغم من أن Yarn لها بعض المزايا في السرعة إلا أنه بالمقارنة نجد بأن سرعات Yarn و npm متقاربة نسبيًا وخصيصًا في إصداراتها الأخيرة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: ميزة التثبيت الصفري اختيارية.
		</p>
	</div>
</blockquote>

<h3>
	تثبيت الحزم
</h3>

<p>
	يوضح ملف yarn.lock في مدير الحزم yarn وملف package-lock.json في مدير الحزم npm قائمة بجميع التبعيات أو الحزم المطلوبة تثبيتها حتى يعمل المشروع. هذا الملف "يقفل" إصدارات التبعية الخاصة بك. بهذه الطريقة عندما يقوم شخص آخر بتشغيل الأمر <code>install yarn</code> أو <code>install npm</code>، سيتلقى إصدارات التبعيات الدقيقة المدرجة في ملف القفل. هذا يضمن أن فريقك لديه إصدارات الحزمة المتطابقة تمامًا مع متطلبات المشروع. كما أنه يساعد في منع الأخطاء التي يمكن أن تظهر بسبب إدخال إصدارات حزم محدثة غير مختبرة.
</p>

<h3>
	الأمن والحماية
</h3>

<p>
	أحد الانتقادات الرئيسية لمدير الحزم npm هو الأمان لأن إصدارات npm السابقة تحتوي على بضع ثغرات أمنية خطيرة، ولكن اعتبارًا من الإصدار 6 من المدير npm اصبح يدقق الحزم أثناء التثبيت ويخبرك إذا عثر على أي ثغرات أمنية. يمكننا إجراء هذا الفحص يدويًا عن طريق تشغيل الأمر <code>npm audit</code> على الحزم المثبتة. إذا عثر على أي ثغرات أمنية سيقدمُ لنا توصيات أمنية.
</p>

<p>
	توضح الصورة التالية تقارير التدقيق npm عن الحزم غير الآمنة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100378" href="https://academy.hsoub.com/uploads/monthly_2022_06/npm-audit.png.4e95eed133bb0d0dcf9faad48580738d.png" rel=""><img alt="npm-audit.png" class="ipsImage ipsImage_thumbnailed" data-fileid="100378" data-unique="4xucyq3sj" src="https://academy.hsoub.com/uploads/monthly_2022_06/npm-audit.thumb.png.cd07e26c16485bd46141469abf1abd57.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	كما ترى في لقطة الشاشة أعلاه، يمكننا تشغيل الأمر <code>npm audit fix</code> لإصلاح ثغرات الحزمة، وسيصلح أشجار التبعية إذا كان من الممكن القيام بذلك. يستخدم كل من Yarn و npm خوارزميات تجزئة التشفير لضمان سلامة الحزم.
</p>

<p>
	يعتمد مدير الحزم Yarn على تقنية تدقيق المجموع Checksum ويشار إليها أيضًا المجموع الاختباري لضمان تكامل البيانات وهذه الخوارزمية مخصصة للكشف عن الخطأ الرياضي. إنها كتلة من البيانات المشتقة من البيانات الأصلية لاكتشاف الأخطاء عند نقل البيانات من جهاز حاسوب إلى آخر. يتحقق من أي خطأ قد يكون حدث أثناء نقل البيانات أو تخزينها. هذا يساعد على ضمان سلامة البيانات. قبل نقل البيانات، سينشئ المرسل رسالة تحسب المجموع الاختباري. وستُرفق هذه الرسالة (الحزمة) بالبيانات الأصلية. بعدها سيحسبُ المتلقي المجموع الاختباري وإذا تساوت قيمتا المجموع الاختباري مع بعضهما، فعندها لا يوجد أي خطأ وتقبل البيانات؛ خلاف ذلك ترفض البيانات.
</p>

<p>
	أما مدير الحزم npm فيعتمد على خوارزمية التجزئة الآمنة SHA-512 للتحقق من سلامة الحزم التي سيُثبتها. إذ يخزن سلاسل SHA-512 لكل حزمة مثبتة في ملف package-lock.json، كما هو موضح في الشيفرة البرمجية أدناه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4341_38" style="">
<span class="str">"lodash"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"4.17.21"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"resolved"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"integrity"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيستخدم المدير NPM مفتاح SHA-512 لإجراء فحص سلامة في كل كتلة حزمة في ملف package-lock.json، كما يدقق كل حزمة أثناء التثبيت وإعلامك بنقاط الضعف المحتملة.
</p>

<h3>
	سهولة التعلم والاستعمال
</h3>

<p>
	أحد الأشياء التي يجب مراعاتها قبل اختيار مدير الحزم هو معرفة الواجهة سهلة الاستخدام. يتضمن ذلك كيف تبدو الأوامر. في الحقيقة إن NPM و Yarn لهما واجهات مختلفة لسطر الأوامر وكلاهما سهل الاستخدام ولديهما تجربة مستخدم جيدة. يتضح هذا عند استخدام أمر مثل <code>npm init</code> و <code>yarn init</code>. كلاهما لديه دليل تفاعلي يساعد المستخدمين على تهيئة مشروع Node.js.
</p>

<table>
<thead><tr>
<th>
				وجه المقارنة
			</th>
			<th>
				مدير الحزم npm
			</th>
			<th>
				مدير الحزم Yarn
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				تثبيت التبعيات
			</td>
			<td>
				<code>npm install</code>
			</td>
			<td>
				<code>yarn</code>
			</td>
		</tr>
<tr>
<td>
				تثبيت حزمة معينة
			</td>
			<td>
				<code>npm install package_name@version_number</code> أو <code>npm install package_name</code>
			</td>
			<td>
				<code>yarn add package_name@version_number</code> أو <code>yarn add package_name</code>
			</td>
		</tr>
<tr>
<td>
				إزالة حزمة معينة
			</td>
			<td>
				<code>npm uninstall package_name</code>
			</td>
			<td>
				<code>yarn remove package_name</code>
			</td>
		</tr>
<tr>
<td>
				تهيئة مشروع جديد
			</td>
			<td>
				<code>npm init</code>
			</td>
			<td>
				<code>yarn init</code>
			</td>
		</tr>
<tr>
<td>
				تشغيل سكربت
			</td>
			<td>
				<code>npm run script_name</code>
			</td>
			<td>
				<code>yarn script_name</code>
			</td>
		</tr>
</tbody>
</table>
<h2>
	كيف نختار مدير الحزم الأنسب لمشروعنا؟
</h2>

<p>
	غطينا أوجه التشابه والاختلاف المختلفة بين مديري الحزم npm و yarn ، لكننا لم نحدد أيهما أفضل وأيهما يجب أن نختار. كما هو الحال دائمًا، تعتمد الإجابة على المشاريع التي نعمل عليها ومتطلباتها. وعمومًا يمكنك اختيار مدير الحزم npm إذا كنت راضيًا عن سير العمل الحالي، ولا تريد تثبيت أداة إضافية. أما إذا أردت بعض الميزات الرائعة المفقودة في npm فيجب عليك اختيار Yarn وتذكر بأن كِلا المديرين يتطوران باستمرار ويصححان المشاكل الموجودة بهما بل ويضيفان ميزات جديدة.
</p>

<h2>
	الخاتمة
</h2>

<p>
	تعرفنا في هذا المقال عن أهمية مدير الحزم في مشاريع الويب الحديثة واستعرضنا موجز تاريخي سريع عنها وفصلنا في مهامها وأنواعها وانتقلنا بعدها للتعرف على الفوارق بين مديريّ الحزم Yarn و npm فبدأنا بسرد موجز مختصر لتاريخ كل منها وفهمنا الفارق بينها في العديد من النواحي مثل مجتمع المطورين وإقبالهم على تبني كل مدير وتعمقنا في فهم فعالية كل مدير من خلال طريقة تثبيت للحزم وتأثيرها على الفرق البرمجية وسلاسة التطوير وفهمنا متى نستخدم كل واحد منها وعمومًا يمكن أن يكون الفرق ريثما تصدر تحديثة جديدة ولكن في الوقت الحالي يعد الخيارين رائعين في إدارة شجرة تبعيات المشروع وصيانتها.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		مقال <a href="https://www.sitepoint.com/yarn-vs-npm/" rel="external nofollow">Yarn vs npm: Everything You Need to Know</a> لكتاتبه Ivaylo Gerchev.
	</li>
	<li>
		<a href="https://www.npmtrends.com/npm-vs-yarn" rel="external nofollow">npm vs yarn</a>.
	</li>
	<li>
		مقال <a href="https://www.geeksforgeeks.org/difference-between-npm-and-yarn/" rel="external nofollow">Difference between npm and yarn</a> لكاتبه Parikshit Hooda.
	</li>
	<li>
		مقال <a href="https://www.section.io/engineering-education/npm-vs-yarn-which-one-to-choose/" rel="external nofollow">Choosing Between NPM and Yarn</a> لكاتبه Joseph Chege.
	</li>
	<li>
		مقال <a href="https://waverleysoftware.com/blog/yarn-vs-npm/" rel="external nofollow">YARN VS NPM: WHY AND HOW TO MIGRATE FROM NPM TO YARN</a> لكاتبه Viktor Tsymbal.
	</li>
	<li>
		مقال <a href="https://www.whitesourcesoftware.com/free-developer-tools/blog/npm-vs-yarn-which-should-you-choose/" rel="external nofollow">?NPM vs. Yarn: Which Package Manager Should You Choose</a> لكاتبه Alfrick Opidi.
	</li>
</ul>
<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">دليلك الشامل إلى مدير الحزم npm في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D9%84%D8%AD%D9%86-composer-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%85%D8%A7%D8%AF%D9%8A%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-php-r1123/" rel="">مدخل إلى الملحن composer: مدير الاعتماديات والحزم في PHP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/18-%D9%85%D8%AB%D8%A7%D9%84%D8%A7-%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-yum-r282/" rel="">18 مثالا لاستخدام مدير الحزم YUM</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-apt-%D8%8Cyum-%D8%8Cdnf-%D8%8Cpkg-r231/" rel="">أساسيات إدارة الحزم: apt ،yum ،dnf ،pkg</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1597</guid><pubDate>Thu, 09 Jun 2022 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x628;&#x64A;&#x646; &#x645;&#x62D;&#x631;&#x631; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x641;&#x64A;&#x645; Vim &#x648;&#x646;&#x627;&#x646;&#x648; Nano</title><link>https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/6294aa9d70617_-----Vim--Nano(1).png.340744c846886edd734d155e1f780963.png" /></p>

<p>
	نحتاج دومًا إلى استخدام محرر النصوص للكثير من الأمور، منها تدوين الملاحظات أو كتابة شيفرة برنامج أو إجراء تعديل على ملف ضبط من إعدادات النظام وغيرها من الحالات التي يكثر فيها التعامل مع النصوص.
</p>

<p>
	تمتلك كل توزيعة لينكس برامج تحرير للنصوص مثبتة مسبقًا بغض النظر عن متطلبات المستخدم، مثل Gedit و Geany و Kate وغيرها التي تعد جميعها محررات حديثة مزودة بواجهة رسومية GUI، ولكن لا يفضل الجميع استخدام محرر نصوص بواجهة رسومية في جميع الحالات بل يُفضّل البعض استخدام محرر نصوص بواسطة سطر الأوامر، ويتيح <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">نظام تشغيل لينكس</a> هذا الخيار أيضًا حيث يوجد العديد من برامج تحرير النصوص التي يمكن استخدامها من سطر الأوامر ومن أشهرها برنامجي <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71/" rel="">فيم Vim</a> ونانو Nano، اللذان يستخدمان في <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر CLI</a> وهي اختصار Command Line Interface، يقدم كل منهما معظم الميزات الأساسية لمحرر النصوص ويوجد بعض الاختلافات فمثلًا يأتي نانو مدمجًا في معظم توزيعات لينكس، بينما يحتاج فيم في بعض التوزيعات إلى تثبيته يدويًا.
</p>

<p>
	لا يمكن اتخاذ القرار بشأن استخدام أي منهما إلا بعد التعرّف على تفاصيل وإمكانيات كل منهما وهو ما سنعرضه في هذا المقال.
</p>

<h2>
	1. مقدمة عن محرر النصوص فيم ومحرر النصوص نانو
</h2>

<h3>
	محرر النصوص فيم
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100177" href="https://academy.hsoub.com/uploads/monthly_2022_05/Vim.PNG.bc207fbb1bc2c40ba8d8de4677a6397e.PNG" rel=""><img alt="Vim.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="100177" data-unique="lzpthttom" src="https://academy.hsoub.com/uploads/monthly_2022_05/Vim.thumb.PNG.99370f9f4c3a537f5600dc890d02f1a6.PNG" style="width: 600px; height: auto;"></a>
</p>

<p>
	أُصدر فيم عام 1991 وهو نسخة محسّنة من محرر النصوص Vi الذي طُوّر عام 1976 <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">لنظام التشغيل</a>. يونكس Unix، ويُعرف فيم أيضًا باسم محرر نصوص المبرمِج، وذلك بسبب امتلاكه العديد من الميزات التي تساعد في تعديل ملفات البرنامج، ولا يعتبر فيم حكرًا على المبرمجين حيث يمكن استخدامه لكتابة وتحرير ملفات نصية عادية حتى مع امتلاكه إمكانيات متطورة.
</p>

<h3>
	محرر النصوص GNU nano
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100176" href="https://academy.hsoub.com/uploads/monthly_2022_05/6294aa986a112_GNUNano.PNG.77371376b7c3d1cb52a3dd660feaf871.PNG" rel=""><img alt="GNU Nano.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="100176" data-unique="sxgk2bgyc" src="https://academy.hsoub.com/uploads/monthly_2022_05/6294aa9bac099_GNUNano.thumb.PNG.f5348ea0f5a98862a6d5159b68bb7a59.PNG" style="width: 600px; height: auto;"></a>
</p>

<p>
	أما المحرر غنو نانو GNU nano ويُعرف اختصارًا باسم نانو Nano، فهو محرر نصوص بسيط يعمل بواسطة سطر الأوامر ويعتمد على يونكس Unix ومستوحى من بيكو Pico، حيث كان Pico جزءًا من مجموعة Pine للبريد الإلكتروني التي طورتها جامعة واشنطن في عام 1989 ولكن كان من الصعب تضمينه في توزيعات لينكس لعدم امتلاكه ترخيص GPL، لذا طُوِّر محرر النصوص نانو كبديل مجاني للمحرر Pico، كما عُرف نانو في البداية باسم tip ثم أعاد ريتشارد ستولمان Richard Stallman تسميته مباشرةً قبل أن يعلن نانو برنامجًا رسميًا من برامج GNU.
</p>

<p>
	أهم ما يميز هذا المحرر هو سهولة الاستخدام حيث يمكن لأي شخص استعماله وليس بالضرورة أن يكون مبرمجًا.
</p>

<h2>
	2. مزايا محرر فيم مقابل مزايا محرر نانو
</h2>

<p>
	فيما يلي الاختلافات الرئيسية بين فيم ونانو.
</p>

<p>
	تُلخص الميزات الرئيسية لمحرر فيم بالميزات التالية:
</p>

<ul>
<li>
		يتيح التراجع عن آخر خطوة Undo لعدة مرات
	</li>
	<li>
		التركيز على تركيب الجملة
	</li>
	<li>
		التعديل بواسطة سطر الأوامر
	</li>
	<li>
		إكمال اسم الملف
	</li>
	<li>
		تقسيم شاشة العمل والذاكرة
	</li>
	<li>
		التشعب
	</li>
	<li>
		استرداد العمل السابق
	</li>
	<li>
		سريع الأداء لاعتماده على تجزئة المهام
	</li>
</ul>
<p>
	أما الميزات الرئيسية لمحرر نانو، فتلخص بالميزات التالية:
</p>

<ul>
<li>
		فتح ملفات متعددة معًا
	</li>
	<li>
		تصفح المستند بالاعتماد على الأسطر
	</li>
	<li>
		التراجع عن خطوة ما
	</li>
	<li>
		إعادة تنفيذ خطوة معينة
	</li>
	<li>
		تلوين التركيب النحوي للجملة
	</li>
	<li>
		ترقيم الأسطر
	</li>
</ul>
<p>
	نلاحظ بشكل عام أن فيم يوفر إمكانيات أكثر تقدمًا ومع ذلك يقدم كلاهما الميزات الأساسية لتحرير ملفات نصية مثل ضبط إحدى إعدادات النظام وملفات الشيفرات البرمجية.
</p>

<h2>
	3. استخدام محرر النصوص
</h2>

<p>
	يعتبر فتح ملف موجود مسبقًا في كل من فيم أو نانو عملية سهلة وتتحقق من خلال كتابة اسم محرر النصوص المراد استخدامه (فيم أو نانو) متبوعًا بمسار الملف كما يلي:
</p>

<pre class="ipsCode">
vim Documents/text.txt
nano Documents/text.txt
</pre>

<p>
	ولكن هناك ما هو أكثر بكثير من مجرد الوصول إلى ملف أو فتحه باستخدام محرر النصوص، فيما يلي قائمة ببعض نقاط للمقارنة بين فيم ونانو حول استخدام كل منهما:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				نانو Nano
			</th>
			<th>
				فيم Vim
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				سهل الاستخدام
			</td>
			<td>
				يعمل في عدة أوضاع Mode-driven
			</td>
		</tr>
<tr>
<td>
				سهل التعلم
			</td>
			<td>
				صعب التعلم
			</td>
		</tr>
<tr>
<td>
				مصمم بهدف إجراء تعديلات سريعة
			</td>
			<td>
				استرداد العمل السابق بسهولة
			</td>
		</tr>
<tr>
<td>
				 
			</td>
			<td>
				تلوين التركيب النحوي للجملة دلالةً على وجود خطأ
			</td>
		</tr>
<tr>
<td>
				 
			</td>
			<td>
				يوفر قدرات متقدمة
			</td>
		</tr>
</tbody>
</table>
<p>
	الفرق الأساسي بين نانو وفيم هو أن الجمهور المستهدف مختلف تمامًا.
</p>

<h3>
	استخدام فيم
</h3>

<p>
	يعمل في أوضاع تحرير متعددة Mode-driven منها:
</p>

<ul>
<li>
		الوضع العادي
	</li>
	<li>
		الوضع المرئي
	</li>
	<li>
		وضع الإدراج
	</li>
	<li>
		الكتابة بواسطة سطر الأوامر
	</li>
	<li>
		التحرير بواسطة سطر الأوامر
	</li>
</ul>
<p>
	يعمل فيم بشكل افتراضي في الوضع العادي، ويمكن ضبط فيم كأداة كتابة حتى مع كل الأوضاع، وتتصرف أزرار لوحة المفاتيح نفسها تصرفًا مختلفًا تبعًا للوضع المفعَّل، فمثلًا يمثل زر h على لوحة المفتاتيح الحرف h في وضع الكتابة ويتحول في الوضع العادي إلى زر حركة فيحرك مؤشر الكتابة إلى اليسار بمقدار حرف واحد، ويحرك الحرف l (حرف L صغير) مؤشر الكتابة إلى اليمين بمقدار حرف واحد، وينقل الحرف j مؤشر الكتابة إلى الأسفل بمقدار سطر، وينقل الحرف k مؤشر الكتابة إلى الأعلى بمقدار سطر، كما يمكن استخدام أزرار الأسهم للتنقل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100180" href="https://academy.hsoub.com/uploads/monthly_2022_05/6294aab01fe70_Vim.PNG.bec0ed23363edbe0e567894ac25d6a2b.PNG" rel=""><img alt="شاشة سطر الأوامر عند تشغيل Vim بلا وسطاء.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="100180" data-unique="wy2bbs6bp" src="https://academy.hsoub.com/uploads/monthly_2022_05/6294aab259681_Vim.thumb.PNG.b6d7c4ecb0fe2cca7f884c897202d31c.PNG" style="width: 600px; height: auto;"></a>
</p>

<p>
	أما المفاتيح الأكثر استخدامًا فهي: "w" و "b" و "e" وشرحها وفق التالي:
</p>

<ul>
<li>
		"w": ينقل مؤشر الكتابة إلى الكلمة التالية، وإذا كان المؤشر موجودًا في بداية الكلمة فينقله إلى بداية الكلمة التالية
	</li>
	<li>
		"b": ينقل المؤشر إلى بداية الكلمة الموجودة على يسار الكلمة الحالية
	</li>
	<li>
		"e": ينقل المؤشر إلى نهاية الكلمة الموجودة على اليمين
	</li>
</ul>
<p>
	كما يمكن مزج الأرقام بهذه المفاتيح، مثال: يؤدي الضغط على "6w" إلى تحريك المؤشر بمقدار ست كلمات للأمام.
</p>

<p>
	التبديل بين الأوضاع المختلفة يتطلب الضغط على مفاتيح محددة مثل:
</p>

<ul>
<li>
		"i": لتفعيل وضع الإدراج
	</li>
	<li>
		"CTRL + C": للعودة إلى الوضع العادي
	</li>
	<li>
		"wq:": لحفظ الملف وإغلاق النافذة
	</li>
</ul>
<p>
	كل ما سبق هو لمحة بسيطة عن فيم، لمعرفة المزيد يمكن استخدام الأمر vimtutor الذي يقدم معلومات عن الأوامر الأساسية للتعامل مع الملفات كحذف ملف ما أو تعديله أو حفظه وغيرها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100178" href="https://academy.hsoub.com/uploads/monthly_2022_05/6294aaa508a34_Vim.PNG.00908a428d2531df3bebfa67982cffd2.PNG" rel=""><img alt="أوامر Vim.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="100178" data-unique="83iwiv3xk" src="https://academy.hsoub.com/uploads/monthly_2022_05/6294aaaa2ac6f_Vim.thumb.PNG.210ba680069372d20913d3c06db0126d.PNG" style="width: 600px; height: auto;"></a>
</p>

<h3>
	استخدام GNU nano
</h3>

<p>
	يمتلك نانو واجهة أساسية تفاعلية تحتوي على معلومات مهمة في أسفل النافذة تسهل عمل المستخدم عند تنفيذ الإجراءات الأساسية ولا يحتاج إلى الاستعانة بصفحة دليل المستخدم أو أي توثيق آخر.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="100179" href="https://academy.hsoub.com/uploads/monthly_2022_05/6294aaac7d503_nano.PNG.e8e8ae34bb0b559da99ceb28a897e682.PNG" rel=""><img alt="شاشة سطر الأوامر عند تشغيل nano بلا وسطاء.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="100179" data-unique="b1yr21cht" src="https://academy.hsoub.com/uploads/monthly_2022_05/6294aaae1a190_nano.thumb.PNG.1cce72accea63e0b25fbd8154a8214d6.PNG" style="width: 600px; height: auto;"></a>
</p>

<p>
	هذا هو السبب الأساسي في تفوق نانو على فيم من ناحية سهولة الاستخدام، ومع ذلك لا تزال بعض المصطلحات المستخدمة في نانو مصطلحات قديمة، مثال على ذلك أمر "Write Out" يعني اكتب التعديلات بدلًا من "Save" يعني احفظ التعديلات على الملف وأمر "Where Is" يعني أين كذا بدلًا من "Search" يعني ابحث عن كذا، لكنها لا تعتبر مشكلة كبيرة إذ من السهل التعود عليه.
</p>

<p>
	لا يتشابه نانو مع استخدام Notepad أو Gedit (برامج تمتلك واجهة رسومية)، حيث عادةً ما تكون تركيبة المفاتيح لإجراء عملية القص هي "Ctrl + X" في معظم برامج التحرير الحديثة ولكن تكون في نانو "Ctrl + K".
</p>

<p>
	يستخدم الرمز "^" للإشارة إلى استخدام مفتاح Ctrl كمفتاح تعديل ويستخدم كمجموعة مع المفاتيح المجاورة له.
</p>

<p>
	توجد أيضًا مجموعات مفاتيح والاختصارات مثل:
</p>

<ul>
<li>
		Ctrl + F لتحريك المؤشر إلى الأمام
	</li>
	<li>
		Ctrl + B الانتقال إلى الخلف
	</li>
	<li>
		Ctrl + X للخروج
	</li>
	<li>
		Ctrl + O لحفظ الملف أو حفظ الملف باسم
	</li>
	<li>
		Alt + U للتراجع عن الإجراء الأخير
	</li>
	<li>
		Ctrl + ← كلمة واحدة للخلف
	</li>
	<li>
		Ctrl + → كلمة واحدة للأمام
	</li>
</ul>
<p>
	مما سبق، يناسب محرر نانو المبتدئين خصوصًا عندما يكون كل ما يحتاجه المستخدم هو تحرير ملف من حين لآخر.
</p>

<h2>
	4. سهولة التعلم
</h2>

<p>
	نلاحظ مما سبق أن فيم يختلف عن محرر النصوص التقليدي وهذا هو السبب الأساسي لكونه برنامجًا صعب الممارسة في الفترة الأولى من تعلمه واستخدامه، بينما تعتبر إمكانيات فيم المتقدمة ميزة بالنسبة للمستخدمين ذوي الخبرة وخاصة من ناحية توفير الوقت.
</p>

<p>
	بناء على ذلك، إذا كان المستخدم مبرمجًا أو يقوم بتحرير العديد من الملفات بين الحين والآخر فاستخدام فيم كمحرر نصوص خاص به يعتبر الخيار الأنسب، بالمقابل يحتاج نانو إلى وقت أقل لتعلمه نظرًا لامتلاكه بعض التشابه مع محرر النصوص المعتمد على الواجهة الرسومية مثل Gedit أو Notepad.
</p>

<h2>
	هل أستعمل فيم أم نانو؟
</h2>

<p>
	يتيح فيم تنفيذ مجموعة متنوعة من المهام وذلك بسبب تميزه بالمرونة، ولكن من الصعب الاعتياد على آلية عمله وأوضاعه بالنسبة للمستخدم العادي، بينما يعتبر التعامل مع نانو أسهل لأنه يتيح للمستخدم القيام بالتعديلات بوضوح وبساطة.
</p>

<p>
	غالبًا ما يوصى باستخدام محرر النصوص نانو في البداية، وعند الحاجة إلى مزيد من الميزات أو إنجاز المهام بشكل أسرع يمكن الانتقال إلى فيم.
</p>

<h2>
	الأسئلة الشائعة حول فيم ونانو
</h2>

<ul>
<li>
		هل فيم أفضل من نانو؟ من الناحية التقنية نعم، ولكن إذا لم تكن هناك حاجة إلى ميزات إضافية فإن استخدام فيم سيشكل عبئًا على المستخدم.
	</li>
	<li>
		هل يستخدم المبرمجون فيم؟ نعم، يميل المبرمجون ومديرو الأنظمة إلى استخدام فيم بسبب إمكانياته المتقدمة.
	</li>
	<li>
		هل نانو أكثر شهرة؟ نعم، يعتبر نانو أكثر شهرة لكونه يأتي مدمجًا مع معظم <a href="https://academy.hsoub.com/devops/linux/%d8%a7%d9%84%d8%af%d9%84%d9%8a%d9%84-%d8%a7%d9%84%d9%86%d9%87%d8%a7%d8%a6%d9%8a-%d9%84%d8%a7%d8%ae%d8%aa%d9%8a%d8%a7%d8%b1-%d8%aa%d9%88%d8%b2%d9%8a%d8%b9%d8%a9-%d9%84%d9%8a%d9%86%d9%83%d8%b3-r48/" rel="">توزيعات لينكس</a> ويعتمد عليه معظم المستخدمين بينما فيم يستخدمه قلة من الناس.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للمقال <a href="https://itsfoss.com/vim-vs-nano/" rel="external nofollow">Vim vs Nano: What Should You Choose?‎</a> لصاحبه Pratham Patel.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AD%D8%B1%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%B9%D9%85%D9%84%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1438/" rel="">محررات النصوص المستعملة في تطوير مواقع الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r76/" rel="">.تعرف على أساسيات Vim - الجزء الثاني</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1590</guid><pubDate>Mon, 06 Jun 2022 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x628;&#x64A;&#x626;&#x629; &#x627;&#x644;&#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x645;&#x62A;&#x643;&#x627;&#x645;&#x644;&#x629; IDE</title><link>https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/6241ac88122d4_-----IDE.png.58df892ed3cc50c5dde9b12a43b60d59.png" /></p>

<p>
	تعد بيئة التطوير المتكاملة Integrated development environments -واختصارًا IDE- من الأدوات التي تستخدم في البرمجة، حيث يحدِث امتلاك بيئة جيدة فرقًا كبيرًا عند كتابة شيفرة بلغة برمجة معينة، ويساعد على تجنب الأخطاء التي قد لا تُكتشف إلا عند تجميع الشيفرة أو اختبارها.
</p>

<p>
	لكنك ستجد عند بداية <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a> أو تعلم لغة برمجة معينة العديد من خيارات بيئات التطوير المتكاملة، مع معرفة قليلة بالخيارات التي توفرها، بل في فهم ماهية بيئة التطوير المتكاملة بالضبط، حيث لا توجد مواصفات رسمية لما هو مؤهل لأن يكون بيئة تطوير، لذلك سيوضح هذا المقال الميزات التي تتوفر غالبًا في بيئات التطوير المتكاملة، وكيفية البحث عن بيئة مناسبة لك.
</p>

<h2>
	ميزات بيئة التطوير المتكاملة IDE الأساسية
</h2>

<p>
	يشير مصطلح "بيئة التطوير المتكاملة IDE" إلى بعض الميزات الأساسية، منها:
</p>

<h3>
	محرر الشيفرة
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94912" href="https://academy.hsoub.com/uploads/monthly_2022_03/6241add9e2825_EmacsmodeinEclipse.png.958f4f2b909905a1ab12c7e2797f0341.png" rel=""><img alt="Emacs mode in Eclipse.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94912" data-unique="vvv6mmz89" src="https://academy.hsoub.com/uploads/monthly_2022_03/6241add9e2825_EmacsmodeinEclipse.png.958f4f2b909905a1ab12c7e2797f0341.png"></a>
</p>

<p>
	يُصرف معظم الوقت عند استخدام بيئة التطوير في كتابة الشيفرة البرمجية، لذا يجب أن تحتوي بيئة التطوير على مكان مناسب لذلك، وأن يكون محرر النص فعالًا، لأنه إذا لم يكن كذلك فلن ترغب باستخدامه مرةً أخرى بالتأكيد. وتحتوي بيئة التطوير الجيدة على أنماط محاكاة المحرر التي تمكن المبرمجين المعتادين على محرر النصوص emacs أو <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r71/" rel="">vim</a> من استخدام نفس مجموعات المفاتيح التي اعتادوا عليها.
</p>

<h3>
	تلوين بنية الجملة البرمجية وإبرازها
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94915" href="https://academy.hsoub.com/uploads/monthly_2022_03/6241adf9bdce1_SyntaxhighlightinginEclipse.png.dcb07c0ccd93dad7846989eb56b8b85a.png" rel=""><img alt="Syntax highlighting in Eclipse.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94915" data-unique="aq1drkzre" src="https://academy.hsoub.com/uploads/monthly_2022_03/6241adf9bdce1_SyntaxhighlightinginEclipse.png.dcb07c0ccd93dad7846989eb56b8b85a.png"></a>
</p>

<p>
	تعرض بيئة التطوير كلًا من الكلمات المفتاحية والمتغيرات والمكتبات والوظائف بألوان مختلفة، ويُعد هذا التمييز إضافةً تسهّل عملية كتابة الشيفرة.
</p>

<h3>
	فحص التعليمات البرمجية
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94911" href="https://academy.hsoub.com/uploads/monthly_2022_03/6241add5ab7fb_CodelintinginPyCharmCommunityEdition.png.7ed54375346c755e31e51c16ad7d5c82.png" rel=""><img alt="Code linting in PyCharm Community Edition.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94911" data-unique="gbg74f4za" src="https://academy.hsoub.com/uploads/monthly_2022_03/6241add5ab7fb_CodelintinginPyCharmCommunityEdition.png.7ed54375346c755e31e51c16ad7d5c82.png"></a>
</p>

<p>
	تحدد بيئة التطوير الأخطاء المرتكبة أثناء كتابة التعليمات البرمجية، وتسلط الضوء عليها، وتسمى عملية كشف الأخطاء المحتملة في الشيفرة "linting"، ولا يقتصر الأمر على اكتشاف الأخطاء الفادحة فقط، حيث تشجع بعض اللغات -مثل <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">Python</a>- المطورين على كتابة تعليمات برمجية وفقًا لدليل رسمي لطريقة كتابة اللغة، وتحذرك بيئة التطوير عندما تنحرف عن توصيات مقترحات تحسين بايثون Python Enhancement Proposals، واختصارًا <a href="https://www.python.org/dev/peps/" rel="external nofollow">PEP</a>.
</p>

<h3>
	تصحيح الشيفرة
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94910" href="https://academy.hsoub.com/uploads/monthly_2022_03/6241add214a9f_CodecorrectioninPyCharmCommunityEdition.png.f0e2d45f35d3d7a60746a27052578164.png" rel=""><img alt="Code correction in PyCharm Community Edition.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94910" data-unique="cwggj3loh" src="https://academy.hsoub.com/uploads/monthly_2022_03/6241add214a9f_CodecorrectioninPyCharmCommunityEdition.png.f0e2d45f35d3d7a60746a27052578164.png"></a>
</p>

<p>
	تمثل عملية تدقيق الشيفرة Linting اكتشاف الأخطاء المحتملة، ويمكن للعديد من بيئات التطوير تنفيذ ذلك بناءً على مخطط أولي للصيغ المتوقعة، وستقترح بيئة التطوير الجيدة تصحيحات لهذه الأخطاء، بل ستقترح تصحيحات مشتقةً من الطريقة التي تعمل بها لغة البرمجة التي تكتب بها الشيفرة، بمعنىً آخر ستقترح <a href="https://academy.hsoub.com/programming/java/%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-programming-environment-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1020/" rel="">بيئة تطوير جافا Java</a> إصلاحات منطقيةً <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D9%84%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-java-r599/" rel="">للغة جافا</a>، لا <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">للغة بايثون Python</a> أو <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">++C</a>، وهذه الاقتراحات ليست مثاليةً دائمًا، حيث يمكن أن تقترح بيئة التطوير إصلاحًا بناءً على تلبية مواصفات اللغة فقط، لكنه قد يكون في سياق خاطئ، ومن الممكن أن يتسبب في تلف الشيفرة، لذا يعد فهمك للغة البرمجة هنا أمرًا مهمًا، ولك أن تستخدم التصحيحات الآلية للعثور على الأخطاء الواضحة، والتي يمكن أن توفر عليك الكثير من البحث والاستبدال اليدوي، وحتى إعادة بناء التعليمات البرمجية refactoring.
</p>

<h3>
	الاطلاع على المشروع
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94909" href="https://academy.hsoub.com/uploads/monthly_2022_03/6241adcb9d63b_AJavaprojectinEclipse.png.81945979c56d57f2d8a162704b929c43.png" rel=""><img alt="A Java project in Eclipse.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94909" data-unique="17p52yxsf" src="https://academy.hsoub.com/uploads/monthly_2022_03/6241adcb9d63b_AJavaprojectinEclipse.png.81945979c56d57f2d8a162704b929c43.png"></a>
</p>

<p>
	تُجمع التعليمات البرمجية والأصول الثابتة معًا حرفيًا أو افتراضيًا، بحيث لا تُترك أي عناصر مهمة عندما تكون جاهزًا لتوزيع شيفرتك، ولمعظم بيئات التطوير مفهوم ما عن دليل المشروع، تمامًا كما تفترض Autotools أو <a href="https://en.wikipedia.org/wiki/CMake" rel="external nofollow">CMake</a> وجود تخطيط قياسي لملفات شيفرة المشروع، ويجرد بعضها بنية التعليمات البرمجية ومكتباتها حتى تتمكن من رؤية كل شيء تعتمد عليه التعليمات البرمجية، مثل مجموعة الأدوات المثبتة افتراضيًا على مستوى النظام، والشيفرة التي أنشأتها على جهازك.
</p>

<p>
	يعتمد مستوى تقسيم ملفات الشيفرة الذي تحتاجه على مهاراتك في التنظيم، وتعقّب الاعتمادية dependency tracking، واللغة والمكتبات المستخدمة، ولكن يُفضل -بغض النظر عن مدى تعقيد المشروع أو بساطته- أن تعرف بيئة تطويرك الدليل الذي يجب أن يكون افتراضيًا عند البحث عن الملفات والمكتبات والأصول.
</p>

<h3>
	الإلمام بالأدوات المختلفة المستخدمة لتطوير الشيفرة البرمجية
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94914" href="https://academy.hsoub.com/uploads/monthly_2022_03/6241ade84c774_QtCreatoroptions.png.57e01750bd91b3939559a9b6a3dd7e5c.png" rel=""><img alt="Qt Creator options.png" class="ipsImage ipsImage_thumbnailed" data-fileid="94914" data-unique="428wxeuwe" src="https://academy.hsoub.com/uploads/monthly_2022_03/6241ade84c774_QtCreatoroptions.png.57e01750bd91b3939559a9b6a3dd7e5c.png"></a>
</p>

<p>
	تحتوي بيئة التطوير عادةً على نظام إدارة لتعيين المكتبات المستخدمة في الشيفرة، ووقت التشغيل، وخيارات المترجم، وتتبع التحكم في الإصدار، وهو أمر مهم في أي نظام، ولكنه يصبح ضروريًا على الأجهزة التي تُثبت عليها إصدارات متعددة من الأدوات، فمثلًا عند تثبيت كل من مجموعة أدوات Qt4 و Qt و Python 2 و Python 3، وإصدارات مختلفة من Java runtimes، ومكتبات 32 بت و64 بت، وGCC و LLVM وغيرها، يُفضل أن يكون على الجهاز بيئة تطوير IDE تدير كيفية بناء المشروع وتجميعه، للحصول على نتائج متسقة.
</p>

<p>
	يعد إنشاء التعليمات البرمجية الخاصة وتشغيلها وتصحيحها من داخل بيئة التطوير ميزةً مهمةً، وبذلك لا تحتاج إلى تطبيق تعديل التعليمات البرمجية لتشغيل المترجم أو تشغيل مصحح أخطاء، ويؤدي وجود هذه الميزات في واجهة واحدة إلى اتحاد عمليةٍ كانت متباينةً غالبًا.
</p>

<h2>
	هل استخدام بيئة التطوير المتكاملة اختياري؟
</h2>

<p>
	لا حاجة من الناحية التقنية إلى بيئة التطوير المتكاملة IDE لكتابة التعليمات البرمجية، بغض النظر عما يقال نظريًا، فمهما كانت اللغة التي تكتب بها، فسينتهي الأمر بتحليل النص ومعالجته بواسطة المترجم، لذلك كل ما تحتاجه حقًا هو <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AD%D8%B1%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%B9%D9%85%D9%84%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1438/" rel="">محرر نصوص</a> جيد وسلسلة أدوات بناء، وجميع الميزات التي سبق ذكرها اختيارية، ولا تحتوي كل بيئات التطوير المتكاملة IDE على كل هذه الميزات، ويمكن تجربة أنواع مختلفة، واكتشاف الميزات المهمة، ثم اختيار ما يناسبك بناءً على التجربة.
</p>

<h2>
	البحث عن بيئة تطوير متكاملة مفتوحة المصدر
</h2>

<p>
	يوجد الكثير من بيئات التطوير مفتوحة المصدر، بعضها مخصص للغة برمجة واحدة، مثل Eric وPayza وSpyder وPyCharm، وهي مخصصة <a href="https://wiki.hsoub.com/Python" rel="external">للغة بايثون Python</a>، وبعضها مخصص للغة جافا مثل BlueJ و IntelliJ، وبعضها الأخر مخصص للغات مختلفة وأطر عمل مختلفة، بحيث يمكن استخدامها في العديد من المشاريع المختلفة أو في المشاريع التي تستخدم لغات متعددة، ويمكنك دائمًا عدم استخدام بيئة تطوير متكاملة، واستخدام محرر نصوص فقط، أو محرر نصوص أُعدّ ليعمل مثل بيئة تطوير، مثل محرر النصوص <a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-vim-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-r76/" rel="">Vim</a>.
</p>

<p>
	وأهم ميزة لبيئة التطوير المتكاملة هي توفير الراحة والكفاءة، ولكن كما هو الحال مع أي أداة جديدة، يمكنك توقع منحنى تعليمي عندما تبدأ باستخدامها، لكنك ستفهم واجهتها لاحقًا، وستعرف مكان العثور على ميزات جديدة بسرعة، مما سيجعلك مبرمجًا أفضل وأسرع، وإذا لم تحصل على ذلك من بيئة تطوير واحدة، فجرّب بيئةً أخرى مختلفةً، ولا بد أن تجد ما يناسبك منها، نظرًا لوجود العديد من بيئات التطوير المتكاملة مفتوحة المصدر الرائعة لتختار من بينها.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/resources/what-ide" rel="external nofollow">What is an IDE?‎</a> من موقع opensource.com.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/c-sharp/dotnet/%D9%84%D9%85%D8%AD%D8%A9-%D8%B9%D9%86-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-net-core-r998/" rel="">لمحة عن بيئة تطوير NET Core.</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/web/wordpress/%D9%86%D9%82%D9%84-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-%D9%85%D9%86-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AD%D9%84%D9%8A%D8%A9-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A5%D9%86%D8%AA%D8%A7%D8%AC%D9%8A-%D8%A8%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-r108/" rel="">نقل ووردبريس من بيئة التطوير المحلية إلى خادوم إنتاجي بسهولة وسرعة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%86%D8%A7%D8%A1-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1473/" rel="">بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1513</guid><pubDate>Mon, 28 Mar 2022 13:03:41 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x646;&#x645;&#x648;&#x630;&#x62C; &#x643;&#x627;&#x645;&#x644; &#x644;&#x633;&#x644;&#x633;&#x644;&#x629; &#x623;&#x62F;&#x648;&#x627;&#x62A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x645;&#x646; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/%D8%A8%D9%86%D8%A7%D8%A1-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1473/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/6206106ee8848_----------.png.bdb8d89e40af0ed9235acf41c7c97185.png" /></p>

<p>
	سنعمل في هذا المقال على ترسيخ معرفتك بأدوات <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تطوير الويب</a> من طرف العميل من خلال إرشادك خلال عملية بناء نموذج لسلسلة أدوات toolchain، وسنقطع شوطًا طويلًا في إعداد بيئة تطوير ووضع أدوات التحويل في مكانها لنشر تطبيقك فعليًا على Netlify.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>.
	</li>
	<li>
		<strong>الهدف</strong>: ترسيخ ما تعلمناه حتى الآن من خلال العمل على دراسة حالة كاملة لسلسلة أدوات.
	</li>
</ul>
<p>
	هناك فعليًا مجموعات غير محدودة من الأدوات مع طرق استخدامها المختلفة، وما تراه في هذا المقال هو طريقة واحدة فقط يمكن من خلالها استخدام الأدوات المميزة لمشروع ما.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: لا يلزم تشغيل كل هذه الأدوات في سطر الأوامر، إذ يتمتع العديد من محرري الشيفرات اليوم -مثل VS Code وAtom- بدعم تكامل أدوات متعددة باستخدام الإضافات plugins.
		</p>
	</div>
</blockquote>

<h2>
	وصف دراسة الحالة
</h2>

<p>
	سنستخدم سلسلة الأدوات التي سننشئها في هذا المقال لبناء ونشر موقع صغير يعطي قائمة بيانات مأخوذة من إحدى واجهات برمجة التطبيقات المفتوحة التابعة لوكالة ناسا فيما يتعلق بالأجسام الفضائية التي يحُتمَل أن تكون خطرة وتهدد وجودنا على الأرض:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91765" href="https://academy.hsoub.com/uploads/monthly_2022_02/01_will-it-miss-screenshot.png.7f9472a21f3ecedfd70c808397a184e8.png" rel=""><img alt="01_will-it-miss-screenshot.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91765" data-unique="e1uardsug" src="https://academy.hsoub.com/uploads/monthly_2022_02/01_will-it-miss-screenshot.png.7f9472a21f3ecedfd70c808397a184e8.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	كما يمكنك مشاهدة نسخة حية من الموقع على <a href="https://near-misses.netlify.app/" rel="external nofollow">near-misses.netlify.com</a>.
</p>

<h2>
	الأدوات المستخدمة في سلسلة أدواتنا
</h2>

<p>
	سنستخدم الأدوات والميزات التالية:
</p>

<ul>
<li>
		<a href="https://wiki.hsoub.com/React/introducing_jsx" rel="external">JSX</a>: مجموعة من امتدادات الصياغة ذات الصلة بإطار العمل <a href="https://wiki.hsoub.com/React" rel="external">React</a> والتي تتيح تطبيق أشياء مثل تحديد بنى المكونات ضمن جافاسكربت. لن تحتاج إلى معرفة إطار عمل React لاتباع هذا المقال، لكننا ضمّنا هذه الأداة لإعطائك فكرة عن كيفية دمج لغة ويب غير أصيلة non-native ضمن سلسلة أدوات.
	</li>
	<li>
		أحدث ميزات جافاسكربت المبنية مسبقًا (في وقت كتابة النسخة الأجنبية من هذا المقال) مثل <code>import</code>.
	</li>
	<li>
		أدوات تطوير مفيدة مثل <a href="https://prettier.io" rel="external nofollow">Prettier</a> للتنسيق و<a href="https://eslint.org/" rel="external nofollow">Eslint</a> لكشف الأخطاء في الصياغة.
	</li>
	<li>
		<a href="https://postcss.org/" rel="external nofollow">PostCSS</a> لتوفير إمكانات تداخل CSS.
	</li>
	<li>
		<a href="https://parceljs.org/" rel="external nofollow">Parcel</a>: لبناء وتقليل حجم شيفرتنا وكتابة محتوى ملف الإعداد تلقائيًا.
	</li>
	<li>
		GitHub: لإدارة التحكم في الشيفرة المصدرية.
	</li>
	<li>
		<a href="https://www.netlify.com/" rel="external nofollow">Netlify</a>: لأتمتة عملية النشر.
	</li>
</ul>
<p>
	يمكن ألّا تكون على دراية بجميع الميزات والأدوات السابقة أو ما تفعله، ولكن لا داعي للقلق، لأننا سنشرحها لاحقًا.
</p>

<h2>
	سلاسل الأدوات وتعقيدها المتوارث
</h2>

<p>
	كلما زاد عدد الروابط الموجودة في سلسلة أدواتك، كانت أكثر تعقيدًا ويُحتمَل أن يكون ترابطها هشًا، وإعدادها أكثر تعقيدًا وسهلة الكسر. بينما كلما قل عدد الروابط، زادت مرونة سلسلة الأدوات.
</p>

<p>
	تختلف مشاريع الويب عن بعضها بعضًا، لذلك يجب التفكير في الأجزاء الضرورية من سلسلة أدواتك والنظر في كل جزء بعناية.
</p>

<p>
	أصغر سلسلة أدوات هي سلسلة لا تحتوي على روابط على الإطلاق. يمكنك كتابة شيفرة HTML يدويًا، واستخدام لغة جافاسكربت الصرفة Vanilla JavaScript دون وجود أطر عمل أو لغات وسيطة، وتحميلها يدويًا إلى خادم للاستضافة.
</p>

<p>
	لكن يُرجَّح أن تستفيد متطلبات البرامج الأكثر تعقيدًا من استخدام الأدوات للمساعدة في تبسيط عملية التطوير. كما يجب تضمين الاختبارات قبل النشر إلى خادم الإنتاج الخاص بك للتأكد من أن برنامجك يعمل على النحو المنشود، وهذا يبدو بالفعل وكأنه سلسلة أدوات ضرورية.
</p>

<p>
	سنستخدم في مشروعنا سلسلة أدوات مصممة خصيصًا للمساعدة في تطوير برامجنا ودعم الخيارات التقنية التي تُجرَى أثناء مرحلة تصميم البرنامج، ولكن سنتجنب أيّ أدوات غير ضرورية، بهدف تقليل التعقيد إلى الحد الأدنى. كان بإمكاننا تضمين أداة لتقليل أحجام ملفات SVG أثناء الإنشاء مثلًا، ولكن يحتوي هذا المشروع على 4 صور SVG فقط التي صغّرناها يدويًا باستخدام الأداة <a href="https://www.npmjs.com/package/svgo" rel="external nofollow">SVGO</a> قبل إضافتها إلى المشروع.
</p>

<h2>
	المتطلبات الأساسية
</h2>

<p>
	ذكرنا خدمتين على الويب في قائمة الأدوات أعلاه إلى جانب الأدوات التي سنثبّتها وستساهم في سلسلة الأدوات الخاصة بنا. إذًا لننتهز الفرصة للتأكد من إعدادها قبل المواصلة، وستحتاج إلى إنشاء حسابات في GitHub وNetlify.
</p>

<ul>
<li>
		GitHub هي خدمة مستودع لشيفرة مصدرية تضيف مجموعة ميزات خاصة بمجتمع المطورين مثل تتبع المشكلات وإصدارات المشروع التالية وغير ذلك. سنرفع لاحقًا الشيفرة إلى مستودع شيفرة GitHub الذي سيضيف تأثيرًا تسلسليًا لنشر البرامج على الصفحة الرئيسية في الويب.
	</li>
	<li>
		Netlify هي خدمة استضافة لمواقع الويب الثابتة، أي مواقع الويب التي تتكون بالكامل من ملفات لا تتغير في الوقت الحقيقي، والتي تتيح النشر عدة مرات في اليوم واستضافة المواقع الثابتة من جميع الأنواع بحريّة. توفر Netlify الصفحة الرئيسية على الويب، وبالتالي توفّر استضافة مجانية لنشر تطبيق الاختبار الخاص بنا عليه.
	</li>
</ul>
<p>
	يمكنك التسجيل في <a href="https://github.com/" rel="external nofollow">GitHub</a> (من خلال النقر على رابط التسجيل Sign Up في الصفحة الرئيسية إن لم يكن لديك حساب سابقًا، واتبع تعليمات استخدام حساب GitHub في عملية الاستيثاق على Netlify (انقر على تسجيل Sign Up، ثم اختر GitHub من قائمة "التسجيل باستخدام إحدى القوائم التالية Sign up with one of the following")، لذلك ما عليك سوى إنشاء حساب جديد.
</p>

<p>
	ستحتاج لاحقًا إلى ربط حساب Netlify بمستودع GitHub لنشر هذا المشروع، وسنرى كيفية تطبيق ذلك في المقال التالي.
</p>

<h2>
	مراحل الأدوات
</h2>

<p>
	تُنظَّم سلسلة الأدوات ضمن المراحل التالية:
</p>

<ul>
<li>
		شبكة الأمان Safety Net: تجعل تجربة تطوير البرمجيات مستقرة وأكثر كفاءة، ونشير إليها بوصفها بيئة التطوير.
	</li>
	<li>
		التحويل Transformation: هي الأدوات التي تتيح استخدام أحدث ميزات لغة مثل لغة جافاسكربت أو لغة أخرى مثل JSX أو <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> في عملية التطوير، ثم تحوّل شيفرتنا لكي يستمر إصدار الإنتاج يعمل على مجموعة متنوعة من المتصفحات الحديثة والقديمة.
	</li>
	<li>
		ما بعد التطوير Post Development: هي الأدوات التي تُشغَّل بعد الانتهاء من التطوير لضمان وصول برنامجك إلى الويب واستمراره في العمل. سننظر في إضافة اختبارات إلى شيفرتنا، ونشر التطبيق باستخدام Netlify لتكون متاحةً على الويب ويمكن للمستخدمين مشاهدته.
	</li>
</ul>
<p>
	لنبدأ العمل بدءًا من بيئة التطوير خاصتنا.
</p>

<h2>
	إنشاء بيئة تطوير
</h2>

<p>
	يُنظَر أحيانًا إلى هذا الجزء من سلسلة الأدوات على أنه يؤخر العمل الفعلي، إذ ستقضي كثيرًا من الوقت في محاولة جعل البيئة مناسبة تمامًا. لكن يمكنك النظر إلى هذا الجزء بالطريقة نفسها التي تُعِد بها بيئة عملك المادية، إذ يجب أن يكون الكرسي مريحًا ومجهزًا في وضع جيد، ويجب استخدام منافذ طاقة وواي فاي وUSB، ويمكن لبعض الزخارف أو الموسيقى أن تحسين مزاجك، إذ تُعَد هذه الأشياء مهمة لتنفّذ أفضل عمل ممكن، وتجهز لمرة واحدة فقط إن احسنت تجهيزها.
</p>

<p>
	وكذلك يجب إعداد بيئة التطوير الخاصة بك بالطريقة نفسها، ويمكن إعدادها مرة واحدة فقط وإعادة استخدامها في العديد من المشاريع المستقبلية، إذا أُعِدت بطريقة صحيحة. يمكن أن ترغب في مراجعة هذا الجزء من سلسلة الأدوات بطريقة شبه منتظمة والتفكير فيما إذا كان هناك أيّ ترقيات أو تغييرات يجب إدخالها، ولكن لا ينبغي أن يكون هذا مطلوبًا كثيرًا.
</p>

<p>
	ستعتمد سلسلة أدواتك على احتياجاتك الخاصة، ولكن بالنسبة لمثالنا الخاص بسلسلة أدوات كاملة، فإن الأدوات التي يجب تثبيتها مقدمًا هي:
</p>

<ul>
<li>
		أدوات تثبيت المكتبة Library Installation Tools لإضافة الاعتماديات.
	</li>
	<li>
		التحكم في مراجعة الشيفرة Code Revision Control.
	</li>
	<li>
		أدوات ترتيب الشيفرة Code Tidying Tools لتنظيم شيفرات جافاسكربت وCSS وHTML.
	</li>
	<li>
		أدوات كشف أخطاء الشيفرة Code Linting Tools.
	</li>
</ul>
<h3>
	أدوات تثبيت المكتبة
</h3>

<p>
	سنستخدم مدير الحزم npm لتثبيت أدواتنا، ويجب أن يكون لديك Node.js وnpm مثبتين مسبقًا.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: بالرغم من عدم وضوح عملية التثبيت، إلا أن تثبيت npm يؤدي إلى تثبيت أداة مجانية تسمى npx التي سنستخدمها لاحقًا للمساعدة في تشغيل الأدوات المثبَّتة كاعتماديات محلية للمشروع.
		</p>
	</div>
</blockquote>

<p>
	سنستخدم npm لتثبيت الأجزاء اللاحقة من سلسلة أدواتنا، وسنثبّت git للمساعدة في التحكم في المراجعة.
</p>

<h3>
	التحكم في مراجعة الشيفرة
</h3>

<p>
	يُحتمَل أنك سمعت عن أداة Git من قبل، إذ تعد أداة <a href="https://git-scm.com/" rel="external nofollow">Git</a> حاليًا من أكثر أدوات التحكم في مراجعة الشيفرة المصدرية شيوعًا والمتاحة للمطورين، إذ توفر التحكم في مراجعة الشيفرة البرمجية بالإضافة للعديد من المزايا مثل طريقة نسخ عملك الاحتياطي في مكان بعيد وآلية العمل ضمن فريق في المشروع نفسه دون الخوف من الكتابة فوق شيفرة بعضنا بعضًا.
</p>

<p>
	Git وGitHub مختلفان، إذ تُعَد أداة Git أداةً التحكم في المراجعة، بينما يُعَد موقع <a href="https://github.com/" rel="external nofollow">GitHub</a> مخزنًا على الإنترنت لمستودعات Git بالإضافة إلى عدد من الأدوات المفيدة للعمل معها. لاحظ أنه بالرغم من أننا نستخدم GitHub في هذا المقال، إلا أن هناك العديد من البدائل بما في ذلك <a href="https://about.gitlab.com/" rel="external nofollow">GitLab</a> و<a href="https://about.gitlab.com/" rel="external nofollow">Bitbucket</a>، كما يمكنك استضافة مستودعات git الخاصة بك على جهازك الشخصي ببساطة.
</p>

<p>
	سيساعد استخدام التحكم في المراجعة في مشاريعك وإدراجه بوصفه جزءًا من سلسلة الأدوات في إدارة تطور شيفرتك، وتوفر طريقة للالتزام بكتل العمل أثناء تقدمك، جنبًا إلى جنب مع تعليقات مثل "تنفيذ ميزة جديدة معينة"، أو "إصلاح خطأ معين بسبب تغييرات محددة".
</p>

<p>
	كما يمكن أن يتيح لك التحكم في المراجعة تفريع شيفرة مشروعك، وإنشاء إصدار منفصل وتجربة وظائف جديدة، دون أن تؤثر تلك التغييرات على الشيفرة الأصلية.
</p>

<p>
	أخيرًا، يمكن أن يساعدك على التراجع عن تغييرات أو إعادة الشيفرة إلى الوقت الذي كانت تعمل فيه عند إدخال خطأ في مكان ما دون إصلاحه، وسيحتاجُ جميع المطورين ذلك من حين لآخر.
</p>

<p>
	يمكن تنزيل Git وتثبيته عبر موقع <a href="https://git-scm.com/downloads" rel="external nofollow">git-scm</a> على الويب. نزّل برنامج التثبيت المناسب لنظامك، وشغّله، واتبع التعليمات التي تظهر على الشاشة.
</p>

<p>
	يمكنك التفاعل مع git بعدة طرق مختلفة، من استخدام سطر الأوامر إلى استخدام تطبيق git GUI لإصدار الأوامر من خلال الضغط على الأزرار، أو حتى من داخل محرر الشيفرة الخاص بك مباشرةً، كما هو موضح في المثال التالي في Visual Studio Code:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91766" href="https://academy.hsoub.com/uploads/monthly_2022_02/02_vscode-git.png.efbc02d29eb8ddb7687e9e468746a2d4.png" rel=""><img alt="02_vscode-git.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91766" data-unique="7ayt7ftkf" src="https://academy.hsoub.com/uploads/monthly_2022_02/02_vscode-git.png.efbc02d29eb8ddb7687e9e468746a2d4.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	لكن تثبيت git هو كل ما نحتاجه حاليًا.
</p>

<h3>
	أدوات ترتيب الشيفرة
</h3>

<p>
	سنستخدم الأداة Prettier لترتيب شيفرتنا في المشروع. ثبّت Prettier، أو يمكنك تثبيتها بوصفها أداة مساعدة عالمية باستخدام الطرفية Terminal.
</p>

<p>
	يمكنك التحقق مما إذا كانت Prettier مثبّتة عالميًا باستخدام الأمر التالي:
</p>

<pre class="ipsCode">
prettier -v
</pre>

<p>
	ستحصل في حالة التثبيت على رقم إصدار مُعاد مثل رقم الإصدار 2.0.2، وإن لم تكن مثبّتة، فسيعيد شيئًا مثل "الأمر غير موجود command not found". إن لم تكن مثبّتة، فثبتها باستخدام الأمر التالي:
</p>

<pre class="ipsCode">
npm install prettier -g
</pre>

<p>
	يمكن بعد ذلك تشغيل الشيفرة وترتيبها في سطر الأوامر على أساس ملف من أي مكان على حاسوبك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_945_9" style="">
<span class="pln">prettier --write ./src/index.html</span></pre>

<p>
	 استخدمنا في الأمر السابق الأداة Prettier مع الراية <code>‎--write</code>، إذ ستفهمُ Prettier أن هذا يعني أنه "إذا كانت هناك مشاكل في تنسيق شيفرتك، فتابع وأصلحها، ثم احفظ الملف"، وهذا يناسب عملية التطوير الخاصة بنا، ولكن يمكننا استخدام Prettier بدون الراية وستتحقق من الملف فقط. يُعد التحقق من الملف -دون حفظه- مفيدًا لأغراض مثل عمليات التحقق المُشغَّلة قبل الإصدار، أي "لا تصدر أيّ شيفرة ليس تنسيقها صحيحًا".
</p>

<p>
	ولكن تشغيل الأمر على كل ملف أمرًا صعبًا، وبالتالي سيكون استخدام أمر واحد لذلك مفيدًا جدًا، وينطبق الشيء نفسه على أدوات كشف الأخطاء Linting الخاصة بنا.
</p>

<p>
	هناك العديد من الطرق المختلفة لحل هذه المشكلة ومنها:
</p>

<ul>
<li>
		استخدام سكربتات npm لتشغيل أوامر متعددة من سطر الأوامر دفعة واحدة مثل الأمر <code>npm run tidy-code</code>.
	</li>
	<li>
		استخدام خطّافات جيت git hooks الخاصة لاختبار ما إذا كانت الشيفرة منسَّقة قبل الالتزام.
	</li>
	<li>
		استخدام إضافات plugins محرر الشيفرة لتشغيل أوامر prettier في كل مرة يُحفَظ الملف فيها.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: ما هو خطّاف جيت git hook؟ يوفر Git -وليس GitHub- نظامًا يتيح إرفاق الإجراءات المسبقة واللاحقة بالمهام التي تؤديها باستخدام git مثل تنفيذ الشيفرة. يمكن أن تكون خطّافات git معقدة بعض الشيء، لكن بمجرد وضعها في مكانها يمكن أن تكون قوية جدًا. إذا كنت مهتمًا باستخدام الخطافات، فإن <a href="https://github.com/typicode/husky" rel="external nofollow">Husky</a> هو مسار مبسط إلى حد كبير لاستخدام الخطافات.
		</p>
	</div>
</blockquote>

<p>
	أحد الإضافات المفيدة في VS Code هو Prettier Code Formatter من Esben Petersen التي تتيح تنسيق الشيفرة تلقائيًا عند الحفظ، وهذا يعني تنسيق أي ملف في المشروع الذي نعمل عليه، بما في ذلك ملفات HTML وCSS وجافاسكربت وJSON وmarkdown وغير ذلك. كل ما يحتاجه محرّر الشيفرة هو تفعيل "التنسيق عند الحفظ Format On Save".
</p>

<p>
	كما يمكن استخدام Prettier دون الحاجة إلى تهيئة أي شيء، إن كنت راضيًا عن الإعدادات الافتراضية.
</p>

<h3>
	أدوات كشف أخطاء الشيفرة
</h3>

<p>
	يساعد كشف الأخطاء Linting في تحسين جودة الشيفرة، وهو وسيلة لكشف الأخطاء المحتمَلة في وقت مبكر أثناء عملية التطوير. كما أنه مكون رئيسي في سلسلة الأدوات وتتضمنه العديد من مشاريع التطوير افتراضيًا.
</p>

<p>
	تكون أدوات كشف أخطاء تطوير الويب خاصة بلغة جافاسكربت في أغلب الأحيان، بالرغم من توفر القليل منها للغتي HTML وCSS. إذا اُستخدِم عنصر HTML غير معروف أو خاصية CSS غير صالحة، فيُحتمَل ألّا ينكسر أيّ شيء نظرًا للطبيعة المرنة لهاتين اللغتين. بينما تُعَد لغة جافاسكربت أكثر هشاشة، فمثلًا يؤدي استدعاء دالة غير موجودة عن طريق الخطأ إلى تعطّل الشيفرة، لذلك يُعَد كشف أخطاء شيفرة جافاسكربت مهمًا جدًا خاصةً في المشاريع الكبيرة.
</p>

<p>
	أداة الانتقال إلى كشف أخطاء شيفرة جافاسكربت هي <a href="https://eslint.org/" rel="external nofollow">Eslint</a> التي تُعَد أداة قوية ومتعددة الاستخدامات، ولكن يمكن أن تكون صعبة نوعًا ما لإعدادها بطريقة صحيحة، إذ يستغرق الأمر غالبًا عدة ساعات في محاولة الحصول على الإعداد الصحيح تمامًا.
</p>

<p>
	ستظهر الأداة Eslint خطأ بأنه لا يمكنها العثور على ملف الإعداد إذا شغّلته. يدعم ملف الإعداد تنسيقات متعددة ولكننا سنستخدم في مشروعنا ملف <code>‎.eslintrc.json</code>، وتعني النقطة في بداية اسم الملف أن الملف مخفي افتراضيًا. تُثبَّت الأداة Eslint باستخدام npm، لذلك لديك خيار تثبيت هذه الأداة تثبيتًا محليًا أو عامًا، إذ يوصَى باستخدام كليهما:
</p>

<ul>
<li>
		يجب دائمًا تضمين Eslint كاعتمادية محلية في المشاريع التي تنوي مشاركتها ليتمكن أي شخص من إنشاء نسخته الخاصة اتباع القواعد التي طبقتها على المشروع.
	</li>
	<li>
		يجب أن تفكر في تثبيت الأداة Eslint تثبيتًا عامًا لتمكنك من استخدامها بسرعة للتحقق من أي ملف تريده.
	</li>
</ul>
<p>
	لن نشرح جميع ميزات Eslint في هذا المقال، لكننا سنضع إعدادًا مناسبًا لمشروعنا الخاص ومتطلباته. ضع في بالك أنه إذا رغبتَ في تحسين وفرض قاعدة حول كيفية ظهور شيفرتك أو التحقق من صحتها، فيُحتمَل أن يُطبَّق ذلك باستخدام إعداد Eslint الصحيح.
</p>

<p>
	سنقدم لاحقًا ملف إعداد Eslint، إذ يمكن أن يؤدي تشغيل الأمر إلى إنشاء بعض المعلومات المفيدة. فيما يلي مثال لخرج Eslint:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_945_17" style="">
<span class="pun">.</span><span class="str">/my-project/</span><span class="pln">src</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">js
   </span><span class="lit">2</span><span class="pun">:</span><span class="lit">8</span><span class="pln"> error </span><span class="str">'React'</span><span class="pln"> is defined but never used  no</span><span class="pun">-</span><span class="pln">unused</span><span class="pun">-</span><span class="pln">vars
 </span><span class="lit">22</span><span class="pun">:</span><span class="lit">20</span><span class="pln"> error </span><span class="str">'body'</span><span class="pln"> is defined but never used   no</span><span class="pun">-</span><span class="pln">unused</span><span class="pun">-</span><span class="pln">vars
 </span><span class="lit">96</span><span class="pun">:</span><span class="lit">19</span><span class="pln"> error </span><span class="str">'b'</span><span class="pln"> is defined but never used      no</span><span class="pun">-</span><span class="pln">unused</span><span class="pun">-</span><span class="pln">vars

</span><span class="pun">✖</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> problems </span><span class="pun">(</span><span class="lit">3</span><span class="pln"> errors</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> warnings</span><span class="pun">)</span></pre>

<p>
	يُعَد دعم تكامل محرر الشيفرة مفيدًا لأداة Eslint، ويُحتمَل أن يكون أكثر فائدة لأنه يمكن أن يقدم لنا ملاحظات في الوقت الحقيقي عند ظهور المشاكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91767" href="https://academy.hsoub.com/uploads/monthly_2022_02/03_eslint-error.png.c0dd33f15d3e159c8e9f7ccaa43dbad2.png" rel=""><img alt="03_eslint-error.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91767" data-unique="nhhzhf19t" src="https://academy.hsoub.com/uploads/monthly_2022_02/03_eslint-error.png.c0dd33f15d3e159c8e9f7ccaa43dbad2.png" style="width: 700px; height: auto;"></a>
</p>

<h2>
	إعداد المشروع الأولي
</h2>

<p>
	يمكن إعداد مشروع جديد بأمان باستخدام هذه الأدوات مع العلم أن العديد من المشاكل الأساسية ستُكتشَف مبكرًا.
</p>

<p>
	يمكننا إنشاء المشروع وتثبيت الأدوات الأولية وإنشاء ملفات إعداد أولية باستخدام سطر الأوامر، ثم ستشعر بما يجب أن يكون عليه الإعداد الافتراضي بمجرد تكرار هذه العملية عدة مرات.
</p>

<p>
	حسنًا لنبدأ الإعداد المشروع الأولي.
</p>

<ol>
<li>
		ابدأ بفتح الطرفية، وانتقل إلى مكان يمكنك العثور عليه والوصول إليه بسهولة مثل سطح المكتب Desktop أو المجلد الرئيسي أو مجلد المستندات.
	</li>
	<li>
		ثم شغّل الأوامر التالية لإنشاء مجلد لتحتفظ بمشروعك فيه، وانتقل إلى المجلد:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_945_22" style="">
<span class="pln">mkdir will</span><span class="pun">-</span><span class="pln">it</span><span class="pun">-</span><span class="pln">miss
cd will</span><span class="pun">-</span><span class="pln">it</span><span class="pun">-</span><span class="pln">miss</span></pre>

<ol start="3">
<li>
		سننشئ الآن دليلًا جديدًا لجميع شيفرات تطوير موقع الويب لنضعها فيه. شغّل الأمر التالي:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_945_20" style="">
<span class="pln">mkdir src</span></pre>

<p>
	يختلف تنظيم الشيفرة من فريق لآخر. سنضع شيفرة مشروعنا المصدرية في الدليل <code>src</code>.
</p>

<ol start="4">
<li>
		تأكد من أنك داخل جذر الدليل <code>will-it-miss</code>، ثم أدخِل الأمر التالي لبدء وظيفة التحكم في شيفرة git المصدرية التي تعمل في الدليل:
	</li>
</ol>
<pre class="ipsCode">
git init
</pre>

<p>
	هذا يعني أنك ستتمكن من بدء تخزين مراجعات محتويات المجلد، وحفظها في مستودع بعيد وغير ذلك.
</p>

<ol start="5">
<li>
		أدخل الأمر التالي بعد ذلك لتحويل دليلك إلى حزمة npm مع المزايا التي ناقشناها في المقال السابق:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_945_24" style="">
<span class="pln">npm init --force</span></pre>

<p>
	سيؤدي الأمر السابق إلى إنشاء ملف <code>package.json</code> افتراضي يمكننا تهيئته لاحقًا إذا رغبنا في ذلك. كما يؤدي استخدام الراية <code>‎--force</code> في أن ينشئ الأمر ملف <code>package.json</code> افتراضي على الفور دون طرح جميع الأسئلة المعتادة حول المحتويات التي تريدها كما رأينا سابقًا، وسنحتاج فقط الإعدادات الافتراضية حاليًا، وبالتالي نوفر قليلًا من الوقت.
</p>

<h3>
	الحصول على ملفات شيفرة المشروع
</h3>

<p>
	سنحصل في هذه المرحلة على ملفات شيفرة المشروع (HTML وCSS وجافاسكربت وغيرها)، وسنضعها في الدليل <code>src</code>. لن نعلّمك كيفية عملها، لأنه ليس هدف هذا المقال، بل هدفنا تشغيل الأدوات لتتعلّم كيفية عملها.
</p>

<ol>
<li>
		يمكنك الحصول على ملفات الشيفرة من خلال زيارة <a href="https://github.com/remy/mdn-will-it-miss" rel="external nofollow">هذه الصفحة</a> وتنزيل وفك ضغط محتويات هذا المستودع على قرصك الصلب المحلي في مكانٍ ما. يمكنك تنزيل المشروع بأكمله كملف مضغوط عن طريق تحديد خيار نسخ Clone أو تنزيل Download ثم Download ZIP.
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91768" href="https://academy.hsoub.com/uploads/monthly_2022_02/04_github-will-it-miss.png.4a817f4e480d98e1d990189695195478.png" rel=""><img alt="04_github-will-it-miss.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91768" data-unique="7rume5yeu" src="https://academy.hsoub.com/uploads/monthly_2022_02/04_github-will-it-miss.thumb.png.4a4dd79f7c18eacb64129de078028d1f.png"></a>
</p>

<ol start="2">
<li>
		انسخ محتويات الدليل <code>src</code> الخاص بالمشروع إلى دليل <code>src</code> فارغ حاليًا. أصبحت ملفات مشروعنا في مكانها الصحيح، هذا كل ما نحتاجه حاليًا.
	</li>
</ol>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: يمكنك إعداد المشروع على جهازك المحلي من خلال الانتقال إلى الدليل الجذر للمجلد الذي فكّينا ضغطه، وفتح الطرفية في هذا الموقع، ثم تنفيذ الأمر <code>npm install</code> في الطرفية، مما يؤدي إلى تثبيت جميع اعتماديات المشروع المخزنة في الملف <code>package.json</code>.
		</p>
	</div>
</blockquote>

<h3>
	تثبيت أدواتنا
</h3>

<p>
	حان الوقت الآن لتثبيت المجموعة الأولية من الأدوات التي سنستخدمها في بيئة التطوير الخاصة بنا. شغّل الأمر ضمن الدليل الجذر لمشروعك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_945_26" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev eslint prettier babel</span><span class="pun">-</span><span class="pln">eslint</span></pre>

<p>
	هناك شيئان مهمان يجب ملاحظتهما حول الأمر الذي شغّلته للتو. الأول هو أننا نثبّت الاعتماديات محليًا على المشروع، إذ يُعَد تثبيت الأدوات محليًا أفضل لمشروع معين. يتيح التثبيت محليًا إعادة إنشاء الإعداد بسهولة على أجهزة أخرى، ولكن لا يشمل ذلك الخيار <code>‎--global</code>.
</p>

<p>
	الجزء الثاني المهم من أمر التثبيت هو الخيار <code>‎--save-dev</code> الذي يخبر npm أن هذه الاعتماديات المعينة مطلوبة فقط من أجل التطوير، لذلك يسردها npm في الملف <code>package.json</code> ضمن اعتماديات التطوير <code>devDependencies</code> وليس ضمن الاعتماديات <code>devDependencies</code>، وبالتالي إذا ثُبِّتَ هذا المشروع في وضع الإنتاج، فلن تُثبَّت هذه الاعتماديات. يمكن أن يحتوي المشروع النموذجي على العديد من اعتماديات التطوير التي لا حاجة إليها لتشغيل الشيفرة فعليًا في الإنتاج، ويؤدي إبقائها كاعتماديات منفصلة إلى تقليل العمل غير الضروري عند النشر في عملية الإنتاج، وهو ما سنلقي نظرة عليه في المقال التالي.
</p>

<p>
	يجب تطبيق إعداد قبل البدء في تطوير شيفرة التطبيق الفعلية لتعمل أدواتنا بطريقة صحيحة، وهو ليس شرطًا أساسيًا في تطوير الويب، ولكن يُعَد إعداد الأدوات بطريقة صحيحة أمرًا مفيدًا، إذا ساعد في اكتشاف الأخطاء أثناء عملية التطوير، وهو أمر مفيد خاصة لأداة Eslint.
</p>

<h3>
	إعداد أدواتنا
</h3>

<p>
	سنضيف ملفات الإعداد في جذر المشروع -وليس في الدليل <code>src</code>- لإعداد الأدوات Prettier وEslint. يمكنك العثور على ملفات الإعداد في جذر المشروع، والتي لا تحتوي غالبًا على خيارات الإعداد المعبَّر عنها ببنية JSON، بالرغم من أن أدواتنا والعديد من الأدوات الأخرى تدعم بنية YAML أيضًا، والتي يمكنك التبديل إليها إذا كانت المفضلة لديك.
</p>

<ol>
<li>
		أنشئ أولًا ملفًا في جذر الدليل <code>will-it-Miss</code> وسمّه <code>‎.prettierrc.json</code>.
	</li>
	<li>
		يمكنك إعداد Prettier من خلال وضع المحتويات التالية في الملف <code>‎.prettierrc.json</code>:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_945_28" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"singleQuote"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"trailingComma"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"es5"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا استخدمت الإعدادات السابقة مع تنسيقات Prettier الخاصة بلغة جافاسكربت، فستستخدم علامات اقتباس فردية لجميع القيم المقتبسة، ولن تستخدم فواصل لاحقة، وهي ميزة أحدث في ECMAScript ستتسبب في حدوث أخطاء في المتصفحات القديمة.
</p>

<ol start="3">
<li>
		يجب إعداد Eslint بعد ذلك من خلال إنشاء ملف آخر في جذر الدليل <code>will-it-miss</code> بالاسم <code>‎.eslintrc.json</code>، وضع فيه المحتويات التالية:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_945_30" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"env"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"es6"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"browser"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"extends"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"eslint:recommended"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"parserOptions"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"ecmaVersion"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"sourceType"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"module"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"rules"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"no-console"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يشير إعداد Eslint السابق إلى أننا نريد استخدام إعدادات Eslint الموصَى بها، وأننا سنسمح باستخدام ميزات ES6 مثل <code>map()‎</code> أو <code>Set()‎</code>، ويمكننا استخدام عبارات استيراد الوحدة <code>import</code>، وأن استخدام التابع <code>console.log()‎</code> مسموح.
</p>

<ol start="4">
<li>
		لكننا نستخدم صيغة JSX الخاصة بإطار عمل React في ملفات المشروع المصدرية، ويمكن أن تستخدم في مشاريعك الحقيقية إطار العمل React أو Vue أو أي إطار عمل آخر، ويمكن ألّا تستخدم إطار عمل على الإطلاق. سيؤدي وضع صيغة JSX في شيفرة جافاسكربت إلى أن تظهر أخطاء Eslint بسرعة كبيرة من الإعداد الحالي، لذلك سنحتاج إلى إضافة مزيد من إعدادات Eslint لقبول ميزات JSX. يجب أن يبدو ملف الإعداد config file النهائي على النحو التالي:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_945_15" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"env"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"es6"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"browser"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"extends"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"eslint:recommended"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"plugin:react/recommended"</span><span class="pun">],</span><span class="pln">
  </span><span class="str">"parserOptions"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"ecmaVersion"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"sourceType"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"module"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"ecmaFeatures"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"jsx"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"plugins"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"react"</span><span class="pun">],</span><span class="pln">
  </span><span class="str">"rules"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"semi"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"error"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"no-console"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"react/jsx-uses-vars"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"error"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بما أن الإعداد يستخدم مكونًا إضافيًا يسمى React، فيجب تثبيت اعتمادية التطوير هذه حتى تكون الشيفرة موجودة لتشغيل هذا الجزء من عملية كشف الأخطاء.
</p>

<ol start="5">
<li>
		شغّل الأمر التالي في الطرفية ضمن جذر مجلد مشروعك:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_945_32" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev eslint</span><span class="pun">-</span><span class="pln">plugin</span><span class="pun">-</span><span class="pln">react</span></pre>

<p>
	هناك <a href="https://eslint.org/docs/rules/" rel="external nofollow">قائمة كاملة بقواعد Eslint</a> التي يمكنك تعديلها وإعدادها وفقًا لمحتواك، بالإضافة إلى ذلك نشرت العديد من الشركات والفرق إعدادات Eslint الخاصة بها، والتي يمكن أن تكون مفيدة في بعض الأحيان إما للاستلهام منها أو لتحديد إحداها التي تناسب معاييرك الخاصة.
</p>

<p>
	وبذلك اكتمل إعداد بيئة التطوير، وأصبحنا جاهزين لكتابة الشيفرة تقريبًا.
</p>

<h2>
	أدوات البناء والتحويل
</h2>

<p>
	سنستخدم إطار العمل React في مشروعنا، مما يعني استخدام صيغة JSX في الشيفرة المصدرية. كما سنستخدم في مشروعنا أحدث ميزات لغة جافاسكربت.
</p>

<p>
	هناك مشكلة وهي أنه لا يوجد متصفح يدعم JSX بطريقة أصيلة، لإنها لغة وسيطة ويُفترَض أن تُصرَّف إلى لغات يفهمها المتصفح في شيفرة الإنتاج.
</p>

<p>
	إذا حاول المتصفح تشغيل شيفرة جافاسكربت المصدرية، فستظهر أخطاء على الفور، إذ يحتاج المشروع إلى أداة بناء لتحويل الشيفرة المصدرية إلى شيء يمكن أن يفهمه المتصفح دون مشاكل. هناك عدد من الخيارات لأدوات التحويل، وبالرغم من أن <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">WebPack</a> هو خيار شائع، لكننا سنستخدم Parcel في مشروعنا لأنها تتطلب إعدادًا أقل بكثير.
</p>

<p>
	تعمل Parcel على أساس أنها ستحاول إعداد متطلبات التطوير الخاصة بك على الفور، إذ ستراقب الشيفرة وتشغّل خادم ويب لإعادة التحميل مباشرةً أثناء التطوير، وبالتالي ستثبّت Parcel اعتماديات برامجنا تلقائيًا كما هو مشار إليها في الشيفرة المصدرية. كما ستهتم Parcel بتثبيت أي أدوات تحويل وإعداد المطلوب دون الحاجة إلى التدخل في معظم الحالات، ويمكن أن تُجمّع Parcel الشيفرة البرمجية وتجهّزها للنشر في وضع الإنتاج مع الاهتمام بالتصغير ومتطلبات توافق المتصفح.
</p>

<p>
	لذلك يجب تثبيت اعتمادية parcel في مشروعنا من خلال تشغيل الأمر التالي في طرفيتك:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2659_12" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev parcel</span><span class="pun">-</span><span class="pln">bundler</span></pre>

<h3>
	استخدام الميزات المستقبلية
</h3>

<p>
	تستخدم شيفرة مشروعنا بعض ميزات الويب الجديدة بما في ذلك الميزات الجديدة جدًا التي لم يكتمل توحيدها بعد مثل استخدام اقتراح W3C لتداخل CSS بدلًا من الوصول إلى أداة مثل الأداة <a href="https://wiki.hsoub.com/Sass" rel="external">Sass</a>. يسمح تداخل nesting <a href="https://academy.hsoub.com/programming/css/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D8%B3%D9%8A%D9%82-css-r551/" rel="">لغة CSS</a> بأن تتداخل محدّدات وخصائص CSS ضمن بعضها بعضًا، وبالتالي ينشأ مجال محدِّد selector أكثر تحديدًا. كانت Sass من أوائل المعالجات المسبقة التي تدعم التداخل -إن لم تكن الأولى فعلًا- ولكن يبدو أن التداخل سيُوحَّد قريبًا، مما يعني أننا سنوفره في متصفحاتنا دون الحاجة إلى أدوات البناء.
</p>

<p>
	حتى ذلك الحين، ستحوّل Parcel بين CSS المتداخلة وCSS المدعومة بطريقة أصيلة بمساعدة PostCSS. بما أننا قررنا أن مشروعنا يجب أن يستخدم تداخل CSS بدلًا من Sass، فسيحتاج المشروع إلى تضمين إضافة PostCSS.
</p>

<p>
	لنستخدم <code>postcss-preset-env</code> التي تتيح "استخدام CSS المستقبلية اليوم use tomorrow's CSS today" من خلال اتباع الخطوات التالية:
</p>

<ol>
<li>
		أضف ملفًا بالاسم <code>‎.postcssrc</code> إلى جذر دليل المشروع.
	</li>
	<li>
		أضف المحتويات التالية إلى الملف الجديد التي ستمنحنا تلقائيًا وصولًا كاملًا إلى أحدث ميزات CSS:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2659_15" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"plugins"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"postcss-preset-env"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"stage"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وهذا كل ما نحتاجه. تذكر أن Parcel تثبّت الاعتماديات افتراضيًا.
</p>

<p>
	يمكن أن تكون هذه المرحلة من سلسلة أدواتنا صعبة، لأننا اخترنا أداة تحاول تقليل الإعداد والتعقيد، لكن لا يوجد شيء يجب تطبيقه أثناء مرحلة التطوير، إذ تُستورَد الوحدات استيرادًا صحيحًا، وتحوَّل CSS المتداخلة إلى CSS عادية تحويلًا صحيحًا، ولا تعوق عمليةُ البناء عمليةَ التطوير.
</p>

<h2>
	تشغيل التحويل
</h2>

<p>
	سنشغّل خادم Parcel في سطر الأوامر لبدء العمل في مشروعنا الذي سيراقب التغييرات في شيفرتنا ويثبّت الاعتماديات تلقائيًا، وبالتالي لن نضطر إلى الانتقال ذهابًا وإيابًا بين الشيفرة وسطر الأوامر.
</p>

<ol>
<li>
		انتقل إلى الطرفية وشغّل الأمر التالي لبدء تشغيل Parcel في الخلفية:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_945_35" style="">
<span class="pln">npx parcel src/index.html</span></pre>

<p>
	يجب أن ترى الخرج التالي بمجرد تثبيت الاعتماديات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2659_19" style="">
<span class="typ">Server</span><span class="pln"> running at http</span><span class="pun">:</span><span class="com">//localhost:1234</span><span class="pln">
</span><span class="pun">✨</span><span class="pln">  </span><span class="typ">Built</span><span class="pln"> in </span><span class="lit">129ms</span><span class="pun">.</span></pre>

<p>
	كما يثبّت Parcel الاعتماديات التي سنستخدمها في شيفرتنا، بما في ذلك react وreact-dom وreact-async-hook وdate-fns وformat-number، وبالتالي سيكون تشغيل Parcel الأول أطول من التشغيل المعتاد.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: إذا شغّلتَ Parcel وواجهت الخطأ <code>Error: ENOENT: no such file or directory</code>، فيجب إيقاف العملية باستخدام الاختصار <code>Ctrl + C</code> ثم حاول إعادة تشغيله.
		</p>
	</div>
</blockquote>

<p>
	يعمل الخادم الآن على <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%88-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1435/" rel="">عنوان URL</a> المطبوع، وهو localhost: 1234 في حالتنا.
</p>

<ol start="2">
<li>
		انتقل إلى <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> السابق في متصفحك وسترى التطبيق قيد التشغيل.
	</li>
</ol>
<p>
	يمتلك Parcel خدعة ذكية أخرى وهي أن أي تغييرات تطرأ على شيفرتك المصدرية ستؤدي إلى تحديث في المتصفح. يمكنك تجربة هذه الميزة من خلال ما يلي:
</p>

<ol>
<li>
		حمّل الملف <code>src/components/App.js</code> في محرر النصوص المفضل لديك.
	</li>
	<li>
		ابحث عن النص "near misses"، واستبدله بشيء مثل "flying fish".
	</li>
	<li>
		احفظ الملف، ثم ارجع مباشرة إلى التطبيق المُشغَّل في متصفحك. ستلاحظ تحديث المتصفح تلقائيًا، وتغيير السطر "‎<date> there will be <number> near misses" في أعلى الصفحة.</number></date>
</li>
</ol>
<p>
	كما يمكنك تجربة استخدام Eslint وPrettier. حاول إزالة المسافة البيضاء من أحد ملفاتك وحاول وضع ‎‎‎Prettier عليه لتنظيفه، أو أدخل خطأً صياغيًا في أحد ملفات جافاسكربت الخاصة بك وشاهد الأخطاء التي يعطيها Eslint عندما تحاول استخدام Parcel لبنائه مرة أخرى.
</p>

<h2>
	الخلاصة
</h2>

<p>
	أنشأنا في هذا المقال بيئة تطوير محلية بسيطة إلى حد ما لإنشاء تطبيق فيها، إذ يمكنك في هذه المرحلة أثناء تطوير برامج الويب صياغة شيفرة برنامجك الذي تنوي إنشائه. بما أننا نتكلم عن تعلم الأدوات المتعلقة بتطوير الويب، وليس شيفرة <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تطوير الويب</a> نفسها، فلن نعلمك كتابة شيفرة فعلية.
</p>

<p>
	لذلك كتبنا مثالًا لمشروع لتستخدم فيه أدواتك. نقترح عليك العمل في المقال التالي باستخدام شيفرة مثالنا، ثم يمكنك محاولة تغيير محتويات دليل <code>src</code> في مشروعك ونشره على Netlify، وسنناقش مرحلة النشر على Netlify في المقال التالي.
</p>

<p>
	هذا المقال جزء من سلسلة مقالات بعنوان <a href="https://academy.hsoub.com/search/?tags=%D8%AA%D8%B9%D9%84%D9%85%20%D8%AA%D8%B7%D9%88%D9%8A%D8%B1%20%D8%A7%D9%84%D9%88%D9%8A%D8%A8&amp;sortby=newest&amp;page=1" rel="">تعلم تطوير الويب</a> والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain" rel="external nofollow">Introducing a complete toolchain</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">أساسيات إدارة الحزم في تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">دليل استخدام سطر الأوامر في عملية تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">فهم أدوات تطوير الويب من طرف العميل</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1473</guid><pubDate>Tue, 22 Feb 2022 16:08:00 +0000</pubDate></item><item><title>&#x623;&#x633;&#x627;&#x633;&#x64A;&#x627;&#x62A; &#x625;&#x62F;&#x627;&#x631;&#x629; &#x627;&#x644;&#x62D;&#x632;&#x645; &#x641;&#x64A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x645;&#x646; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/62060acdc2266_---------.png.6d795d89b8556fb4737a7907bebb6baa.png" /></p>

<p>
	سنلقي في هذا المقال نظرة على مدير الحزم Package Manager بشيء من التفصيل لفهم كيفية استخدامه في مشاريعنا لتثبيت اعتماديات Dependencies أدوات المشروع وتحديثها وغير ذلك.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>.
	</li>
	<li>
		<strong>الهدف</strong>: فهم مدراء ومستودعات الحزم، وسبب حاجتنا لها، وأساسيات استخدامها.
	</li>
</ul>
<h2>
	اعتماديات المشروع
</h2>

<p>
	تعدّ الاعتمادية Dependency جزءًا من برنامج تابع لجهة خارجية كتبه شخص ما غيرك لحل مشكلة نيابة عنك. يمكن أن يحتوي مشروع الويب على أي عدد من الاعتماديات، تتراوح من لا شيء إلى اعتماديات متعددة، ويمكن أن تتضمن اعتمادياتك اعتماديات فرعية لم تثبّتها صراحة، إذ يمكن لاعتمادياتك أن يكون لها اعتمادياتها الخاصة.
</p>

<p>
	من الأمثلة البسيطة على الاعتماديات المفيدة التي يمكن أن يحتاجها مشروعك الشيفرة البرمجية لحساب التواريخ النسبية بوصفها نصًا قابلًا للقراءة. يمكنك بالتأكيد كتابة هذه الشيفرة بنفسك، ولكن يمكن أن يحل شخص آخر هذه المشكلة بدلًا عنك، فلا داعي لنضيع الوقت في إعادة كتابة هذه الشيفرة (ليس علينا إعادة اختراع العجلة). يُحتمَل اختبار اعتمادية موثوقة لطرف ثالث في مواقف متعددة مختلفة، مما يجعلها أقوى وأكثر توافقًا من حلك مع المتصفحات.
</p>

<p>
	يمكن أن تكون اعتمادية المشروع عبارة عن مكتبة أو إطار عمل جافاسكربت كامل مثل <a href="https://wiki.hsoub.com/React" rel="external">React</a> و<a href="https://academy.hsoub.com/files/22-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-vuejs/" rel="">Vue</a> أو أداة مساعدة صغيرة جدًا مثل مكتبة التاريخ القابلة للقراءة، أو يمكن أن تكون أداة سطر أوامر مثل Prettier أو Eslint التي تحدثنا عنها في المقال السابق.
</p>

<p>
	إن لم نستخدم أدوات البناء الحديثة، فيمكن تضمَّين مثل هذه الاعتماديات في مشروعك باستخدام عنصر <code>&lt;script&gt;</code> بسيط، ولكن يمكن ألا تعمل هذه الطريقة مباشرة، وبالتالي ستحتاج بعض الأدوات الحديثة لتجميع الشيفرة والاعتماديات مع بعضها بعضًا عند إصدارها على الويب. الحزمة Bundle هي المصطلح المُستخدَم للإشارة إلى ملف على <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم الويب</a> الخاص بك الذي يحتوي على شيفرة جافاسكربت الخاصة ببرنامجك، وتُضغَطُ الحزمة قدر الإمكان للمساعدة في تقليل الوقت الذي يستغرقه تنزيل البرنامج وعرضه في متصفح زوار موقعك.
</p>

<p>
	لا يُعَد تتبّع بعض الاعتماديات أمرًا صعبًا إذا وجدت أداة أفضل تريد استخدامها بدلًا من الأداة الحالية، أو أُصدِر إصدار جديد من الاعتمادية تريد التحديث إليه، ولكن يمكن أن يصبح هذا النوع من الأشياء صعبًا في المشاريع الكبيرة التي تحتوي على اعتماديات متعددة، لذلك يمكنك استخدام مدير حزم Package Manager مثل <a href="https://www.npmjs.com/" rel="external nofollow">npm</a>، لأنه سيضمن إضافة الشيفرة وإزالتها بطريقة نظيفة، بالإضافة إلى مجموعة من المزايا الأخرى.
</p>

<h2>
	مدير الحزم
</h2>

<p>
	مدير الحزم هو نظام يدير اعتماديات مشروعك، ويوفّر طريقة لتثبيت اعتماديات جديدة يُشار إليها باسم "الحزم Packages"، وإدارة مكان تخزين الحزم على نظام ملفاتك، وتقديم إمكانيات لنشرها.
</p>

<p>
	يمكنك تنزيل اعتماديات مشروعك وتخزينها يدويًا دون استخدام مدير حزم، ولكن مدير الحزم سيتعامل بسلاسة مع تثبيت الحزم وإلغاء تثبيتها. إن لم تستخدم مدير حزم، فسيتعين عليك التعامل مع ما يلي يدويًا:
</p>

<ul>
<li>
		العثور على كافة ملفات جافاسكربت الصحيحة الخاصة بالحزمة.
	</li>
	<li>
		التحقق منها للتأكد من عدم وجود أي ثغرات أمنية.
	</li>
	<li>
		تنزيلها ووضعها في الأماكن الصحيحة في مشروعك.
	</li>
	<li>
		كتابة الشيفرة لتضمين الحزمة أو الحزم في تطبيقك، إذ يمكن تطبيق ذلك باستخدام وحدات جافاسكربت.
	</li>
	<li>
		تطبيق الخطوات نفسها مع جميع اعتماديات الحزم الفرعية التي يُحتمَل وجود عشرات أو مئات منها.
	</li>
	<li>
		إزالة جميع الملفات مرة أخرى إذا أردت إزالة الحزم.
	</li>
</ul>
<p>
	كما يتعامل مدير الحزم مع الاعتماديات المكرَّرة، وهو أمر مهم وشائع في تطوير الواجهة الأمامية.
</p>

<p>
	لديك خياران لمكان تثبيت اعتمادياتك في حالة npm ومدير الحزم المستند إلى جافاسكربت وNode، ويمكن تثبيت الاعتماديات بطريقة عامة أي لكل المشاريع أو محليًا في مشروع معين فقط. بالرغم من وجود كثير من إيجابيات التثبيت العام، إلّا أن إيجابيات التثبيت المحلي لكل مشروع مهمة مثل قابلية نقل الشيفرة وقفل الإصدار.
</p>

<p>
	إذا اعتمد مشروعك مثلًا على <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">Webpack</a> مع إعداد معين، فيجب التأكد من عمل هذا الإعداد عند تثبيت المشروع على جهاز آخر أو العودة إليه لاحقًا. إذا ثُبِّت إصدار مختلف من <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">Webpack</a>، فيمكن ألّا يكون متوافقًا، لذلك يجب تثبيت الاعتماديات محليًا في المشروع.
</p>

<p>
	حاول تنزيل وتشغيل مشروع قائم، فإذا نجح وعملت جميع الاعتماديات مباشرة، فعندئذٍ لديك اعتماديات محلية يجب أن تشكرها على حقيقة أن الشيفرة قابلة للنقل.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: ليس npm مدير الحزم الوحيد المتاح، فمدير الحزم البديل الناجح والشائع هو <a href="https://yarnpkg.com/" rel="external nofollow">Yarn</a> الذي يثبت الاعتماديات باستخدام خوارزمية مختلفة يمكن أن توفر تجربة مستخدم أسرع. كما يوجد عدد من العملاء الناشئين الآخرين مثل <a href="https://pnpm.io/" rel="external nofollow">pnpm</a>.
		</p>
	</div>
</blockquote>

<h2>
	سجلات الحزم Package Registries
</h2>

<p>
	يحتاج مدير الحزم ليعمل إلى معرفة المكان الذي نثبّت الحزم منه، وهو ما يدعَى بسجل الحزمة Package Registry. السجل هو مكان مركزي تُنشَر الحزمة عليه وبالتالي يمكن تثبيتها منه. يُعدُّ npm اسم سجل الحزم الأكثر استخدامًا لحزم جافاسكربت.
</p>

<p>
	ليس npm الخيار الوحيد لإدارة سجل حزمتك، إذ تتيح لك منتجات مثل <a href="https://azure.microsoft.com/en-us/" rel="external nofollow">Microsoft Azure</a> إنشاء وكلاء لسجل npm إذ يمكنك تجاوز أو قفل حزم معينة، كما يقدم <a href="https://github.com/features/packages" rel="external nofollow">GitHub</a> خدمة تسجيل حزمة، ويُحتمَل ظهور مزيد من الخيارات مع مرور الوقت. المهم هو التأكد من اختيارك لأفضل سجل مناسب لك، ولكن ستستخدم العديد من المشاريع مدير الحزم npm، وسنستخدمها في أمثلتنا.
</p>

<h2>
	استخدام نظام الحزم المجتمعي
</h2>

<p>
	لنعرض مثالًا باستخدام مدير وسجل الحزم لتثبيت أداة مساعدة لسطر الأوامر.
</p>

<p>
	<a href="https://parceljs.org/" rel="external nofollow">Parcel</a> هي أداة ذكية يستخدمها المطورون لمراقبة محتويات الشيفرة لاستدعاء الاعتماديات، وتثبّتها تلقائيًا أيّ اعتماديات ترى أن شيفرتنا بحاجة إليها، ويمكنها إنشاء الشيفرة تلقائيًا.
</p>

<p>
	ثبّتنا الأداة Prettier تثبيتًا عامًا لكل المشاريع في المقال السابق، ولكن سنستخدم الآن npm لتثبيت Parcel محليًا باستخدام أفضل الممارسات، وسنثبّتها بوصفها جزءًا من تطبيق تجريبي.
</p>

<h3>
	إعداد التطبيق بوصفه حزمة npm
</h3>

<p>
	أنشئ أولًا مجلدًا جديدًا لتخزين تطبيقنا التجريبي ضمنه في مكان يمكنك إيجاده بسهولة مرة أخرى، وسنسميه <code>parcel-experiment</code>، ولكن بالطبع يمكنك تسميته ما تشاء:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1147_20" style="">
<span class="pln">mkdir parcel</span><span class="pun">-</span><span class="pln">experiment
cd parcel</span><span class="pun">-</span><span class="pln">experiment</span></pre>

<p>
	لنهيّئ بعد ذلك تطبيقنا <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">كحزمة npm</a> التي تُنشئ ملف الإعداد <code>package.json</code> الذي يسمح بحفظ تفاصيل الإعداد الخاصة بنا في حال أردنا إعادة إنشاء هذه البيئة لاحقًا، أو نشر الحزمة على سجل npm بالرغم من أن ذلك إلى حد ما خارج نطاق هذا المقال.
</p>

<p>
	اكتب الأمر التالي، وتأكد من أنك داخل الدليل <code>parcel-experiment</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1147_22" style="">
<span class="pln">npm init</span></pre>

<p>
	ستُطرَح بعض الأسئلة عليك الآن، ثم سينشئ npm ملف <code>package.json</code> افتراضي بناءً على إجاباتك:
</p>

<ul>
<li>
		<code>name</code>: الاسم الذي يعرّف التطبيق. اضغط على زر Enter لقبول الاسم <code>parcel-experiment</code> الافتراضي.
	</li>
	<li>
		<code>version</code>: رقم إصدار البداية الخاص بالتطبيق. اضغط على زر Enter لقبول الإصدار <code>1.0.0</code> الافتراضي.
	</li>
	<li>
		<code>description</code>: وصف سريع للغرض من التطبيق. اكتب شيئًا بسيطًا مثل "حزمة npm بسيطة للتعرف على كيفية استخدام npm"، ثم اضغط على زر Enter.
	</li>
	<li>
		<code>entry point</code>: يمثل ملف جافاسكربت ذي المستوى الأعلى في التطبيق. الملف <code>index.js</code> الافتراضي مناسب حاليًا، ثم اضغط على زر Enter.
	</li>
	<li>
		<code>test command</code> و<code>git repository</code> و<code>keywords</code>: اضغط على زر Enter لترك هذه الخيارات فارغة حاليًا.
	</li>
	<li>
		<code>author</code>: مؤلف المشروع. اكتب اسمك، ثم اضغط على زر Enter.
	</li>
	<li>
		<code>license</code>: ترخيص نشر الحزمة. اضغط على زر Enter لقبول الإعداد الافتراضي حاليًا.
	</li>
</ul>
<p>
	اضغط على زر Enter مرة أخرى لقبول هذه الإعدادات.
</p>

<p>
	انتقل إلى المجلد <code>parcel-experiment</code> وستجد أن لديك الملف <code>package.json</code>. افتحه ويجب أن يبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_17" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"parcel-experiment"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"A simple npm package to learn about using npm"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Chris Mills"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذ يمثل ملف الإعداد الذي يحدد حزمتك.
</p>

<h3>
	تثبيت Parcel
</h3>

<p>
	شغّل الأمر التالي لتثبيت Parcel محليًا:
</p>

<pre class="ipsCode">
npm install parcel-bundler
</pre>

<p>
	سنكون بعد ذلك جاهزين للتطوير الحديث من طرف العميل الذي يعني استخدام أدوات البناء لتسهيل العمل على المطور. ألقِ نظرة أخرى على ملف <code>package.json</code> أولًا، ستلاحظ أن npm أضاف حقلًا جديدًا وهو الاعتماديات dependencies:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_24" style="">
<span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"parcel-bundler"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^1.12.4"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا نقلتَ في المستقبل قاعدة شيفرتك إلى موقع آخر على جهاز آخر، فيمكنك إعادة إنشاء الإعداد نفسه عن طريق تشغيل الأمر <code>npm install</code>، وسينظر npm في الاعتماديات ثم يثبّتها لك. لكن أحد مساوئ npm هو أن Parcel متاح فقط ضمن التطبيق <code>parcel-experiment</code>، إذ لن تتمكن من تشغيله في مجلد مختلف.
</p>

<h3>
	إعداد التطبيق
</h3>

<p>
	يتوقّع Parcel أن يعمل ملف <code>index.html</code> وملف <code>index.js</code>، ولكن بخلاف ذلك ليست بنية مشروعك مُعلَنة. يمكن أن تكون الأدوات الأخرى مختلفة، ولكن تسهّل الأداة Parcel على الأقل من تجربتنا الأولية.
</p>

<p>
	يجب إضافة ملف <code>index.html</code> إلى دليل عملنا. أنشئ الملف <code>index.html</code> في دليل الاختبار الخاص بك، وضع فيه المحتويات التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1147_26" style="">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en-US"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">My test page</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./index.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	يجب بعد ذلك إضافة ملف <code>index.js</code> في دليل الملف <code>index.html</code> نفسه. يمكن أن يكون الملف <code>index.js</code> فارغًا حاليًا، ولكن يجب أن يكون موجودًا لذلك أنشئه الآن.
</p>

<h3>
	تطبيق Parcel
</h3>

<p>
	سنشغّل الآن أداة Parcel المثبَّتة حديثًا. شغّل الأمر التالي في طرفيتك:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1147_28" style="">
<span class="pln">parcel index.html</span></pre>

<p>
	يجب أن ترى شيئًا كالتالي مطبوعًا على طرفيتك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_31" style="">
<span class="typ">Server</span><span class="pln"> running at http</span><span class="pun">:</span><span class="com">//localhost:1234</span><span class="pln">
</span><span class="pun">✨</span><span class="pln">  </span><span class="typ">Built</span><span class="pln"> in </span><span class="lit">193ms</span><span class="pun">.</span></pre>

<p>
	إذا واجهتَ مشكلةً في الطرفية التي تعرض خطأ من النوع "الأمر غير موجود command not found"، فحاول تشغيل الأمر السابق باستخدام الأداة <code>npx</code> مثل الأمر <code>npx parcel index.html</code>.
</p>

<p>
	يمكننا الآن الاستفادة من نظام حزمة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7%D9%87%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA%D8%9F-%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA-r525/" rel="">جافاسكربت</a> المجتمعي الكامل. يوجد حاليًا خادم ويب محلي يعمل على العنوان <code><a href="http://localhost:1234" ipsnoembed="true" rel="external nofollow">http://localhost:1234</a></code>، فإذا انتقلت إليه، فلن ترى أي شيء حاليًا، ولكن ستعيد Parcel بنائه وستعمل على تحديث الخادم تلقائيًا عند إجراء تعديلات على تطبيقك لتتمكن من رؤية تأثير التحديث مباشرة.
</p>

<p>
	لنفترض أننا نريد عرض التواريخ النسبية التي يمكن قراءتها، مثل "منذ ساعتين 2‎ hours ago" و"قبل 4 أيام 4‎ days ago" وما إلى ذلك. يُعَد التابع <code>formatDistanceToNow()‎</code> الخاص بالحزمة <a href="https://date-fns.org/" rel="external nofollow"><code>date-fns</code></a> مفيدًا لذلك، وهناك حزم أخرى تطبّق الشي نفسه.
</p>

<p>
	أضف الشيفرة التالية في ملف <code>index.js</code> واحفظها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_33" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> formatDistanceToNow </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'date-fns'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> date </span><span class="pun">=</span><span class="pln"> </span><span class="str">'1996-09-13 10:00:00'</span><span class="pun">;</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> formatDistanceToNow</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="pln">date</span><span class="pun">))</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">' ago'</span><span class="pun">;</span></pre>

<p>
	ارجع إلى <code><a href="http://localhost:1234" ipsnoembed="true" rel="external nofollow">http://localhost:1234</a></code> وسترى كم مضى منذ بلوغ المؤلف 18 عامًا.
</p>

<p>
	ما يميز الشيفرة السابقة هو أنها تستخدم الدالة <code>formatDistanceToNow()‎</code> من الحزمة <code>date-fns</code> التي لم نثبّتها. اكتشفت الأداة Parcel أنك بحاجة الوحدة، وبحثت عنها في سجل الحزمة <code>npmjs.com</code>، وثبّتتها محليًا تلقائيًا. يمكنك إثبات ذلك من خلال البحث في ملف <code>package.json</code> مرة أخرى، وسترى تحديث حقل الاعتماديات <code>dependencies</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_35" style="">
<span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"date-fns"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^2.12.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"parcel-bundler"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^1.12.4"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما أضافت Parcel الملفات المطلوبة لشخص آخر لأخذ هذا المشروع وتثبيت أي اعتماديات استخدمناها. إذا ألقيت نظرة على الدليل الذي شغّلتَ أمر <code>parcel</code> فيه، فستجد عددًا من الملفات الجديدة مثل:
</p>

<ul>
<li>
		<code>node_modules</code>: ملفات اعتماديات Parcel وdate-fns.
	</li>
	<li>
		<code>dist</code>: دليل التوزيع distribution directory، وهو الملفات المُجمَّعة والمُصغَّرة تلقائيًا التي أنشتأها Parcel، والملفات المُخدَّمة على العنوان <code>localhost:1234</code>، وهي الملفات التي ستحمّلها إلى خادم الويب الخاص بك عند إطلاق الموقع عبر الإنترنت ليراه المستخدمين.
	</li>
</ul>
<p>
	طالما أننا نعرف اسم الحزمة، فيمكننا استخدامها في شيفرتنا وستوقِف Parcel تشغيل الحزمة -أو نسخة منها- وتجلبها وتثبّتها في دليلنا المحلي ضمن <code>node_modules</code>.
</p>

<h3>
	بناء الشيفرة للإنتاج
</h3>

<p>
	لا تعدّ الشيفرة السابقة جاهزة للإنتاج، إذ يكون لمعظم أنظمة أدوات البناء وضع تطوير Development Mode ووضع إنتاج Production Mode. الاختلاف المهم هو أن الكثير من الميزات المفيدة التي ستستخدمها في التطوير ليست ضرورية في الموقع النهائي، لذلك ستُستبعَد للإنتاج مثل استبدال الوحدة وإعادة التحميل المباشر والشيفرة المصدرية غير المضغوطة والمُعلَّقة. هذه بعض ميزات تطوير الويب الشائعة المفيدة جدًا في مرحلة التطوير، ولكنها ليست مفيدة في مرحلة الإنتاج.
</p>

<p>
	أوقِف أمر Parcel السابق باستخدام الاختصار <code>Ctrl + C</code>. يمكننا الآن تجهيز أساسات موقع لنشره وهميًا، إذ توفّر Parcel أمرًا إضافيًا لإنشاء ملفات مناسبة للنشر، وإنشاء حزم Bundles مع خيار البناء.
</p>

<p>
	شغّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1147_37" style="">
<span class="pln">parcel build index.html</span></pre>

<p>
	ويجب أن ترى الناتج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_39" style="">
<span class="pun">✨</span><span class="pln">  </span><span class="typ">Built</span><span class="pln"> in </span><span class="lit">9.35s</span><span class="pun">.</span><span class="pln">

dist</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">project</span><span class="pun">.</span><span class="pln">fb76efcf</span><span class="pun">.</span><span class="pln">js</span><span class="pun">.</span><span class="pln">map    </span><span class="lit">648.58</span><span class="pln"> KB     </span><span class="lit">64ms</span><span class="pln">
dist</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">project</span><span class="pun">.</span><span class="pln">fb76efcf</span><span class="pun">.</span><span class="pln">js        </span><span class="lit">195.74</span><span class="pln"> KB    </span><span class="lit">8.43s</span><span class="pln">
dist</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">html                        </span><span class="lit">288</span><span class="pln"> B    </span><span class="lit">806ms</span></pre>

<p>
	وجهة ملفات الإنتاج هي الدليل <code>dist</code>.
</p>

<h3>
	تقليل حجم ملف التطبيق
</h3>

<p>
	حجم حزمة جافاسكربت <code>my-project.fb76efcf.js</code> هو 195K وهو حجم كبير جدًا، بالرغم من أن كل ما تفعله هو طباعة سطر نصي. هناك بالتأكيد بعض الحسابات، ولكننا لسنا بحاجة إلى 195K من شيفرة جافاسكربت لذلك.
</p>

<p>
	يجدر بك التساؤل عما إذا كانت أدوات التطوير تطبّق الشيء الصحيح عن استخدامها، إذ يبلغ حجم الحزمة 200K تقريبًا في مثالنا لأنها تضمّنت مكتبة <code>date-fns</code> الكاملة، وليس الدالة التي نستخدمها فقط.
</p>

<p>
	إذا تجنّبنا استخدام أي أدوات تطوير واستخدمنا العنصر <code>&lt;script src=""‎&gt;</code> مع إصدار مستضاف من <code>date-fns</code>، فسيحدث الشيء نفسه تقريبًا، وستُنزَّل المكتبة عند تحميل صفحة مثالنا في المتصفح.
</p>

<p>
	يمكننا أن نطلب من البرنامج أثناء وجود الأدوات على أجهزتنا فحص استخدامنا للشيفرة وتضمين الدوال التي نستخدمها في مرحلة الإنتاج فعليً فقط، وهي عملية تُعرف باسم هز الشجرة Tree Shaking. يُعَد ذلك منطقيًا لأننا نريد تقليل حجم الملف وبالتالي تحميل تطبيقنا في أسرع وقت ممكن، وتتيح الأدوات المختلفة تطبيق تقنية هز الشجرة بطرق مختلفة.
</p>

<p>
	هناك ثلاثة عروض رئيسية للأدوات التي تنشئ حزمًا من الشيفرة المصدرية هي: Webpack و<a href="https://rollupjs.org/guide/en/" rel="external nofollow">Rollup</a> وParcel. سيكون هناك مزيد من الخيارات المتاحة، لكن تُعَد الأنواع التالية شائعة الاستخدام:
</p>

<ul>
<li>
		تقدم أداة RollUp تقنية هز الشجرة وتقسيم الشيفرة.
	</li>
	<li>
		تتطلب أداة Webpack إعدادًا، بالرغم من أن البعض يقلل من تعقيد إعداد Webpack الخاص بالمطورين.
	</li>
	<li>
		هناك في حالة Parcel -قبل الإصدار Parcel 2- راية خاصة مطلوبة هي <code>‎--experimental-scope-hoisting</code> التي ستهز الشجرة أثناء البناء.
	</li>
</ul>
<p>
	لنستخدم Parcel حاليًا، بما أننا ثبّتناها سابقًا. لنشغّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1147_41" style="">
<span class="pln">parcel build index.html --experimental-scope-hoisting</span></pre>

<p>
	سترى أن هذا الأمر يحدث فرقًا كبيرًا:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_1147_43" style="">
<span class="pun">✨</span><span class="pln">  </span><span class="typ">Built</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> </span><span class="lit">7.87s</span><span class="pun">.</span><span class="pln">

dist</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">project</span><span class="pun">.</span><span class="lit">86f8a5fc</span><span class="pun">.</span><span class="pln">js    </span><span class="lit">10.34</span><span class="pln"> KB    </span><span class="lit">7.17s</span><span class="pln">
dist</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">html                   </span><span class="lit">288</span><span class="pln"> B    </span><span class="lit">753ms</span></pre>

<p>
	أصبح حجم الحزمة حوالي 10K، وهذا أفضل بكثير.
</p>

<p>
	إذا أردنا إصدار هذا المشروع إلى خادم، فسنعدّل الملفات الموجودة في مجلد <code>dist</code>، إذ تتعامل Parcel تلقائيًا مع جميع تغييرات اسم الملف. نوصي بإلقاء نظرة على الشيفرة المصدرية في <code>dist/index.html</code> لتتمكن من رؤية التغييرات التي أجرتها Parcel تلقائيًا.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: لم يكن الإصدار Parcel 2 صادرًا في وقت كتابة النسخة الأجنبية من هذا المقال، ولكن ستبقى جميع هذه الأوامر تعمل عند إصداره لأن مؤلفي Parcel لديهم الحس السليم لتسمية الأداة بطريقة مختلفة. يمكنك تثبيت Parcel 1.x من خلال تثبيت <code>parcel-bundler</code>، لكن الإصدار parcel 2.x يسمّى <code>parcel</code>.
		</p>
	</div>
</blockquote>

<p>
	توجد الكثير من الأدوات المتاحة، وينمو نظام حزمة جافاسكربت المجتمعي بمعدل غير مسبوق، وهو أمر له إيجابيات وسلبيات. كما تُجرَى تحسينات طوال الوقت، لكن يجب معرفة قدرة الأداة قبل اختيارها.
</p>

<h2>
	دليل عملاء مدير الحزم
</h2>

<p>
	ثبّتنا في مثالنا حزمة Parcel باستخدام npm، ولكن هناك بعض البدائل كما ذكرنا سابقًا، لذلك لنلقِ نظرة عليها. سنذكر بعض مدراء الحزم وهي:
</p>

<ul>
<li>
		npm في <a href="https://www.npmjs.com/" rel="external nofollow">npmjs.org</a>.
	</li>
	<li>
		pnpm في <a href="https://pnpm.js.org" rel="external nofollow">pnpm.js.org</a>.
	</li>
	<li>
		yarn في <a href="https://yarnpkg.com/" rel="external nofollow">yarnpkg.com</a>.
	</li>
</ul>
<p>
	يتشابه npm وpnpm من وجهة نظر سطر الأوامر، ويهدف pnpm إلى الحصول على تكافؤ كامل مع خيارات الوسطاء التي يقدمها npm، ولكنهما يختلفان إذ يستخدم pnpm طريقة مختلفة لتنزيل الحزم وتخزينها على حاسوبك بهدف تقليل المساحة الإجمالية المطلوبة على القرص الصلب. سنستخدم npm في الأمثلة التالية، ويمكنك استخدام pnpm وسيعمل الأمر بطريقة صحيحة.
</p>

<p>
	يُعتقَد أن yarn أسرع من npm من ناحية عملية التثبيت في أغلب الأحيان، وهذا مهم للمطورين بسبب وجود قدر كبير من الوقت الضائع في انتظار تثبيت الاعتماديات والنسخ إلى الحاسوب.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>: مدير الحزم npm غير مطلوب لتثبيت الحزم من سجل npm، بالرغم من أنهما يشتركان في الاسم نفسه. يمكن أن يستخدم كل من pnpm وyarn تنسيق <code>package.json</code> نفسه الذي يستخدمه npm، ويمكنهما تثبيت أي حزمة من سجل npm وسجلات الحزم الأخرى.
	</p>
</blockquote>

<p>
	لنراجع الآن الإجراءات الشائعة التي يجب تنفيذها مع مدير الحزم.
</p>

<h3>
	بدء مشروع جديد
</h3>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1147_45" style="">
<span class="pln">npm init
yarn init</span></pre>

<p>
	ستوجهك الأوامر السابقة وترشدك عبر سلسلة من الأسئلة لوصف مشروعك مثل الاسم والترخيص والوصف وما إلى ذلك، ثم إنشاء ملف <code>package.json</code> الذي يحتوي على معلومات وصفية حول مشروعك واعتمادياته.
</p>

<h3>
	تثبيت الاعتماديات
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_47" style="">
<span class="pln">npm install date</span><span class="pun">-</span><span class="pln">fns
yarn add date</span><span class="pun">-</span><span class="pln">fns</span></pre>

<p>
	رأينا أمر التثبيت <code>install</code> قيد التنفيذ أعلاه الذي سيؤدي إلى إضافة حزمة <code>date-fns</code> مباشرة إلى دليل العمل ضمن دليل فرعي يسمى <code>node_modules</code> مع اعتماديات <code>date-fns</code>.
</p>

<p>
	سيثبّت الأمر السابق افتراضيًا أحدث إصدار من <code>date-fns</code>، ولكن يمكنك التحكم في ذلك، إذ يمكنك استخدام الأمر <code>date-fns@1</code> الذي يمنحك أحدث إصدار من 1‎.x وهو 1.30.1، أو يمكنك تجربة الأمر <code>date-fns@^2.3.0</code> الذي يعطي أحدث إصدار يتضمن الإصدار 2.3.0 أو بعده (2.8.1 في وقت كتابة النسخة الأجنبية من هذا المقال).
</p>

<h3>
	تحديث الاعتماديات
</h3>

<pre class="ipsCode">
npm update
yarn upgrade
</pre>

<p>
	سينظر الأمر السابق في الاعتماديات المثبَّتة حاليًا ويحدّثها عند وجود تحديث متاح ضمن المجال المحدّد في الحزمة. حُدِّد المجال في إصدار الاعتمادية في ملف <code>package.json</code> الخاص بك مثل <code>date-fns^2.0.1</code>، إذ يعني المحرف <code>^</code> جميع الإصدارات الثانوية وإصدارات تصحيح الأخطاء التي تتضمن الإصدار 2.0.1 والإصدارات التي بعدها باستثناء الإصدار 3.0.0.
</p>

<p>
	يُحدَّد الإصدار باستخدام نظام يسمى <a href="https://semver.org/" rel="external nofollow">semver</a> الذي يبدو معقدًا بعض الشيء في توثيقه، ولكن يمكن تبسيطه من خلال النظر فقط في المعلومات الموجزة، ويُمثّل الإصدار بالشكل <code>MAJOR.MINOR.PATCH</code> مثل 2.0.1، إذ يمثل الرقم 2 الإصدار الرئيسي Major Version ويمثل الرقم 1 إصدار تصحيح الأخطاء Patch Version. يمكنك تجربة قيم Semver باستخدام <a href="https://semver.npmjs.com/" rel="external nofollow">حاسبة semver</a>.
</p>

<p>
	يجب أن نتذكر أن الأمر <code>npm update</code> لن يرقّي الاعتماديات خارج المجال المحدَّد في ملف <code>package.json</code>، وإنما يثبت الإصدار الموجود على وجه التحديد.
</p>

<h3>
	تدقيق الثغرات
</h3>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1147_49" style="">
<span class="pln">npm audit
yarn audit</span></pre>

<p>
	سيؤدي الأمر السابق إلى التحقق من كامل شجرة اعتمادية مشروعك وتشغيل الإصدارات المحدَّدة التي تستخدمها لتلافي ثغرات قاعدة البيانات وإعلامك إذا كانت هناك حزم ذات ثغرات محتمَلة في مشروعك.
</p>

<p>
	يُعَد <a href="https://snyk.io/" rel="external nofollow">مشروع Snyk</a> نقطة انطلاق جيدة للتعرف على الثغرات، والذي يغطي كلًا من حزم جافاسكربت ولغات البرمجة الأخرى.
</p>

<h3>
	التحقق من الاعتمادية
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_51" style="">
<span class="pln">npm ls date</span><span class="pun">-</span><span class="pln">fns
yarn why date</span><span class="pun">-</span><span class="pln">fns</span></pre>

<p>
	يظهِر الأمر السابق إصدار الاعتمادية المُثبَّتة وكيفية تضمينها في مشروعك، ويُحتمَل سحب حزمة أخرى ذات مستوى أعلى في <code>date-fns</code>. كما يمكن أن يكون لديك إصدارات متعددة من حزمة معينة في مشروعك، ولوحظ هذا الأمر عدة مرات مع حزمة <a href="https://lodash.com/" rel="external nofollow">lodash</a> أنها مفيدة جدًا لضمان عمل كافة الشيفرة البرمجية.
</p>

<p>
	يمكن أن ترغب في التحقق بالضبط من الإصدار المثبت، بالرغم من أن مدير الحزم سيبذل قصارى جهده لإزالة تكرار الحزم.
</p>

<h2>
	إنشاء أوامرك الخاصة
</h2>

<p>
	يدعم مدير الحزم إنشاء أوامرك الخاصة وتنفيذها من سطر الأوامر، إذ يمكننا استخدام الأمر التالي مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_55" style="">
<span class="pln">npm run dev
</span><span class="pun">#</span><span class="pln"> </span><span class="pun">‫</span><span class="pln"> </span><span class="pun">أو</span><span class="pln"> yarn run dev</span></pre>

<p>
	سيؤدي الأمر السابق إلى تشغيل سكربت مخصص لبدء مشروعنا في وضع التطوير، ويمكننا تضمينه بانتظام في جميع المشاريع إذ يميل إعداد التطوير المحلي إلى العمل بطريقة مختلفة عن كيفية تشغيله في عملية الإنتاج.
</p>

<p>
	إذا حاولت تشغيل هذا السكربت في مشروع اختبار Parcel الخاص بك، فيُحتمَل أن يدّعي أن السكربت dev مفقود، لأن npm وyarn يبحثان عن خاصية تسمى dev ضمن الخاصية <code>scripts</code> لملف <code>package.json</code> الخاص بك.
</p>

<p>
	يمكن أن تشغّل Parcel خادم تطوير باستخدام الأمر <code>parcel serve filename.html</code>، لأننا سنرغب في استخدامه كثيرًا أثناء عملية التطوير، لذلك لننشئ أمر مخصَّصًا مختصرًا هو "dev" في ملف <code>package.json</code>.
</p>

<p>
	يجب أن يكون لديك ملف <code>package.json</code> ضمن الدليل <code>parcel-experiment</code>. افتحه ويجب أن تبدو الخاصية <code>scripts</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_57" style="">
<span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pun">,</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	حدّثها لتبدو كما يلي، ثم احفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_59" style="">
<span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"dev"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"parcel serve index.html"</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	أضفنا الأمر <code>dev</code> المخصص بوصفه سكربت npm.
</p>

<p>
	حاول الآن تشغيل الأمر التالي في طرفيتك، وتأكد من أنك ضمن الدليل <code>parcel-experiment</code>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1147_61" style="">
<span class="pln">npm run dev</span></pre>

<p>
	يجب أن يبدأ الأمر السابق Parcel ويقدم ملف <code>index.html</code> الخاص بك على خادم التطوير المحلي كما رأينا سابقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1147_63" style="">
<span class="typ">Server</span><span class="pln"> running at http</span><span class="pun">:</span><span class="com">//localhost:1234</span><span class="pln">
</span><span class="pun">✨</span><span class="pln">  </span><span class="typ">Built</span><span class="pln"> in </span><span class="lit">5.48s</span><span class="pun">.</span></pre>

<p>
	كما تُعَد أوامر npm وyarn ذكية من لأنها ستبحث عن أدوات سطر الأوامر المثبَّتة محليًا للمشروع قبل محاولة العثور عليها من خلال الطرق التقليدية، ويخزّن حاسوبك عادةً البرامج ويسمح بإيجادها. يمكنك <a href="https://docs.npmjs.com/cli/v8/commands/npm-run-script" rel="external nofollow">معرفة المزيد حول تعقيدات الأمر <code>run</code></a>، بالرغم من أن سكربتاتك ستعمل بصورة جيدة في معظم الحالات.
</p>

<p>
	يمكنك إضافة جميع الأنواع إلى الخاصية <code>scripts</code> التي تساعدك على أداء عملك.
</p>

<p>
	إلى هنا تنتهي جولتنا في التعرف على مدير الحزم، وخطوتنا التالية هي بناء نموذج لسلسلة أدوات، ووضع كل ما تعلمناه حتى الآن موضع التنفيذ.
</p>

<p>
	هذا المقال جزء من سلسلة مقالات بعنوان <a href="https://academy.hsoub.com/search/?tags=%D8%AA%D8%B9%D9%84%D9%85%20%D8%AA%D8%B7%D9%88%D9%8A%D8%B1%20%D8%A7%D9%84%D9%88%D9%8A%D8%A8&amp;sortby=newest&amp;page=1" rel="">تعلم تطوير الويب</a> والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management" rel="external nofollow">Package management basics</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/workflow/%D8%A8%D9%86%D8%A7%D8%A1-%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1473/" rel="">بناء نموذج كامل لسلسلة أدوات تطوير الويب من طرف العميل</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/" rel="">أدوات مطوري الويب المدمجة في المتصفحات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">فهم أدوات تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-helm-%D9%85%D8%AF%D9%8A%D8%B1-%D8%AD%D8%B2%D9%85-kubernetes-r470/" rel="">مدخل إلى Helm: مدير حزم Kubernetes</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c-sharp/dotnet/%D9%86%D8%B8%D8%A7%D9%85-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-nuget-%D9%81%D9%8A-dot-net-r982/" rel="">نظام إدارة الحزم NuGet في dot NET</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/freebsd/%D9%83%D9%8A%D9%81-%D8%AA%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-freebsd-101-%D8%A8%D9%88%D8%A7%D8%B3%D8%B7%D8%A9-pkg-r101/" rel="">كيف تدير الحزم على نظام FreeBSD 10.1 بواسطة Pkg</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1472</guid><pubDate>Thu, 17 Feb 2022 16:08:01 +0000</pubDate></item><item><title>&#x623;&#x62F;&#x648;&#x627;&#x62A; &#x645;&#x637;&#x648;&#x631;&#x64A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x627;&#x644;&#x645;&#x62F;&#x645;&#x62C;&#x629; &#x641;&#x64A; &#x627;&#x644;&#x645;&#x62A;&#x635;&#x641;&#x62D;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61e047e355b99_-------.png.5de332094f0135adfab1a12b6b299061.png" /></p>

<p>
	تحتوي كل <a href="https://academy.hsoub.com/apps/general/%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85-%D8%A7%D9%84%D8%B1%D9%82%D9%85%D9%8A-r382/" rel="">متصفحات الويب</a> الحديثة مجموعة أدوات فعّالة لمطوري الويب تنفذ مجالًا واسعًا من الوظائف من فحص ملفات HTML وCSS وجافاسكريبت إلى عرض ملف دعم طلبته الصفحة والمدة التي يستغرقها حتى يُحمّل. يشرح هذا المقال كيفية استعمال الوظائف الأساسية لأدوات <a href="https://academy.hsoub.com/freelance/client-care/%D9%83%D9%85%D8%B5%D9%85%D9%85%D9%85%D8%B7%D9%88%D8%B1-%D9%88%D9%8A%D8%A8-%D9%85%D8%B3%D8%AA%D9%82%D9%84%D8%8C-%D9%85%D8%A7-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%B6%D8%A7%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%8A-%D9%8A%D9%85%D9%83%D9%86-%D8%AA%D9%82%D8%AF%D9%8A%D9%85%D9%87%D8%A7-%D9%84%D8%B9%D9%85%D9%8A%D9%84%D9%83-r119/" rel="">مطوري ويب</a> DevTools الخاصة بمتصفحك.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: قبل أن تشرع بتنفيذ الأمثلة التي سنطرحها في المقال، افتح <a href="https://mdn.github.io/beginner-html-site-scripted/" rel="external nofollow">موقع أمثلة المبتدئين</a>، فلا بدّ أن يكون مفتوحًا عند تنفيذ الخطوات التي نشرحها تاليًا.
		</p>
	</div>
</blockquote>

<h2>
	فتح أدوات مطوري ويب في متصفح
</h2>

<p>
	ستجد أدوات مطوري الويب ضمن نافذة فرعية تبدو نوعًا ما كالصورة التالية وذلك بحسب المتصفح الذي تستخدمه.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-55.png.bccc35a46a9ab413636e011784306f27.png" data-fileid="92744" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="92744" data-unique="qxgzaq04c" src="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-55.thumb.png.9cfa4cda629dd4af0c447df1126ffac0.png" alt="2022-02-24_18-55.png"></a>
</p>

<p>
	لكن كيف ستظهر هذه الأدوات؟
</p>

<ul>
<li>
		<strong>باستخدام لوحة المفاتيح</strong>: اضغط على الأزرار <code>CTRL</code> + <code>Shift</code> + <code>I</code>، ماعدا المتصفحات التالية:

		<ul>
<li>
				مايكروسوفت إنترنت إكسبلورر وإيدج: اضغط على <code>F12</code>.
			</li>
			<li>
				ماك أو إس: اضغط على <code>⌘</code> + <code>⌥</code> +<code>I</code>.
			</li>
		</ul>
</li>
	<li>
		<strong>باستخدام شريط القائمة Menu Bar</strong>:
	</li>
</ul>
<p>
	اضغط زر القائمة <img alt="menu.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89093" data-unique="vfsum8cx0" src="https://academy.hsoub.com/uploads/monthly_2022_01/menu.png.59e4785dd02bbe468b5e8a6042bbb0b2.png"> ثم:
</p>

<ul>
<li>
		فايرفوكس: اضغط على "مطوري ويب Web Developer" ثم اختر "تبديل الأدوات Toggle Tools" أو "أدوات Tools" ثم اختر "مطوري الويب Web Developer" ثم اختر "تبديل الأدوات Toggle Tools".
	</li>
	<li>
		كروم: اضغط على "المزيد من الأدوات More Tools" ثم اختر "أدوات المطورين Developer Tools".
	</li>
	<li>
		سفاري: اضغط على "تطوير Develop" ثم اختر "إظهار فاحص الويب Show Web Inspector"، وإن لم تجد قائمة "تطوير Develop" انتقل إلى المتصفح سفاري Safari ثم اختر "تفضيلات Preferences" ثم اختر "متقدم Advanced" ثم فعّل الخيار "أظهر قائمة التطوير في شريط القائمة Show Develop menu in menu bar".
	</li>
	<li>
		أوبرا: اضغط على "مطورون Developer" ثم اختر "أدوات المطورين Developer tools".
	</li>
	<li>
		<strong>باستخدام قوائم السياق Context Menu</strong>: اضغط باستمرار بالزر الأيمن للفأرة على أي عنصر في صفحة الويب (انقر <code>CTRL</code> في ماك) واختر "فحص عنصر Inspect Element" من قائمة السياق التي ستظهر (فائدة إضافية: تظلل هذه الطريقة مباشرة شيفرة العنصر الذي نقرت عليه).
	</li>
</ul>
<p style="text-align: center;">
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="92743" data-unique="vdv81kthe" src="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-56.png.07d2dbbc93fb589d116ff121dcd4d422.png" alt="2022-02-24_18-56.png"></p>

<h2>
	فحص شجرة العناصر DOM ومحرر تنسيقات CSS
</h2>

<p>
	تُظهر أدوات مطوري ويب عند فتحها نافذة الفاحص Inspector افتراضيًا والذي يبدو تقريبًا كما تعرضه لقطة الشاشة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-57.png.1157e86c36a7095f96924a2fc78c3364.png" data-fileid="92742" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="92742" data-unique="xhrrmdgr1" src="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-57.thumb.png.dcb7a511785b5b1cef81f6e6a8ff4aea.png" alt="2022-02-24_18-57.png"></a>
</p>

<p>
	تعرض هذه الأداة نتيجة شيفرة HTML في زمن التنفيذ، كما تعرض تنسيقات CSS المطبقة على كل عنصر من عناصر الصفحة. تتيح لك الأداة أن تعدّل عناصر HTML وCSS أيضًا، وأن تشاهد أثر التغييرات التي أحدثتها مباشرة في المتصفح.
</p>

<p>
	إن لم تظهر نافذة الفاحص مباشرة:
</p>

<ul>
<li>
		اضغط أو المس النافذة "فاحص Inspector" في فايرفوكس.
	</li>
	<li>
		اضغط أو المس النافذة "مستكشف DOM Explore" أو اضغط <code>CTRL</code> + <code>1</code> في إنترنت إكسبلورر.
	</li>
	<li>
		اضغط أو المس النافذة "عناصر Elements" في مايكروسوفت إيدج وأوبرا وكروم.
	</li>
	<li>
		لا تظهر عناصر التحكم بصورة واضحة في سفاري، لكن من المفترض أن ترى نافذة HTML إن لم تختر شيئًا آخر. اضغط على زر "تنسيق Style" لعرض تنسيقات CSS.
	</li>
</ul>
<h3>
	استكشاف عمل فاحص DOM
</h3>

<p>
	انقر في البداية بالزر اليميني للفأرة (أو اضغط <code>CTRL</code>) على عنصر HTML ضمن فاحص شجرة DOM وتحقق من محتويات قائمة السياق التي ستظهر. تختلف عناصر القائمة من متصفح لآخر، لكن المهمة منها موجودة غالبًا في جميع المتصفحات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-59.png.0630efd08a52379d3dbe9ca3d42e8035.png" data-fileid="92741" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="92741" data-unique="ubqhefxtd" src="https://academy.hsoub.com/uploads/monthly_2022_02/2022-02-24_18-59.thumb.png.0e357fafc3ff5103191b9200b85318f4.png" alt="2022-02-24_18-59.png"></a>
</p>

<ul>
<li>
		<strong>حذف عقدة Delete Node</strong>: (تجدها أحيانًا "حذف عنصر Delete Element") تحذف العنصر المحدد.
	</li>
	<li>
		<strong>تحرير كملف HTML</strong>: (تجده أحيانًا "إضافة سمة Add Attribute/‎ تحرير نص Edit Text") وذلك لتعديل ملف HTML ومشاهدة نتيجة التعديلات مباشرة، وهو مفيد جدًا للتنقيح والاختبار.
	</li>
	<li>
		<strong>تمرير Hover/‎ تفعيل Active/‎ تركيز Focus</strong>: تجبر العنصر على تغيير الحالة التي يُعرض بها، وبالتالي ستتمكن من مشاهدة تنسيقه في كل حالة.
	</li>
	<li>
		<strong>نسخ Copy/‎ نسخ بصيغة HTML</strong>: عنصر HTML الذي تختاره.
	</li>
	<li>
		يمكن أن تجد في بعض المتصفحات خيارات مثل "نسخ CSS" و"نسخ Xpath" لكي تنسخ مُحدِّد CSS أو تعبير Xpath المرتبط بعنصر HTML الحالي.
	</li>
</ul>
<p>
	حاول أن تعدّل في شجرة DOM، انقر نقرًا مزدوجًا على عنصر ثم انقر بالزر اليميني عليه واختر "تحرير كملف HTML" من قائمة السياق التي تظهر. يمكنك إحداث التغييرات التي تريد لكن ليس بالإمكان حفظ هذه التغييرات.
</p>

<h3>
	استكشاف محرر تنسيقات CSS
</h3>

<p>
	يعرض محرر CSS افتراضيًا القواعد المطبقة على العنصر المختار:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89087" href="https://academy.hsoub.com/uploads/monthly_2022_01/05_css_editor.png.60c299102fa3b7e6aafba3ae84b4dd3d.png" rel=""><img alt="05_css_editor.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89087" data-unique="vopr85j65" src="https://academy.hsoub.com/uploads/monthly_2022_01/05_css_editor.thumb.png.7b2595d0a216d1eafe80591212e2844d.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	تُساعدك هذه الميزات في النقاط التالية:
</p>

<ul>
<li>
		عرض <a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-css-r70/" rel="">قواعد CSS</a> المطبقة على العنصر الحالي مرتبة من الأكثر تخصيصًا لهذا العنصر إلى الأقل.
	</li>
	<li>
		عرض ما يحدث عند حذف تصريح ما، ولكن لا بدّ أن تنقر قبل ذلك على صندوق التحقق الموجود إلى جوار التصريح.
	</li>
	<li>
		عرض المكافئات الطويلة لخاصية محددة عند النقر على السهم الصغير الموجود إلى جوارها.
	</li>
	<li>
		عرض نتيجة تغيير التنسيق مباشرة بعد وضع قيم جديدة للخاصيات. انقر على اسم الخاصية أو قيمتها ليظهر لك مربع نص ثم اكتب ضمنه القيمة الجديدة.
	</li>
	<li>
		ستجد إلى جانب قاعدة اسم الملف ورقم السطر الذي عُرِّفت القاعدة ضمنه. انقر على القاعدة لتنتقل إلى عرض القاعدة في مكان وجودها الأصلي حيث يمكنك عادةً تعديلها وحفظ التعديلات التي أجريتها.
	</li>
	<li>
		يمكنك أن تنقر أيضًا على القوس المعقوص لأي قاعدة لإظهار مربع نص ضمن سطر جديد إذ يمكنك كتابة قاعدة جديدة كليًا وفقًا لما تحتاج.
	</li>
</ul>
<p>
	لاحظ وجود عدة نوافذ فرعية يمكن النقر عليها في نافذة CSS:
</p>

<ul>
<li>
		<strong>التنسيق المحوسب Computed</strong>: وتعرض لك التنسيق كما هو مطبق على العنصر (القيم النهائية الجاهزة للتطبيق ضمن المتصفح).
	</li>
	<li>
		<strong>عرض المخطط Layout</strong>: تتكون هذه المنطقة في فايرفوكس من:
		<ul>
<li>
				نموذج صندوقي Box Model: ويعرض نموذجًا صندوقيًا بصريًا لخصائص العنصر لتتمكن مباشرة من الاطلاع على الهوامش والحدود والمساحات الفارغة حوله، وحجم المحتوى الذي يضمه.
			</li>
			<li>
				شبكة العرض Grid: إن استخدام تنسيق الشبكة CSS Grid ضمن الصفحة التي تتفحصها، سيساعدك هذا القسم على رؤية تفاصيل هذه الشبكة.
			</li>
			<li>
				الخطوط Fonts: وتظهر الخطوط المطبقة على العنصر.
			</li>
		</ul>
</li>
</ul>
<h2>
	منقح جافاسكربت
</h2>

<p>
	يتيح منقح <a href="https://academy.hsoub.com/programming/javascript/%D8%B9%D9%84%D8%A7%D9%82%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A8%D8%AA%D8%B7%D9%88%D8%B1-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1310/" rel="">جافاسكربت</a> مراقبة قيمة المتغيرات وضبط نقاط إيقاف التنفيذ Breakpoints، وهي أماكن في شيفرتك تريد إيقاف تنفيذ الشيفرة عندها لتتحقق من المشاكل التي تمنع الشيفرة من العمل بالطريقة المطلوبة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89088" href="https://academy.hsoub.com/uploads/monthly_2022_01/06_javascript_debugger.png.19615cc514d0c191c0976dfc1967ab00.png" rel=""><img alt="06_javascript_debugger.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89088" data-unique="rbacbbmk8" src="https://academy.hsoub.com/uploads/monthly_2022_01/06_javascript_debugger.thumb.png.0532587c41db1ddd2c149fabc519b659.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	للوصول إلى المنقح:
</p>

<ul>
<li>
		<strong>فايرفوكس</strong>: اضغط على زر القائمة<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89093" href="https://academy.hsoub.com/uploads/monthly_2022_01/menu.png.59e4785dd02bbe468b5e8a6042bbb0b2.png" rel="" style="text-align: center; background-color: rgb(255, 255, 255);"><img alt="menu.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89093" data-unique="o5s5u8zep" src="https://academy.hsoub.com/uploads/monthly_2022_01/menu.png.59e4785dd02bbe468b5e8a6042bbb0b2.png"></a> ثم اضغط على "أدوات المطور Web Developer" ثم اضغط على "المنقح Debugger". كما يمكن الضغط على المفاتيح <code>CTRL</code> + <code>Shift</code> + <code>I</code> معًا. وإن كانت أدوات تطوير ويب مفتوحة مسبقًا، اضغط فقط على نافذة "المنقح Debugger".
	</li>
	<li>
		<strong>إنترنت إكسبلورر 11 وإيدج</strong>: اضغط على المفتاح <code>F12</code> ثم المفتاحين <code>CTRL</code> + <code>3</code>. إن كانت أدوات مطوري ويب مفتوحة مسبقًا، انقر على نافذة "المنقح Debugger".
	</li>
	<li>
		<strong>سفاري</strong>: افتح نافذة أدوات مطور ويب ثم اضغط على نافذة "المنقح Debugger".
	</li>
</ul>
<h3>
	استكشاف منقح جافاسكربت
</h3>

<p>
	ستجد ثلاثة أقسام في منقح جافاسكربت على فايرفوكس
</p>

<h4>
	قائمة الملفات
</h4>

<p>
	وهو القسم الأول على يسار نافذة المنقح، يتضمن لائحة الملفات المرتبطة بالصفحة التي تنقحها. اختر الملف الذي ترغب بالعمل عليه بالنقر عليه لتظهر محتوياته ضمن القسم الأوسط من نافذة المنقح.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89089" href="https://academy.hsoub.com/uploads/monthly_2022_01/07_debugger_file_list.png.64ceaebf692425ffe454c84de239e7fc.png" rel=""><img alt="07_debugger_file_list.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89089" data-unique="82hemmbgy" src="https://academy.hsoub.com/uploads/monthly_2022_01/07_debugger_file_list.png.64ceaebf692425ffe454c84de239e7fc.png" style="width: 500px; height: auto;"></a>
</p>

<h4>
	الشيفرة المصدرية
</h4>

<p>
	وتساعدك في وضع نقاط إيقاف التنفيذ أينما ترغب في ملف الشيفرة. لاحظ نقطة الإيقاف في الصورة التالية، وهي عند السطر المظلل الذي يحمل الرقم 18:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89090" href="https://academy.hsoub.com/uploads/monthly_2022_01/08_debugger_break_points.png.a9b3ebb0b173b3bbb5796763b87640da.png" rel=""><img alt="08_debugger_break_points.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89090" data-unique="7cjxo14kq" src="https://academy.hsoub.com/uploads/monthly_2022_01/08_debugger_break_points.png.a9b3ebb0b173b3bbb5796763b87640da.png" style="width: 500px; height: auto;"></a>
</p>

<h3>
	مراقبة التعابير ونقاط إيقاف التنفيذ
</h3>

<p>
	تعرض لك هذه الأداة العبارات التي أضفتها ونقاط إيقاف التنفيذ التي وضعتها، وهي موجودة على يمين نافذة المنقح.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			لاحظ كيف يعرض القسم الأول "مراقبة العبارات Watch expressions" في الصورة التي سنعرضها تاليًا كيف أُضيف المتغير "listitems". يمكن عرض المصفوفة التي تضم قيم المتغير بالنقر عليه.
		</p>
	</div>
</blockquote>

<p>
	تُرتّب نقاط إيقاف التنفيذ في القسم الثاني "نقاط إيقاف التنفيذ Breakpoints". لاحظ كيف وضعت نقطة إيقاف في ملف الشيفرة "example.js" قبل السطر البرمجي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_16" style="">
<span class="pln">listItems</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">inputNewItem</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span></pre>

<p>
	يظهر القسمان الأخيران فقط عند تنفيذ الشيفرة. إذ يعرض القسم "مكدس الاستدعاءات Call Stack" الشيفرة التي جرى تنفيذها حتى الوصول إلى السطر الحالي. لاحظ أنّ الشيفرة موجودة في الدالة التي تعالج نقرة الفأرة، وأن الشيفرة في حالة إيقاف بفعل نقطة الإيقاف. وأخيرًا يعرض القسم "المجالات Scopes" القيم المختلفة التي تنتجها الشيفرة وبالإمكان متابعتها. تعرض الصورة التالية على سبيل المثال <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%88%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-instance-methods-%D9%88%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-instance-variables-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1108/" rel="">الكائنات</a> الموجودة في الشيفرة إضافة إلى الدالة "AddItemClick".
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89091" href="https://academy.hsoub.com/uploads/monthly_2022_01/09_breakpoints_sections.png.362df00578c203940f3479e3a43afddb.png" rel=""><img alt="09_breakpoints_sections.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89091" data-unique="gfr92zreu" src="https://academy.hsoub.com/uploads/monthly_2022_01/09_breakpoints_sections.thumb.png.a5d198562ca4b96c492fef9eaf034a22.png"></a>
</p>

<h2>
	طرفية جافاسكربت
</h2>

<p>
	وهي أداة غاية في الفائدة لتنقيح ملفات جافاسكربت التي لا تعمل كما هو متوقع. تسمح لك الأداة بتنفيذ أسطر الشيفرة بالموازنة مع الصفحة التي يعرضها المتصفح، وتبلغك بالأخطاء التي يصادفها المتصفح عندما يحاول تنفيذ الشيفرة. للولوج إلى طرفية جافاسكربت المدمجة مع المتصفح:
</p>

<ul>
<li>
		إن كانت نافذة أدوات مطوري ويب مفتوحة انقر على نافذة "طرفية Console"، وإن لم تكن مفتوحة يمكنك في فايرفوكس الضغط على المفاتيح <code>CTRL</code> + <code>SHIFT</code> + <code>K</code> معًا أو من خلال:

		<ul>
<li>
				اضغط على رمز القائمة<img alt="menu.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89093" data-unique="ufv616gc9" src="https://academy.hsoub.com/uploads/monthly_2022_01/menu.png.59e4785dd02bbe468b5e8a6042bbb0b2.png">
</li>
		</ul>
</li>
</ul>
<p>
	ثم اضغط على "أدوات المطورين Web Developer" ثم اضغط على "طرفية ويب Web Console" أو "أدوات Tools" ثم اضغط على "أدوات المطورين Web Developer" ثم اضغط على "طرفية الويب Web Console".
</p>

<ul>
<li>
		في بقية المتصفحات، افتح نافذة أدوات مطوري ويب ثم انقر على نافذة الطرفية.
	</li>
</ul>
<p>
	ينتج عن فتح الطرفية نافذة مشابهة للنافذة في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89092" href="https://academy.hsoub.com/uploads/monthly_2022_01/10_java_console.png.a204676b9ab69404ea9fbbffe4de4dc6.png" rel=""><img alt="10_java_console.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89092" data-unique="liqumyyam" src="https://academy.hsoub.com/uploads/monthly_2022_01/10_java_console.thumb.png.446c03bd844cf1322e6d148c00a6ec5f.png"></a>
</p>

<p>
	لتختبر الطرفية، حاول كتابة مقطع الشيفرة التالي ضمنها مقطعًا مقطعًا (ثم اضغط "Enter"). لنكتب أولًا التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_19" style="">
<span class="pln">   alert</span><span class="pun">(</span><span class="str">'hello!'</span><span class="pun">);</span></pre>

<p>
	ثانيًا جرب كتابة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_21" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">).</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor </span><span class="pun">=</span><span class="pln"> </span><span class="str">'purple'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> myWordmark </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
myWordmark</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">'src'</span><span class="pun">,</span><span class="str">'https://blog.mozilla.org/press/wp-content/themes/OneMozilla/img/mozilla-wordmark.png'</span><span class="pun">);</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'h1'</span><span class="pun">).</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">myWordmark</span><span class="pun">);</span></pre>

<p>
	حاول الآن إدخال نسخة خاطئة من الشيفرة وراقب ما يحدث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_23" style="">
<span class="pln">    alert</span><span class="pun">(</span><span class="str">'hello!);</span></pre>

<p>
	أو هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_918_25" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">cheeseSelector</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">).</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor </span><span class="pun">=</span><span class="pln"> </span><span class="str">'purple'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> myWordmark </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
myBanana</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">'src'</span><span class="pun">,</span><span class="str">'https://blog.mozilla.org/press/wp-content/themes/OneMozilla/img/mozilla-wordmark.png'</span><span class="pun">);</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'h1'</span><span class="pun">).</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">myWordmark</span><span class="pun">);</span></pre>

<p>
	ستبدأ بملاحظة طبيعة الأخطاء التي يعيدها المتصفح. يمكن أن تكون هذه الأخطاء مشفرة، ولكن من السهل تحليلها وإيجاد المشاكل الكامنة وراءها.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools" rel="external nofollow">How does the Internet work</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-devtools-r277/" rel="">مدخل إلى أدوات التطوير في متصفح الويب DevTools</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1-r665/" rel="">أدوات المطور</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%8A%D8%A8%D8%9F-r1436/" rel="">ما هي الأدوات المستخدمة في بناء مواقع ويب؟ </a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r541/" rel="">كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-devtools-%D9%81%D9%8A-chrome-r554/" rel="">كيف تستخدم أدوات المطور DevTools في Chrome</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1439</guid><pubDate>Sun, 13 Feb 2022 16:02:00 +0000</pubDate></item><item><title>&#x62F;&#x644;&#x64A;&#x644; &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x633;&#x637;&#x631; &#x627;&#x644;&#x623;&#x648;&#x627;&#x645;&#x631; &#x641;&#x64A; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x645;&#x646; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/6205fd1ca51d3_-----------(1).png.1cc10998e8cf8d0035c9e941f9eb5f02.png" /></p>

<p>
	سيُطلَب منك بلا شك خلال عملية التطوير تشغيل بعض الأوامر في الطرفية Terminal أو على <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر Command Line</a>، لذلك سنقدّم من خلال هذا المقال مقدمة عن الطرفية، والأوامر الأساسية التي ستحتاج إلى إدخالها، وكيفية ربط الأوامر مع بعضها بعضًا، وإضافة أدوات واجهة سطر الأوامر Command Line Interface -أو CLI اختصارًا- الخاصة بك.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>.
	</li>
	<li>
		<strong>الهدف</strong>: فهم سطر الأوامر أو الطرفية، والأوامر الأساسية التي يجب تعلمها، وكيفية تثبيت أدوات سطر أوامر جديدة.
	</li>
</ul>
<h2>
	الطرفية Terminal
</h2>

<p>
	الطرفية هي واجهة نصية تُستخدَم لتنفيذ البرامج النصية. إذا شغّلتَ <a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">أدوات تطوير الويب</a>، فهناك فرصة كبيرة لفتح سطر الأوامر وتشغيل بعض الأوامر لاستخدام الأدوات التي اخترتها، إذ سيُشار إلى هذه الأدوات باسم أو أدوات <a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-cli-%D9%81%D9%8A-php-r1176/" rel="">واجهة سطر الأوامر</a> Command Line Interface أو أدوات CLI اختصارًا.
</p>

<p>
	يمكن استخدام عدد كبير من الأدوات عن طريق كتابة الأوامر في سطر الأوامر، ويكون العديد منها مثبتًا مسبقًا على نظامك، ويمكن تثبيت أدوات أخرى من سجلات الحزم التي تشبه متاجر التطبيقات، ولكن تُستخدَم غالبًا للأدوات والبرامج المستندة إلى سطر الأوامر.
</p>

<p>
	يفتقر سطر الأوامر إلى تجربة المستخدم، بل إن التجربة الأولى مع سطر الأوامر غالبًا ما تكون متعبة وصعبة الاستخدام نسبيًا، إذ يحتوي على شاشة فارغة ومؤشر وامض مع قليل من المساعدة الواضحة المتاحة بشأن ما يجب تطبيقه، لكننا نعدك أن استخدامه سيصبح أسهل مع قليل من التوجيه والممارسة، وسنُساعدك في هذا المقال للبدء في هذه البيئة.
</p>

<p>
	نشأت الطرفية في الخمسينات والستينات من القرن الماضي، ولكن شكلها الأصلي لا يشبه ما نستخدمه اليوم. بقيت الطرفية منذ ذلك الحين ميزة ثابتة في جميع <a href="https://academy.hsoub.com/apps/operating-systems/" rel="">أنظمة التشغيل</a> من أجهزة سطح المكتب إلى خوادم السحابة إلى الحواسيب الصغيرة مثل Raspberry PI Zero وحتى الهواتف المحمولة. توفر الطرفية وصولًا مباشرًا إلى نظام ملفات الحاسوب الأساسي والميزات منخفضة المستوى، وبالتالي تُعَد مفيدة لأداء المهام المعقدة بسرعة. كما أنها مفيدة في عملية الأتمتة مثل كتابة أمر لتحديث أسماء مئات الملفات فوريًا كتعديل اسم الملف "ch01-xxxx.png" إلى "ch02-xxxx.png"، فإذا حدّثتَ أسماء الملفات باستخدام تطبيق Finder أو Explorer GUI، فسيستغرق الأمر وقتًا طويلًا.
</p>

<p>
	يمكنك من خلال الشكل الآتي رؤية بعض الأشكال المختلفة للبرامج المتوفرة التي يمكن أن تنقلك إلى الطرفية، وتظهَر موجِّهات الأوامر المتوفرة في ويندوز مثل برامج Cmd و Powershell التي يمكن تشغيلها من قائمة ابدأ عن طريق كتابة اسم البرنامج.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91757" href="https://academy.hsoub.com/uploads/monthly_2022_02/01_WinTerminals.png.ba8a11fe6498e532be21909455c643ac.png" rel=""><img alt="01_WinTerminals.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91757" data-unique="voe8hedp2" src="https://academy.hsoub.com/uploads/monthly_2022_02/01_WinTerminals.png.ba8a11fe6498e532be21909455c643ac.png" style="width: 800px; height: auto;"></a>
</p>

<p>
	كما يوضّح الشكل التالي تطبيق طرفية نظام ماك macOS:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91758" href="https://academy.hsoub.com/uploads/monthly_2022_02/02_macTerminal.png.e8e55bdd83b84bcb1a5d5893eedefd17.png" rel=""><img alt="02_macTerminal.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91758" data-unique="pug1f1ffi" src="https://academy.hsoub.com/uploads/monthly_2022_02/02_macTerminal.png.e8e55bdd83b84bcb1a5d5893eedefd17.png" style="width: 700px; height: auto;"></a>
</p>

<h3>
	كيفية الوصول إلى الطرفية
</h3>

<p>
	يستخدم العديد من المطورين حاليًا أدوات تستند إلى يونيكس مثل الطرفية والأدوات التي يمكنك الوصول إليها من خلالها، إذ تدعم العديد من الأدوات الموجودة على الويب الأنظمة التي تستند إلى يونيكس، ولكن لا داعي للقلق، فهي متوفرة في معظم الأنظمة. سنوضّح فيما يلي كيفية الوصول إلى الطرفية على النظام الذي تريده.
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس</a> أو يونيكس: تحتوي أنظمة <a href="https://academy.hsoub.com/devops/linux/" rel="">لينكس</a> أو يونيكس <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B7%D8%B1%D9%81%D9%8A%D9%91%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-terminal-r18/" rel="">طرفية</a> متاحة افتراضيًا مدرجَة ضمن تطبيقاتك.
	</li>
	<li>
		نظام ماك macOS: يحتوي نظامًا يسمى داروين Darwin يقع أسفل <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-gui-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1070/" rel="">واجهة المستخدم الرسومية</a> ويشبه نظام يونيكس، ويوفر الطرفية والوصول إلى الأدوات ذات المستوى المنخفض. الطرفية متاحة على نظام ماك من خلال المسار Applications/Utilities/Terminal.
	</li>
	<li>
		ويندوز: لم يكن استخدام الطرفية أو سطر الأوامر على ويندوز بسيطًا أو سهلًا كما هو الحال في أنظمة التشغيل الأخرى، ولكن الأمور تتحسن مع مرور الوقت. لطالما امتلك ويندوز برنامجًا شبيهًا بالطرفية يسمى Cmd أو موجه الأوامر، ولكنه ليس متكافئًا مع أوامر يونيكس، بل يكافئ موجه ويندوز دوز Windows DOS ذي النمط القديم. توجد برامج أفضل لتوفير طرفية على ويندوز مثل <a href="https://github.com/PowerShell/PowerShell" rel="external nofollow">PowerShell</a> وGitbash الذي يكون جزءًا من مجموعة أدوات <a href="https://gitforwindows.org/" rel="external nofollow">git for Windows</a>. لكن أفضل خيار لنظام ويندوز حاليًا هو نظام ويندوز الفرعي لنظام لينكس Windows Subsystem for Linux -أو WSL اختصارًا، وهو طبقة توافق لتشغيل أنظمة تشغيل لينكس مباشرة من داخل الإصدار رقم 10 من ويندوز، مما يتيح لك تشغيل طرفية حقيقية مباشرة على ويندوز دون الحاجة إلى آلة افتراضية، ويمكن تثبيت نظام WSL مباشرة من متجر ويندوز مجانًا.
	</li>
</ul>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91759" href="https://academy.hsoub.com/uploads/monthly_2022_02/03_wsl.png.46607a2bb190dffe373a50b44cfbfede.png" rel=""><img alt="03_wsl.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91759" data-unique="0tjy9jxa9" src="https://academy.hsoub.com/uploads/monthly_2022_02/03_wsl.thumb.png.ccbeb72dd984233245734d25b8e6aaf6.png" style="width: 750px; height: auto;"></a>
</p>

<p>
	نوصي بشدة بتثبيت نظام WSL، ويمكنك التمسك باستخدام موجّه الأوامر الافتراضي <code>cmd</code> إذ ستعمل أدوات متعددة بطريقة صحيحة، ولكنك ستجد الأمور أسهل إذا كان لديك تكافؤ أفضل مع أدوات يونيكس.
</p>

<p>
	هناك فرق بين الطرفية وسطر الأوامر، فالطرفية تقنيًا هي برنامج يبدأ ويتصل <a href="https://academy.hsoub.com/certificates/redhat/rhcsa/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B5%D8%AF%D9%81%D8%A9-shell-%D9%81%D9%8A-red-hat-enterprise-linux-r40/" rel="">بالصدفة Shell</a> التي تُعَد بيئة جلستك إذ يمكنك تخصيص أشياء مثل الموجه والاختصارات، بينما سطر الأوامر هو السطر الحرفي الذي تكتب فيه الأوامر ويومض المؤشر.
</p>

<p>
	إذا استخدمت أدواتٍ مثل Visual Studio Code، فهناك مجموعة كبيرة من الامتدادات التي يمكن استخدامها كوكيل Proxy لاستخدام أوامر الطرفية دون الحاجة إلى استخدامها مباشرة، وبالرغم من وجود الكثير من الأدوات المتاحة في سطر الأوامر. لكنك لن تجد امتدادًا لمحرر الشيفرات لكل ما تريده، لذلك سيتعين عليك اكتساب خبرة في استخدام الطرفية في النهاية.
</p>

<h2>
	أوامر المدمجة الأساسية في الطرفية
</h2>

<p>
	إليك بعض الأشياء التي يمكن لسطر الأوامر تطبيقها، بالإضافة إلى أسماء الأدوات ذات الصلة بكل حالة:
</p>

<ul>
<li>
		انقل نظام ملفات حاسوبك إلى جانب مهام المستوى الأساسي مثل الإنشاء والنسخ وإعادة التسمية والحذف:
		<ul>
<li>
				التنقل بين المجلدات باستخدام الأمر <code>cd</code>.
			</li>
			<li>
				إنشاء مجلد باستخدام الأمر <code>mkdir</code>.
			</li>
			<li>
				إنشاء ملفات وتعدّيل بياناتها الوصفية باستخدام الأمر <code>touch</code>.
			</li>
			<li>
				نسخ الملفات باستخدام الأمر <code>cp</code>.
			</li>
			<li>
				نقل الملفات باستخدام الأمر<code>mv</code>.
			</li>
			<li>
				حذف الملفات أو المجلدات باستخدام الأمر <code>rm</code>.
			</li>
		</ul>
</li>
	<li>
		تنزيل الملفات الموجودة في <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%88-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1435/" rel="">عناوين URL</a> محدَّدة باستخدام الأمر <code>curl</code>.
	</li>
	<li>
		البحث عن أجزاء نصية ضمن مجموعات نصية أكبر باستخدام الأمر <code>grep</code>.
	</li>
	<li>
		عرض محتويات ملف بمقدار صفحة تلو الأخرى باستخدام الأمر <code>less</code> و<code>cat</code>.
	</li>
	<li>
		معالجة وتحويل مجاري النصوص مثل تغيير جميع وسوم <code>&lt;div&gt;</code> في ملف HTML إلى <code>&lt;article&gt;</code> باستخدام الأوامر <code>awk</code> و<code>tr</code> و<code>sed</code>.
	</li>
</ul>
<h3>
	التنقل في سطر الأوامر
</h3>

<p>
	ستحتاج حتمًا إلى الانتقال إلى مسار معين عندما تزور سطر الأوامر. تشغّل جميع أنظمة التشغيل برنامجها الطرفي في المسار الموجود فيه سطر الأوامر، وغالبًا سترغبُ بالانتقال إلى لمسار مختلف.
</p>

<p>
	يتيح لك <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-cd-%D9%88-pwd-%D9%88-ls-%D9%84%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r305/" rel="">الأمر cd</a> تغيير الدليل Change Directory، وليس هذا الأمر برنامجًا وإنما مبني مسبقًا، إذ لا يمكنك حذفه عن طريق الخطأ. لا داعي للقلق كثيرًا بشأن ما إذا كان الأمر مبنيًا مسبقًا أم لا، ولكن ضع في بالك أن الأوامر المبنية مسبقًا تظهر في جميع الأنظمة القائمة على نظام يونيكس.
</p>

<p>
	يمكنك تغيير المسار من خلال كتابة الأمر <code>cd</code> في الطرفية متبوعًا بالمجلد الذي تريد الانتقال إليه. يمكنك استخدام الأمر <code>cd Desktop</code> بافتراض وجود المجلد ضمن المسار الرئيسي كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91760" href="https://academy.hsoub.com/uploads/monthly_2022_02/04_winTerminalsCd.png.6cdcfff42cab69533fab1284c098c1fe.png" rel=""><img alt="04_winTerminalsCd.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91760" data-unique="7bfkkkrid" src="https://academy.hsoub.com/uploads/monthly_2022_02/04_winTerminalsCd.png.6cdcfff42cab69533fab1284c098c1fe.png" style="width: 580px; height: auto;"></a>
</p>

<p>
	اكتب الأمر التالي في طرفية نظامك:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_22" style="">
<span class="pln">cd </span><span class="typ">Desktop</span></pre>

<p>
	إذا أردتَ الرجوع إلى المسار السابق، فيمكنك استخدام نقطتين كما يلي:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_24" style="">
<span class="pln">cd</span><span class="pun">..</span></pre>

<p>
	هناك اختصار في الطرفية مفيد جدًا وهو استخدام مفتاح <code>tab</code> لإكمال الأسماء تلقائيًا التي تعرف أنها موجودة بدلًا من كتابة اسم المجلد بالكامل فمثلًا كتابة الأمر <code>cd D</code> ثم الضغط على مفتاح <code>tab</code>، سيكمل سطر الأوامر كتابة اسم المجلد Desktop نيابةً عنك، بشرط وجوده في المجلد الحالي.
</p>

<p>
	إذا كان المجلد الذي تريد الانتقال إليه متداخلًا، فيجب أن تعرف المسار للوصول إليه، إذ يصبح ذلك أسهل عندما تصبح أكثر دراية بمعمارية نظام ملفاتك، ولكن إن لم تكن متأكدًا من المسار، فيمكنك اكتشافه باستخدام الأمر <code>ls</code>، ومن خلال النقر في نافذة المستكشف Explorer أو Finder لمعرفة مكان المجلد بناءً على مكان وجودك حاليًا.
</p>

<p>
	إذا أردت الانتقال مثلًا إلى مجلد يسمى <code>src</code> يقع ضمن مجلد اسمه <code>project</code> موجود على سطح المكتب <code>Desktop</code>، فيمكنك كتابة هذه الأوامر الثلاثة للوصول إلى هناك من مجلدك الرئيسي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_26" style="">
<span class="pln">cd </span><span class="typ">Desktop</span><span class="pln">
cd project
cd src</span></pre>

<p>
	ولكن يمكنك كتابة أمر واحد مع العناصر المختلفة في المسار مفصول بينها بشرطة مائلة للأمام، تمامًا كما تفعل عند تحديد مسارات للصور أو الأصول الأخرى في شيفرة <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a> أو HTML أو <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a>:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_30" style="">
<span class="pln">cd </span><span class="typ">Desktop</span><span class="pun">/</span><span class="pln">project</span><span class="pun">/</span><span class="pln">src</span></pre>

<p>
	لاحظ أن تضمين شرطة مائلة في المسار يجعله مسارًا مطلقًا مثل <code>‎/Users/your-user-name/Desktop</code>، إذ يؤدي حذف الشرطة المائلة في المقدمة كما فعلنا سابقًا إلى جعل المسار مسارًا نسبيًا متعلقًا بمجلد العمل الحالي، وهذا هو بالضبط ما تراه مع عناوين URL في متصفح الويب، إذ تعني الشرطة المائلة في البداية جذر موقع الويب، بينما يعني حذف الشرطة المائلة أن عنوان URL متعلق بصفحتي الحالية. يستخدم نظام ويندوز الشرطة المائلة للخلف بدلًا من الشرطة المائلة للأمام مثل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_32" style="">
<span class="pln"> cd </span><span class="typ">Desktop</span><span class="pln">\project\src
</span></pre>

<h3>
	سرد محتويات مجلد
</h3>

<p>
	الأمر <code>ls</code> مبني مسبقًا في نظام التشغيل يونيكس وهو اختصار للكلمة الأجنبية List، ووظيفته سرد محتويات المجلد الذي تتواجد فيه حاليًا. لاحظ أن هذا الأمر لن ينجح إذا استخدمتَ موجّه أوامر ويندوز الافتراضي <code>cmd</code>، فالأمر المكافئ له هو <code>dir</code>.
</p>

<p>
	شغّل الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4333_35" style="">
<span class="pln">ls</span></pre>

<p>
	يمنحك <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%AD%D8%AA%D8%B1%D9%81-%D8%A7%D9%84%D8%A3%D9%85%D8%B1-ls-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r558/" rel="">الأمر ls</a> قائمة بالملفات والمجلدات الموجودة في مجلد العمل الحالي، ولكنها معلومات أساسية، وستحصل فقط على اسم كل عنصر موجود دون معرفة نوعه. يمكن أن يمنحك تغيير بسيط في استخدام هذا الأمر كثيرًا من المعلومات.
</p>

<h3>
	خيارات الأوامر
</h3>

<p>
	تحتوي معظم أوامر الطرفية على خيارات، هي المعدّلات Modifiers التي تضيفها إلى نهاية الأمر، مما يجعله يتصرف بطريقة مختلفة. تتكون هذه الخيارات عادةً من مسافة بعد اسم الأمر، متبوعة بشرطة، متبوعة بحرف واحد أو أكثر.
</p>

<p>
	جرب الأمر التالي على سبيل المثال، وشاهد النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_37" style="">
<span class="pln">ls </span><span class="pun">-</span><span class="pln">l</span></pre>

<p>
	يمنحك الخيار <code>‎-l</code> في الأمر <code>ls</code> قائمة بملف أو مجلد واحد في كل سطر ومعلومات أخرى. يمكن التعرف على المجلدات من خلال البحث عن الحرف "d" على الجانب الأيسر من السطور، إذ يمكننا استخدام الأمر <code>cd</code> مع هذه المجلدات.
</p>

<p>
	يوضّح الشكل الآتي طرفية نظام ماك الصرفة في الجزء العلوي، وطرفية مخصّصة مع بعض الرموز والألوان الإضافية في الجزء السفلي، وكلاهما يعرض نتائج تشغيل الأمر <code>ls -l</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91761" href="https://academy.hsoub.com/uploads/monthly_2022_02/05_macTerminalsLs.png.93f77cc23fbc613637ae5453242ea13d.png" rel=""><img alt="05_macTerminalsLs.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91761" data-unique="8e4tg1pb0" src="https://academy.hsoub.com/uploads/monthly_2022_02/05_macTerminalsLs.png.93f77cc23fbc613637ae5453242ea13d.png" style="width: 550px; height: auto;"></a>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>: يمكنك معرفة الخيارات المتوفرة لكل أمر من خلال إلقاء نظرة على صفحة الدليل الخاصة به باستخدام الأمر <code>man</code>، متبوعًا باسم الأمر الذي تريد البحث عنه مثل <code>man ls</code>. سيؤدي هذا الأمر إلى فتح صفحة الدليل في عارض الملفات النصية الافتراضي في الطرفية مثل البرنامج <code>less</code>، إذ يجب أن تكون بعد ذلك قادرًا على التمرير عبر الصفحة باستخدام مفاتيح الأسهم أو آلية أخرى مشابهة، إذ تسرد صفحة الدليل جميع الخيارات مع التفاصيل. يجب الخروج من صفحة الدليل بعد الانتهاء منها باستخدام أمر الخروج quit الخاص بعارض النصوص مثل "q" في <code>less</code>. يمكنك أيضًا تشغيل أمر به خيارات متعددة في الوقت نفسه من خلال وضعها جميعًا ضمن سلسلة واحدة بعد حرف الشرطة مثل <code>ls -lah</code> أو <code>ls -ltrh</code>. حاول إلقاء نظرة على صفحة دليل man الخاصة بالأمر ls لمعرفة ما تفعله هذه الخيارات الإضافية.
	</p>
</blockquote>

<h3>
	الإنشاء والنسخ والنقل والإزالة
</h3>

<p>
	هناك عدد من الأوامر الأساسية الأخرى التي يُحتمَل أن تستخدمها كثيرًا أثناء عملك مع الطرفية. هذه الأوامر بسيطة جدًا، لذا لن نشرحها بقدر كبير من التفاصيل مثل الأمرين السابقين. استخدم أمثلة الأوامر التالية لفهمها:
</p>

<ul>
<li>
		<code>mkdir</code>: يؤدي هذا الأمر إلى إنشاء مجلد جديد ضمن المجلد الحالي الذي تتواجد فيه، مع الاسم الذي تقدمه بعد الأمر، فمثلًا سينشئ الأمر <code>mkdir my-awesome-website</code> مجلدًا جديدًا اسمه <code>my-awesome-website</code>.
	</li>
	<li>
		<code>rmdir</code>: يزيل هذا الأمر المجلد المحدد إذا كان فارغًا فقط، فمثلًا سيزيل الأمر <code>rmdir my-awesome-website</code> المجلد الذي أنشأناه سابقًا. إذا أردتَ إزالة مجلد غير فارغ وإزالة كل ما يحتويه، فيمكنك استخدام الخيار <code>‎-r</code> وهو اختصار recursive تكراري، ولكن يجب الانتباه عند استخدامه، إذ يجب أن تتأكد من عدم وجود أيّ شيء يمكن أن تحتاجه داخل المجلد، إذ سيختفي إلى الأبد.
	</li>
	<li>
		<code>touch</code>: ينشئ هذا الأمر ملفًا فارغًا جديدًا ضمن المجلد الحالي، فمثلًا ينشئ الأمر <code>touch mdn-example.md</code> ملفًا فارغًا جديدًا اسمه <code>mdn-example.md</code>.
	</li>
	<li>
		<code>mv</code>: ينقل هذا الأمر ملفًا من موقع الملف الأول المحدَّد إلى موقع الملف المحدد الثاني، وينقل الأمر <code>mv mdn-example.md mdn-example.txt</code> الملف <code>mdn-example.md</code> في المجلد الحالي إلى الملف <code>mdn-example.txt</code> في المجلد الحالي (تُكتَب المواقع كمسارات ملفات)، إذ يُعَد الملف منقولًا، ولكن هذا الأمر يعيد تسمية الملف فعليًا.
	</li>
	<li>
		<code>cp</code>: يشبه الأمر <code>mv</code> في الاستخدام، إذ ينشئ الأمر <code>cp</code> نسخة من الملف الموجود ضمن الموقع الأول المحدد في الموقع الثاني المحدَّد، إذ يُنشئ الأمر <code>cp mdn-example.txt mdn-example.txt.bak</code> نسخة من الملف <code>mdn-example.txt</code> بالاسم <code>mdn-example.txt.bak</code>، ويمكنك بالطبع تسمية هذه النسخة باسم آخر إذا أدرتَ ذلك.
	</li>
	<li>
		<code>rm</code>: يزيل هذا الأمر الملف المحدَّد، فمثلًا يحذف الأمر <code>rm mdn-example.txt</code> ملفًا اسمه <code>mdn-example.txt</code>. لاحظ أن هذا الحذف نهائي ولا يمكن التراجع عنه عبر سلة المحذوفات الموجودة غالبًا على واجهة مستخدم سطح المكتب.
	</li>
</ul>
<p>
	تسمح لك العديد من أوامر الطرفية باستخدام العلامات النجمية بوصفها محارف بدل Wild Card مع أيّ تسلسل من المحارف، مما يسمح بتشغيل عملية على عدد كبير من الملفات في الوقت نفسه، وتتطابق جميعها مع النمط المحدد، إذ يحذف الأمر <code>rm mdn-*‎</code> جميع الملفات التي تبدأ بسلسلة المحارف <code>mdn-‎</code>، بينما يحذف الأمر <code>rm mdn-*.bak</code> جميع الملفات التي تبدأ بسلسلة المحارف <code>mdn-‎</code> وتنتهي بسلسلة المحارف <code>‎.bak</code>.
</p>

<h2>
	هل تعد الطرفية خطيرة؟
</h2>

<p>
	عليك توخي الحذر عند استخدام الطرفية. لا تحمل الأوامر البسيطة الكثير من المخاطر، ولكن إذا جمّعتَ أوامرًا أكثر تعقيدًا، فعليك التفكير مليًا فيما ستفعله هذه الأوامر، ومحاولة اختبارها أولًا قبل تشغيلها في المجلد المحدَّد.
</p>

<p>
	لنفترض أن لديك 1000 ملف نصي في مجلد، وأردت الاطلاع عليها جميعًا وحذف الملفات التي يحتوي اسمها على سلسلة فرعية معينة. إن لم تكن حذرًا، يمكن أن ينتهي بك الأمر بحذف شيء مهم، وبالتالي تفقد عملك خلال هذه العملية. إحدى العادات الجيدة التي يجب عليك اتباعها هي كتابة الأمر ضمن محرر نصوص، ثم إنشاء نسخة احتياطية من مجلدك وتشغيل الأمر على هذه النسخة أولًا لاختباره.
</p>

<p>
	إن لم تكن مرتاحًا لتجربة أوامر الطرفية على جهازك الخاص، فيمكنك تجربتها في مكان آمن هو <a href="https://glitch.com/" rel="external nofollow">Glitch.com</a> الذي يُعَد مكانًا رائعًا لتجربة شيفرة تطوير الويب، بالإضافة إلى إمكانية الوصول إلى طرفية، إذ يمكنك تشغيل كل هذه الأوامر مباشرة فيها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91762" href="https://academy.hsoub.com/uploads/monthly_2022_02/06_glitch.png.8a8d23ef57e7eaf9ddb54249d89b8302.png" rel=""><img alt="06_glitch.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91762" data-unique="j8igdd1u8" src="https://academy.hsoub.com/uploads/monthly_2022_02/06_glitch.thumb.png.37dc4fab50c2d47d2fafcf3154e68584.png"></a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91763" href="https://academy.hsoub.com/uploads/monthly_2022_02/08_glitch.png.a494b0bcee89eb1271d33fe34318a007.png" rel=""><img alt="08_glitch.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91763" data-unique="dz5rn9cmk" src="https://academy.hsoub.com/uploads/monthly_2022_02/08_glitch.thumb.png.7c61331099f2bf39691ceb70eb4c49a2.png"></a>
</p>

<p>
	يُعَد <a href="https://tldr.sh/" rel="external nofollow">tldr.sh</a> من الموارد الرائعة للحصول على نظرة عامة وسريعة على أوامر طرفية معينة، وهي خدمة توثيق مقادَة من المجتمع على غرار MDN، ولكنها خاصة بالأوامر الطرفية.
</p>

<p>
	لنتعلّم الآن كيفية توصيل الأدوات مع بعضها في سطر الأوامر.
</p>

<h2>
	ربط الأوامر باستخدام الأشرطة العمودية
</h2>

<p>
	تعرّفنا سابقًا على الأمر <code>ls</code> الذي يعرض محتويات المجلد الحالي:
</p>

<pre class="ipsCode">
ls
</pre>

<p>
	ولكن إذا أردنا حساب عدد الملفات والمجلدات بسرعة ضمن المجلد الحالي، فلن يستطيع الأمر <code>ls</code> ذلك من تلقاء نفسه.
</p>

<p>
	هناك أداة أخرى متاحة من يونيكس تسمى <code>wc</code> تحسب عدد الكلمات أو الأسطر أو المحارف أو البايتات لكل ما يُوضَع فيها، إذ يمكن أن يكون ذلك ملفًا نصيًا، إذ ينتج عن تنفيذ الأمر التالي عدد الأسطر في الملف <code>myfile.txt</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4333_41" style="">
<span class="pln">wc </span><span class="pun">-</span><span class="pln">l myfile</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	كما يمكنها حساب عدد الأسطر لخرج أي أمر يُربَط معها من خلال رمز الشريط العمودي <code>|</code>، وسيحسبُ الأمر التالي عدد الأسطر الناتجة عن الأمر <code>ls</code> -أي ما سيطبعه إلى الطرفية إذا شُغِّل بمفرده- ويخرج عدد الأسطر في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_43" style="">
<span class="pln">ls </span><span class="pun">|</span><span class="pln"> wc </span><span class="pun">-</span><span class="pln">l</span></pre>

<p>
	بما أن الأمر <code>ls</code> يطبع كل ملف أو مجلد على سطر خاص به، فهذا يعطينا عدد الأدلة والملفات بفعالية.
</p>

<p>
	تطبع أدوات سطر أوامر يونيكس النص إلى الطرفية، ويشار إليها بالطباعة إلى الخرج المعياري Printing To Standard Output أو <code>STDOUT</code> اختصارًا، إذ يمكن لعدد كبير من الأوامر قراءة المحتوى من مجرى الدخل المعروف باسم الدخل المعياري Standard Input أو <code>STDIN</code> اختصارًا.
</p>

<p>
	يربط المعامل <code>|</code> هذه المدخلات والمخرجات مع بعضها، مما يسمح ببناء عمليات أكثر تعقيدًا لتناسب احتياجاتنا، إذ يمكن أن يصبح خرج أحد الأوامر دخلًا للأمر الذي بعده. يطبع الأمر <code>ls</code> عادةً خرجه إلى <code>STDOUT</code>، ولكن يُربَط خرجه مع الأمر <code>wc</code> من خلال الرمز <code>|</code> سيأخذ الأمر <code>wc</code> هذا الخرج بوصفه دخلًا له، ويحسب عدد الأسطر، ويطبعها إلى <code>STDOUT</code>.
</p>

<h2>
	مثال أكثر تعقيدا
</h2>

<p>
	سنحاول أولًا جلب محتويات صفحة "fetch" من MDN باستخدام الأمر <code>curl</code> الذي يمكن استخدامه لطلب المحتوى من عناوين URL مثل الرابط الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_45" style="">
<span class="pln">https</span><span class="pun">:</span><span class="com">//developer.mozilla.org/en-US/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/WindowOrWorkerGlobalScope/fetch</span></pre>

<p>
	كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_47" style="">
<span class="pln">curl https</span><span class="pun">:</span><span class="com">//developer.mozilla.org/en-US/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/WindowOrWorkerGlobalScope/fetch</span></pre>

<p>
	لن تحصل على خرج بسبب إعادة توجيه الصفحة إلى <a href="https://developer.mozilla.org/en-US/docs/Web/API/fetch" rel="external nofollow">‎/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/fetch</a>، إذ يجب إعلام الأمر <code>curl</code> صراحةً باتباع عمليات إعادة التوجيه باستخدام الراية <code>‎-L</code>.
</p>

<p>
	لنلقِ نظرةً على الترويسات التي يعيدها <code>developer.mozilla.org</code> باستخدام الراية <code>‎-I</code> الخاصة بالأمر <code>curl</code>، ونطبع جميع عمليات إعادة التوجيه التي يرسلها الموقع إلى الطرفية عن طريق ربط خرج الأمر <code>curl</code> مع الأمر <code>grep</code> باستخدام الرمز <code>|</code>، وسنطلبُ من الأمر <code>grep</code> إعادة جميع الأسطر التي تحتوي على الكلمة "location".
</p>

<p>
	حاول تشغيل الأمر التالي (لاحظ أن هناك إعادة توجيه واحدة فقط قبل أن نصل إلى الصفحة الأخيرة):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_49" style="">
<span class="pln">curl https</span><span class="pun">:</span><span class="com">//developer.mozilla.org/en-US/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/WindowOrWorkerGlobalScope/fetch -L -I | grep location</span></pre>

<p>
	يجب أن يبدو الخرج كما يلي، إذ سينتج الأمر curl أولًا بعض عدّادات التنزيل أو ما شابه ذلك:
</p>

<pre class="ipsCode">
location: /en-US/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/fetch
</pre>

<p>
	كما يمكننا تحويل محتويات سطور الموقع <code>location:‎</code>، وإضافة الأصل الأساسي إلى بداية كل منها لنحصل على عناوين URL كاملة مطبوعة، لذلك سنضيف الأمر <code>awk</code>، وهي لغة برمجة تشبه جافاسكربت أو <a href="https://wiki.hsoub.com/Ruby" rel="external">روبي Ruby</a> أو <a href="https://wiki.hsoub.com/Python" rel="external">بايثون Python</a>، ولكنها أقدم منها.
</p>

<p>
	شغّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_51" style="">
<span class="pln">curl https</span><span class="pun">:</span><span class="com">//developer.mozilla.org/en-US/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/WindowOrWorkerGlobalScope/fetch -L -I | grep location | awk '{ print "https://developer.mozilla.org" $2 }'</span></pre>

<p>
	ويجب أن يكون الخرج النهائي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_53" style="">
<span class="pln">https</span><span class="pun">:</span><span class="com">//developer.mozilla.org/en-US/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/fetch</span></pre>

<p>
	خصّصنا الخرج من خلال دمج هذه الأوامر لإظهار عناوين URL الكاملة التي يعيد خادم Mozilla التوجيه من خلالها عندما نطلب العنوان <code>‎/docs/Web/<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>/WindowOrWorkerGlobalScope/fetch</code>.
</p>

<h2>
	إضافة مزايا
</h2>

<p>
	ألقينا نظرة على بعض الأوامر المبنية مسبقًا التي يأتي نظامك مزودًا بها، ولنلقِ نظرة الآن على كيفية تثبيت أداة CLI لجهة خارجية والاستفادة منها.
</p>

<p>
	يتوفر حاليًا النظام المجتمعي الواسع للأدوات القابلة للتثبيت لتطوير واجهة الويب الأمامية ضمن <a href="https://www.npmjs.com/" rel="external nofollow">npm</a>، وهي خدمة استضافة حزم يملكها القطاع الخاص، وتعمل بصورة وثيقة مع <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a>، ولكن يمكنك أن تتوقع رؤية مزيد من مزودي الحزم مع مرور الوقت.
</p>

<p>
	يؤدي تثبيت Node.js إلى تثبيت أداة سطر أوامر npm وأداة إضافية تعتمد على <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">npm</a> تسمى npx، وتوفر أداة npm بوابة لتثبيت أدوات سطر أوامر إضافية. يعمل <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">Node.js</a> وnpm بالطريقة نفسها في جميع الأنظمة: ماك macOS وويندوز Windows ولينكس Linux.
</p>

<p>
	ثبّت أداة npm على نظامك الآن مع تنزيل وتشغيل مثبّت Node.js المناسب لنظام تشغيلك. إذا طُلب منك ذلك، فتأكد من تضمين npm كجزء من عملية التثبيت.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91764" href="https://academy.hsoub.com/uploads/monthly_2022_02/09_npmInstallOption.png.4889e43deae8f19a2063371b2b09ab95.png" rel=""><img alt="09_npmInstallOption.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91764" data-unique="e2bx13srp" src="https://academy.hsoub.com/uploads/monthly_2022_02/09_npmInstallOption.png.4889e43deae8f19a2063371b2b09ab95.png" style="width: 550px; height: auto;"></a>
</p>

<p>
	كما سنتعرّف على أداة <a href="https://prettier.io" rel="external nofollow">Prettier</a>، وهي أداة لتنسيق الشيفرات البرمجية وتحتوي على عدة خيارات بسيطة.
</p>

<h3>
	مكان تثبيت أدوات CLI
</h3>

<p>
	يمكننا تثبيت الأدوات بطريقة عامة باستخدام <code>npm</code> لنتمكن من الوصول إليها في أي مكان، أو محليًا في مجلد المشروع الحالي. هناك إيجابيات وسلبيات في كل طريقة، وهذه القائمة توضح إيجابيات وسلبيات التثبيت العام للأدوات:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				إيجابيات التثبيت العام
			</th>
			<th>
				سلبيات التثبيت العام
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				يمكن الوصول إليها من أي مكان في الطرفية
			</td>
			<td>
				يمكن ألّا تكون متوافقة مع شيفرة مشروعك
			</td>
		</tr>
<tr>
<td>
				تُثبَّت مرة واحدة فقط
			</td>
			<td>
				لن يتمكن المطورون الآخرون في فريقك من الوصول إلى هذه الأدوات، مثل مشاركة قاعدة شيفرتك عبر أداة مثل git.
			</td>
		</tr>
<tr>
<td>
				يستهلك مساحة أقل على القرص الصلب
			</td>
			<td>
				تتعلق هذه النقطة بالنقطة السابقة، إذ يصعب نسخ شيفرة المشروع (إذا ثبّت أدواتك محليًا، فيمكن إعدادها كاعتماديات وتثبيتها باستخدام الأمر <code>npm install</code>).
			</td>
		</tr>
<tr>
<td>
				الإصدار نفسه دائمًا
			</td>
			<td>
				 
			</td>
		</tr>
<tr>
<td>
				يبدو مثل أي أمر يونيكس آخر
			</td>
			<td>
				 
			</td>
		</tr>
</tbody>
</table>
<p>
	يُحتمَل أن يكون التأثير السلبي للتثبيت العام أكبر بكثير من الفوائد بالرغم من أن قائمة السلبيات أقصر، ولكننا سنثبّت الأدوات بطريقة عامة لإبقاء الأمور بسيطة. سنلقي نظرة أكثر على عمليات التثبيت المحلية وفوائدها في المقال التالي.
</p>

<h3>
	تثبيت Prettier
</h3>

<p>
	سنثبّت أداة Prettier بوصفها أداة مساعدة عامة لسطر الأوامر، وهي أداة تنسيق شيفرة لمطوري الواجهة الأمامية، وتركز على اللغات المستندة إلى لغة جافاسكربت بالإضافة إلى دعمها لغات HTML و CSS و SCSS و <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> وغير ذلك. يمكن أن تطبّق أداة Prettier ما يلي:
</p>

<ul>
<li>
		حفظ العبء المعرفي المتمثل في جعل النمط متسقًا يدويًا عبر جميع ملفات شيفرتك، إذ يمكن أن تطبّق Prettier ذلك تلقائيًا.
	</li>
	<li>
		مساعدة المتعلمين الجدد في تطوير الويب على تنسيق شيفرتهم من خلال تطبيق أفضل الممارسات.
	</li>
	<li>
		يمكن تثبيتها على أي نظام تشغيل وكجزء مباشر من أدوات المشروع، مما يضمن لزملائك وأصدقائك الذين يعملون على شيفرتك أن يستخدموا نمط الشيفرة الذي تستخدمه.
	</li>
	<li>
		يمكن تهيئتها للتشغيل عند الحفظ، أو أثناء الكتابة، أو حتى قبل نشر الشيفرة.
	</li>
</ul>
<p>
	افتح الطرفية بعد تثبيت نود وشغّل الأمر التالي لتثبيت Prettier:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_4333_61" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="kwd">global</span><span class="pln"> prettier</span></pre>

<p>
	تصبح بعد ذلك أداة Prettier متاحة في الطرفية وفي أيّ مكان في نظام ملفاتك.
</p>

<p>
	سيؤدي تشغيل الأمر بدون أي وسطاء -كما هو الحال مع العديد من الأوامر الأخرى- إلى تقديم معلومات حول كيفية الاستخدام والمساعدة. جرب الأمر التالي وشاهد النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_63" style="">
<span class="pln">prettier</span></pre>

<p>
	يجب أن يبدو الخرج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_67" style="">
<span class="typ">Usage</span><span class="pun">:</span><span class="pln"> prettier </span><span class="pun">[</span><span class="pln">options</span><span class="pun">]</span><span class="pln"> </span><span class="pun">[</span><span class="pln">file</span><span class="pun">/</span><span class="pln">glob </span><span class="pun">...]</span><span class="pln">

</span><span class="typ">By</span><span class="pln"> default</span><span class="pun">,</span><span class="pln"> output is written to stdout</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Stdin</span><span class="pln"> is read </span><span class="kwd">if</span><span class="pln"> it is piped to </span><span class="typ">Prettier</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> no files are given</span><span class="pun">.</span><span class="pln">

</span><span class="pun">…</span></pre>

<p>
	يجب دائمًا الاطلاع على معلومات الاستخدام على الأقل، حتى لو كانت طويلة، لأنها ستساعدك في فهم كيفية استخدام الأداة بطريقة أفضل.
</p>

<p>
	لنجرب استخدام الأداة Prettier لنتمكن من معرفة كيفية عملها. أنشئ أولًا مجلدًا جديدًا في مكان ما على نظام ملفاتك يسهل العثور عليه مثل مجلد اسمه <code>prettier-test</code> على سطح المكتب <code>Desktop</code>، ثم احفظ الشيفرة التالية في ملف جديد يسمى <code>index.js</code> ضمن مجلد الاختبار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_69" style="">
<span class="kwd">const</span><span class="pln"> myObj </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
a</span><span class="pun">:</span><span class="lit">1</span><span class="pun">,</span><span class="pln">b</span><span class="pun">:{</span><span class="pln">c</span><span class="pun">:</span><span class="lit">2</span><span class="pun">}}</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> printMe</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">){</span><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">.</span><span class="pln">b</span><span class="pun">.</span><span class="pln">c</span><span class="pun">)}</span><span class="pln">
printMe</span><span class="pun">(</span><span class="pln">myObj</span><span class="pun">)</span></pre>

<p>
	يمكننا أن نشغّل prettier في قاعدة الشيفرة للتحقق من حاجة شيفرتنا لتعديلٍ ما. شغّل الأمر <code>cd</code> للانتقال إلى مجلدك، وشغّل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_71" style="">
<span class="pln">prettier </span><span class="pun">--</span><span class="pln">check index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ويجب أن تحصل على الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_73" style="">
<span class="typ">Checking</span><span class="pln"> formatting</span><span class="pun">...</span><span class="pln">
index</span><span class="pun">.</span><span class="pln">js
</span><span class="typ">Code</span><span class="pln"> style issues found </span><span class="kwd">in</span><span class="pln"> the above file</span><span class="pun">(</span><span class="pln">s</span><span class="pun">).</span><span class="pln"> </span><span class="typ">Forgot</span><span class="pln"> to run </span><span class="typ">Prettier</span><span class="pun">?</span></pre>

<p>
	لذلك هناك بعض أنماط الشيفرات التي يمكن إصلاحها، سيؤدي إضافة الخيار <code>‎--write</code> إلى الأمر prettier إلى إصلاح هذه المشاكل، مما يجعلنا نركز على كتابة شيفرة مفيدة فقط.
</p>

<p>
	شغّل إصدار الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_4333_75" style="">
<span class="pln">prettier </span><span class="pun">--</span><span class="pln">write index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	وستحصل على الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_4333_81" style="">
<span class="typ">Checking</span><span class="pln"> formatting</span><span class="pun">...</span><span class="pln">
index</span><span class="pun">.</span><span class="pln">js
</span><span class="typ">Code</span><span class="pln"> style issues fixed </span><span class="kwd">in</span><span class="pln"> the above file</span><span class="pun">(</span><span class="pln">s</span><span class="pun">).</span></pre>

<p>
	ولكن إذا نظرت إلى ملف جافاسكربت الخاص بك، فستجد أنه منسَّق مثل الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4333_79" style="">
<span class="kwd">const</span><span class="pln"> myObj </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  a</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  b</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> c</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> printMe</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">.</span><span class="pln">b</span><span class="pun">.</span><span class="pln">c</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
printMe</span><span class="pun">(</span><span class="pln">myObj</span><span class="pun">);</span></pre>

<p>
	يمكنك جعل هذا التنسيق جزءًا تلقائيًا من عمليتك بناءً على طريقة سير عملك. الأتمتة هي المكان الذي تتفوق فيه الأدوات، وأفضل أتمتة هي الأتمتة الذي "يحدث على الفور" دون الحاجة إلى إعداد أي شيء.
</p>

<p>
	هناك عدد من الطرق التي يمكن من خلالها تحقيق الأتمتة مع أداة Prettier. هناك بعض الموارد الممتازة عبر الإنترنت للمساعدة، على الرغم من أن ذلك خارج نطاق هذا المقال. يمكنك استدعاء أداة Prettier:
</p>

<ul>
<li>
		قبل تأكيد شيفرتك في مستودع git باستخدام Husky.
	</li>
	<li>
		عندما تضغط على "حفظ" في محرر الشيفرة، سواء أكان محرر VS Code أو Atom أو Sublime Text.
	</li>
	<li>
		بوصفها جزءًا من فحوصات التكامل المستمرة باستخدام أدوات مثل Github Actions.
	</li>
</ul>
<p>
	تفضيلنا الشخصي هو الخيار الثاني -أثناء استخدام محرر VS Code، وتعمل Prettier على تشغيل وتنظيف أي تنسيق تحتاجه في كل عملية حفظ.
</p>

<h2>
	أدوات أخرى
</h2>

<p>
	إليك قائمة مختصرة ممتعة من الأدوات لتجربتها:
</p>

<ul>
<li>
		<a href="https://github.com/sharkdp/bat" rel="external nofollow"><code>bat</code></a>: النسخة الأفضل من الأداة <code>cat</code> (تُستخدم <code>cat</code> لطباعة محتويات الملفات).
	</li>
	<li>
		<a href="https://denilson.sa.nom.br/prettyping/" rel="external nofollow"><code>prettyping</code></a>: نفّذ الأمر <code>ping</code> على سطر الأوامر. تُعَد أداة ping مفيدة للتحقق مما إذا كان الخادم يستجيب.
	</li>
	<li>
		<a href="https://htop.dev/" rel="external nofollow"><code>htop</code></a>: عارض للعمليات، مفيد عندما يكون هناك شيء ما يجعل مروحة وحدة المعالجة المركزية الخاصة بك تتصرف مثل محرك نفاث وتريد تحديد البرنامج المخالف.
	</li>
	<li>
		<a href="https://tldr.sh/#installation" rel="external nofollow"><code>tldr</code></a>: متاح بوصفه أداة لسطر الأوامر.
	</li>
</ul>
<p>
	لاحظ أن بعض الاقتراحات السابقة يمكن أن تحتاج إلى التثبيت باستخدام npm، كما فعلنا مع Prettier.
</p>

<p>
	هذا المقال جزء من سلسلة مقالات بعنوان <a href="https://academy.hsoub.com/search/?tags=%D8%AA%D8%B9%D9%84%D9%85%20%D8%AA%D8%B7%D9%88%D9%8A%D8%B1%20%D8%A7%D9%84%D9%88%D9%8A%D8%A8&amp;sortby=newest&amp;page=1" rel="">تعلم تطوير الويب</a> والتي تشرح كامل عملية تطوير الويب من واجهات أمامية وخلفية بالكامل.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line" rel="external nofollow">Command line crash course</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/" rel="">أدوات مطوري الويب المدمجة في المتصفحات</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">فهم أدوات تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r541/" rel="">كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%B5%D9%81%D8%AD%D8%A9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D9%88%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D8%AD%D8%B1%D9%83-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-r572/" rel="">الفرق بين صفحة الويب وموقع الويب وخادم الويب ومحرك البحث</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1471</guid><pubDate>Tue, 08 Feb 2022 16:00:00 +0000</pubDate></item><item><title>&#x641;&#x647;&#x645; &#x623;&#x62F;&#x648;&#x627;&#x62A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x645;&#x646; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/61fabde75b48d_-------.png.dc1988f86a7b7492920cd9442fe97d33.png" /></p>

<p>
	يمكن أن تكون أدوات تطوير الويب من طرف العميل غير مفهومة بالنسبة لكثير من المطورين، لذلك سنوضّح من خلال سلسلة من المقالات الغرض من بعض أدوات <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تطوير الويب</a> الأكثر شيوعًا من طرف العميل، وسنشرح أيضًا الأدوات التي يمكنك ربطها مع بعضها بعضًا، وكيفية تثبيتها باستخدام مدير الحزم، والتحكم فيها باستخدام سطر الأوامر، وسنقّدّم مثالًا كاملًا عن سلسلة أدوات يوضح كيفية زيادة الإنتاجية، ولكن في البداية وقبل محاولة استخدام هذه الأدوات يجب أن تتعلم أساسيات لغات HTML وCSS وجافاسكربت JavaScript. وفّرنا من خلال موسوعة حسوب مرجعًا لكل من لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a> يمكنك البدء منه.
</p>

<p>
	سنوضح في هذه السلسلة من المقالات المواضيع التالية لفهم أدوات تطوير الويب من طرف العميل:
</p>

<ol>
<li>
		نظرة عامة على أدوات تطوير الويب من طرف العميل: نقدّم في هذا المقال نظرة عامة على أدوات الويب الحديثة، وأنواع الأدوات المتاحة وأين ستصادفها في دورة حياة تطوير تطبيقات الويب، وكيفية العثور على المساعدة باستخدام هذه الأدوات.
	</li>
	<li>
		دورة مكثفة لفهم سطر الأوامر: سيُطلب منك بلا شك تشغيل بعض الأوامر في <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B7%D8%B1%D9%81%D9%8A%D9%91%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-terminal-r18/" rel="">الطرفية Terminal</a> أو في سطر الأوامر خلال عملية التطوير، حيث يقدم هذا المقال مقدمة إلى الطرفية والأوامر الأساسية التي ستحتاج إلى إدخالها، وكيفية ربط الأوامر مع بعضها بعضًا، وكيفية إضافة أدوات واجهة سطر الأوامر Command Line Interface -أو CLI اختصارًا.
	</li>
	<li>
		أساسيات إدارة الحزم: سنلقي نظرة على مدراء الحزم بشيء من التفصيل لفهم كيفية استخدامها في مشاريعك لتثبيت اعتماديات Dependencies أدوات المشروع وتحديثها وغير ذلك.
	</li>
	<li>
		سلسلة أدوات كاملة: سنعمل في المقالين الأخيرين من السلسلة على ترسيخ معرفتك بالأدوات من خلال إرشادك خلال عملية بناء نموذج لسلسلة أدوات عن طريق إعداد بيئة تطوير ووضع أدوات التحويل في مكانها لنشر تطبيقك فعليًا على Netlify. كما سنقدم دراسة حالة مع إعداد بيئة التطوير الخاصة بنا وإعداد أدوات تحويل شيفرتنا.
	</li>
	<li>
		نشر التطبيق: سنأخذ في المقال الأخير من هذه السلسلة مثالًا عن سلسلة الأدوات التي أنشأناها في المقال السابق ونضيفها لنتمكن من نشر التطبيق، إذ سنرفع الشيفرة البرمجية للمشروع على موقع مشاركة الشيفرات جيت هب GitHub، وننشرها باستخدام نيتليفاي Netlify، وسنوضّح كيفية إضافة اختبار بسيط لهذه العملية.
	</li>
</ol>
<p>
	لنبدأ بمقالنا الأول من هذه السلسلة من خلال إلقاء نظرة سريعة على أدوات تطوير الويب من طرف العميل.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بمفاهيم لغات <a href="http://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="http://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="http://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a> الأساسية.
	</li>
	<li>
		<strong>الهدف</strong>: فهم أنواع الأدوات من طرف العميل وكيفية العثور عليها والحصول على المساعدة بشأنها.
	</li>
</ul>
<h2>
	استخدام الأدوات الحديثة
</h2>

<p>
	أصبحت كتابة برمجيات الويب أكثر تعقيدًا بمرور الوقت، وبالرغم من ذلك لا يزال كتابة برمجيات الويب يدويًا بلغة <a href="http://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="http://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="http://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a> أمرًا ممكنًا إلا أنه يوجد الآن مجموعة كبيرة من الأدوات التي يمكن للمطورين استخدامها لتسريع عملية إنشاء موقع ويب أو تطبيق.
</p>

<p>
	توجد بعض الأدوات المستقرة التي أصبحت أسماء مألوفة وشائعة في مجتمع تطوير الويب، بالإضافة إلى الأدوات الجديدة التي نراها تصدر كل يوم لحل مشكلات معينة، فيمكن أن تكتب برنامجًا للمساعدة في عملية التطوير الخاصة بك ولحل مشكلة معينة لا يمكن أن تحلها الأدوات الحالية.
</p>

<p>
	يمكنك استخدام عدد هائل من الأدوات وتضمينها في مشروع واحد، كما يمكنك استخدام أداة واحدة مثل <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">Webpack</a> وإعداد ملف تكوينها من مئات الأسطر، وغالبًا ما ينظر المبرمجين المبتدئين لهذه الشيفرة وكأنها تعويذات تؤدي المهمة بطريقة سحرية ولن يفهمها إلا المهندسون الخبراء. إلا أنه في الواقع غالبية الخبراء يواجهون مشاكلًا في استخدام الأدوات من وقت لآخر، إذ يمكن إضاعة ساعات في محاولة تشغيل مسار أدوات قبل لمس سطر واحد من شيفرة التطبيق. لذلك لا داعي للقلق، فأنت لست وحدك.
</p>

<p>
	سنزودك من خلال هذا المقال بنقطة بداية مفيدة لفهم أساسيات استخدام أدوات الويب، إذ يُفضَّل أن تبدأ على نطاق صغير، ثم تشق طريقك تدريجيًا إلى الاستخدامات أكثر تقدمًا.
</p>

<h2>
	نظام أدوات المطورين الحديث
</h2>

<p>
	يعدّ النظام البيئي Ecosystem الحديث لأدوات المطورين في يومنا الحالي ضخم نسبيًا، لذا يجب من الأفضل تكوين فكرة عامة عن المشاكل الرئيسية التي تحلها هذه الأدوات. إذا انتقلت إلى محرك البحث المفضل لديك وبحثت عن "أدوات مطور الواجهة الأمامية"، فستصل لمجموعة كبيرة من النتائج تتراوح من محرّرات النصوص والمتصفحات وحتى نوع الأقلام التي يمكنك استخدامها لتدوين الملاحظات.
</p>

<p>
	وبالرغم من أن اختيارك لمحرّر الشيفرة هو بالتأكيد جزء من خيارات الأدوات، ولكن في هذه السلسلة من المقالات سنتجاوز ذلك من خلال التركيز على أدوات المطور التي تساعدك على إنتاج شيفرة ويب ذات كفاءة عالية.
</p>

<p>
	يمكنك تصنيف الأدوات من طرف العميل ضمن الفئات الثلاث التالية:
</p>

<ul>
<li>
		<strong>شبكة الأمان Safety Net</strong>: أدوات مفيدة أثناء تطوير شيفرتك.
	</li>
	<li>
		<strong>التحويل Transformation</strong>: الأدوات التي تحول الشيفرة بطريقة ما مثل تحويل لغة وسيطة إلى لغة جافاسكربت التي يمكن أن يفهمها المتصفح.
	</li>
	<li>
		<strong>أدوات ما بعد التطوير Post-development</strong>: الأدوات المفيدة بعد كتابة شيفرتك مثل أدوات الاختبار والنشر.
	</li>
</ul>
<p>
	لنلقِ نظرة على كل واحدة من هذه الفئات بمزيد من التفصيل.
</p>

<h3>
	شبكة الأمان
</h3>

<p>
	هي الأدوات التي تحسّن شيفرتك البرمجية التي تكتبها، ويجب أن تكون هذه الأدوات خاصةً ببيئة التطوير الخاصة بك، بالرغم من ذلك من غير المألوف أن يكون لدى الشركات سياسة أو إعدادات جاهزة للتثبيت لكي تضمن أن يستخدم جميع مطوري هذه الشركات العمليات نفسها.
</p>

<p>
	تتضمن هذه الأدوات أيّ شيء يسهّل عملية التطوير فيما يتعلق بإنشاء شيفرة مستقرة وموثوقة. كما يجب أن تساعدك أدوات شبكة الأمان في منع حدوث الأخطاء أو تصحيحها تلقائيًا دون الحاجة إلى إنشاء شيفرة من نقطة الصفر في كل مرة.
</p>

<p>
	سنوضّح فيما يلي بعض أنواع أدوات شبكة الأمان الشائعة التي يستخدمها المطورون.
</p>

<h4>
	منقحات الصياغة Linters
</h4>

<p>
	منقحات الصياغة هي أدوات تتحقق من شيفرتك وتخبرك بوجود أخطاء وبأنواعها وسطور الشيفرة التي توجد فيها. يمكن ضبط منقحات الصياغة للإبلاغ عن الأخطاء، وللإبلاغ عن أيّ انتهاكات لدليل النمط المحدَّد الذي يستخدمه فريقك مثل الشيفرة التي تستخدم عددًا خاطئًا من المسافات البادئة، أو استخدام صياغة قالب Template Literals بدلًا من صياغة سلسلة نصية عادية String Literals.
</p>

<p>
	يعدّ <a href="https://eslint.org/" rel="external nofollow">Eslint</a> منقّح أخطاء معياري خاص بلغة جافاسكربت ويمكن ضبط هذه الأداة لاكتشاف أخطاء الصياغة المحتملة وتشجيعك لاستخدام أفضل الممارسات ضمن شيفرتك البرمجية. شاركت بعض الشركات والمشاريع <a href="https://www.npmjs.com/search?q=keywords:eslintconfig" rel="external nofollow">إعدادات Eslint الخاصة بها</a>، ويمكنك العثور على أدوات اكتشاف أخطاء للغات أخرى مثل <a href="http://csslint.net/" rel="external nofollow">Csslint</a>.
</p>

<p>
	كما يمكنك استخدام <a href="https://webhint.io/" rel="external nofollow">Webhint</a> وهو منقح أخطاء مفتوح المصدر يمكن ضبطه للويب، ويعرض أفضل الممارسات لاستخدامها بما في ذلك أساليب الوصول والأداء والتوافق مع المتصفحات باستخدام <a href="https://github.com/mdn/browser-compat-data" rel="external nofollow">بيانات توافق متصفح MDN</a> والأمان واختبار تطبيقات الويب ذات الصفحة الواحدة PWA وغير ذلك. كما يتوفر <a href="https://webhint.io/docs/user-guide/" rel="external nofollow">كأداة سطر أوامر Node.js</a> و<a href="https://marketplace.visualstudio.com/items?itemName=webhint.vscode-webhint" rel="external nofollow">كامتداد VS Code</a>.
</p>

<h4>
	التحكم بالشيفرة البرمجية
</h4>

<p>
	يُعرَف أيضًا باسم أنظمة التحكم بالإصدارات Version Control Systems أو <abbr title="Version Control Systems | أنظمة التحكم بالنُّسخ">VCS</abbr> اختصارًا، ويُعَد التحكم بالشيفرة البرمجية ضروريًا لدعم العمل الفردي أو ضمن فريق. يتضمن نظام <abbr title="Version Control Systems | أنظمة التحكم بالنُّسخ">VCS</abbr> إصدارًا محليًا من الشيفرة التي تجري تغييرات عليها، ثم ترفع التغييرات إلى إصدار رئيسي من الشيفرة داخل مستودع بعيد مُخزَّن على خادم في مكان ما. هناك عادةً طريقة للتحكم في التعديلات التي تُطبَّق على النسخة الرئيسية من الشيفرة وتنسيقها ومتى تطبَّق، وبالتالي لا يكتب فريق المطورين فوق عمل بعضهم بعضًا.
</p>

<p>
	يعد نظام جيت هاب <a href="https://academy.hsoub.com/programming/workflow/git/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-git-r658/" rel="">Git</a> من أشهر نظم التحكم بالشيفرة البرمجية الذي يستخدمه معظم المطورين حاليًا، إذ يمكن الوصول إليه عبر سطر الأوامر أو عبر واجهات سهلة الاستخدام. كما يمكنك رفع نسخة من المشروع إلى الخادم الخاص بك باستخدام شيفرتك في مستودع جيت، أو يمكنك استخدام موقع ويب مستضاف للتحكم بالشيفرة المصدرية مثل <a href="https://github.com/" rel="external nofollow">جيت هب GitHub</a> أو <a href="https://about.gitlab.com" rel="external nofollow">جيت لاب GitLab</a> أو <a href="https://academy.hsoub.com/apps/productivity/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-bitbucket-r456/" rel="">بيت باكيت BitBucket</a>، ولكن سنستخدم GitHub في مثالنا.
</p>

<h4>
	مُنسِّقات الشيفرة Code Formatters
</h4>

<p>
	ترتبط مُنسِّقات الشيفرة إلى حد ما بمنقّحات الصياغة، باستثناء أنه بدلًا من الإشارة إلى الأخطاء في شيفرتك، فإنها عادةً ما تميل إلى التأكد من تنسيق شيفرتك الصحيح وفقًا لقواعد النمط الخاصة بك، وتعمل تلقائيًا على إصلاح الأخطاء التي تعثر عليها.
</p>

<p>
	أحد الأمثلة الشائعة لمنسقات الشيفرة البرمجية هو <a href="https://prettier.io/" rel="external nofollow">Prettier</a>، والذي سنستخدمه لاحقًا في مثالنا.
</p>

<h4>
	الحزم Bundlers أو Packagers
</h4>

<p>
	هي الأدوات التي تجعل شيفرتك جاهزةً لعملية الإنتاج عن طريق تقنية هز الشجرة Tree-Shaking مثلًا للتأكّد من إدراج أجزاء مكتبات الشيفرة التي تستخدمها فعليًا فقط في شيفرة الإنتاج النهائي، أو التصغير Minifying لإزالة كل مسافة فارغة في شيفرة الإنتاج، مما يصغّرها قدر الإمكان قبل رفعها إلى الخادم.
</p>

<p>
	تعد أداة <a href="https://parceljs.org/" rel="external nofollow">Parcel</a> من الأدوات الذكية التي يمكنها تطبيق المهام المذكورة السابقة، كما أنها تساعد أيضًا في حزم الملفات أو الملحقات assets مثل HTML وCSS وملفات الصور ضمن حزم ملائمة يمكنك نشرها لاحقًا، وتضيف اعتماديات تلقائيًا كلما حاولت استخدامها. كما يمكن لهذه الأداة التعامل مع بعض مهام تحويل الشيفرة نيابة عنك.
</p>

<p>
	يذكر أن أداة <a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A3%D8%AF%D8%A7%D8%A9-webpack-r1235/" rel="">ويب باك Webpack</a> هي أشهر أداة حزم تطبّق مهامًا مماثلة.
</p>

<h3>
	التحويل Transformation
</h3>

<p>
	تتيح هذه المرحلة من دورة حياة تطبيق الويب كتابة شيفرة برمجية إما في شيفرة مستقبلية مثل تحويل الشيفرة البرمجية لأحدث ميزات لغة CSS أو جافاسكربت التي يمكن ألّا تدعمها بعض المتصفحات حتى الآن، أو إنشاء شيفرة برمجية مكافئة لشيفرة برمجية مكتوبة بلغة معينة مثل لغة <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a>، لتتوافق الشيفرة التي أنشأتها الأداة المطلوبة مع المتصفح لاستخدامها في عملية الإنتاج.
</p>

<p>
	يُنظَر إلى تطوير الويب على أنه مؤلَّف من ثلاث لغات هي HTML وCSS وجافاسكربت، وهناك أدوات تحويل لجميع هذه اللغات. يقدّم التحويل فائدتين رئيسيتين هما:
</p>

<ol>
<li>
		القدرة على كتابة شيفرة برمجية باستخدام أحدث ميزات اللغة وتحويلها إلى شيفرة تعمل على جميع الأجهزة، فيمكن أن ترغب مثلًا في كتابة شيفرة بلغة جافاسكربت باستخدام ميزات لغة جديدة متطورة، ولكن لا يزال لديك شيفرة الإنتاج النهائي التي تعمل على المتصفحات القديمة التي لا تدعم هذه الميزات. تشمل الأمثلة التالية:
		<ul>
<li>
				<a href="https://babeljs.io/" rel="external nofollow">Babel</a>: مصرّف جافاسكربت JavaScript الذي يسمح للمطورين بكتابة شيفرة باستخدام أحدث إصدارات جافاسكربت، والتي يأخذها Babel ويحوّلها إلى إصدار جافاسكربت قديم يمكن لمزيد من المتصفحات فهمه. كما يمكن للمطورين كتابة ونشر إضافات Babel.
			</li>
			<li>
				<a href="https://postcss.org/" rel="external nofollow">PostCSS</a>: تطبّق هذه الأداة الشيء نفسه الذي يطبّقه Babel، ولكن مع ميزات CSS المتطورة. إذا لم تكن هناك طريقة مكافئة لتطبيق شيء ما باستخدام ميزات CSS القديمة، فسيثبّت PostCSS تعويض نقص دعم المتصفحات Polyfill بلغة جافاسكربت لمحاكاة تأثير CSS الذي تريده.
			</li>
		</ul>
</li>
	<li>
		خيار كتابة الشيفرة بلغة مختلفة تمامًا وتحويلها إلى لغة متوافقة مع الويب مثل:
		<ul>
<li>
				<a href="https://wiki.hsoub.com/Sass" rel="external">Sass / SCSS</a>: يتيح لك هذا الامتداد من لغة CSS استخدام المتغيرات والقواعد المتداخلة والمزج والدوال والعديد من الميزات الأخرى، إذ يُعَد بعضها متاحًا في لغة CSS الأصلية مثل المتغيرات، وبعضها ليس كذلك.
			</li>
			<li>
				<a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a>: هي مجموعة شاملة من لغة جافاسكربت التي تقدم مجموعة من الميزات الإضافية. يحوّل مصرّف TypeScript شيفرة TypeScript إلى جافاسكربت عند البناء بهدف الإنتاج.
			</li>
			<li>
				توفّر أطر العمل مثل <a href="https://wiki.hsoub.com/React" rel="external">React</a> و<a href="https://emberjs.com/" rel="external nofollow">Ember</a> و<a href="https://vuejs.org/" rel="external nofollow">Vue</a> الكثير من الوظائف مجانًا وتسمح لك باستخدامها عبر صيغة مخصَّصة مبنية على لغة جافاسكربت الصرفة Vanilla JavaScript. تعمل شيفرة جافاسكربت الخاصة بإطار العمل في الخلفية لتفسير هذه البنية المخصَّصة وتقديمها بوصفها تطبيق ويب نهائي.
			</li>
		</ul>
</li>
</ol>
<h3>
	أدوات ما بعد التطوير Post Development
</h3>

<p>
	تضمن أدوات ما بعد التطوير أن يصل برنامجك إلى الويب ويستمر في عمله، إذ تتضمن مرحلة ما بعد التطوير عمليات النشر وأطر عمل الاختبار وأدوات التدقيق وغير ذلك.
</p>

<p>
	تُعَد هذه المرحلة بأنها المرحلة التي تحتاج أقل قدر من التفاعل النشط بحيث تُشغَّل تلقائيًا بمجرد تهيئتها، وتخبرك بحدوث خطأ ما.
</p>

<h4>
	أدوات الاختبار Testing Tools
</h4>

<p>
	تأخذ هذه الأدوات شكل أداة تختبر تلقائيًا شيفرت البرمجية للتأكد من صحتها قبل المضي قدمًا مثل رفع تعديلات إلى مستودع جيب هب GitHub Repo. يمكن أن يشمل ذلك الكشف عن الأخطاء Linting، ويشمل إجراءات أكثر تعقيدًا مثل اختبارات الوحدة إذ تشغّل جزءًا من شيفرتك، مع التأكد من أنها تتصرف كما ينبغي.
</p>

<ul>
<li>
		تشمل أطر عمل اختبارات الكتابة <a href="https://jestjs.io/" rel="external nofollow">Jest</a> و<a href="https://mochajs.org/" rel="external nofollow">Mocha</a> و<a href="https://jasmine.github.io/" rel="external nofollow">Jasmine</a>.
	</li>
	<li>
		تتضمن أنظمة التشغيل والاختبارات الآلية <a href="https://travis-ci.org" rel="external nofollow">Travis CI</a> و<a href="https://travis-ci.org" rel="external nofollow">Jenkins</a> و<a href="https://circleci.com/" rel="external nofollow">Circle CI</a> وغيرها.
	</li>
</ul>
<h4>
	أدوات النشر Deployment Tools
</h4>

<p>
	تسمح أنظمة النشر بنشر موقع الويب الخاص بك، وهي متاحة لكل من المواقع الثابتة والديناميكية، وتميل للعمل جنبًا إلى جنب مع أنظمة الاختبار، إذ ستنتظرك سلسلة الأدوات إلى أن ترفع التغييرات إلى المستودع البعيد، وتجري بعض الاختبارات لمعرفة ما إذا كانت التغييرات مناسبة، وإذا نجحت الاختبارات، فستنشر تطبيقك تلقائيًا على موقع إنتاج.
</p>

<p>
	تعد <a href="https://www.netlify.com/" rel="external nofollow">Netlify</a> واحدة من أكثر أدوات النشر شيوعًا في الوقت الحالي، ولكن هناك أدوات أخرى مثل <a href="https://vercel.com/" rel="external nofollow">Vercel</a> و<a href="https://academy.hsoub.com/programming/workflow/git/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-github-pages-r724/" rel="">Github Pages</a>.
</p>

<h4>
	أدوات ما بعد التطوير الأخرى
</h4>

<p>
	هناك عدد من أنواع الأدوات الأخرى المتاحة للاستخدام في مرحلة ما بعد التطوير، بما في ذلك <a href="https://codeclimate.com/" rel="external nofollow">Code Climate</a> لجمع مقاييس جودة الشيفرة، وامتداد متصفح Webhint لإجراء تحليل وقت التشغيل للتوافق مع المتصفحات وعمليات التحقق الأخرى، و<a href="https://probot.github.io/" rel="external nofollow">Github bots</a> لتوفير المزيد من ميزات GitHub القوية، و<a href="https://updown.io/" rel="external nofollow">Updown</a> لتوفير مراقبة وقت تشغيل التطبيق وغير ذلك الكثير.
</p>

<h3>
	أنواع الأدوات
</h3>

<p>
	تُطبَّق أنواع الأدوات المختلفة في دورة حياة التطوير وفق ترتيب معين، ولكن كن مطمئنًا أنك لست مضطرًا إلى أن يكون لديك كل هذه الأدوات لإصدار موقع ويب، فلن لا تحتاج لأيٍّ منها. لكن سيؤدي تضمين بعض هذه الأدوات في عملياتك إلى تحسين تجربة التطوير، ويُحتمَل أن يؤدي إلى تحسين جودة شيفرتك الإجمالية.
</p>

<p>
	يستغرق استقرار أدوات المطور الجديدة بعض الوقت حسب تعقيدها. تشتهر إحدى أشهر الأدوات وهي Webpack بكونها معقدة للغاية للتعامل معها، ولكن كان هناك ضغط كبير لتبسيط الاستخدام في أحدث إصدار رئيسي، لذا قُلٍّل الإعداد المطلوب إلى الحد الأدنى.
</p>

<p>
	ليس هناك حل سحري يضمن النجاح باستخدام الأدوات، ولكن ستجد تدفقات عمل تناسبك أو تناسب فريقك ومشاريعك مع زيادة خبرتك، ويجب أن تكون سلسلة الأدوات شيئًا يمكنك نسيانه وأن تركّز على العمل فقط، بمجرد تسوية جميع مكامن الخلل في العملية.
</p>

<h2>
	كيفية اختيار أداة معينة والحصول عليها
</h2>

<p>
	تميل معظم الأدوات إلى كتابتها وإصدارها بصورة منفصلة، لذلك لا تتوفر أبدًا في المكان أو التنسيق نفسه، على الرغم من وجود مساعدة شبه مؤكدة، فيمكن أن يكون العثور على مساعدة في استخدام أداة أو حتى اختيار الأداة التي تريد استخدامها أمرًا صعبًا. المعرفة حول أفضل الأدوات لاستخدامها هي معرفة مجتمعية إلى حد ما، مما يعني أنه إن لم تكن بالفعل في مجتمع الويب، فستكون معرفة الأدوات التي تريدها بالضبط أمرًا صعبًا، وهذا هو أحد الأسباب التي دفعتنا إلى كتابة هذه السلسلة من المقالات، ونأمل أن نقدم تلك الخطوة الأولى التي يصعب إيجادها بطريقة أخرى.
</p>

<p>
	ستحتاج على الأرجح إلى مجموعة الأشياء التالية:
</p>

<ul>
<li>
		نجح المدرسون أو الموجهون أو الزملاء الطلاب ذوو الخبرة أو الزملاء الذين لديهم بعض الخبرة في حل هذه المشكلات من قبل، ويمكنهم تقديم المشورة.
	</li>
	<li>
		مكان محدد مفيد للبحث، إذ تكون عمليات البحث العامة على الويب عن أدوات مطور الواجهة الأمامية عديمة الفائدة إلا إن عرفتَ اسم الأداة التي تبحث عنها.
		<ul>
<li>
				إذا استخدمتَ <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">مدير الحزم NPM</a> لإدارة اعتمادياتك على سبيل المثال، فيُفضَّل الانتقال إلى <a href="https://www.npmjs.com/" rel="external nofollow">صفحة npm الرئيسية</a> والبحث عن نوع الأداة التي تبحث عنها. حاول مثلًا البحث عن "التاريخ date" إن أردتَ أداة تنسيق التاريخ، أو "المُنسِّق formatter" إذا كنت تبحث عن مُنسق شيفرة عام. انتبه إلى درجات الشعبية والجودة والصيانة، وآخر تحديث للحزمة. انقر على صفحات الأداة لمعرفة عدد تنزيلات الحزمة الشهرية، واحتوائها على توثيق جيد يمكنك استخدامه للتأكد من أنها تطبّق ما تريده، وبالتالي تُعَد مكتبة date-fns أداة تنسيق تاريخ جيدة لاستخدامها.
			</li>
			<li>
				إذا أردت البحث عن إضافة Plugin لدمج وظائف الأدوات في محرّر الشيفرة، فألقِ نظرة على صفحة الإضافات / الامتدادات لمحرر الشيفرة، وراجع حزم Atom وامتدادات VSCode على سبيل المثال. ألقِ نظرة على الامتدادات المميزة في الصفحة الأولى، وحاول مرة أخرى البحث عن نوع الامتداد الذي تريده (أو اسم الأداة مثل البحث عن "eslint" في صفحة امتدادات <a href="https://academy.hsoub.com/questions/11533-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-%D8%A7%D9%84%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D9%81%D9%8A-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D8%A3%D9%83%D9%88%D8%A7%D8%AF-visual-studio-code/" rel="">VSCode</a>). إذا حصلتَ على نتائج، فألقِ نظرة على معلومات عدد النجوم أو التنزيلات التي يحتويها الامتداد، إذ يُعَد ذلك مؤشرًا على جودته.
			</li>
		</ul>
</li>
	<li>
		المنتديات المتعلقة بالتنمية لطرح أسئلة حول الأدوات التي يجب استخدامها مثل قسم <a href="https://academy.hsoub.com/questions/c3-programming/" rel="">الأسئلة والأجوبة البرمجية</a> في أكاديمية حسوب.
	</li>
</ul>
<p>
	إذا اخترت أداة لاستخدامها، فيجب أن يكون طريقة فهم الأداة هو الصفحة الرئيسية لمشروع الأداة والذي يكون غالبًا موقع ويب كامل أو يكون مستندًا تمهيديًا واحدًا في مستودع الشيفرة.
</p>

<p>
	يمكن أن ترغب في العثور على بعض البرامج التعليمية المخصصة لبدء استخدام أنواع معينة من الأدوات، لذا تعد المقالات البرمجية ومقالات DevOps في أكاديمية حسوب أماكن الانطلاق الرائعة للبحث.
</p>

<p>
	يُحتمَل أن تمر عبر العديد من الأدوات المختلفة أثناء البحث عن الأدوات المناسبة لك، وأن تجربها لمعرفة ما إذا كانت منطقية ومدعومة جيدًا وتطبّق ما تريده منها. يُعَد كل ذلك مناسبًا للتعلم، وسيصبح الطريق أسلس كلما اكتسبت مزيدًا من الخبرة.
</p>

<h2>
	الخلاصة
</h2>

<p>
	قدّمنا من خلال هذا المقال مقدمة بسيطة عن أدوات الويب من طرف العميل، وسنقدّم في المقال القادم دورة مكثفة عن سطر الأوامر، إذ تُستدعَى كثير من الأدوات منه، وسنلقي نظرة على ما يمكن أن يفعله سطر الأوامر ثم نحاول تثبيت الأداة الأولى ونستخدمها.
</p>

<p>
	هذا المقال جزء من سلسلة مقالات بعنوان <a href="https://academy.hsoub.com/search/?tags=%D8%AA%D8%B9%D9%84%D9%85%20%D8%AA%D8%B7%D9%88%D9%8A%D8%B1%20%D8%A7%D9%84%D9%88%D9%8A%D8%A8&amp;sortby=newest&amp;page=1" rel="">تعلم تطوير الويب</a> والتي تشرح كامل <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">عملية تطوير الويب</a> من واجهات أمامية وخلفية بالكامل.
</p>

<p>
	ترجمة -وبتصرُّف- للمقالين <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools" rel="external nofollow">Understanding client-side web development tools</a> و<a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview" rel="external nofollow">Client-side tooling overview</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-devtools-r277/" rel="">مدخل إلى أدوات التطوير في متصفح الويب DevTools</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r541/" rel="">كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/freelance/jobs/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D9%85%D8%B5%D9%85%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D8%B7%D9%88%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%85%D8%B9%D8%B1%D9%81%D8%A9-%D8%A7%D9%84%D8%A3%D9%86%D8%B3%D8%A8-%D8%A8%D9%8A%D9%86%D9%87%D9%85%D8%A7-r408/" rel="">الفرق بين مصمم الويب ومطور الويب وكيفية معرفة الأنسب بينهما</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A7%D9%84%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-r1436/" rel="">الأدوات المستخدمة في بناء مواقع ويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1462</guid><pubDate>Wed, 02 Feb 2022 16:00:00 +0000</pubDate></item><item><title>&#x645;&#x62D;&#x631;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x639;&#x645;&#x644;&#x629; &#x641;&#x64A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x645;&#x648;&#x627;&#x642;&#x639; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/workflow/%D9%85%D8%AD%D8%B1%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%B9%D9%85%D9%84%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1438/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61e03d85853c6_---------.png.a26c5713d1086595874f0cb7f7f9dca9.png" /></p>

<p>
	سنلقي الضوء في هذا المقال على بعض الأشياء التي ينبغي التفكير بها عند تثبيت محرر نصوص لتطوير مواقع الويب. ننصحك قبل الشروع بقراءة المقال أن تطلع على مقال <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%8A%D8%A8%D8%9F-r1436/" rel="">ما هي الأدوات المستخدمة في بناء مواقع ويب؟</a>
</p>

<h2>
	مقدمة عن المحررات البرمجية لتطوير الويب
</h2>

<p>
	تتكون مواقع الويب في معظمها من ملفات نصية، ولكي تختبر تجربة مريحة وممتعة في رحلة تطوير موقع ويب، لا بدّ من اختيار محرر النصوص بحكمة!
</p>

<p>
	ستجد عددًا كبيرًا من محررات النصوص لكونها أمرًا أساسيًا في <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">علوم الحاسوب</a> (وبالطبع تطوير الويب جزء من علوم الحاسوب). عليك -إن أردنا التكلم بمثالية- أن تجرب أكبر عدد ممكن من المحررات ثم تختار ما تشعر أنه المناسب لاحتياجاتك، لكننا سنحاول إرشادك لتبدأ بطريقة صحيحة.
</p>

<p>
	إليك بعض التساؤلات الأساسية التي ينبغي أخذها بعين الاعتبار:
</p>

<ul>
<li>
		أي نظام تشغيل سأعمل عليه؟
	</li>
	<li>
		ما هي التقنيات التي أريد أن أتعامل معها؟
	</li>
	<li>
		ما الميزات الأساسية التي أتوقع وجودها في محرر النصوص؟
	</li>
	<li>
		هل أريد إضافة ميزات إضافية إلى محرر النصوص؟
	</li>
	<li>
		هل أحتاج إلى دعم أو مساعدة أثناء استخدام محرر النصوص؟
	</li>
	<li>
		هل يهمني مظهر محرر النصوص وشعوري عند العمل عليه؟
	</li>
</ul>
<p>
	لاحظ أننا لم نذكر التكلفة وهي أمر هام بكل وضوح، لكن تكلفة المنتج لا ترتبط بالضرورة بجودته أو إمكاناته. ويمكن جدًا تجد محرر نصوص مجاني ومناسب.
</p>

<p>
	إليك قائمة ببعض المحررات الأكثر شعبية:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th style="text-align:right">
				المحرر
			</th>
			<th style="text-align:right">
				رخصة الاستخدام
			</th>
			<th style="text-align:right">
				السعر
			</th>
			<th style="text-align:right">
				نظام التشغيل
			</th>
			<th style="text-align:right">
				آلية دعم المنتج
			</th>
			<th style="text-align:right">
				توثيقات المنتج
			</th>
			<th style="text-align:right">
				قابل للتوسّع
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:right">
				<a href="https://atom.io/" rel="external nofollow">Atom</a>
			</td>
			<td style="text-align:right">
				MIT/BSD
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://discuss.atom.io/categories" rel="external nofollow">منتديات</a>
			</td>
			<td style="text-align:right">
				<a href="https://atom.io/docs/latest/" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				<a href="https://atom.io/packages" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="http://bluefish.openoffice.nl/" rel="external nofollow">Bluefish</a>
			</td>
			<td style="text-align:right">
				GPL 3
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://bfwiki.tellefsen.net/index.php/Mailinglists" rel="external nofollow">قوائم بريدية</a>، <a href="http%20s://bfwiki.tellefsen.net/index.php/Main_Page" rel="">مستندات تعاونية wikis</a>
			</td>
			<td style="text-align:right">
				<a href="http://bluefish.openoffice.nl/manual/" rel="external nofollow">دليل استخدام على ويب</a>
			</td>
			<td style="text-align:right">
				نعم
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="http://brackets.io/" rel="external nofollow">Brackets</a>
			</td>
			<td style="text-align:right">
				MIT/BSD
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://groups.google.com/forum/#!forum/brackets-dev" rel="external nofollow">منتديات</a>، <a href="https://webchat.freenode.net/?channels=brackets" rel="external nofollow">محادثة عبر الإنترنت IRC</a>
			</td>
			<td style="text-align:right">
				<a href="https://github.com/adobe/brackets/wiki" rel="external nofollow">مستندات تعاونية على غت-هاب GitHub Wiki</a>
			</td>
			<td style="text-align:right">
				<a href="https://ingorichter.github.io/BracketsExtensionTweetBot/" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://panic.com/coda/" rel="external nofollow">Coda</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				$99
			</td>
			<td style="text-align:right">
				ماك
			</td>
			<td style="text-align:right">
				<a href="https://twitter.com/panic" rel="external nofollow">تويتر</a>، <a href="https://panic.com/qa" rel="external nofollow"> منتديات</a>، <a href="mailto:coda@panic.com" rel="">بريد إلكتروني</a>
			</td>
			<td style="text-align:right">
				<a href="https://panic.com/coda/#book" rel="external nofollow">كتاب إلكتروني</a>
			</td>
			<td style="text-align:right">
				<a href="https://panic.com/coda/plugins.php" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="http://www.codelobster.com/" rel="external nofollow">CodeLobster</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="http://www.codelobster.com/forum/index.php" rel="external nofollow">منتديات</a>، <a href="mailto:support@codelobster.com" rel="">بريد إلكتروني</a>)
			</td>
			<td style="text-align:right">
				<a href="https://www.codelobsteride.com/help/" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				نعم
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.gnu.org/software/emacs/" rel="external nofollow">Emacs</a>
			</td>
			<td style="text-align:right">
				GPL 3
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://www.gnu.org/software/emacs/manual/efaq.html" rel="external nofollow">الأسئلة الأكثر شيوعًا</a>، <a href="https://mail.gnu.org/mailman/listinfo/help-gnu-emacs" rel="external nofollow">قوائم بريدية</a>، <a href="news://gnu.emacs.help" rel="external nofollow">مجموعات إخبارية</a>
			</td>
			<td style="text-align:right">
				<a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/index.html" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				نعم
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.macrabbit.com/espresso/" rel="external nofollow">Espresso</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				$75
			</td>
			<td style="text-align:right">
				ماك
			</td>
			<td style="text-align:right">
				<a href="https://www.macrabbit.com/support/" rel="external nofollow">الأسئلة الأكثرشيوعًا</a>، <a href="mailto:support@macrabbit.com" rel="">البريد الإلكتروني</a>
			</td>
			<td style="text-align:right">
				لا توجد توثيقات موجهة إلى المستخدم النهائي، فقط توثيقات للإضافات <a href="http://wiki.macrabbit.com/" rel="external nofollow">plug-in doc</a>
			</td>
			<td style="text-align:right">
				نعم
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://wiki.gnome.org/Apps/Gedit" rel="external nofollow">Gedit</a>
			</td>
			<td style="text-align:right">
				GPL
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://mail.gnome.org/mailman/listinfo/gedit-list" rel="external nofollow">قوائم بريد إلكتروني</a>، <a href="irc://irc.gnome.org/%23gedit" rel="external nofollow">محادثات عبر الإنترنت</a>
			</td>
			<td style="text-align:right">
				<a href="https://help.gnome.org/users/gedit/stable/" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				<a href="https://wiki.gnome.org/Apps/Gedit/PluginsLists" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://kate-editor.org/" rel="external nofollow">Kate</a>
			</td>
			<td style="text-align:right">
				LGPL, GPL
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="mailto:kwrite-devel@kde.org" rel="">قوائم بريد إلكتروني</a>، <a href="irc://irc.kde.org/kate" rel="external nofollow">IRC</a>
			</td>
			<td style="text-align:right">
				<a href="https://docs.kde.org/stable5/en/applications/kate/index.html" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				نعم
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://komodoide.com/komodo-edit/" rel="external nofollow">Komodo Edit</a>
			</td>
			<td style="text-align:right">
				MPL
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="http://forum.komodoide.com/" rel="external nofollow">منتديات</a>
			</td>
			<td style="text-align:right">
				<a href="http://docs.activestate.com/komodo/8.5/" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				<a href="https://komodoide.com/resources/addons/" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.notepad-plus-plus.org/" rel="external nofollow">Notepad++</a>
			</td>
			<td style="text-align:right">
				GPL
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز
			</td>
			<td style="text-align:right">
				<a href="https://sourceforge.net/p/notepad-plus/discussion/" rel="external nofollow">منتديات</a>
			</td>
			<td style="text-align:right">
				<a href="https://npp-wiki.tuxfamily.org/index.php?title=Main_Page" rel="external nofollow">Wiki</a>
			</td>
			<td style="text-align:right">
				<a href="https://npp-wiki.tuxfamily.org/index.php?title=Plugin_Central" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.pspad.com/" rel="external nofollow">PSPad</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز
			</td>
			<td style="text-align:right">
				<a href="http://gogogadgetscott.info/pspad/dotazy.htm" rel="external nofollow">FAQ</a>، <a href="https://forum.pspad.com/" rel="external nofollow">منتديات</a>
			</td>
			<td style="text-align:right">
				<a href="http://gogogadgetscott.info/pspad/" rel="external nofollow">مساعدة عبر الإنترنت</a>
			</td>
			<td style="text-align:right">
				<a href="https://www.pspad.com/en/pspad-extensions.php" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.sublimetext.com/" rel="external nofollow">Sublime Text</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				$70
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://www.sublimetext.com/forum/viewforum.php?f=3" rel="external nofollow">منتديات</a>
			</td>
			<td style="text-align:right">
				<a href="https://www.sublimetext.com/docs/3/" rel="external nofollow">رسمية</a>،<a href="http://docs.sublimetext.info/en/latest/index.html" rel="external nofollow">غير رسمية</a>
			</td>
			<td style="text-align:right">
				<a href="https://sublime.wbond.net/" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://macromates.com/" rel="external nofollow">TextMate</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				$50
			</td>
			<td style="text-align:right">
				ماك
			</td>
			<td style="text-align:right">
				<a href="https://twitter.com/macromates" rel="external nofollow">تويتر</a>، <a href="https://webchat.freenode.net/?channels=textmate" rel="external nofollow">محادثة عبر الإنترنت</a>، <a href="https://lists.macromates.com/listinfo/textmate" rel="external nofollow">قوائم بريد إلكتروني</a>، <a href="mailto:tm-support@macromates.com" rel="">بريد إلكتروني</a>
			</td>
			<td style="text-align:right">
				<a href="https://manual.macromates.com/en/" rel="external nofollow">دليل استخدام على الإنترنت</a>، <a href="https://wiki.macromates.com/Main/HomePage" rel="external nofollow">مستندات تعاونية</a>
			</td>
			<td style="text-align:right">
				<a href="https://wiki.macromates.com/Main/Plugins" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.barebones.com/products/textwrangler/" rel="external nofollow">TextWrangler</a>
			</td>
			<td style="text-align:right">
				مغلق المصدر
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ماك
			</td>
			<td style="text-align:right">
				<a href="https://www.barebones.com/support/textwrangler/faqs.html" rel="external nofollow">الأسئلة الأكثر شيوعًا</a>، <a href="https://groups.google.com/forum/#!forum/textwrangler" rel="external nofollow">منتديات</a>
			</td>
			<td style="text-align:right">
				<a href="http://ash.barebones.com/TextWrangler_User_Manual.pdf" rel="external nofollow">دليل استخدام PDF</a>
			</td>
			<td style="text-align:right">
				لا
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://www.vim.org/" rel="external nofollow">Vim</a>
			</td>
			<td style="text-align:right">
				<a href="http://vimdoc.sourceforge.net/htmldoc/uganda.html#license" rel="external nofollow">رخصة مصدر مفتوح خاصة</a>
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://www.vim.org/maillist.php#vim" rel="external nofollow">قوائم بريد إلكتروني</a>
			</td>
			<td style="text-align:right">
				<a href="http://vimdoc.sourceforge.net/" rel="external nofollow">دليل استخدام على الإنترنت</a>
			</td>
			<td style="text-align:right">
				<a href="https://www.vim.org/scripts/script_search_results.php?order_by=creation_date&amp;direction=descending" rel="external nofollow">نعم</a>
			</td>
		</tr>
<tr>
<td style="text-align:right">
				<a href="https://code.visualstudio.com/download" rel="external nofollow">Visual Studio Code</a>
			</td>
			<td style="text-align:right">
				<a href="https://github.com/microsoft/vscode" rel="external nofollow">مفتوح المصدر</a> خاضع لرخصة MIT/ (رخصة خاصة بالمنتج)
			</td>
			<td style="text-align:right">
				مجاني
			</td>
			<td style="text-align:right">
				ويندوز، ماك، لينوكس
			</td>
			<td style="text-align:right">
				<a href="https://code.visualstudio.com/docs/supporting/faq" rel="external nofollow">الأسئلة الأكثر شيوعًا</a>
			</td>
			<td style="text-align:right">
				<a href="https://code.visualstudio.com/docs" rel="external nofollow">توثيق</a>
			</td>
			<td style="text-align:right">
				<a href="https://marketplace.visualstudio.com/VSCode" rel="external nofollow">نعم</a>
			</td>
		</tr>
</tbody>
</table>
<p>
	سنلقي نظرة في الفقرات التالية على بعض النقاط التي يجب أن نأخذها بعين الاعتبار عند اختيار محرر نصي.
</p>

<h2>
	معايير الاختبار
</h2>

<p>
	ولكن ما الذي عليك التفكير به عند اختيار محرر نصي؟ هيا بنا لنخوض أكثر في التفاصيل.
</p>

<h3>
	نظام التشغيل الذي تعمل عليه
</h3>

<p>
	بالرغم من أن الموضوع ما هو إلا تفضيلات شخصية، ولكن تذكر أن بعض المحررات متاحة فقط لأنظمة تشغيل محددة. وإن أردت التنقل من نظام تشغيل إلى آخر، سيحد ذلك من خياراتك. تسهل عليك محررات النصوص التي تعمل عبر المنصات المختلفة Cross-Platform في هذه الحالة أمر الانتقال من نظام إلى آخر.
</p>

<p>
	تأكد في البداية من نظام التشغيل الذي تستخدمه ثم اعثر على محرر نصوص يدعمه. تحدد محررات النصوص على مواقعها الإلكترونية الأنظمة التي تدعمها، يمكن ألّا تدعم بعضها سوى إصداراتٍ معينة من أنظمة التشغيل (ويندوز 7 وما بعد وليس فيستا مثلًا). إن كنت تعمل على نظام التشغيل أوبونتو فمن الأفضل البحث ضمن مركز برامج أوبونتو Ubuntu Software Center. إنّ عالم أنظمة التشغيل لينوكس/ يونكس واسع ومتنوع ويمكن ألّا تتوافق حزم البرمجيات بين توزيعة وأخرى. وبالتالي إن وقع اختيارك على محرر نصوص معين لا تدعمه توزيعة <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس</a> التي تعمل عليها، عليك ترجمة الشيفرة المصدرية للبرنامج بنفسك لتناسب توزيعتك (وهذا عمل للمتمرسين وليس لضعاف القلوب!).
</p>

<h3>
	التقنيات التي أريد التعامل معها
</h3>

<p>
	يمكن لأي محرر نصوص أن يقرأ ويحرر أي نص عمومًا. وهذا أمر جيد إن أردت أن تترك لنفسك ملاحظات هنا وهناك، لكن عند تطوير مواقع ويب وكتابة شيفرات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a>، يمكن أن يصل بك الأمر إلى كتابة ملفات كبيرة ومعقدة. لهذا وفر على نفسك العناء واستخدم محرر نصوص يفهم التقنيات التي تتعامل معها. تساعدك الكثير من المحررات النصية في هذا الأمر من خلال مميزاتها التي تقدمها مثل:
</p>

<ul>
<li>
		<strong>تلوين العبارات البرمجية</strong> <a href="https://ar.wikipedia.org/wiki/%D8%AA%D8%B9%D9%84%D9%8A%D9%85_%D8%A7%D9%84%D8%B5%D9%8A%D8%BA%D8%A9" rel="external nofollow">Syntax Highlighting</a><strong></strong>: سيغدو الملف أكثر وضوحًا عند تلوين شيفرات الأوامر والكلمات المفتاحية بألوان مختلفة تناسب التقانة التي تستخدمها.
	</li>
	<li>
		<strong>الإكمال التلقائي للشيفرة</strong> <a href="https://ar.wikipedia.org/wiki/%D8%A5%D9%83%D9%85%D8%A7%D9%84_%D8%AA%D9%84%D9%82%D8%A7%D8%A6%D9%8A" rel="external nofollow">Code Completion</a>: توفر عملية الإكمال التلقائي للبنى البرمجية التي تكتب شيفرتها الوقت كأن يغلق المحرر واسمات HTML المفتوحة، أو أن يقترح عليك خاصية معينة عند كتابة تنسيق CSS.
	</li>
	<li>
		<strong>استخدام مقاطع شيفرة (أو قصاصات برمجية) </strong><a href="https://ar.wikipedia.org/wiki/%D9%82%D8%B5%D8%A7%D8%B5%D8%A9_(%D8%A8%D8%B1%D9%85%D8%AC%D8%A9)" rel="external nofollow">Code Snippets</a>: تستخدم العديد من التقنيات هيكلية متشابهة عند كتابة مستنداتها كما تلاحظ عند كتابة مستندات HTML. لذلك عند استخدامك لمقاطع شيفرة جاهزة سيوفر عليك عناء إعادة كتابتها مرة تلو الأخرى.
	</li>
	<li>
		تدعم معظم محررات النصوص حاليًا تلوين العبارات البرمجية، ولا تدعم بالضرورة الميزتين الباقيتين. لذلك تأكد قبل كل شيء من دعم المتصفح الذي ستعتمده ميزة تلوين العبارات البرمجية لكل من <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://https://wiki.hsoub.com/JavaScript" rel="external nofollow">جافا سكربت</a>.
	</li>
</ul>
<h3>
	الميزات الأساسية التي تتوقع أن تجدها في محرر النصوص
</h3>

<p>
	يعتمد ذلك على احتياجاتك ومخططاتك. إليك بعض الميزات الوظيفية التي تساعدك غالبًا في عملك:
</p>

<ul>
<li>
		البحث والاستبدال في مستند أو أكثر اعتمادًا على التعابير النمطية <a href="https://academy.hsoub.com/devops/linux/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-r63/" rel="">Regular Expressions</a> أو غيرها من العبارات التي تحتاجها.
	</li>
	<li>
		الانتقال السريع إلى سطر محدد.
	</li>
	<li>
		عرض قسمين من ملف طويل معًا.
	</li>
	<li>
		عرض شيفرة HTML التي تكتبها كما ستظهر على المتصفح.
	</li>
	<li>
		اختيار عدة أسطر من أماكن مختلفة معًا.
	</li>
	<li>
		إظهار مجلدات مشروعك وما تحويه من ملفات.
	</li>
	<li>
		التنسيق التلقائي لشيفراتك باستخدام محسنات مظهر الشيفرة Code Beautifier.
	</li>
	<li>
		التدقيق الإملائي.
	</li>
	<li>
		الإزاحة التلقائية للشيفرة وفقًا لإعدادات الإزاحة.
	</li>
</ul>
<h3>
	إمكانية إضافة ميزات إضافية إلى المحرر
</h3>

<p>
	تأتي بعض المحررات القابلة للتوسيع مع عدد قليل من الميزات المدمجة، ويمكن توسيع إمكاناتها بميزات إضافية وفقًا لاحتياجاتك.
</p>

<p>
	إن لم تكن متأكدًا من المميزات التي تحتاجها أو افتقر محررك لهذه المميزات، عليك البحث عن محرر قابل للتوسع. وأفضل المحررات هي التي تزودك بالعديد من الإضافات وتقدم لك طريقة للبحث عن هذه الإضافات وتثبيتها تلقائيًا (نتكلم عن محرر مثالي هنا).
</p>

<p>
	إن رغبت باستخدام الكثير من الميزات الوظيفية التي أبطأت محررك نظرًا لزيادة الإضافات التي ثبّتها، جرّب استخدام بيئة تطوير متكاملة Integrated Development Environment واختصارًا <a href="https://io.hsoub.com/programming/3469-%D9%85%D9%86%D8%B5%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%A7%D8%B0%D8%A7-%D8%A3%D9%82%D9%88%D9%84-%D8%AA%D8%AD%D8%AF%D9%8A%D8%AF%D8%A7" rel="external">IDE</a>. تؤمن لك هذه البيئات الكثير من الأدوات ضمن واجهة واحدة، وعلى الرغم من صعوبتها على المبتدئين تبقى خيارًا قائمًا إن شعرت أن محررك محدود القدرة. إليك بعض بيئات العمل المتكاملة:
</p>

<ul>
<li>
		<a href="http://www.aptana.com/" rel="external nofollow">Aptana Studio</a>.
	</li>
	<li>
		<a href="https://eclipse.org/" rel="external nofollow">Eclipse</a>.
	</li>
	<li>
		<a href="https://komodoide.com/" rel="external nofollow">Komodo IDE</a>.
	</li>
	<li>
		<a href="https://netbeans.org/" rel="external nofollow">NetBeans IDE</a>.
	</li>
	<li>
		<a href="https://www.visualstudio.com/" rel="external nofollow">Visual Studio</a>.
	</li>
	<li>
		<a href="https://www.jetbrains.com/webstorm/" rel="external nofollow">WebStorm</a>.
	</li>
</ul>
<h3>
	الحاجة إلى الدعم أو المساعدة أثناء استخدام المحرر
</h3>

<p>
	من الجيد أن تعلم إن كنت ستتلقى مساعدة أو دعمًا عندما تستخدم برنامجًا معينًا. تحقق من أمرين اثنين عندما يتعلق الأمر بمحرر النصوص:
</p>

<ol>
<li>
		المحتوى الموجه للمستخدم (الأسئلة الأكثر شيوعًا ودليل المستخدم والمساعدة المباشرة عبر الإنترنت).
	</li>
	<li>
		النقاشات مع المطورين ومستخدمين آخرين (المنتديات والبريد الإلكتروني ومحادثات عبر الإنترنت IRC).
	</li>
</ol>
<p>
	استخدم التوثيق المكتوب عندما ترغب في تعلم استخدام محرر النصوص، وتواصل مع الآخرين إن واجهت مشاكل في تثبيت البرنامج أو أثناء استخدامه.
</p>

<h3>
	أهمية مظهر المحرر وتجربة استخدامه
</h3>

<p>
	بالرغم من أن الأمر مجرد تفضيلات شخصية إلّا أنه يمكن أن يرغب البعض في تخصيص كل تفصيل في <a href="https://academy.hsoub.com/design/user-interface/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-%D9%8A%D8%AC%D8%A8-%D8%AA%D8%B9%D9%84%D9%85%D9%87%D8%A7-%D9%85%D9%86-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%85%D8%A4%D9%84%D9%85%D8%A9-r657/" rel="">واجهة المستخدم</a> ابتداءً بالألوان وانتهاءً بمواقع الأزرار. تتنوع المحررات النصية تبعًا لهذا المنظور، لذا تحقق من ذلك أولًا. لن تجد صعوبة في إيجاد محرر نصوص يغير سماته اللونية، لكن إن أردت تفاصيلًا أكثر خصوصية لن تجد مفرًا من استخدام IDE.
</p>

<h2>
	تثبيت المحرر وإعداده للعمل
</h2>

<p>
	لن تجد أي تعقيدات في تثبيت محرر النصوص، لكن طريقة التثبيت ستختلف من منصة لأخرى، ولا ينبغي أن تكون صعبة إطلاقًا:
</p>

<ul>
<li>
		<strong>ويندوز</strong>: ستجد ملف التثبيت بإحدى اللاحقتين <code>exe.</code> أو <code>msi.</code> ويأتي البرنامج مضغوط ضمن أرشيف مثل <code>zip.</code> أو <code>7z</code> أو <code>rar.</code>، وعليك في هذه الحالة تثبيت برنامج إضافي لاستخراج البرنامج من الأرشيف المضغوط. يدعم ويندوز <code>zip.</code> افتراضيًا، فلا حاجة لتثبيت أية برامج إضافية.
	</li>
	<li>
		<strong>ماك</strong>: يمكنك تنزيل ملف التثبيت ذو اللاحقة <code>dmg.</code> من موقع الويب الخاص بالمحرر، كما ستجد الكثير من المحررات النصية ضمن متجر آبل مما يسهِّل عملية التثبيت.
	</li>
	<li>
		<strong>لينوكس</strong>: بإمكانك الانطلاق من الواجهة الرسومية لبرنامج مدير الحزم Packet Manager في التوزيعات الأكثر شعبية من لينوكس مثل Ubuntu Software Center أو Mintinstall أو Gnome Software وغيرها من البرامج. ستجد أيضًا ملفًا لاحقته <code>deb.</code> أو <code>rpm.</code> للبرنامج في مرحلة ما قبل التجميع أو الحزم Prepackaging، لكنك ستستخدم في غالب الأحيان خادم المستودعات Repository Server الخاص بالتوزيعة التي تعمل عليها، أما الحالة الأسوء فهي ترجمة الملفات المصدرية للمحرر ليعمل على توزيعتك. خذ وقتك في قراءة تعليمات التثبيت التي يوفرها موقع ويب الخاص بالمحرر.
	</li>
	<li>
		يستمر نظام التشغيل في فتح الملفات النصية باستخدام محرر النصوص الافتراضي حتى تغير ارتباط الملف File Association. ويعني ذلك تحديد المحرر المفضل الذي يستخدمه نظام التشغيل في فتح الملف النصوص عند النقر المضاعف عليه.
	</li>
</ul>
<p>
	بعد أن تُثبت المحرر النصوص الذي يلبي احتياجاتك، لا بدّ من وضع اللمسات الأخير على بيئتك الأساسية لتطوير مواقع ويب، أو يمكنك أن تكتب بنفسك شيفرة أول صفحة ويب إن أردت استخدام بيئة عملك مباشرة.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Available_text_editors" rel="external nofollow">What text editors are available</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%88-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1435/" rel="">ما هو عنوان URL في الويب؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D8%AA%D9%83%D9%84%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%A7%D8%AF%D9%8A%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D9%85%D9%84%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%8A%D8%A8%D8%9F-r1437/" rel="">ما التكلفة المادية الكاملة لبناء موقع ويب؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%B1%D9%88%D8%A7%D8%A8%D8%B7-%D8%A7%D9%84%D8%AA%D8%B4%D8%B9%D8%A8%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1434/" rel="">مفهوم الروابط التشعبية في مواقع الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1438</guid><pubDate>Sat, 29 Jan 2022 17:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x62F;&#x648;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645;&#x629; &#x641;&#x64A; &#x628;&#x646;&#x627;&#x621; &#x645;&#x648;&#x627;&#x642;&#x639; &#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/workflow/%D8%A7%D9%84%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-r1436/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61def3192033b_--------.png.6804c0a9239bc91f75b06573438a22cb.png" /></p>

<p>
	يمكنك تنزيل معظم <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-devtools-r277/" rel="">البرامج التي تحتاجها لتطوير صفحات ومواقع الويب</a> مجانًا وسنزوّدك بعدة روابط خلال هذا المقال.
</p>

<p>
	عمومًا ستحتاج إلى أدوات التطوير لكي:
</p>

<ul>
<li>
		تنشئ أو تحرر صفحة ويب.
	</li>
	<li>
		رفع الملفات إلى خادم ويب.
	</li>
	<li>
		استعراض موقع ويب.
	</li>
</ul>
<p>
	تحتوي معظم <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AB%D8%A7%D9%86%D9%8A-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-processes-%D9%81%D9%8A-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-r977/" rel="">أنظمة التشغيل</a> على محرر نصي ومتصفح ويب يمكن استخدامهما لعرض مواقع الويب. وبالتالي لن تحتاج سوى برامج لنقل الملفات إلى <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r574/" rel="">خادم ويب</a>.
</p>

<p>
	إذًا، سنتعلم في هذا المقال عن المكونات البرمجية التي تحتاجها إن أردت تحرير أو رفع أو عرض موقع ويب والتي تساعدك في عملك أثناء تطوير مواقع الويب.
</p>

<p>
	ننصحك قبل الشروع في قراءة المقال أن تطلع على مقال <a href="https://academy.hsoub.com/devops/servers/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%B5%D9%81%D8%AD%D8%A9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D9%88%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D8%AD%D8%B1%D9%83-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-r572/" rel="">الفرق بين صفحات الويب ومواقع الويب وخوادم الويب ومحركات البحث</a>.
</p>

<h2>
	إنشاء وتحرير صفحات الويب
</h2>

<p>
	تحتاج إلى محرر نصي لتتمكن من تحرير أو <a href="https://academy.hsoub.com/programming/html/html-%D9%88-css-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D8%B4%D8%A6-%D9%85%D9%88%D9%82%D8%B9%D8%A7-%D9%85%D9%86-%D8%B9%D8%AF%D8%A9-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r286/" rel="">إنشاء صفحات الويب</a>، تساعدك محررات النصوص على تعديل الملفات النصية غير المنسّقة إذ تسمح لك بعض أنواع محررات الملفات مثل المحررات النصية الغنية Rich Text Format واختصارًا RTF، بإضافة تنسيقات إلى الخطوط المستخدمة كتثخين الخط أو وضع سطر تحت الكلمات، ولكن بالرغم من ذلك لا تعد محررات النصوص تلك ملائمة لكتابة صفحات الويب. لهذا عليك التفكير قليلًا قبل أن تقرر ما المحرر الذي ستستخدمه لأنك ستعمل عليه كثيرًا أثناء بناء موقع الويب.
</p>

<p>
	تأتي معظم أنظمة التشغيل المخصصة للحواسب المكتبية مع محرر نصوص بسيط وسهل الاستخدام لكنه يفتقر إلى بعض الميزات المفيدة عند كتابة شيفرات صفحات الويب. لكن إن أردت شيئًا أكثر أناقة، فستجد الكثير من الأدوات التي توفرها شركات خارجية. تأتي محررات النصوص التي تقدمها الشركات مع ميزات إضافية كتلوين العبارات القواعدية والإكمال التلقائي للشيفرة البرمجية وإخفاء أو إظهار مقاطع محددة، والبحث في الشيفرة. إليك قائمة مختصرة ببعض المحررات:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th style="text-align:right">
				نظام التشغيل
			</th>
			<th style="text-align:right">
				المحرر المدمج مع نظام التشغيل
			</th>
			<th style="text-align:right">
				محررات يؤمنها طرف ثالث
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:right">
				ويندوز
			</td>
			<td style="text-align:right">
				Notepad
			</td>
			<td style="text-align:right">
				Notepad++, Visual Studio Code, Web Storm, Brackets ShiftEdit, Sublime Text
			</td>
		</tr>
<tr>
<td style="text-align:right">
				ماك أو إس
			</td>
			<td style="text-align:right">
				TextEdit
			</td>
			<td style="text-align:right">
				TextWrangler, Visual Studio Code, Brackets ShiftEdit, Sublime Text
			</td>
		</tr>
<tr>
<td style="text-align:right">
				لينوكس
			</td>
			<td style="text-align:right">
				Vi (All Unix) GEdit (Gnome) Kate (KDE) LeafPad (Xfce)
			</td>
			<td style="text-align:right">
				Emacs, Vim, Visual Studio Code, Brackets ShiftEdit, Sublime Text
			</td>
		</tr>
<tr>
<td style="text-align:right">
				كروم أو إس
			</td>
			<td style="text-align:right">
				 
			</td>
			<td style="text-align:right">
				ShiftEdit
			</td>
		</tr>
</tbody>
</table>
<p>
	إليك لقطة شاشة لأحد محررات النصوص المتقدمة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89030" href="https://academy.hsoub.com/uploads/monthly_2022_01/61def33394db9_01_notpad_screenshot.png.a4cdf577e580c3ac482444f54adf2d6d.png" rel=""><img alt="01_notpad++_screenshot.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89030" data-unique="88v3obljg" src="https://academy.hsoub.com/uploads/monthly_2022_01/61def3364f035_01_notpad_screenshot.thumb.png.84d76500d92330c9a0e99e53b10717e1.png"></a>
</p>

<p>
	إليك أيضًا لقطة شاشة لمحرر نصوص على الويب:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="89031" href="https://academy.hsoub.com/uploads/monthly_2022_01/02_web_based_text_editor.png.93fd0778a1c46fcffdf10b5b8e3aa8ee.png" rel=""><img alt="02_web_based_text_editor.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89031" data-unique="c1skmnssz" src="https://academy.hsoub.com/uploads/monthly_2022_01/02_web_based_text_editor.png.93fd0778a1c46fcffdf10b5b8e3aa8ee.png"></a>
</p>

<h2>
	رفع الملفات إلى ويب
</h2>

<p>
	عليك أن ترفع صفحات موقعك إلى خادم ويب عندما يكتمل بناؤه ويصبح جاهزًا لاتاحته للناس. يمكنك شراء مساحة تخزين من أي مزود خدمة تختاره والذي سيرسل لك بدوره -عند إكمال عملية الشراء- بريدًا إلكترونيًا يضم معلومات الوصول إلى مساحة التخزين الخاصة بك على شكل عنوان SFTP URL (عنوان يستخدم برتوكول FTP الآمن)، بالإضافة إلى اسم المستخدم وكلمة المرور وغيرها من المعلومات اللازمة للاتصال بخادم الويب. تذكر أنّ بروتوكول SFTP أصبح قديم الطراز وبدأت أنظمة رفع ملفات جديدة باكتساب شعبية مثل <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%90%D9%85-rsync-%D9%84%D9%85%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%85%D8%AC%D9%84%D9%91%D8%AF%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AC%D9%87%D8%A7%D8%B2-%D8%A7%D9%84%D9%85%D8%AD%D9%84%D9%91%D9%8A-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%88%D9%85-r50/" rel="">RSync </a>و<a href="https://help.github.com/articles/using-a-custos" rel="external nofollow">Git/GitHub</a>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: يعد البروتوكول FTP غير آمن لذلك تأكد من دعم مزود الخدمة الذي يستضيف موقعك لبروتوكولات الاتصال الآمن مثل FSTP وRSync مع <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr>.
		</p>
	</div>
</blockquote>

<p>
	تُعد خطوة رفع الملفات إلى خادم الويب خطوة مهمة جدًا من خطوات بناء الموقع، إليك قائمة مختصرة لبعض البرامج المجانية التي تعمل من ناحية العميل لرفع الملفات:
</p>

<table>
<thead><tr>
<th style="text-align:left">
				نظام التشغيل
			</th>
			<th colspan="2">
				برمجيات FTP
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:left">
				ويندوز
			</td>
			<td style="text-align:right">
				<ul>
<li>
						WinSCP
					</li>
					<li>
						Moba Xterm
					</li>
				</ul>
</td>
			<td rowspan="3">
				(<a href="https://filezilla-project.org/" rel="external nofollow">FileZilla</a> (All OS
			</td>
		</tr>
<tr>
<td style="text-align:left">
				لينوكس
			</td>
			<td style="text-align:right">
				(Nautilus/Files (Gnome) Dolphin (KDE
			</td>
		</tr>
<tr>
<td style="text-align:left">
				ماك أو إس
			</td>
			<td style="text-align:right">
				Cyberduck
			</td>
		</tr>
<tr>
<td style="text-align:left">
				كروم أو إس
			</td>
			<td style="text-align:right">
				(ShiftEdit (All OS
			</td>
			<td>
				 
			</td>
		</tr>
</tbody>
</table>
<h2>
	تصفح مواقع ويب
</h2>

<p>
	لا بد من وجود <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D8%AD%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1338/" rel="">متصفح ويب</a> لاستعراض المواقع وستجد الكثير من الخيارات التي تلبي احتياجاتك. لكن عندما تطور موقع ويب لا بدّ من اختباره على أحد المتصفحات الرئيسية التالية لتتأكد أن موقعك سيعمل عند معظم المستخدمين:
</p>

<ul>
<li>
		<a href="https://www.mozilla.org/en-US/firefox/new" rel="external nofollow">موزيلا فايرفوكس</a>.
	</li>
	<li>
		<a href="https://www.google.com/chrome" rel="external nofollow">جوجل كروم</a>.
	</li>
	<li>
		<a href="https://windows.microsoft.com/en-US/internet-explorer/download" rel="external nofollow">مايكروسوفت انترنت أكسبلورر</a> أو <a href="https://www.microsoft.com/en-us/edge" rel="external nofollow">مايكروسوفت إيدج</a>.
	</li>
	<li>
		<a href="https://www.apple.com/safari" rel="external nofollow">آبل سفاري</a>.
	</li>
</ul>
<p>
	فإن كنت تستهدف مجموعة محددة (منصة تقنية أو بلد محدد)، لا بدّ أن تختبر موقعك على متصفحات أخرى مثل <a href="https://www.opera.com" rel="external nofollow">أوبرا</a> و<a href="https://www.konqueror.org/" rel="external nofollow">Konqueror</a> أو <a href="https://www.ucweb.com/ucbrowser/" rel="external nofollow">UC Browser</a>.
</p>

<p>
	تتعقد عملية اختبار المواقع لأن بعض المتصفحات لن تعمل إلا على أنظمة تشغيل محددة. فلن يعمل آبل سفاري سوى على أنظمة تشغيل آبل أي أو إس وماك أو إس ولن يعمل إنترنت إكسبلورر سوى على ويندوز. في حالات كهذه من الأفضل استخدام خدمات مثل <a href="http://browsershots.org/" rel="external nofollow">Browsershots</a> أو <a href="https://www.browserstack.com/" rel="external nofollow">Browserstack</a>. إذ سيعرضُ لك موقع Browsershots لقطات شاشة لموقعك كما سيبدو على عدة متصفحات، بينما يمنحك Browserstack إمكانية الوصول الكاملة عن بعد إلى محاكيات افتراضية Virtual Machines تساعدك على تجربة موقعك على أكثر البيئات شيوعًا. يمكنك أيضًا إعداد محاكي افتراضي خاص بك، لكن الأمر سيتطلب الأمر خبرات إضافية (إن قررت المضي بهذا الخيار، تزوّدك مايكروسوفت ببعض الأدوات التي تساعدك بما فيها <a href="https://modern.ie/" rel="external nofollow">محاكي افتراضي جاهز للاستخدام</a>).
</p>

<p>
	اختبر موقعك بشتى الوسائل على أجهزة حقيقية وخاصة أجهزة الهواتف المحمولة الحقيقية، فالمحاكيات هي تكنولوجيا جديدة لا زالت قيد التطوير لذلك فهي أقل وثوقية من محاكيات أجهزة سطح المكتب. وطالما أن أجهزة الهاتف المحمولة غالية الثمن، ننصحك أن تلق نظرة على <a href="https://opendevicelab.com/" rel="external nofollow">Open Device Lab initiative</a>. يمكنك أيضًا مشاركة الأجهزة إن أردت اختبار موقعك على منصات عدة دون أن تنفق الكثير من المال.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_software_do_I_need" rel="external nofollow">What software do I need to build a website</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%88-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1435/" rel="">ما هو عنوان URL في الويب؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D8%AA%D9%83%D9%84%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%A7%D8%AF%D9%8A%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D9%85%D9%84%D8%A9-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%8A%D8%A8%D8%9F-r1437/" rel="">ما التكلفة المادية الكاملة لبناء موقع ويب؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%A7-%D9%87%D9%8A-%D9%85%D8%AD%D8%B1%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%B9%D9%85%D9%84%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1438/" rel="">ما هي محررات النصوص المستعملة في تطوير مواقع الويب؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA%D8%9F-r1439/" rel="">ما هي أدوات مطوري الويب المدمجة في المتصفحات؟</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1436</guid><pubDate>Sat, 22 Jan 2022 17:00:00 +0000</pubDate></item><item><title>[&#x641;&#x64A;&#x62F;&#x64A;&#x648;] &#x623;&#x633;&#x627;&#x633;&#x64A;&#x627;&#x62A; &#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644; &#x623;&#x62F;&#x627;&#x629; Webpack</title><link>https://academy.hsoub.com/programming/workflow/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A3%D8%AF%D8%A7%D8%A9-webpack-r1235/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/60af69701ecdf_---Webpack.png.5d124f16df5d2726ac5a8507552487e5.png" /></p>

<iframe width="560" height="450" src="https://www.youtube.com/embed/M3c9R_xSF1o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

<p>
	يشرح في هذا الفيديو كيفية تثبيت واستخدام أداة التحزيم والبناء Webpack للمساعدة في تطوير صفحات الويب وتطبيقات JavaScript، كما نشرح في <a href="https://academy.hsoub.com/learn/front-end-web-development/" rel="">دورة تطوير واجهات المستخدم</a> الأداة Webpack عمليًا بالتفصيل، وللاطلاع على معلومات شاملة حول Webpack، يمكنك الاطلاع على <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">دليل Webpack الشامل</a>.
</p>
]]></description><guid isPermaLink="false">1235</guid><pubDate>Thu, 27 May 2021 12:31:42 +0000</pubDate></item><item><title>&#x627;&#x644;&#x639;&#x645;&#x644; &#x645;&#x639; &#x623;&#x62F;&#x627;&#x629; &#x627;&#x644;&#x62A;&#x62D;&#x632;&#x64A;&#x645; webpack &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; React</title><link>https://academy.hsoub.com/programming/workflow/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A3%D8%AF%D8%A7%D8%A9-%D8%A7%D9%84%D8%AA%D8%AD%D8%B2%D9%8A%D9%85-webpack-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-r1169/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e544c3eaa9_----webpack---React.png.d26fcd48fa3c55f606ed8a72f69f2b1b.png" /></p>

<p>
	لقد نال تطوير المواقع باستخدام React سمعة سيئة نظرًا لحاجته إلى أدوات كثيرة كان من الصعب تهيئتها بالشكل المناسب. أما في أيامنا هذه فقد أصبح استخدام React مريحًا بفضل البرنامج <a data-ss1615745993="1" data-ss1615746541="1" href="https://github.com/facebookincubator/create-react-app" rel="external nofollow">create-react-app</a>. ويمكن القول أنه لم توجد قبله أية آليات مهمة لتطوير المواقع باستخدام JavaScript من جانب المتصفح.
</p>

<p>
	لكن لا يمكن الاعتماد إلى الأبد على الحلول السحرية التي يقدمها هذا البرنامج، وعلينا أن نحاول الاطلاع على ما يجري خلف الكواليس. إنّ أحد اللاعبين الأساسيين في جعل تطبيقات React جاهزة للعمل هو برنامج تجميع يدعى <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/" rel="external nofollow">webpack</a>.
</p>

<h2>
	تجميع وحزم الملفات
</h2>

<p>
	لقد طورنا تطبيقاتنا بتقسيمها إلى وحدات منفصلة، ثم أدرجنا هذه الوحدات في الأماكن التي تتطلبها. لكن ليس لدى المتصفح أية فكرة عن كيفية التعامل مع الشيفرة المجزأة ضمن وحدات، حتى ضمن وحدات ES6 التي عُرّفت في المعيار ECSMAScript.
</p>

<p>
	ولهذا السبب، يجب إعادة تجميع الوحدات لكي تتعامل معها المتصفحات، أي يجب تحويل جميع ملفات الشيفرة إلى ملف واحد يحتوي على شيفرة التطبيق بالكامل. فعندما نشرنا نسخة الإنتاج من تطبيق React للواجهة الأمامية في القسم 3، نفذنا عملية تجميع للتطبيق باستخدام الأمر <code>npm run build</code>. حيث جمّع سكربت npm الشيفرة المصدرية باستخدام المجمّع webpack تحت الستار، ونتج عنها مجموعة من الملفات ضمن المجلد "build".
</p>

<pre class="ipsCode">
├── asset-manifest.json
├── favicon.ico
├── index.html
├── manifest.json
├── precache-manifest.8082e70dbf004a0fe961fc1f317b2683.js
├── service-worker.js
└── static
    ├── css
    │   ├── main.f9a47af2.chunk.css
    │   └── main.f9a47af2.chunk.css.map
    └── js
        ├── 1.578f4ea1.chunk.js
        ├── 1.578f4ea1.chunk.js.map
        ├── main.8209a8f2.chunk.js
        ├── main.8209a8f2.chunk.js.map
        ├── runtime~main.229c360f.js
        └── runtime~main.229c360f.js.map
</pre>

<p>
	حيث يمثل الملف "index.html" الموجود في جذر المجلد "build" الملف الرئيسي للتطبيق، فهو الذي يحمّل ملفات JavaScript المجمّعة داخل المعرّف <code>&lt;script&gt;</code> (يوجد في الواقع ملفي JavaScript مجمّعين).
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_605_7" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;title&gt;</span><span class="pln">React App</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"/static/css/main.f9a47af2.chunk.css"</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="tag">&gt;&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"root"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"/static/js/1.578f4ea1.chunk.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"/static/js/main.8209a8f2.chunk.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	فيمكن أن نرى من خلال المثال الذي أنشأناه باستخدام create-react-app، أنّ سكربت بناء التطبيق يجمّع أيضًا ملفات تنسيق CSS في ملف واحد باسم "static/css/main.f9a47af2.chunk.css/".
</p>

<p>
	تجري عملية التجميع في واقع الأمر لتعريف نقطة دخول إلى التطبيق، وهي عادة الملف "index.js". فعندما يجمّع webpack الشيفرة فإنه يضم كل الشيفرات التي ستدرجها نقطة الدخول والشيفرة التي ستدرج تلك المُدرجات، وهكذا.
</p>

<p>
	وطالما أنّ بعض الملفات التي تُدرج ستكون على شكل حزم مثل React وRedux وAxios، فسيضم ملف JavaScript المُجمّع محتوى كل مكتبة من تلك المكتبات.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		اعتمدت الآلية القديمة لتقسيم شيفرة التطبيقات إلى ملفات متعددة على حقيقة أنّ الملف index.html سيحمل كل ملفات JavaScript المنفصلة بمساعدة المعرّفات script. وهذا ما سبب انخفاضًا في الأداء، لأنّ تحميل كل ملف على حدى سينتج تبعات غير مستحبة. لهذا فالطريقة المفضلة حاليًا هي تجميع الملفات ضمن ملف واحد.
	</p>
</blockquote>

<p>
	سننشئ تاليًا يدويًا ومن الصفر ملف تهيئة webpack يناسب تطبيق React. لننشئ أولًا مجلدًا جديدًا لمشروعنا يحوي على المجلدين الفرعيين "build" و"src" وداخلهما الملفات التالية:
</p>

<pre class="ipsCode">
├── build
├── package.json
├── src
│   └── index.js
└── webpack.config.js
</pre>

<p>
	ستكون محتويات الملف "package.json" كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_9" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack-part7"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"0.0.1"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"practising webpack"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{},</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"MIT"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنثبت webpack كالتالي:
</p>

<pre class="ipsCode">
npm install --save-dev webpack webpack-cli
</pre>

<p>
	نحدد عمل webpack من خلال الملف "webpack.config.js" والذي نعطيه قيمًا أولية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_11" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> config</span></pre>

<p>
	سنعرف بعدها سكربت npm يُدعى "build" سينفذ عملية التجميع مع webpack:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_13" style="">
<span class="com">// ...</span><span class="pln">
</span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack --mode=development"</span><span class="pln">
</span><span class="pun">},</span><span class="pln">
</span><span class="com">// ...</span></pre>

<p>
	سنضع بعض الشيفرة في الملف "src/index.js"
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_15" style="">
<span class="kwd">const</span><span class="pln"> hello </span><span class="pun">=</span><span class="pln"> name </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">hello $</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}`)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما ننفذ الأمر <code>npm run build</code>، سيجمّع webpack شيفرة تطبيقنا وسينتج عنه ملف جديد "main.js" ستجده ضمن المجلد "build":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59662" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/bundling_by_npm_webpack_01.png.0cc26a3704c405145bb2266a2285b861.png" rel=""><img alt="bundling_by_npm_webpack_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59662" data-unique="g409hq0xs" src="https://academy.hsoub.com/uploads/monthly_2021_03/bundling_by_npm_webpack_01.png.0cc26a3704c405145bb2266a2285b861.png"></a>
</p>

<p>
	يحتوي الملف على العديد من النقاط المهمة. كما يمكننا أن نرى الشيفرة التي كتبناها سابقًا في آخر الملف:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59669" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/main_content_02.png.aa92b2be9c2030f5210a3baa858bc914.png" rel=""><img alt="main_content_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59669" data-unique="76mz2ndl8" src="https://academy.hsoub.com/uploads/monthly_2021_03/main_content_02.png.aa92b2be9c2030f5210a3baa858bc914.png"></a>
</p>

<p>
	سنضيف الآن الملف "App.js" إلى المجلد src، وسيحتوي الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_17" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	لندرج الوحدة App ضمن الملف "index.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_19" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> hello </span><span class="pun">=</span><span class="pln"> name </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">hello $</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}`)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">App</span><span class="pun">()</span></pre>

<p>
	عندما نجمع التطبيق من جديد بالأمر <code>npm run build</code>، سنجد أن webpack قد ميّز ملفين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59674" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/webpack_bundle_two_files_03.png.a495896568f3819e2c411a7557bb7ebe.png" rel=""><img alt="webpack_bundle_two_files_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59674" data-unique="ro4hrqa8w" src="https://academy.hsoub.com/uploads/monthly_2021_03/webpack_bundle_two_files_03.png.a495896568f3819e2c411a7557bb7ebe.png"></a>
</p>

<p>
	ستجد شيفرة التطبيق في نهاية ملف التجميع وبطريقة مبهمة نوعًا ما:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_21" style="">
<span class="com">/***/</span><span class="pln"> </span><span class="str">"./src/App.js"</span><span class="pun">:</span><span class="pln">
</span><span class="com">/*!********************!*\
  !*** ./src/App.js ***!
  \********************/</span><span class="pln">
</span><span class="com">/*! exports provided: default */</span><span class="pln">
</span><span class="com">/***/</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">module</span><span class="pun">,</span><span class="pln"> __webpack_exports__</span><span class="pun">,</span><span class="pln"> __webpack_require__</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

</span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">eval</span><span class="pun">(</span><span class="str">"__webpack_require__.r(__webpack_exports__);\nconst App = () =&gt; {\n  return null\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (App);\n\n//# sourceURL=webpack:///./src/App.js?"</span><span class="pun">);</span><span class="pln">

</span><span class="com">/***/</span><span class="pln"> </span><span class="pun">}),</span><span class="pln">

</span><span class="com">/***/</span><span class="pln"> </span><span class="str">"./src/index.js"</span><span class="pun">:</span><span class="pln">
</span><span class="com">/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/</span><span class="pln">
</span><span class="com">/*! no exports provided */</span><span class="pln">
</span><span class="com">/***/</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">module</span><span class="pun">,</span><span class="pln"> __webpack_exports__</span><span class="pun">,</span><span class="pln"> __webpack_require__</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

</span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">eval</span><span class="pun">(</span><span class="str">"__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _App__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./App */ \"./src/App.js\");\n\n\nconst hello = name =&gt; {\n  console.log(`hello ${name}`)\n};\n\nObject(_App__WEBPACK_IMPORTED_MODULE_0__[\"default\"])()\n\n//# sourceURL=webpack:///./src/index.js?"</span><span class="pun">);</span><span class="pln">

</span><span class="com">/***/</span><span class="pln"> </span><span class="pun">})</span></pre>

<h2>
	ملف التهيئة
</h2>

<p>
	لنلق نظرة على الملف "webpack.config.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_23" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> config</span></pre>

<p>
	كُتب ملف التهيئة باستخدام JavaScript، وصُدّر كائن التهيئة باستخدام عبارة وحدة Node. ستشرح عبارات التهيئة البسيطة التي كتبناها نفسها بنفسها. حيث تحدد الخاصية <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/concepts/#entry" rel="external nofollow">entry</a> لكائن التهيئة الملف الذي سيستخدم كنقطة دخول إلى التطبيق المُجمّع. بينما تحدد الخاصية <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/concepts/#output" rel="external nofollow">output</a> المكان الذي ستُخزّن فيه الشيفرة المجمعة.يجب تعريف المسار الهدف على شكل مسار مطلق، ويسهل عمل ذلك باستخدام التابع <a data-ss1615745993="1" data-ss1615746541="1" href="https://wiki.hsoub.com/Node.js/path#path.resolve.28.5B%E2%80%A6paths.5D.29" rel="external">path.resolve</a>. كما سنستخدم المتغير العام <a data-ss1615745993="1" data-ss1615746541="1" href="https://wiki.hsoub.com/Node.js/globals#_dirname" rel="external">‎__dirname</a> في Node والذي سيخزّن مسار الوصول إلى المجلد الحالي.
</p>

<h2>
	تجميع تطبيق React
</h2>

<p>
	لنحوّل تطبيقنا إلى تطبيق React بسيط.
</p>

<p>
	سنثبّت أولًا المكتبات الضرورية:
</p>

<pre class="ipsCode">
npm install react react-dom
</pre>

<p>
	ثم سنحول تطبيقنا إلى تطبيق React بإضافة التعريفات التالية إلى الملف "index.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_25" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">ReactDOM</span><span class="pln"> from </span><span class="str">'react-dom'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App'</span><span class="pln">

</span><span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;,</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</span><span class="pun">))</span></pre>

<p>
	كما سنجري التغييرات التالية على الملف "App.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_27" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">hello webpack</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	سنحتاج أيضًا إلى الملف "build/index.html" والذي سيشكل الصفحة الرئيسية للتطبيق والتي ستحمل بدورها شيفرة JavaScript المجمعة لتطبيقنا باستخدام المعرّف <code>script</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_605_29" style="">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">React App</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"root"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./main.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	ستواجهنا المشكلة التالية عند تجميع التطبيق:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59661" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/bundle_react_problem_04.png.17ed3efa04d195990563ce503feaa342.png" rel=""><img alt="bundle_react_problem_04.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59661" data-unique="9qwv1x7cp" src="https://academy.hsoub.com/uploads/monthly_2021_03/bundle_react_problem_04.png.17ed3efa04d195990563ce503feaa342.png"></a>
</p>

<h2>
	المُحمّلات
</h2>

<p>
	تنص رسالة الخطأ السابقة الناتجة عن webpack، على أننا قد نحتاج إلى مُحمّل مناسب لتجميع الملف App.js بالشكل الصحيح. لا يعرف webpack افتراضيًا سوى تجميع شيفرة JavaScript الأساسية. وربما أغفلنا فكرة أننا نستخدم عمليًا <a data-ss1615745993="1" data-ss1615746541="1" href="https://facebook.github.io/jsx/" rel="external nofollow">JSX</a> لتصيير واجهات العرض في React، ولتوضيح ذلك، سنعرض الشيفرة التالية والتي لا تمثل شيفرة JavaScript نظامية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_31" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">hello webpack</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إنّ الشيفرة السابقة مكتوبة باستخدام JSX التي تؤمن طريقة بديلة لتعريف عناصر React ضمن المعرف <code>&lt;div&gt;</code> للغة HTML.
</p>

<p>
	يمكن استخدام <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/concepts/loaders/" rel="external nofollow">المُحمّلات</a> لإبلاغ webpack عن الملفات التي ينبغي معالجتها قبل تجميعها. لنهيئ مُحملًا لتطبيقنا، يحوّل شيفرة JSX إلى شيفرة JavaScript نظامية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_33" style="">
<span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    
      rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">      
          </span><span class="pun">{</span><span class="pln">        
              test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">        
              loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pun">,</span><span class="pln">        
              options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">          
                  presets</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'@babel/preset-react'</span><span class="pun">],</span><span class="pln">        
              </span><span class="pun">},</span><span class="pln">      
          </span><span class="pun">},</span><span class="pln">    
      </span><span class="pun">],</span><span class="pln">  
  </span><span class="pun">},}</span></pre>

<p>
	عُرّف المُحمّل ضمن الخاصية <code>module</code> في المصفوفة <code>rules</code>.
</p>

<p>
	يتألف تعريف المُحمّل من ثلاثة أقسام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_35" style="">
<span class="pun">{</span><span class="pln">
  test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">
  loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pun">,</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    presets</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'@babel/preset-react'</span><span class="pun">]</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحدد الخاصية <code>test</code> بأن المُحمِّل مخصص للملفات التي تنتهي باللاحقة "js"، بينما تحدد الخاصية <code>loader</code> أن معالجة هذه الملفات ستجري باستخدام <a data-ss1615745993="1" data-ss1615746541="1" href="https://github.com/babel/babel-loader" rel="external nofollow">babel-loader</a>، أما الخاصية <code>options</code> فتستخدم لتحديد معاملات تهيئ وظيفة المحمّل.
</p>

<p>
	لنثبت المُحمِّل والحزم التي يحتاجها كاعتمادية تطوير:
</p>

<pre class="ipsCode">
npm install @babel/core babel-loader @babel/preset-react --save-dev
</pre>

<p>
	سينجح الآن تجميع التطبيق.
</p>

<p>
	لو عدّلنا قليلًا في شيفرة المكوّن <code>APP</code>، ثم ألقينا نظرة على الشيفرة المجمّعة، سنلاحظ أنّ النسخة المجمّعة من المكوّن ستكون على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_37" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
  react__WEBPACK_IMPORTED_MODULE_0___default</span><span class="pun">.</span><span class="pln">a</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="pln">
    </span><span class="str">'div'</span><span class="pun">,</span><span class="pln">
    </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
    </span><span class="str">'hello webpack'</span><span class="pln">
  </span><span class="pun">)</span></pre>

<p>
	يمكن أن نلاحظ من خلال هذا المثال، أنّ عناصر React قد كُتبت بعد التجميع بشيفرة JavaScript نظامية بدلًا من عبارات JSX، وذلك باستخدام الدالة <a data-ss1615745993="1" data-ss1615746541="1" href="https://wiki.hsoub.com/React/react_without_jsx" rel="external">createElement</a> التي تقدمها React.
</p>

<p>
	يمكننا اختبار التطبيق المجمّع بتشغيل الملف "build/index.html" من خلال المتصفح:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59672" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/testing_bundled_app_05.png.e8d40b79786f673127eadcb44b4ec46b.png" rel=""><img alt="testing_bundled_app_05.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59672" data-unique="sodpqoyo8" src="https://academy.hsoub.com/uploads/monthly_2021_03/testing_bundled_app_05.png.e8d40b79786f673127eadcb44b4ec46b.png"></a>
</p>

<p>
	تجدر الإشارة إلى أن استخدام عبارة awit/async ضمن شيفرة التطبيق، سيمنع بعض المتصفحات من إظهار أي شيء. يقودنا <a data-ss1615745993="1" data-ss1615746541="1" href="https://stackoverflow.com/questions/33527653/babel-6-regeneratorruntime-is-not-defined" rel="external nofollow">البحث عن رسالة الخطأ الظاهرة على الطرفية</a> إلى المشكلة. علينا تثبيت اعتمادية مفقودة أو أكثر. في حالتنا نجد أن الاعتمادية <a data-ss1615745993="1" data-ss1615746541="1" href="https://babeljs.io/docs/en/babel-polyfill" rel="external nofollow">‎@babel/polyfill</a> هي العنصر المفقود:
</p>

<pre class="ipsCode">
npm install @babel/polyfill
</pre>

<p>
	لنجري التعديلات التالية على الخاصية <code>entry</code> لكائن تهيئة webpack، وذلك ضمن الملف "webpack.config.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_39" style="">
<span class="pln">  entry</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'@babel/polyfill'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">]</span></pre>

<p>
	يحتوي ملف التهيئة الآن كل ما يلزم لتطوير تطبيقات React.
</p>

<h2>
	نواقل الشيفرة
</h2>

<p>
	تُعرّف عملة <a data-ss1615745993="1" data-ss1615746541="1" href="https://en.wiktionary.org/wiki/transpile" rel="external nofollow">نقل الشيفرة</a> (Transpilling) بأنها عملية تحويل شيفرة JavaScript من شكل إلى آخر. ويشير المصطلح بشكل عام، إلى عملية ترجمة الشيفرة المصدرية بتحويلها من لغة إلى أخرى.
</p>

<p>
	عند استخدام تعليمات التهيئة التي شرحناها سابقًا، فإننا نقلنا عمليًا شيفرة JSX إلى شيفرة JavaScript نظامية بمساعدة <a data-ss1615745993="1" data-ss1615746541="1" href="https://babeljs.io/" rel="external nofollow">babel</a> والذي يمثل حاليًا الأداة الأكثر شعبية لهذا الغرض.
</p>

<p>
	وكما ذكرنا في القسم 1، أنّ معظم المتصفحات لا تدعم آخر الميزات التي قدمتها ES6 وES7، ولهذا تُنقل الشيفرة عادةً إلى نسخة JavaScript التي تتوافق مع معايير ES5.
</p>

<p>
	تُعرّف عملية النقل التي ينفذها Babel كإضافة. إذ يستخدم المطورين في الواقع <a data-ss1615745993="1" data-ss1615746541="1" href="https://babeljs.io/docs/plugins/" rel="external nofollow">مُهيَّئات</a> جاهزة (presets) وهي مجموعة من الإضافات المهيئة مسبقًا.
</p>

<p>
	سنستخدم حاليًا المُهيَّئة <a data-ss1615745993="1" data-ss1615746541="1" href="https://babeljs.io/docs/plugins/preset-react/" rel="external nofollow">‎@babel/preset-react</a> لنقل الشيفرة المصدرية لتطبيقنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_41" style="">
<span class="pun">{</span><span class="pln">
  test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">
  loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pun">,</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    presets</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'@babel/preset-react'</span><span class="pun">]</span><span class="pln">  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنضيف المهيَّئة <a data-ss1615745993="1" data-ss1615746541="1" href="https://babeljs.io/docs/plugins/preset-env/" rel="external nofollow">‎@babel/preset-env</a> التي تحتوي على كل شيئ قد نحتاجه في نقل الشيفرة التي تستخدم آخر ميزات اللغة إلى معيار ES5.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_43" style="">
<span class="pun">{</span><span class="pln">
  test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">
  loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pun">,</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    presets</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'@babel/preset-env'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'@babel/preset-react'</span><span class="pun">]</span><span class="pln">  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنثبت المهيِّئة كالتالي:
</p>

<pre class="ipsCode">
npm install @babel/preset-env --save-dev
</pre>

<p>
	ستتحول الشيفرة بعد نقلها إلى أسلوب JavaScript القديم. فسيصبح تعريف المكوّن <code>App</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_45" style="">
<span class="kwd">var</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> _react2</span><span class="pun">.</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'div'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hello webpack'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لاحظ أن المتغيرات قد عرفت باستخدام التعليمة <code>var</code> لأن المعيار ES5 لا يفهم التعليمة <code>const</code>. وكذلك لم تُستعمل الدوال السهمية، بل استخدمت التعليمة <code>function</code>.
</p>

<h2>
	التنسيق باستخدام CSS
</h2>

<p>
	لنضف بعض تنسيقات CSS إلى التطبيق، وذلك بإنشاء الملف "src/index.css":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_47" style="">
<span class="pun">.</span><span class="pln">container </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
  background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="pun">#</span><span class="pln">dee8e4</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لننسق المكوّن <code>App</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_49" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"container"</span><span class="pun">&gt;</span><span class="pln">
      hello webpack
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ثم سندرج ملف التنسيق ضمن الملف "index.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_51" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="str">'./index.css'</span></pre>

<p>
	سيسبب ذلك انهيار عملية نقل الشيفرة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59673" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/transpill_break_06.png.dc5a34c56c23ab2e9c011aff5f29c34a.png" rel=""><img alt="transpill_break_06.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59673" data-unique="j0elk0ktz" src="https://academy.hsoub.com/uploads/monthly_2021_03/transpill_break_06.png.dc5a34c56c23ab2e9c011aff5f29c34a.png"></a>
</p>

<p>
	إذ علينا عند استخدام CSS أن نستخدم المُحمّلين <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/loaders/css-loader/" rel="external nofollow">css</a> و<a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/loaders/style-loader/" rel="external nofollow">style</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_53" style="">
<span class="pun">{</span><span class="pln">
  rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">
      loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pun">,</span><span class="pln">
      options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        presets</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'@babel/preset-react'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'@babel/preset-env'</span><span class="pun">],</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">      
       test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.css$/</span><span class="pun">,</span><span class="pln">      
       use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'style-loader'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'css-loader'</span><span class="pun">],</span><span class="pln">    
    </span><span class="pun">},</span><span class="pln">  
  </span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تقتقضي مهمة المحمّل <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/loaders/css-loader/" rel="external nofollow">css loader</a> تحميل ملف CSS بينما مهمة <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/loaders/style-loader/" rel="external nofollow">style loader</a> هي توليد وإضافة عنصر التنسيق الذي يحتوي على كل التنسيقات التي يستخدمها التطبيق.
</p>

<p>
	وهكذا ستُعرّف تنسيقات CSS ضمن الملف الرئيسي للتطبيق "index.html". وبالتالي لا حاجة لإدراج التنسيقات ضمنه. ويمكن عند الحاجة توليد تنسيقات CSS ثم وضعها في ملف منفصل باستخدام الإضافة <a data-ss1615745993="1" data-ss1615746541="1" href="https://github.com/webpack-contrib/mini-css-extract-plugin" rel="external nofollow">mini-css-extract-plugin</a>.
</p>

<p>
	عند تثبيت المحمَّلين كالتالي:
</p>

<pre class="ipsCode">
npm install style-loader css-loader --save-dev
</pre>

<p>
	ستنجح عملية التجميع وسيحمل التطبيق تنسيقًا جديدًا.
</p>

<h2>
	المكتبة Webpack-dev-server
</h2>

<p>
	يمكن تطوير التطبيقات باستخدام طرائق التهيئة السابقة لكنها في الواقع عملية مرهقة (مقارنة بسير نفس العمليات في Java). فعلينا في كل مرة نجري فيها تعديلًا، أن نجمّّّع التطبيق من جديد ونحدّث المتصفح لنختبر ما فعلناه.
</p>

<p>
	تقدم المكتبة <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/guides/development/#using-webpack-dev-server" rel="external nofollow">webpack-dev-server</a> حلًا لمشكلتنا. سنثبتها الآن:
</p>

<pre class="ipsCode">
npm install --save-dev webpack-dev-server
</pre>

<p>
	لنعرف سكربت npm لتشغيل خادم التطوير (dev-server):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_55" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack --mode=development"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"start"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack serve --mode=development"</span><span class="pln">  </span><span class="pun">},</span><span class="pln">
  </span><span class="com">//...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنضف أيضًا الخاصية <code>devServer</code> إلى كائن التهيئة في الملف "webpack.config.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_57" style="">
<span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  devServer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    
      contentBase</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">    
      compress</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">    
      port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">,</span><span class="pln">  
  </span><span class="pun">},</span><span class="pln">  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	سيشغل الأمر خادم التطوير على المنفذ 3000، وبالتالي يمكننا الوصول إلى تطبيقنا على العنوان <a data-ss1615745993="1" data-ss1615746541="1" href="http://localhost:3000/" rel="external nofollow">http://localhost:3000</a> من خلال المتصفح. وعندما نغيّر في الشيفرة سيُحدّث المتصفح الصفحة تلقائيًا.
</p>

<p>
	تتم عملية تحديث الشيفرة بسرعة. فعند استخدام خادم التطوير، لن تجمع الشيفرة بالطريقة المعتادة ضمن الملف "main.js"، بل ستبقى فقط في الذاكرة.
</p>

<p>
	لنوسع الشيفرة بتغيير تعريف المكوّن <code>App</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_59" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">useState</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">counter</span><span class="pun">,</span><span class="pln"> setCounter</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"container"</span><span class="pun">&gt;</span><span class="pln">
      hello webpack </span><span class="pun">{</span><span class="pln">counter</span><span class="pun">}</span><span class="pln"> clicks
      </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setCounter</span><span class="pun">(</span><span class="pln">counter </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)}&gt;</span><span class="pln">
        press
      </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	تجدر الملاحظة أنّ رسائل الخطأ لن تظهر بنفس الطريقة التي ظهرت بها عند استخدام create-react-app في تطوير التطبيق. لهذا السبب يجب أن ننتبه أكثر لما تعرضه الطرفية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59665" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/error_message_changed_07.png.25d147e8482be1cec785028d2fae9562.png" rel=""><img alt="error_message_changed_07.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59665" data-unique="gn95d1u38" src="https://academy.hsoub.com/uploads/monthly_2021_03/error_message_changed_07.png.25d147e8482be1cec785028d2fae9562.png"></a>
</p>

<p>
	سيعمل التطبيق الآن بشكل جيد وستجري العمليات بسلاسة.
</p>

<h2>
	الدلالات المصدرية
</h2>

<p>
	لننقل معالج حدث النقر إلى دالة خاصة به، ولنخزّن القيمة السابقة للعداد في حالة مخصصة له تدعى <code>values</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_61" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">counter</span><span class="pun">,</span><span class="pln"> setCounter</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> setValues</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">()</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> handleClick </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setCounter</span><span class="pun">(</span><span class="pln">counter </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
    setValues</span><span class="pun">(</span><span class="pln">values</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">counter</span><span class="pun">))</span><span class="pln">  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"container"</span><span class="pun">&gt;</span><span class="pln">
      hello webpack </span><span class="pun">{</span><span class="pln">counter</span><span class="pun">}</span><span class="pln"> clicks
      </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={</span><span class="pln">handleClick</span><span class="pun">}&gt;</span><span class="pln">
        press
      </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لن يعمل التطبيق الآن، وستعرض الطرفية رسالة الخطأ التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59671" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/onclick_error_message_08.png.33dcbd76dd9f58c30fd1687b41ce475d.png" rel=""><img alt="onclick_error_message_08.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59671" data-unique="yxcbm8ayj" src="https://academy.hsoub.com/uploads/monthly_2021_03/onclick_error_message_08.png.33dcbd76dd9f58c30fd1687b41ce475d.png"></a>
</p>

<p>
	نعلم أن الخطأ موجود في التابع <code>onClick</code>، لكن في حال كان التطبيق أضخم، سنجد صعوبة بالغة في تقفي أثر الخطأ:
</p>

<pre class="ipsCode">
App.js:27 Uncaught TypeError: Cannot read property 'concat' of undefined
    at handleClick (App.js:27)
</pre>

<p>
	إن مكان الخطأ الذي تحدده الرسالة لا يتطابق مع الموقع الفعلي للخطأ ضمن شيفرتنا المصدرية. فلو نقرنا على رسالة الخطأ، فلن تجد الشيفرة المعروضة متطابقة مع شيفرة التطبيق:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59664" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/diffrent_source_code_location_09.png.35b3a1316a819c778a7bf0a91e956a54.png" rel=""><img alt="diffrent_source_code_location_09.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59664" data-unique="oqwf9ncza" src="https://academy.hsoub.com/uploads/monthly_2021_03/diffrent_source_code_location_09.png.35b3a1316a819c778a7bf0a91e956a54.png"></a>
</p>

<p>
	نحتاج بالطبع إلى رؤية شيفرتنا المصدرية الأساسية عند النقر على رسالة الخطأ. ولحسن الحظ فإصلاح رسالة الخطأ مع ذلك عملية سهلة. سنطلب من webpack أن يولد ما يسمى <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/configuration/devtool/" rel="external nofollow">دلالة مصدرية</a> (source map) للمُجمّع، بحيث يغدو ممكنًا الدلالة على الخطأ الذي يحدث عند التجميع ضمن الشيفرة المصدرية الأساسية.
</p>

<p>
	يمكن توليد الدلالة المصدرية بإضافة الخاصية <code>devtool</code> إلى كائن التهيئة وإسناد القيمة <code>source-map</code> لها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_64" style="">
<span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  devServer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  devtool</span><span class="pun">:</span><span class="pln"> </span><span class="str">'source-map'</span><span class="pun">,</span><span class="pln">  </span><span class="com">// ..</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يجب إعادة تشغيل webpack عند حدوث أية تغيرات في تهيئته. كما يمكننا أيضًا تهيئة البرنامج ليراقب بنفسه التغييرات التي تطرأ عليه، لكن لن نفعل ذلك حاليًا.
</p>

<p>
	ستصبح رسالة الخطأ الآن أوضح بكثير:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59666" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/evident_error_message_10.png.eb8dafcb055d619ab1df2844374c58ef.png" rel=""><img alt="evident_error_message_10.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59666" data-unique="5m23jwppy" src="https://academy.hsoub.com/uploads/monthly_2021_03/evident_error_message_10.png.eb8dafcb055d619ab1df2844374c58ef.png"></a>
</p>

<p>
	طالما أنها تشير إلى الشيفرة التي كتبناها:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59670" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/map_error_to_source_11.png.13c83eb9f0d48dfffaa0336c80595158.png" rel=""><img alt="map_error_to_source_11.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59670" data-unique="bugwf2ggk" src="https://academy.hsoub.com/uploads/monthly_2021_03/map_error_to_source_11.png.13c83eb9f0d48dfffaa0336c80595158.png"></a>
</p>

<p>
	يجعل توليد الدلالة المصدرية من استخدام منقح Chrome ممكنًا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59663" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/chrome_debugging_possible_12.png.420ef0ebfb3adc1c7dd52c5b73a418cc.png" rel=""><img alt="chrome_debugging_possible_12.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59663" data-unique="swd1q0siw" src="https://academy.hsoub.com/uploads/monthly_2021_03/chrome_debugging_possible_12.png.420ef0ebfb3adc1c7dd52c5b73a418cc.png"></a>
</p>

<p>
	سنصلح الثغرة الآن بتهيئة القيمة الأولية للحالة <code>values</code> لتكون مصفوفة فارغة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_66" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">counter</span><span class="pun">,</span><span class="pln"> setCounter</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> setValues</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">([])</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	تصغير الشيفرة
</h2>

<p>
	عندما سننشر التطبيق في مرحلة الإنتاج، سنستخدم الملف المُجمّع "main.js" الذي ولده webpack. إنّ حجم هذا الملف حوالي 974473 بايت على الرغم من أنّ شيفرة تطبيقنا لا تتجاوز عدة أسطر. إنّ هذا الحجم الكبير للملف يعود في الواقع إلى أنّ الملف المُجمَّع سيحتوي الشيفرة المرجعية لكامل مكتبة React. سيؤثر حجم الملف المجمّع لأن المتصفح سيحمّل الملف كاملًا عند تشغيل التطبيق للمرة الأولى. لن تكون هناك مشكلة بالحجم السابق إن كانت سرعة الاتصال بالإنترنت عالية، لكن الاستمرار في إضافة الاعتماديات سيخلق مشكلة في سرعة التحميل وخاصة لدى مستخدمي الهواتف النقالة.
</p>

<p>
	لو تفحصنا محتويات ملف التجميع، سنجد أنه بالإمكان استمثاله بشكل أفضل بكثير بما يخص الحجم، وذلك بإزالة كل التعليقات في الشيفرة. ولا جدوى طبعًا من الاستمثال اليدوي للملف إن كانت هناك أدوات جاهزة لتنفيذ العمل.
</p>

<p>
	تدعى عملية استمثال ملفات JavaScript بالتصغير (minification). وتعتبر الأداة <a data-ss1615745993="1" data-ss1615746541="1" href="http://lisperator.net/uglifyjs/" rel="external nofollow">UglifyJS</a> من أفضل الأدوات المعدة لهذا الغرض.
</p>

<p>
	لم تعد إضافات التصغير في webpack ابتداء من الإصدار 4 بحاجة إلى تهيئة إضافية. عليك فقط تعديل سكربت npm في الملف "package.json" لتحدد أنّ webpack سينفذ عملية التجميع في وضع الإنتاج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_68" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack-part7"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"0.0.1"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"practising webpack"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack --mode=production"</span><span class="pun">,</span><span class="pln">    
    </span><span class="str">"start"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack serve --mode=development"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"MIT"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما نجمّع التطبيق ثانيةً، سيقل حجم الملف "main.js" بشكل واضح.
</p>

<pre class="ipsCode">
$ ls -l build/main.js
-rw-r--r--  1 mluukkai  984178727  132299 Feb 16 11:33 build/main.js
</pre>

<p>
	يشابه خرج عملية التصغير شيفرة C المكتوبة بالأسلوب القديم. حيث حذفت جميع التعليقات والمساحات البيضاء غير الضرورية ومحارف السطر الجديد، كما تم استبدال أسماء المتغيرات بمحرف واحد.
</p>

<pre class="ipsCode">
function h(){if(!d){var e=u(p);d=!0;for(var t=c.length;t;){for(s=c,c=[];++f&lt;t;)s&amp;&amp;s[f].run();f=-1,t=c.length}s=null,d=!1,function(e){if(o===clearTimeout)return clearTimeout(e);if((o===l||!o)&amp;&amp;clearTimeout)return o=clearTimeout,clearTimeout(e);try{o(e)}catch(t){try{return o.call(null,e)}catch(t){return o.call(this,e)}}}(e)}}a.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length&gt;1)
</pre>

<h2>
	تهيئة نسختي التطوير الإنتاج
</h2>

<p>
	سنضيف تاليًا واجهة خلفية إلى التطبيق بتغيير غرض الواجهة الخلفية لتطبيق الملاحظات الذي أضحى مألوفًا لدينا.
</p>

<p>
	سنخزّن المحتوى التالي ضمن الملف "db.json":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_70" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"notes"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"important"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"content"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"HTML is easy"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"5a3b8481bb01f9cb00ccb4a9"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"important"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"content"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Mongo can save js objects"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"5a3b920a61e8c8d3f484bdd0"</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هدفنا هنا هو تهيئة webpack بحيث يستخدم التطبيق خادم JSON على المنفذ 3001 كواجهة خلفية، إن تم استعمال webpack محليًا.
</p>

<p>
	سيهيّأ الملف المجمّع عندها لاستخدام الواجهة الخلفية الموجودة على العنوان <a data-ss1615745993="1" data-ss1615746541="1" href="https://blooming-atoll-75500.herokuapp.com/api/notes." ipsnoembed="false" rel="external nofollow">https://blooming-atoll-75500.herokuapp.com/api/notes.</a>
</p>

<p>
	سنثبت المكتبة axios ونشغل خادم JSON ومن ثم سنجري التعديلات اللازمة على التطبيق. تتطلب عملية التغيير إحضار الملاحظات من الواجهة الخلفية باستخدام خطاف مخصص يدعى <code>useNotes</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_72" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState</span><span class="pun">,</span><span class="pln"> useEffect </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> axios from </span><span class="str">'axios'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> useNotes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">notes</span><span class="pun">,</span><span class="pln"> setNotes</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">([])</span><span class="pln">  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">       
        axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">url</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">      
        setNotes</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
     </span><span class="pun">})</span><span class="pln">  
    </span><span class="pun">},[</span><span class="pln">url</span><span class="pun">])</span><span class="pln">  
    </span><span class="kwd">return</span><span class="pln"> notes</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">counter</span><span class="pun">,</span><span class="pln"> setCounter</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> setValues</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">([])</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://blooming-atoll-75500.herokuapp.com/api/notes'</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> notes </span><span class="pun">=</span><span class="pln"> useNotes</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> handleClick </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setCounter</span><span class="pun">(</span><span class="pln">counter </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
    setValues</span><span class="pun">(</span><span class="pln">values</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">counter</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"container"</span><span class="pun">&gt;</span><span class="pln">
      hello webpack </span><span class="pun">{</span><span class="pln">counter</span><span class="pun">}</span><span class="pln"> clicks
      </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={</span><span class="pln">handleClick</span><span class="pun">}</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">press</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;{</span><span class="pln">notes</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> notes on server </span><span class="pun">{</span><span class="pln">url</span><span class="pun">}&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">    
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	استخدمنا عنوانًا مكتوبًا لخادم الواجهة الخلفية في التطبيق. لكن كيف يمكننا تغيير العنوان بحيث نصبح قادرين على تغييره لكي يدل على خادم الواجهة الخلفية عندما تُجمّع الشيفرة في مرحلة الإنتاج؟
</p>

<p>
	سنغيّر كائن التهيئة في الملف "webpack.config.js" ليصبح دالة بدلًا من كونه تابعًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_74" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">env</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
    output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    devServer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    devtool</span><span class="pun">:</span><span class="pln"> </span><span class="str">'source-map'</span><span class="pun">,</span><span class="pln">
    module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">],</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> config</span></pre>

<p>
	سيبقى التعريف نفسه تقريبًا ماعدا أن كائن التهيئة سيعاد من قبل الدالة. تتلقى الدالة معاملين هما <code>env</code> و<code>argv</code>. يمكن استخدام المعامل الثاني للوصول إلى الخاصية <code>mode</code> المعرفة في سكربت npm.
</p>

<p>
	يمكن استخدام الإضافة <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/plugins/define-plugin/" rel="external nofollow">DefinePlugin</a> في برنامج webpack لتعريف متغيرات عامة افتراضية يمكن استخدامها ضمن الشيفرة المجمّعة. لنعرف إذًا المتغير العام <code>BACKEND_URL</code> الذي يأخذ قيمًا مختلفة بناء على البيئة التي تجري فيها عملية تجميع الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_76" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> webpack </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'webpack'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">env</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'argv'</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">.</span><span class="pln">mode</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> backend_url </span><span class="pun">=</span><span class="pln"> argv</span><span class="pun">.</span><span class="pln">mode </span><span class="pun">===</span><span class="pln"> </span><span class="str">'production'</span><span class="pln">    
  </span><span class="pun">?</span><span class="pln"> </span><span class="str">'https://blooming-atoll-75500.herokuapp.com/api/notes'</span><span class="pln">   
  </span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:3001/api/notes'</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
    output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">
      filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    devServer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      contentBase</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">),</span><span class="pln">
      compress</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
      port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    devtool</span><span class="pun">:</span><span class="pln"> </span><span class="str">'source-map'</span><span class="pun">,</span><span class="pln">
    module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">      
        </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="typ">DefinePlugin</span><span class="pun">({</span><span class="pln">        
            BACKEND_URL</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">backend_url</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">]</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> config</span></pre>

<p>
	يستخدم المتغير العام ضمن الشيفرة بالطريقة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_938_6" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">counter</span><span class="pun">,</span><span class="pln"> setCounter</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> setValues</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">([])</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> notes </span><span class="pun">=</span><span class="pln"> useNotes</span><span class="pun">(</span><span class="pln">BACKEND_URL</span><span class="pun">)</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"container"</span><span class="pun">&gt;</span><span class="pln">
      hello webpack </span><span class="pun">{</span><span class="pln">counter</span><span class="pun">}</span><span class="pln"> clicks
      </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={</span><span class="pln">handleClick</span><span class="pun">}</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">press</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;{</span><span class="pln">notes</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> notes on server </span><span class="pun">{</span><span class="pln">BACKEND_URL</span><span class="pun">}&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">    
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إن كانت هناك اختلافات واسعة في تهيئة نسختي الإنتاج والتطوير. فمن الأفضل <a data-ss1615745993="1" data-ss1615746541="1" href="https://webpack.js.org/guides/production/" rel="external nofollow">فصل تهيئة كل نسخة</a> ضمن ملف خاص بها.
</p>

<p>
	يمكن تحري نسخة الإنتاج المجمّمعة من التطبيق محليًا وذلك بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode">
npx static-server
</pre>

<p>
	ستكون هذه النسخة متاحة بشكل افتراضي على العنوان <a data-ss1615745993="1" data-ss1615746541="1" href="http://localhost:9080/" rel="external nofollow">http://localhost:9080</a>.
</p>

<h2>
	استخدام شيفرة Polyfill
</h2>

<p>
	انتهى تطبيقنا الآن وأصبح جاهزًا للعمل مع مختلف المتصفحات الحديثة الموجودة حاليًا ماعدا Internet Explorer. وذلك لأن <a data-ss1615745993="1" data-ss1615746541="1" href="https://wiki.hsoub.com/JavaScript/Promise" rel="external">الوعود</a> التي تستخدمها axios غير مدعومة من قبل IE.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59668" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/IE_axios_not_supported_13.png.55319aa0c7dda7b41c97675e80afb1f6.png" rel=""><img alt="IE_axios_not_supported_13.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59668" data-unique="a9q0n007r" src="https://academy.hsoub.com/uploads/monthly_2021_03/IE_axios_not_supported_13.thumb.png.baa3532dec5ecf311950c268bb3c5a39.png"></a>
</p>

<p>
	هنالك العديد من الأمور التي لا يدعمها IE. وبعضها مؤذٍ كالتابع <a data-ss1615745993="1" data-ss1615746541="1" href="https://wiki.hsoub.com/JavaScript/Array/find" rel="external">find</a> الذي يستخدم للتعامل مع مصفوفات إذ يفوق قدرة هذا المتصفح.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="59667" data-ss1615745993="1" data-ss1615746541="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/find_method_in_IE_14.png.d480aca2ba5a836934cd7f925fe9f452.png" rel=""><img alt="find_method_in_IE_14.png" class="ipsImage ipsImage_thumbnailed" data-fileid="59667" data-unique="er1cvlhnk" src="https://academy.hsoub.com/uploads/monthly_2021_03/find_method_in_IE_14.png.d480aca2ba5a836934cd7f925fe9f452.png"></a>
</p>

<p>
	فلا يكفي في هذه الحالة نقل الشيفرة من نسخة JavaScript حديثة إلى قديمة مدعومة على نطاق أوسع من قبل المتصفحات. يفهم المتصفح IE الوعود قواعديًا، لكنه ببساطة لا يمتلك البنية الوظيفية لتنفيذها. فالخاصية <code>find</code> العائدة للمصفوفات غير معرفة على سبيل المثال.
</p>

<p>
	فإن أردنا أن يكون تطبيقنا متوافقًا مع IE، سنحتاج إلى شيفرة <a data-ss1615745993="1" data-ss1615746541="1" href="https://remysharp.com/2010/10/08/what-is-a-polyfill" rel="external nofollow">polyfill</a>، وهي شيفرة تضيف الوظائف غير المدعومة إلى المتصفحات القديمة.
</p>

<p>
	يمكن إضافة تلك الشيفرات بمساعدة <a data-ss1615745993="1" data-ss1615746541="1" href="https://babeljs.io/docs/usage/polyfill/" rel="external nofollow">webpack and Babel</a> أو بتثبيت إحدى مكتباتها. فشيفرة polyfill التي تقدمها المكتبة <a data-ss1615745993="1" data-ss1615746541="1" href="https://www.npmjs.com/package/promise-polyfill" rel="external nofollow">promise-polyfill</a> سهلة الاستخدام، وليس علينا سوى إضافة ما يلي إلى شيفرتنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_605_80" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">PromisePolyfill</span><span class="pln"> from </span><span class="str">'promise-polyfill'</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">window</span><span class="pun">.</span><span class="typ">Promise</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  window</span><span class="pun">.</span><span class="typ">Promise</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PromisePolyfill</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	فإن لم يكن الكائن <code>Promise</code> موجودًا، أي انه غير مدعوم من قبل المتصفح، سيُخزّن وعد polyfill في المتغير العام. فإن أضيف وعد polyfill إلى الشيفرة وعمل بالشكل المطلوب، فستعمل بقية الشيفرة بلا أية مشاكل.
</p>

<p>
	يمكنك إيجاد قائمة بمكتبات polyfill بزيارة الموقع <a data-ss1615745993="1" data-ss1615746541="1" href="https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills" rel="external nofollow">github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills</a>، كما يمكنك الاطلاع على توافق المتصفحات مع الواجهات البرمجية المختلفة من خلال زيارة الموقع <a data-ss1615745993="1" data-ss1615746541="1" href="https://caniuse.com/" rel="external nofollow">https://caniuse.com</a>، أو من خلال زيارة موقع <a data-ss1615745993="1" data-ss1615746541="1" href="https://developer.mozilla.org" rel="external nofollow">Mozilla</a>.
</p>

<h2>
	إلغاء التهيئة الافتراضية (تحرير المشروع)
</h2>

<p>
	تستخدم الأداة create-react-app برنامج webpack خلف الستار. فإن لم تجد أنّ التهيئة الافتراضية كافية، يمكنك إلغاءها بعملية تسمى تحرير المشروع (<a data-ss1615745993="1" data-ss1615746541="1" href="https://create-react-app.dev/docs/available-scripts/#npm-run-eject" rel="external nofollow">eject</a>)، والتي يتم التخلص فيها من العمليات خلف الكواليس، كما تُخزّن التهيئة الافتراضية في المجلد <code>config</code> وفي ملف <code>package.json</code> معدّل.
</p>

<p>
	ولن يكون هناك سبيل إلى العودة عند تحرير المشروع، وعلينا تعديل أو صيانة قيم التهيئة يدويًا بعد ذلك. مع ذلك فالتهيئة الافتراضية ليست بهذه البساطة، وبدلًا من تحرير تطبيقك، يمكنك كتابة تهيئة webpack الخاصة بك من الصفر.
</p>

<p>
	كما ننصحك بقراءة ملفات التهيئة للمشاريع المحررة بعناية، فهي ذات قيمة تعليمية كبيرة.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1615745993="1" data-ss1615746541="1" href="https://fullstackopen.com/en/part7/webpack" rel="external nofollow">webpack</a> من سلسلة <a data-ss1615745993="1" data-ss1615746541="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1169</guid><pubDate>Sun, 14 Mar 2021 18:36:38 +0000</pubDate></item><item><title>&#x623;&#x64A;&#x647;&#x645;&#x627; &#x623;&#x641;&#x636;&#x644; &#x643;&#x623;&#x62F;&#x627;&#x629; &#x645;&#x633;&#x627;&#x639;&#x62F;&#x629; Webpack &#x623;&#x645; Browserify &#x645;&#x639; Gulp&#x61F;</title><link>https://academy.hsoub.com/programming/workflow/%D8%A3%D9%8A%D9%87%D9%85%D8%A7-%D8%A3%D9%81%D8%B6%D9%84-%D9%83%D8%A3%D8%AF%D8%A7%D8%A9-%D9%85%D8%B3%D8%A7%D8%B9%D8%AF%D8%A9-webpack-%D8%A3%D9%85-browserify-%D9%85%D8%B9-gulp%D8%9F-r867/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_04/Webpack-2.jpg.b6c1cdf309c86717024d4438ed6fa550.jpg" /></p>
<p>
	إن تزايد تعقيد تطبيقات الوِب في وقتنا الحالي جعل من ضرورة زيادة قابلية تطبيقات الوِب للتوسعة والتطوير أمرًا في غاية الأهمية. وعلى الرغم من أن الحلول القديمة المخصصة لكتابة شيفرات جافاسكربت و jQuery كانت فعَالة وكافية إلى حدٍ ما، ولكن بناء تطبيق وِبٍ في وقتنا الحاضر يتطلب درجة كبير من الانضباط والمنهجية الرسمية في تطوير البرمجيات. وهذه بعض الأمثلة على الممارسات الجيدة في تطوير البرمجيات:
</p>

<ul>
	<li>
		استخدام اختبارات الوحدة (Unit tests) وذلك للتأكد من أن تعديلات ما على الشيفرة البرمجية لن يعطّل شيفرة أُخرى.
	</li>
	<li>
		استخدام عملية كشف الأخطاء المحتملة (Linting) لضمان كتابة شيفرة برمجية خالية من الأخطاء.
	</li>
	<li>
		استخدام طرق مختلفة للبنية الهيكلية للشيفرة البرمجية لكلّ من وضع التطوير ووضع النشر.
	</li>
</ul>

<p>
	بالإضافة إلى ذلك فإن معايير الوِب بشكلها الحالي طرحت تحديات جديدة لتطوير تطبيقات الوِب. على سبيل المثال صفحات الوِب الحالية تُنشئ الكثير من الطلبات غير المتزامنة والّذي بدوره يؤدي إلى خفض أداء تطبيق الوِب انخفاضًا كبيرًا بسبب معالجة طلبات ملفات التنسيق والجافاسكربت. إذ أن لكل طلبٍ من هذه الطلبات (حتى وإن كان لملف صغير الحجم) فإنه سيحتاج إلى ترويسة اتصال (Header) وكُلفة إنشاء اتصال (handshakes). وهذه المشكلة تحديدًا تُعالجُ من خلال تجميع الملفات معًا لتصبح في ملف واحد وبذلك سنستدعي ملف جافاسكربت وملف تنسيق بدلًا من المئات الملفات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36366" href="https://academy.hsoub.com/uploads/monthly_2020_04/1.png.d128a403144cea33a8d8cbad1aa8a75b.png" rel="" data-fileext="png"><img alt="1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36366" data-unique="512fk1k13" src="https://academy.hsoub.com/uploads/monthly_2020_04/1.thumb.png.6505d3ef9ec4e6ef59b300dc3530d843.png"></a>
</p>

<p>
	من الشائع في وقتنا الحالي استخدام لغات ذات معالجة مُسبّقة (language preprocessors) مثل: SASS و JSX والّتي تُترجم من هذه اللغات إلى ملفات جافاسكربت وملفات تنسيقات. بالإضافة إلى ذلك شاع أيضًا استخدام المحوّل (Transpilers) وهو محوّل يغيّر الشيفرة البرمجية من إصدار معين للغة جافاسكربت إلى إصدار أقدم، وذلك لضمان عمل هذه الشيفرة البرمجية على جميع المتصفحات. مثل المحوّل Babel.
</p>

<p>
	إن هذه المهام (والّتي لا علاقة لها بالمنطق البرمجي لتطبيق الوِب بحد ذاته) تمثل عبئًا على على المطور. ولكن لحسن الحظ يوجد حلٌ لهذا الأمر وهو منفذ المهام (Task Runner) والّذي جاء لمساعدتنا في أتمتة هذه المهام حتى نستطيع أن نحسّن من بيئة التطوير مع التركيز على بناء المنطق البرمجي لتطبيق الوِب. بمجرد ضبط الإعدادات الخاصة بمنفذ المهام كلّ ما عليك فعله هو استدعاء أمرٍ على الطرفية وستُنفذ بعدها المهام المؤتمتة.
</p>

<p>
	سأستخدم الأداة Gulp كمنفذ للمهام وذلك لأنه مناسب للمبرمجين وسهل التعلّم ومفهوم (تحدثنا في <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-gulp-r663/" rel="">مقال</a> سابق كيفية استخدام هذه الأداة بكلّ التفاصيل).
</p>

<h2>
	مقدمة سريعة لأداة Gulp
</h2>

<p>
	تتألف واجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>) لهذه الأداة من أربع دوالّ:
</p>

<ul>
	<li>
		<code>gulp.src</code>
	</li>
	<li>
		<code>gulp.dest</code>
	</li>
	<li>
		<code>gulp.task</code>
	</li>
	<li>
		<code>gulp.watch</code>
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36364" href="https://academy.hsoub.com/uploads/monthly_2020_04/2.png.765fc5dc27e7ba54dad75c138d4b8a5d.png" rel="" data-fileext="png"><img alt="2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36364" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_04/2.thumb.png.981b0f8ee0f91e5708d9de47890b448d.png"></a>
</p>

<p>
	في المثال التالي نلاحظ أن المهمة <code>my-first-task</code> ستستخدم ثلاثة دوال من أصل الأربعة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_6" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'my-first-task'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  gulp</span><span class="pun">.</span><span class="pln">src</span><span class="pun">(</span><span class="str">'/public/js/**/*.js'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">concat</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">minify</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">gulp</span><span class="pun">.</span><span class="pln">dest</span><span class="pun">(</span><span class="str">'build'</span><span class="pun">))</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عند تنفيذ المهمة <code>my-first-task</code> ستصغّر أولًا جميع الملفات المطابقة للنمط <code>‎/public/js/**/*.js‏</code> ومن ثمّ ستُنقل إلى المجلد <code>build</code>.
</p>

<p>
	الجميل في التعليمة <code>‏pipe()‎.</code> هي أنه يمكنك أخذ مجموعة من ملفات الإدخال وتمريرها عبر الأنبوب لتنفيذ بعض التحويلات المناسبة عليهم ومن ثمّ إعادة ملفات الخرج المطلوبة. لجعل الأمور أكثر توافقية. غالبًا ما تنفذ العمليات المطلوبة في الإنبوب (مثل: <code>minify()‎</code>) من خلال مكتبات مدير الحزم <code>npm</code>، ونتيجة ذلك من النادر جدًا في التطبيق الفعلي لهذه االعمليات أن نحتاج لكتابة تفاصيل هذه العمليات كتابةً يدوية إلا إذا أردت أن تُعيد تسمية الملفات في الأنبوب. من الجدير بالذكر أن Gulp يَستخدم المجاري (streams) الّتي توفرها <code>node.js</code>، وهذا يسمح بتمرير البيانات الّتي ستُعالَج عبر الأنابيب (pipes) وهذا ما تفعله الدالة ‎.pipe()<code>‎</code>؛ لشرحٍ تفصيليٍ عن المجاري في <code>node.js</code>، سأحيلك إلى هذه <a href="https://github.com/substack/stream-handbook" rel="external nofollow">المقالة</a>.
</p>

<p>
	الخطوة التالية لفهم Gulp هي فهم مصفوفة تبعيات المهام.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_8" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'my-second-task'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'lint'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bundle'</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	في هذا المثال إن المهمة <code>my-second-task</code> تُعيد نتيجة الدالة المجهولة (anonymous function)، وذلك بعد اكتمال مهمة <code>lint</code> ومهمة التجميع <code>bundle</code>. وبذلك يُسمح لنا بفصل الاهتمامات عن بعضها بعضًا كما يمكنك أيضًا إنشاء سلسلة من المهام الصغيرة بمسؤولية واحدة مثل تحويل <code>LESS</code> إلى <code>CSS</code>. وإنشاء مهمة رئيسية (Master Task) والّتي ستستدعي ببساطة جميع المهام الأخرى (الصغيرة) عبر مصفوفة من تبعيات المهام.
</p>

<p>
	وأخيرًا لدينا التعليمة <code>gulp.watch</code> والّذي يراقب التغييرات في ملف (أو ملفات) ما والمطابق لنمط مرّر لها، وما إن يحدث تغيير ما في هذا الملف حتى تُنفذّ سلسلة من المهام المحددة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_10" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'my-third-task'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  gulp</span><span class="pun">.</span><span class="pln">watch</span><span class="pun">(</span><span class="str">'/public/js/**/*.js'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'lint'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'reload'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	في المثال السابق أي تغيير في الملفات الّتي تطابق النمط <code>/public/js/**/*.js</code> سيؤدي إلى تشغيل مهمة كشف الأخطاء المحتملة <code>lint</code> وبعدها مهمة إعادة التحميل <code>reload</code>. إن الاستخدام الشائع للتعليمة <code>gulp.watch</code> هو تشغيل عمليات إعادة التحميل المباشر في المتصفح، وهي ميزة رائعة جدًا أثناء مرحلة التطوير ولن تستطيع العمل بدونها بمجرد أن تجربها.
</p>

<p>
	وإلى هنا نستطيع القول بأننا فهمنا كلّ ما سنحتاج لاستخدامه في الأداة Gulp.
</p>

<h2>
	أين سنستخدم أداة Webpack؟
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36365" href="https://academy.hsoub.com/uploads/monthly_2020_04/3.png.4e1eba7631cf153320c5c496090720a6.png" rel="" data-fileext="png"><img alt="3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36365" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_04/3.thumb.png.0a6beaa9b32d795d78af544b0d8a7ae3.png"></a>
</p>

<p>
	عندما نستخدام نمط <code>CommonJS</code> فإن تجميع كلّ ملفات الجافاسكربت ليصبحوا في ملف واحد ليس بهذه البساطة. إذ إن الخاصية (entry point) والّتي تُسند عادةً للقيمة <code>index.js</code> أو <code>app.js</code> مع سلسلة من التعليمات <code>require</code> أو <code>import</code> الموجودة في أعلى الملف. وسيكون شكل ملف جافاسكربت في الإصدار ES5 على الشكل التالي:
</p>

<pre class="ipsCode">var Component1 = require('./components/Component1');
var Component2 = require('./components/Component2');
</pre>

<p>
	وسيكون شكل ملف جافاسكربت في الإصدار ES6 على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_12" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">Component1</span><span class="pln"> from </span><span class="str">'./components/Component1'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Component2</span><span class="pln"> from </span><span class="str">'./components/Component2'</span><span class="pun">;</span></pre>

<p>
	إن المثالين السابقين يجلبان التبعيات قبل تنفيذ بقية الشيفرات البرمجية في ملف <code>app.js</code>، وممكن أن يكون لهذه التبعيات تبعيات أخرى والّتي ستُجلب أيضًا. وبالإضافة إلى ذلك من الممكن أن تُستدعى نفس التبعية في أماكن متعددة في تطبيق الوِب خاصتك. ولكننا نريد جلب هذه التبعية مرة واحدة فقط. لهذا فأن كانت شجرة التبعيات بعمق عدة مستويات (أي التبعيات ذات هرمية كبيرة) فعندها ستزداد صعوبة تجميع هذه التبعيات في ملف واحد. ولكن لحسن الحظ يوجد حلّ رائع لهذه المشكلة وهو مُجمّع الحزم (الوحدات) مثل:<a href="http://browserify.org/" rel="external nofollow">Browserify</a> أو Webpack.
</p>

<h2>
	لماذا يفضل المطورون استخدام Webpack بدلًا من Gulp؟
</h2>

<p>
	بما أن أداة Webpack (تحدثنا في <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/" rel="">مقال سابق</a> عن كيفية استخدام مجمع الحزم Webpack وأشهر طرق استعمالها في المشاريع) لتجمّيع الحزم وأداة Gulp لتنفيذ المهام من الممكن أن نتوقع أن نرى استخدام هاتين الأداتين مع بعضهما بعضًا، ولكن هنالك توجّه عام نحو استخدام Webpack بدلًا من Gulp وخصيصًا في مجتمع مطوري React. ولكن لما هذا التوجه؟
</p>

<p>
	ببساطة إن قوة أداة Webpack مكنتها من تنفيذ الغالبية العظمى من المهام الملقاة على عاتق منفذ المهام (مثل: Gulp أو أي منفذ مهام عمومًا). فعلى سبيل المثال توفر Webpack خيارات تصغير وخرائط الشيفرة البرمجية المحوّلة Source Maps (لمزيد من المعلومات عنها يمكنك الإطلاع على <a href="https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map" rel="external nofollow">المقال</a> التالي) للشيفرة البرمجية المُجمعّة.
</p>

<p>
	بالإضافة إلى ذلك يمكن استخدامها كوسيط (Middleware) من خلال خادم مخصص يدعى <code>webpack-dev-server</code> والّذي يدعم كلًا من إعادة التحميل المباشر (live reloading) وإعادة التحميل النشط (hot reloading) لصفحات الوِب (والّتي سنتحدث عنها لاحقًا في هذا المقال). وكما يمكنك أيضًا تحويل الشيفرة البرمجية (transpiling) من إصدار جافاسكربت حديث مثل ES6 إلى إصدارٍ قديم مثل: ES5. ويمكنك أيضًا استخدام طريقة المعالجات المُسبقة (pre-processors) أو المُلحقة (post-processors) لملفات التنسيق. وبذلك تُترك عملية إختبار الوحدة (Unit Tests) وعملية كشف الأخطاء المحتملة (linting) كمهام رئيسية مستقلة نظرًا من كوننا قلصنا ما لا يقلّ عن ستة مهام محتملة للأداة Gulp لمهمتين فقط. يلجأ العديد من المطورين لاستخدام <code>NPM Scripts</code> بدلًا من استخدام Gulp، وذلك لتجنب إضافة أداة <code>Gulp</code> إلى المشروع في حين وجود بديل قوي ينوب عنها.
</p>

<p>
	في الحقيقة إن السيئة الرئيسية لاستخدام <code>Webpack</code> هي صعوبة ضبط إعداداته، مما يجعله خيارًا غير مرغوب به في حال أردنا إنشاء مشروع وتشغيله بأسرع وقتٍ ممكن.
</p>

<h2>
	طرق إعداد منفذ مهام
</h2>

<p>
	سنتعرف على ثلاث طرق لإنشاء وإعداد منفذ مهام مساعد في المشاريع البرمجية. وكلّ واحدٍ منهم سينفذ المهام التالية:
</p>

<ul>
	<li>
		إعداد خادم تطوير مع ميزة إعادة التحميل المباشر (live reloading) فور حدوث أي تعديل على صفحة الوِب المُراقبة.
	</li>
	<li>
		تجميع ملفات التنسيق والجافاسكربت (بالإضافة إلى تحويل الشيفرة البرمجية للغة جافاسكربت من الإصدار ES6 إلى ES5، وتحويل ملفات التنسيق من SASS إلى ملفات CSS وخرائط الشيفرة البرمجية المحوّلة) وذلك بطريقة قابلة للتطوير والتوسّع لهذه الملفات المحوّلة.
	</li>
	<li>
		تشغيل اختبار الوحدة (Unit Tests) سواءً كمهمة قائمة بحد ذاتها أو في وضع المراقبة.
	</li>
	<li>
		تشغيل عملية الكشف عن الأخطاء المحتملة (Linting) سواءً كمهمة قائمة بحد ذاتها أو في وضع المراقبة.
	</li>
	<li>
		توفير القدرة على جمع كلّ المهام السابقة عبر أمر واحد لكتابته عبر الطرفية.
	</li>
	<li>
		وجود أمر آخر لتجميع الملفات وضغطها أو تنفيذ تحسينات أخرى عليها من أجل عملية النشر.
	</li>
</ul>

<p>
	وستكون طرق إعداد منفذ المهام على الشكل التالي:
</p>

<ol>
	<li>
		Gulp + Browserify
	</li>
	<li>
		Gulp + Webpack
	</li>
	<li>
		Webpack + NPM Scripts
	</li>
</ol>

<p>
	سنعتمد في تطبيقنا العملي على استخدام React للواجهات الأمامية. في الحقيقة أردت في البداية أن أعمل بدون إطار عمل، ولكن استخدام React سيُبسط مسؤوليات منفذ المهام، إذ سيلزمنا فعليًا وجود ملف <code>HTML</code> واحد فقط، بالإضافة إلى أن React يعمل بسلاسة مع نمط <code>CommonJS</code> ولذلك اعتمدت أخيرًا على استخدامه.
</p>

<p>
	سنعمد على توضيح جميع المزايا والعيوب الخاصة بكل طريقة إعداد، وذلك لمنحك المعلومات الكاملة والصورة الشاملة لاتخاذ قرارٍ صائبٍ يناسب احتياجات المشروع الّذي تعكف على تطويره.
</p>

<p>
	سننشئ <a href="https://github.com/ericgrosse/task-runner-bundler-comparison" rel="external nofollow">مشروعًا</a> على Git وسنضيف له ثلاثة تفريعات من أجل اختبار كلّ طريقة من الطرق
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_14" style=""><span class="pln">git checkout </span><span class="pun">&lt;</span><span class="pln">branch name</span><span class="pun">&gt;</span><span class="pln">
npm prune </span><span class="pun">(</span><span class="pln">optional</span><span class="pun">)</span><span class="pln">
npm install
gulp </span><span class="pun">(</span><span class="pln">or npm start</span><span class="pun">,</span><span class="pln"> depending on the setup</span><span class="pun">)</span></pre>

<p>
	لنناقش الآن كلّ تفريعة (طريقة) من هذه الفروع على حدة.
</p>

<p>
	ستكون البنية الهرمية لمجلد المشروع على الشكل التالي:
</p>

<pre class="ipsCode">- app
 - components
 - fonts
 - styles
- index.html
- index.js
- index.test.js
- routes.js
</pre>

<p>
	سنتطرق لشرح أهم الملفات الموجودة في المشروع وذلك حرصًا على كمال المعلومة. سيكون الملف <code>index.html</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3117_16" style=""><span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">http-equiv</span><span class="pun">=</span><span class="atv">"X-UA-Compatible"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"IE=edge"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"bundle.css"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Gulp Browserify Setup</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"app"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"bundle.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	نلاحظ أن هذا الملف بسيط جدًا إذ إنه يُحمّلُ تطبيق <code>React</code> في الوسم <code>&lt;div id="app"&gt;&lt;/div&gt;</code> ولن نستخدم إلا ملف واحد للتنسيقات وملف آخر للجافاسكربت، في الواقع في طريقة الإعداد هذه لن نستخدم حتى ملف التنسيق <code>bundle.css</code>.
</p>

<p>
	وسيكون الملف <code>index.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_18" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">render</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-dom'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="typ">Router</span><span class="pun">,</span><span class="pln"> browserHistory</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> routes from </span><span class="str">'./routes'</span><span class="pun">;</span><span class="pln">

render</span><span class="pun">(&lt;</span><span class="typ">Router</span><span class="pln"> history</span><span class="pun">={</span><span class="pln">browserHistory</span><span class="pun">}</span><span class="pln"> routes</span><span class="pun">={</span><span class="pln">routes</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;,</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">));</span></pre>

<p>
	نلاحظ أن هذا الملف سيكون بمثابة نقطة الدخول (entry point) لتطبيقنا. إذ إننا بصدد تحميل React Router في الوسم <code>div</code> مع السِمة <code>app</code> الّتي أشرنا لها في الملف السابق.
</p>

<p>
	وسيكون الملف <code>routes.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_20" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="typ">Route</span><span class="pun">,</span><span class="pln"> </span><span class="typ">IndexRoute</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./components/App'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">HomePage</span><span class="pln"> from </span><span class="str">'./components/home/HomePage'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AboutPage</span><span class="pln"> from </span><span class="str">'./components/about/AboutPage'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">ContactPage</span><span class="pln"> from </span><span class="str">'./components/contact/ContactPage'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Route</span><span class="pln"> path</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> component</span><span class="pun">={</span><span class="typ">App</span><span class="pun">}&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">IndexRoute</span><span class="pln"> component</span><span class="pun">={</span><span class="typ">HomePage</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Route</span><span class="pln"> path</span><span class="pun">=</span><span class="str">"about"</span><span class="pln"> component</span><span class="pun">={</span><span class="typ">AboutPage</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Route</span><span class="pln"> path</span><span class="pun">=</span><span class="str">"contact"</span><span class="pln"> component</span><span class="pun">={</span><span class="typ">ContactPage</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="typ">Route</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	نلاحظ أن هذا الملف يحدد مسارات المشروع. وستكون المسارات <code>/</code> و <code>‎/about</code> و <code>‎/contact</code> مرتبطة مع المكونات <code>HomePage</code> و <code>AboutPage</code> و <code>ContactPage</code> على التتالي.
</p>

<p>
	وسيكون الملف <code>index.test.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_22" style=""><span class="kwd">import</span><span class="pln"> expect from </span><span class="str">'expect'</span><span class="pun">;</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'Array'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should return -1 when the value is not present'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    expect</span><span class="pun">(-</span><span class="lit">1</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">,</span><span class="lit">3</span><span class="pun">].</span><span class="pln">indexOf</span><span class="pun">(</span><span class="lit">4</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should correctly filter elements in an array'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> arr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">,</span><span class="lit">3</span><span class="pun">,</span><span class="lit">4</span><span class="pun">,</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> newArr </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">el </span><span class="pun">=&gt;</span><span class="pln"> el </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">newArr</span><span class="pun">.</span><span class="pln">length</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should correctly map elements in an array'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> arr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">,</span><span class="lit">3</span><span class="pun">,</span><span class="lit">4</span><span class="pun">,</span><span class="lit">5</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> newArr </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">el </span><span class="pun">=&gt;</span><span class="pln"> el </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">newArr</span><span class="pun">).</span><span class="pln">toEqual</span><span class="pun">([</span><span class="lit">2</span><span class="pun">,</span><span class="lit">4</span><span class="pun">,</span><span class="lit">6</span><span class="pun">,</span><span class="lit">8</span><span class="pun">,</span><span class="lit">10</span><span class="pun">]);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'Object'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should convert an int to a string with the toString method'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> val </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> valToString </span><span class="pun">=</span><span class="pln"> val</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">val</span><span class="pun">).</span><span class="pln">toNotBe</span><span class="pun">(</span><span class="pln">valToString</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">valToString</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="str">'5'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should correctly test whether an object has a certain property'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> obj </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">};</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">.</span><span class="pln">hasOwnProperty</span><span class="pun">(</span><span class="str">'a'</span><span class="pun">)).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">.</span><span class="pln">hasOwnProperty</span><span class="pun">(</span><span class="str">'c'</span><span class="pun">)).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'String'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should convert a string to lowercase'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'HELLO'</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> strLower </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">();</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">strLower</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="str">'hello'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should convert a string to uppercase'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> strLower </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">();</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">strLower</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="str">'HELLO'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should remove spaces from both ends of a string'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> str </span><span class="pun">=</span><span class="pln"> </span><span class="str">' hello there '</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> strTrimmed </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">();</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">strTrimmed</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="str">'hello there'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'should split the string into an array of strings based on the provided delimiter'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> str </span><span class="pun">=</span><span class="pln"> </span><span class="str">'she-sells-seashells-down-by-the-seashore'</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> strSplit </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'-'</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="typ">Array</span><span class="pun">.</span><span class="pln">isArray</span><span class="pun">(</span><span class="pln">strSplit</span><span class="pun">)).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">strSplit</span><span class="pun">.</span><span class="pln">length</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="lit">7</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نلاحظ أن هذا الملف يحتوي على سلسلة من اختبارات الوحدة (Unit Tests) والّتي ستختبر سلوك الشيفرة الأصلية للجافاسكربت (Native JavaScript) في عملية النشر الفعلية للتطبيق يمكنك إنشاء اختبار لكل مكون React (اختبار واحد على الأقل لكل مكوّن يعالج حالة معينة) واختبار السلوك الخاص بإطار العمل React. وعلى أية حال يكفي أن يكون لديك اختبار وحدة بسيط والّذي نستطيع تشغيله في وضع المراقبة (watch mode عند تفعيل هذا الوضع تثبت ملفات مخصصة لمراقبة أي تغييرات تحصل في الملفات الأخرى وفي حال حصول أي تغييرات في الملفات سيعاد ترجمة الملفات للحصول على الخرج الجديد).
</p>

<p>
	وسيكون الملف <code>components/App.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_24" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="typ">PropTypes</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Header</span><span class="pln"> from </span><span class="str">'./common/Header'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  render</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Header</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">props</span><span class="pun">.</span><span class="pln">children</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="typ">App</span><span class="pun">.</span><span class="pln">propTypes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  children</span><span class="pun">:</span><span class="pln"> </span><span class="typ">PropTypes</span><span class="pun">.</span><span class="pln">object</span><span class="pun">.</span><span class="pln">isRequired
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	يمكن اعتبار الملف السابق حاوية لجميع مكونات العرض الموجودة لدينا. إذ تحتوي كلّ صفحة لدينا على الترويسة <code>&lt;Header/&gt;</code> بالإضافة إلى <code>this.props.children</code> والّذي يقيّم لعرض محتواه في الصفحة نفسها. فعلى سبيل المثال إذا كان في المتصفح <code>/contact</code> سيقيّم إلى <code>ContactPage</code>.
</p>

<p>
	وسيكون الملف <code>components/home/HomePage.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_26" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="typ">Jumbotron</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Grid</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Row</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Col</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Panel</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-bootstrap'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HomePage</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  render</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"HomePage"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Jumbotron</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="typ">Grid</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Home</span><span class="pln"> </span><span class="typ">Page</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="typ">Grid</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="typ">Jumbotron</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Grid</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="typ">Row</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="typ">Col</span><span class="pln"> sm</span><span class="pun">={</span><span class="lit">6</span><span class="pun">}&gt;</span><span class="pln">
              </span><span class="pun">&lt;</span><span class="typ">Panel</span><span class="pln"> header</span><span class="pun">=</span><span class="str">"Panel 1"</span><span class="pun">&gt;</span><span class="pln">
                </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Content</span><span class="pln"> A</span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
                </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Content</span><span class="pln"> B</span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="pun">&lt;/</span><span class="typ">Panel</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="typ">Col</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="typ">Col</span><span class="pln"> sm</span><span class="pun">={</span><span class="lit">6</span><span class="pun">}&gt;</span><span class="pln">
              </span><span class="pun">&lt;</span><span class="typ">Panel</span><span class="pln"> header</span><span class="pun">=</span><span class="str">"Panel 2"</span><span class="pun">&gt;</span><span class="pln">
                </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Content</span><span class="pln"> A</span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
                </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Content</span><span class="pln"> B</span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="pun">&lt;/</span><span class="typ">Panel</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="typ">Col</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="typ">Row</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="typ">Grid</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">HomePage</span><span class="pun">;</span></pre>

<p>
	هذا الملف سيكون الصفحة الرئيسية المعروضة. استخدمت <code>react-bootstrap</code> نظرًا لأن نظام الشبكة (Grid) في إطار العمل <code>Bootstrap</code> ممتاز لإنشاء صفحات متجاوبة. وعند استخدامه استخدامًا صحيحًا سيُقّلل عدد استعلامات الوسائط (<a href="https://wiki.hsoub.com/CSS/@media" rel="external">media queries</a>) الّتي سنكتبها للأجهزة صغيرة الحجم تقليلًا كبيرًا.
</p>

<p>
	المكونات الأخرى المتبقية (مثل: <code>Header</code> و <code>AboutPage</code> و <code>ContactPage</code>) مبنية بطريقة مشابهة (باستخدام <code>react-bootstrap</code> بدون التلاعب بالحالة [state manipulation]).
</p>

<p>
	أما الآن لنتحدث أكثر عن ملفات التنسيق.
</p>

<h2>
	منهجية الملفات التنسيق
</h2>

<p>
	أسلوبي المفضل في تنسيق ملفات React هو امتلاك ملف تنسيق خاص لكلّ مكوّن من المكونات. إذ سيكون هذا التنسيق ضمن نطاق هذا المكون فقط. ستلاحظ أنه في كلّ مكون من المكونات هنالك وسم <code>div</code> ذو مستوى أعلى وسيكون أسم الصنف التابع له مطابق لأسم المكون نفسه. لذلك المكون <code>HomePage.js</code> سيكون مغلف بوسم له الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3117_28" style=""><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"HomePage"</span><span class="tag">&gt;</span><span class="pln">
  ...
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	وسيكون هنالك أيضًا ملف <code>HomePage.scss</code> والّذي سيكون مهيكل على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_30" style=""><span class="lit">@import</span><span class="pln"> </span><span class="str">'../../styles/variables'</span><span class="pun">;</span><span class="pln">

</span><span class="pun">.</span><span class="typ">HomePage</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// Content here</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>لماذا هذه المنهجية مفيدة للغاية؟</strong> لأنها تنتج لنا وحدات (Modular) ذات جودة عالية. مما يُلغي الكثير من المشاكل غير المرغوب بها في التنسيق.
</p>

<p>
	لنفترض أن لدينا مكونين React وهما <code>Component1</code> و <code>Component2</code>. وفي كلّ واحد منهما نريد إعادة تنسيق الوسم <code>h2</code> ليصبح حجم الخط كما في الملف التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_3117_32" style=""><span class="com">/* Component1.scss */</span><span class="pln">
</span><span class="pun">.</span><span class="typ">Component1</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  h2 </span><span class="pun">{</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">30px</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* Component2.scss */</span><span class="pln">
</span><span class="pun">.</span><span class="typ">Component2</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  h2 </span><span class="pun">{</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60px</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إن حجم الخط المخصص للوسم <code>h2</code> في المكونين <code>Component1</code> و <code>Component2</code> مكتوبان بمعزل عن بعضهما البعض سواءً أكان المكونين متجاورين أو متداخلين لن يؤثر أحدهما على الآخر. أي أن المكون سيبدو شكله مثل ما خُطط له تمامًا بغض النظر عن مكان وجوده. في الحقيقة إن الأمر ليس بهذه السهولة دائمًا، ولكنه وبكل تأكيد خطوة كبيرة باتجاه أفضل الممارسات الصحيحة للتنسيق.
</p>

<p>
	وبالنسبة للتنسيقات المُسبقة للمكونات (per-component styles) أحب أن أخصص مجلد تنسيقات <code>styles</code> يحتوي على جميع التنسيقات العامة في الملف <code>global.scss</code> بجانب تنسيقات SASS مخصصة والّتي تكون مسؤولة عن معالجة التنسيقات المخصّصة (في حالتنا مثل: <code>‎_fonts.scss</code> و <code>‎_variables.scss</code> من أجل الخطوط والمتغيّرات على التوالي). تتيح لنا هذه التنسيقات العامة تحديد الشكل العام للتطبيق بأكمله بينما يمكن للتنسيقات الأخرى المخصصة أن تُستورد بحسب الحاجة لها.
</p>

<p>
	والآن بعد أن استكشفنا بعمق الشيفرة البرمجية المشتركة بين كلِّ طرق إعداد منفذ المهام. لننتقل إلى أول طريقة منهم.
</p>

<h2>
	1. طريقة Gulp + Browserify
</h2>

<p>
	يتكون ملف <strong><a href="https://github.com/ericgrosse/task-runner-bundler-comparison/blob/gulp-browserify-setup/gulpfile.js" rel="external nofollow">gulpfile.js</a></strong> من 22 سطرًا من تعليمات لاستيراد المكتبات والحزم و150 سطرًا من الشيفرات البرمجية الأخرى. لذا ومن أجل الإيجاز سنراجع بالتفاصيل أهم النقاط الرئيسية في هذا الملف (مثل: <code>js</code> و <code>css</code> و <code>server</code> و <code>watch</code> و <code>default</code>).
</p>

<h3>
	محزم ملفات جافاسكربت
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_34" style=""><span class="com">// ضبط إعداد Browserify</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> browserify</span><span class="pun">({</span><span class="pln">
  entries</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">entry</span><span class="pun">],</span><span class="pln">
  debug</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  plugin</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln"> </span><span class="pun">[]</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">hmr</span><span class="pun">,</span><span class="pln"> watchify</span><span class="pun">],</span><span class="pln">
  cache</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{},</span><span class="pln">
  packageCache</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="pun">.</span><span class="pln">transform</span><span class="pun">(</span><span class="str">'babelify'</span><span class="pun">);</span><span class="pln">
b</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'update'</span><span class="pun">,</span><span class="pln"> bundle</span><span class="pun">);</span><span class="pln">
b</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'log'</span><span class="pun">,</span><span class="pln"> gutil</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span><span class="pln">

</span><span class="pun">(...)</span><span class="pln">

gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'js'</span><span class="pun">,</span><span class="pln"> bundle</span><span class="pun">);</span><span class="pln">

</span><span class="pun">(...)</span><span class="pln">



</span><span class="kwd">function</span><span class="pln"> bundle</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> b</span><span class="pun">.</span><span class="pln">bundle</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> gutil</span><span class="pun">.</span><span class="pln">log</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="pln">gutil</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Browserify Error'</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">source</span><span class="pun">(</span><span class="str">'bundle.js'</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> minifyJS</span><span class="pun">()))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(!</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> sourcemaps</span><span class="pun">.</span><span class="pln">init</span><span class="pun">({</span><span class="pln">loadMaps</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">})))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(!</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> sourcemaps</span><span class="pun">.</span><span class="pln">write</span><span class="pun">()))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">gulp</span><span class="pun">.</span><span class="pln">dest</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">baseDir</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ في الدالّة <code>bundle</code> أن ملفات الجافاسكربت ستُحزّم باستخدام Browserify وستُستخدم خرائط الشيفرة البرمجية المحوّلة (Source maps) في وضع التطوير بينما ستُستخدم عملية التصغير لملفات جافاسكربت في وضع الإنتاج.
</p>

<p>
	في الحقيقة إن هذا النهج سيء وذلك لعدة أسباب إحداها أن المهمة ستقسّم إلى ثلاثة أجزاء منفصلة. إذ في البداية سننشئ كائن للتحزيم وليكن <code>b</code> وسنمرر له بعض الخيارات المطلوبة، ومن ثمّ نحدد بعض مُعالِجات الأحداث (event handlers). ثم لدينا مهمة Gulp والّتي يجب أن نمرر لها اسم الدالة بدلًا من تمريرها سطريًا (إن <code>b.on('update')</code> تستخدم نفس الطريقة). إن هذه الطريقة غير أنيقة مثل الطريقة الموجودة في مهام Gulp والّتي تتطلب فقط تمرير <code>gulp.src</code> وتوجيه بعض التغييرات.
</p>

<p>
	هناك مشكلة أخرى تجبرنا على اتباع طرق مختلفة لإعادة تحميل ملفات <code>html</code> و <code>css</code> و <code>js</code> في المتصفح. لاحظ طريقة إعداد مهمة المراقب الموضحة في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_36" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'watch'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  livereload</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">({</span><span class="pln">basePath</span><span class="pun">:</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">});</span><span class="pln">

  gulp</span><span class="pun">.</span><span class="pln">watch</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">html</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'html'</span><span class="pun">]);</span><span class="pln">
  gulp</span><span class="pun">.</span><span class="pln">watch</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">css</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'css'</span><span class="pun">]);</span><span class="pln">
  gulp</span><span class="pun">.</span><span class="pln">watch</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">js</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    runSequence</span><span class="pun">(</span><span class="str">'lint'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عندما سيحصل أي تغيير في ملف <code>html</code> سيعاد تشغيل مهمة <code>html</code> من جديد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_38" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'html'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> gulp</span><span class="pun">.</span><span class="pln">src</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">html</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">gulp</span><span class="pun">.</span><span class="pln">dest</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">baseDir</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(!</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> livereload</span><span class="pun">()));</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إن آخر تعليمة في الأنبوب هي عملية إعادة التحميل المباشر<code>livereload()</code> إذا كانت قيمة الخاصية <code>NODE_ENV</code> ليست <code>production</code>، ستُحدث الصفحة في المتصفح تحديثًا تلقائيًا.
</p>

<p>
	يتكرر نفس السيناريو من أجل مُراقب ملفات التنسيق. إذ عند حدوث أي تغيير في أحد ملفات التنسيق تُستدعى المهمة <code>css</code> ويعاد تشغيلها من جديد، وأخر تعليمة في الأنبوب تكون لاستدعاء التحميل المباشر في المتصفح وذلك لعرض التغييرات مباشرة.
</p>

<p>
	غير أن المراقب الخاص بملفات الجافاسكربت لا يستدعي مهمة <code>js</code> على الإطلاق، وإنما معالج الأحداث في الأداة <code>Browserify</code> يتعامل مع إعادة التحميل باستخدام منهجية مختلفة تمامًا (يطلق عليها اسم مبدل الوحدات النشط [hot module replacement]). إن التضارب في هذه المنهجية مزعج للغاية. ولكنه للأسف ضروري وإذا استدعينا تعليمة إعادة التحميل المباشر في نهاية دالة <code>bundle</code> فعندها سيؤدي ذلك إلى إعادة بناء كلّ ملفات جافاسكربت في حال حدث أي تغيير في أي ملف منهم. من الواضح أن هذه المنهجية غير قابلة للتوسّع والتطوّر. إذ كلما زاد عدد ملفات جافاسكربت كلما استغرقت عملية إعادة التجميع وقتًا أطول. وفجأة عملية البناء الّتي كانت تستغرق 500 ميلي ثانية ستستغرق 30 ثانية مما سيُعيق منهجية التطوير الرشيق <a href="https://academy.hsoub.com/entrepreneurship/business/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%84%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%A3%D8%AC%D8%A7%D9%8A%D9%84-agile-r1047/" rel="">أجايل agile</a>.
</p>

<h3>
	محزم ملفات التنسيق
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_43" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'css'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> gulp</span><span class="pun">.</span><span class="pln">src</span><span class="pun">(</span><span class="pln">
    </span><span class="pun">[</span><span class="pln">
      </span><span class="str">'node_modules/bootstrap/dist/css/bootstrap.css'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'node_modules/font-awesome/css/font-awesome.css'</span><span class="pun">,</span><span class="pln">
      config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">css
    </span><span class="pun">]</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(!</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> sourcemaps</span><span class="pun">.</span><span class="pln">init</span><span class="pun">()))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">sass</span><span class="pun">().</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> sass</span><span class="pun">.</span><span class="pln">logError</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">concat</span><span class="pun">(</span><span class="str">'bundle.css'</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> minifyCSS</span><span class="pun">()))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(!</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> sourcemaps</span><span class="pun">.</span><span class="pln">write</span><span class="pun">()))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">gulp</span><span class="pun">.</span><span class="pln">dest</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">baseDir</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">cond</span><span class="pun">(!</span><span class="pln">PROD</span><span class="pun">,</span><span class="pln"> livereload</span><span class="pun">()));</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إن المشكلة الأولى الّتي تظهر هنا هي تضمين المكتبات أو ملفات التنسيق. إذ يجب علينا أن نتذكر أن نغيّر اسم الملف في كلّ مرة يظهر بها إصدار جديد سواءً للمكتبة (أو لأي ملف تنسيق) وذلك بحذف اسم المكتبة القديمة واستبدالها بالجديدة إلى مصفوفة <code>gulp.src</code>؛ بدلًا من إضافة أمر التضمين في مكان مناسب أكثر في الشيفرة البرمجية.
</p>

<p>
	المشكلة الرئيسية الأخرى هي المنطق المعقد في كلّ أنبوب. إذ إنني اضطررت لإضافة مكتبة من مستودع مدير الحزم NPM تدعى <code>gulp-cond</code> فقط لإضافة الجمل الشرطية في تعليمات الأنبوب، والنتيجة النهائية لتعليمات الأنبوب ليس من السهل قراءتها (لأن الأقواس الثلاثية في كلّ مكان!).
</p>

<h3>
	مهمة الخادم
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_45" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'server'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  nodemon</span><span class="pun">({</span><span class="pln">
    script</span><span class="pun">:</span><span class="pln"> </span><span class="str">'server.js'</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هذه المهمة واضحة للغاية. إذ إنه في الأساس مُغلّف لأمر استدعاء <code>nodemon server.js</code> والّذي سيُشغّل ملف <code>server.js</code> في بيئة <code>Node.js</code>. استخدامنا <code>nodemon</code> بدلًا من <code>node</code> إذ ستؤدي أي تغييرات تحدث في الملف إلى إعادة تشغيل الخادم. افتراضيًا <code>nodemon</code> تُعيد تشغيل العملية عند أي تغيير في ملف الجافاسكربت ولهذا السبب من المهم لتضمين ملف <code>nodemon.json</code> للحدّ من مجاله.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_47" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"watch"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"server.js"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنراجع الآن الشيفرة البرمجية الخاصة بملف <strong><a href="https://github.com/ericgrosse/task-runner-bundler-comparison/blob/gulp-browserify-setup/server.js" rel="external nofollow">server.js</a></strong>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_49" style=""><span class="kwd">const</span><span class="pln"> baseDir </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">NODE_ENV </span><span class="pun">===</span><span class="pln"> </span><span class="str">'production'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">'build'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">NODE_ENV </span><span class="pun">===</span><span class="pln"> </span><span class="str">'production'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="lit">8080</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> app </span><span class="pun">=</span><span class="pln"> express</span><span class="pun">();</span></pre>

<p>
	يؤدي هذا الإعداد إلى تعيين الدليل الأساسي للخادم والمنفذ (port) بناءً على بيئة <code>Node.js</code>. وينشئ نسخة من express.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_51" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">require</span><span class="pun">(</span><span class="str">'connect-livereload'</span><span class="pun">)({</span><span class="pln">port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">35729</span><span class="pun">}));</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="kwd">static</span><span class="pun">(</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> baseDir</span><span class="pun">)));</span></pre>

<p>
	ستضيف هذه التعليمات برمجية وسيطة (Middleware) وهي <code>connect-livereload</code> وذلك لأنها الضرورية لإعداد ميزة إعادة التحميل المباشر، كما ستضيف هذه التعليمات أيضًا برمجية وسيطة ساكنة (Static Middleware) والّتي ستتعامل مع الملحقات الثابتة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_53" style=""><span class="pln">app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'/api/sample-route'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">send</span><span class="pun">({</span><span class="pln">
    website</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Toptal'</span><span class="pun">,</span><span class="pln">
    blogPost</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إن الشيفرة البرمجية السابقة هي مجرد مسار بسيط لواجهة برمجة التطبيقات. فإذا انتقلت إلى المسار <code>localhost:3000/api/sample-route</code> في المتصفح سترى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_55" style=""><span class="pun">{</span><span class="pln">
  website</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Toptal"</span><span class="pun">,</span><span class="pln">
  blogPost</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في الواجهات الخلفية للتطبيق سيكون لدينا مجلد كامل مخصص لمسارات الواجهة البرمجية للتطبيقات، وملفات منفصلة لإنشاء اتصالات مع قاعدة البيانات. هكذا أُضيف المسار وبهذه البساطة لإثبات أننا يمكننا بناء الواجهات الخلفية بعد بناء الواجهات الأمامية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_57" style=""><span class="pln">app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'*'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">sendFile</span><span class="pun">(</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'./'</span><span class="pun">,</span><span class="pln"> baseDir </span><span class="pun">,</span><span class="str">'/index.html'</span><span class="pun">));</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هذا المسار الشامل يعني أنه مهما يكن الرابط التشعبي الّذي تودّ الذهاب إليه من خلال المتصفح فسيعرض لك الخادم صفحة <code>index.html</code> الوحيدة، وبعدها تأتي مهمة موجّه المسارات في React لعرض الصفحة المناسبة الّتي طلبتها من جانب العميل بدلًا من جانب الخادم.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_59" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  open</span><span class="pun">(`</span><span class="pln">http</span><span class="pun">:</span><span class="com">//localhost:${port}`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	الشيفرة البرمجية السابقة تُخبر نسخة express للتنصت على المنفذ الّذي خصصناه وفتح تبويب جديد في المتصفح لعنوان الرابط الّذي طلبتَ الوصول إليه.
</p>

<p>
	الشيء الوحيد الّذي لا يعجبني في طريقة إعداد الخادم هو:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_61" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">require</span><span class="pun">(</span><span class="str">'connect-livereload'</span><span class="pun">)({</span><span class="pln">port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">35729</span><span class="pun">}));</span></pre>

<p>
	نظرًا لأننا نستخدم بالفعل <code>gulp-livereload</code> في ملفنا <code>gulpfile</code>، مما يدل على وجود مكانين منفصلين وجب علينا استخدام إعادة التحميل المباشر.
</p>

<h3>
	إعداد المهام الافتراضية
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_63" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'default'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cb</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  runSequence</span><span class="pun">(</span><span class="str">'clean'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'lint'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'html'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'css'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'js'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'fonts'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'server'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'watch'</span><span class="pun">,</span><span class="pln"> cb</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تنفذ المهمة السابقة من خلال كتابة الأمر <code>gulp</code> في الطرفية. الغريب في الأمر أنك تحتاج لاستخدام <code>runSequence</code> من أجل تنفيذ التعليمات تسلسليًا. عادة ما تنفذ المهام بالتوازي ولكن هذه الطريقة ليست مطلوبة دومًا. فعلى سبيل المثال نحتاج لتشغيل مهمة <code>clean</code> قبل مهمة <code>html</code> للتأكد من أن مجلدات الهدف فارغة قبل نقل الملفات إليها. عندما صدرت النسخة الرابعة من <code>gulp 4</code> كان إحدى مميزاتها أنها دعمت تعليمتي <code>gulp.series</code> لتنفيذ التعليمات بالتسلسل و <code>gulp.parallel</code> لتنفيذ التعليمات بالتوازي.
</p>

<p>
	وفضلًا عن ذلك، إن عملية إنشاء واستضافة التطبيق من خلال أمرٍ واحد هو فعلًا أمرٌ رائع، وإمكانية فهم أي جزء من أجزاء سير العمل بسهولة مثل سهولة اختبار مهمة ما في تسلسل التنفيذ. بالإضافة إلى ذلك يمكننا تفكيك المهام الكبير إلى أجزاء أصغر لنهج أكثر دقة في إنشاء واستضافة التطبيق. على سبيل المثال يمكننا إعداد مهمتين منفصلتين لإجراء عمليات إختبار المهام الأخرى وكشف الأخطاء المحتملة (linting). أو يمكن أن يكون لدينا مهمة المضيف والّتي ستُشغل الخادم والمراقب. هذه القدرة على تنظيم المهام قوية جدًا، وخاصةً عندما يتغيّر تطبيقك ويتطلب المزيد من المهام المؤتمتة.
</p>

<h3>
	بناء التطبيق وتخصيصه لوضع التطوير مقابل وضع النشر
</h3>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_65" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">argv</span><span class="pun">.</span><span class="pln">prod</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">NODE_ENV </span><span class="pun">=</span><span class="pln"> </span><span class="str">'production'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
let PROD </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">NODE_ENV </span><span class="pun">===</span><span class="pln"> </span><span class="str">'production'</span><span class="pun">;</span></pre>

<p>
	من خلال استخدام <a href="https://www.npmjs.com/package/yargs" rel="external nofollow">مكتبة</a> <code>yargs</code> الموجودة في مستودعات مدير الحزم <code>NPM</code>. يمكننا تزويد الأداة <code>Gulp</code> بالرايات (Flags). هنا مثلًا سنوجّه ملف <code>gulpfile</code> لتعيين بيئة <code>Node.js</code> في وضع النشر إذا مرّرنا الراية <code>‎--prod</code> مع التعليمة في الطرفية. سيستخدم هذا المتغيّر <code>PROD</code> كشرط للتمييز بين وضع التطوير ووضع النشر في ملف <code>gulpfile</code>. على سبيل المثال أحد الخيارات الّتي نمررها لإعدادات الأداة <code>Browserify</code> هي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_67" style=""><span class="pln">plugin</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln"> </span><span class="pun">[]</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">hmr</span><span class="pun">,</span><span class="pln"> watchify</span><span class="pun">]</span></pre>

<p>
	إن التعليمة الشرطية في الشيفرة السابقة مفيدة جدًا لأنها توفر علينا كتابة ملف <code>gulpfile</code> منفصل لكل من وضع التطوير ووضع النشر، والّذي سيحتوي على الكثير من التعليمات المكررة. بدلًا من ذلك يمكننا تمرير <code>gulp --prod</code> لتشغيل وضعية النشر في تنفيذ المهام. أو تمرير <code>gulp html --prod</code> لتشغيل المهمة <code>html</code> فقط في وضع النشر. من جهة أخرى رأينا سابقًا كيف أن تمرير التعليمات الشرطية عبر الأنبوب مثل: <code>.pipe(cond(!PROD, livereload()))</code> ليست من السهل قراءتها. ولكن في نهاية المطاف أنها مسألة تفضيلات شخصية إذا ما كنت تريد استخدام المتغيير المنطقي (<code>PROD</code>) أو إنشاء ملفين منفصلين (<code>gulpfile</code>) للإعداد.
</p>

<p>
	لننتقل الآن إلى طريقة الإعداد الثانية، وهي عندما نبدل الأداة <code>Browserify</code> لتحل محلها الأداة <code>Webpack</code>.
</p>

<h2>
	2. طريقة Gulp + Webpack
</h2>

<p>
	نلاحظ أن ملف الإعداد <code>gulpfile</code> انخفض حجمه انخفاضًا كبيرًا إذ يحتوي الآن على 12 سطرًا من تعليمات استيراد المكتبات و 99 سطرًا من الشيفرات البرمجية الأخرى. إذا فحصنا من المهمة الافتراضية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_69" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'default'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cb</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  runSequence</span><span class="pun">(</span><span class="str">'lint'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'build'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'server'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'watch'</span><span class="pun">,</span><span class="pln"> cb</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يتطلب إعداد تطبيق الوب خاصتنا الآن خمسة مهام فقط، بدلًا من تسعة. وهو في الحقيقة تحسنٌ كبير. بالإضافة إلى ذلك ألغينا الحاجة لاستخدام ميزة إعادة التحميل المباشر <code>livereload</code>، كما أن مهمة المراقب أصبحت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_71" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'watch'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  gulp</span><span class="pun">.</span><span class="pln">watch</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">js</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    runSequence</span><span class="pun">(</span><span class="str">'lint'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هذا يعني أن المراقب لن يشغل سلوك إعادة بناء التطبيق من جديد. وكميزة إضافية لن نحتاج إلى نقل الملف <code>index.html</code> من <code>app</code> إلى <code>dist</code> أو <code>build</code> بعد الآن.
</p>

<p>
	وبالعودة إلى فكرة تقليل المهام، نلاحظ إن المهام المخصصة لكلٍ من ملفات <code>html</code> و <code>css</code> و <code>js</code> و <code>fonts</code> استبدلت بمهمة واحدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_73" style=""><span class="pln">gulp</span><span class="pun">.</span><span class="pln">task</span><span class="pun">(</span><span class="str">'build'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  runSequence</span><span class="pun">(</span><span class="str">'clean'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'html'</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> gulp</span><span class="pun">.</span><span class="pln">src</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">entry</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">webpack</span><span class="pun">(</span><span class="pln">require</span><span class="pun">(</span><span class="str">'./webpack.config'</span><span class="pun">)))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">gulp</span><span class="pun">.</span><span class="pln">dest</span><span class="pun">(</span><span class="pln">config</span><span class="pun">.</span><span class="pln">paths</span><span class="pun">.</span><span class="pln">baseDir</span><span class="pun">));</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بكلّ بساطة شغّل مهمتي <code>clean</code> و <code>html</code> بالتسلسل، وبمجرد ما ينتهي تنفيذهُم إجلب نقطة الدخول الخاصة بتطبيق الوب خاصتنا ومرّره إلى الأنبوب من خلال الأداة <code>Webpack</code> ثم مررها إلى ملف الإعداد الخاص بالأداة <code>Webpack</code> وهو <code>webpack.config.js</code> وذلك لتهيئه وأرسال الحزمة المُجمّعة الناتجة إلى <code>baseDir</code> (إما <code>dist</code> أو <code>build</code> اعتمادًا على الملف الإعداد ل Node.js).
</p>

<p>
	يمكنك إلقاء نظرة على <a href="https://github.com/ericgrosse/task-runner-bundler-comparison/blob/gulp-webpack-setup/webpack.config.js" rel="external nofollow">ملف الإعداد</a> <code>webpack.config.js</code> الخاص بالأداة <code>Webpack</code>، لكننا لن نشرحه كله، وإنّما سنشرح الخصائص المهمة المسندة للكائن <code>module.exports</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_75" style=""><span class="pln">devtool</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln"> </span><span class="str">'source-map'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'eval-source-map'</span><span class="pun">,</span></pre>

<p>
	تعيّن هذه التعليمة نوع خرائط الشيفرة البرمجية المحوّلة (source maps) والّتي سيستخدمها <code>Webpack</code>. تدعم <code>Webpack</code> مجموعة من الخياراتٍ المتنوعة من خرائط الشيفرة البرمجية المحوّلة. ويوفر كلّ وخيارٍ منهم توازنًا مختلفًا في الأداء. فمنهم خرائط ذات تفاصيل كثيرة مقابل خرائط ذات تفاصيل قليلة ولكن إعادة بنائها سريع (وهو الوقت المستغرق لإعادة تجميع الملف بعد إجراء التغييرات). وبناءً عليه يمكننا استخدام خرائط ذات تفاصيل قليلة من أجل زيادة سرعة إعادة التحميل في وضع التطوير واستخدام خرائط ذات تفاصيل كثيرة في وضع النشر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_77" style=""><span class="pln">entry</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln"> </span><span class="str">'./app/index'</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
</span><span class="pun">[</span><span class="pln">
  </span><span class="str">'webpack-hot-middleware/client?reload=true'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// reloads the page if hot module reloading fails.</span><span class="pln">
  </span><span class="str">'./app/index'</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	هذه هي نقطة الدخول إلى مُجمّع الحزم. لاح المصفوفة المُمررة لنقطة الدخول، وهذا يدلّ على إمكانية استخدام نقاط دخول متعددة. في حالتنا نقطة الدخول هي الملف <code>app/index.js</code>، وكذلك نقطة الدخول الخاصة لإعداد إعادة التحميل النشط للوحدات (hot module reloading)
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_79" style=""><span class="pln">output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln"> __dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/build'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> __dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/dist'</span><span class="pun">,</span><span class="pln">
  publicPath</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/'</span><span class="pun">,</span><span class="pln">
  filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	وهنا نحدد مجلد الخرج والّذي سيوضع فيه الملفات المترجمة والمجمّعة. إن أكثر خيارٍ مربكٍ هنا هو <code>publicPath</code> والّذي يعيّن عنوان الرابط التشعبي الّذي سيُجمّع فيه ويوضع على الخادم. لذلك على سبيل المثال إذا كان <code>publicPath</code> هو <code>/public/assets</code> عندها ستكون الحزمة المجمّعة في هذا المسار <code>/public/assets/bundle.js</code> على الخادم.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_81" style=""><span class="pln">devServer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  contentBase</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln"> </span><span class="str">'./build'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'./app'</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذه التعليمة ستُعلِم الخادم بالمجلد الجذر الّذي ستستخدمه في مشروعك.
</p>

<p>
	إذا شعرت بالإرتباك في طريقة تعيين مجلد الخرج لوضع الملف المجمّع فما عليك سوى تذكر هذه الدلالات لكلّ مما يلي:
</p>

<ul>
	<li>
		<code>path</code> + <code>filename</code>: الموقع المحدد للحزمة المُجمّعة في الشيفرة البرمجية للمشروع.
	</li>
	<li>
		<code>contentBase</code> (مثل: ملف الجذر <code>/</code>) + <code>publicPath</code>: موقع الحزمة على الخادم.
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_83" style=""><span class="pln">plugins</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln">
</span><span class="pun">[</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="pln">optimize</span><span class="pun">.</span><span class="typ">OccurenceOrderPlugin</span><span class="pun">(),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="typ">DefinePlugin</span><span class="pun">(</span><span class="pln">GLOBALS</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ExtractTextPlugin</span><span class="pun">(</span><span class="str">'bundle.css'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="pln">optimize</span><span class="pun">.</span><span class="typ">DedupePlugin</span><span class="pun">(),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="pln">optimize</span><span class="pun">.</span><span class="typ">UglifyJsPlugin</span><span class="pun">({</span><span class="pln">compress</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">warnings</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">}})</span><span class="pln">
</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
</span><span class="pun">[</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="typ">HotModuleReplacementPlugin</span><span class="pun">(),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> webpack</span><span class="pun">.</span><span class="typ">NoErrorsPlugin</span><span class="pun">()</span><span class="pln">
</span><span class="pun">],</span></pre>

<p>
	هذه بعض الملحقات الوظيفية الّتي ستعزز وظائف <code>Webpack</code> بطريقة ما. فعلى سبيل المثال إن الملحق <code>webpack.optimize.UglifyJsPlugin</code> هو المسؤول عن تصغير ملفات جافاسكربت.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_85" style=""><span class="pln">loaders</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln"> include</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'app'</span><span class="pun">),</span><span class="pln"> loaders</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'babel'</span><span class="pun">]},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.css$/</span><span class="pun">,</span><span class="pln">
    loader</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln">
      </span><span class="typ">ExtractTextPlugin</span><span class="pun">.</span><span class="pln">extract</span><span class="pun">(</span><span class="str">'style'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'css?sourceMap'</span><span class="pun">):</span><span class="pln">
      </span><span class="str">'style!css?sourceMap'</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.scss$/</span><span class="pun">,</span><span class="pln">
    loader</span><span class="pun">:</span><span class="pln"> PROD </span><span class="pun">?</span><span class="pln">
      </span><span class="typ">ExtractTextPlugin</span><span class="pun">.</span><span class="pln">extract</span><span class="pun">(</span><span class="str">'style'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'css?sourceMap!resolve-url!sass?sourceMap'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
      </span><span class="str">'style!css?sourceMap!resolve-url!sass?sourceMap'</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(svg|png|jpe?g|gif)(\?\S*)?$/</span><span class="pun">,</span><span class="pln"> loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'url?limit=100000&amp;name=img/[name].[ext]'</span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(eot|woff|woff2|ttf)(\?\S*)?$/</span><span class="pun">,</span><span class="pln"> loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'url?limit=100000&amp;name=fonts/[name].[ext]'</span><span class="pun">}</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	هذه هي المُحمّلات والّتي تعالج بشكل أساسي الملفات الّتي تُضمّن بواسطة <code>require()</code>، وهي تشبه إلى حدٍ ما الأنابيب في Gulp والّتي تمكنك من ربط المُحمّلات مع بعضهم بعضًا.
</p>

<p>
	لنستكشف أحد المُحمّلات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_87" style=""><span class="pun">{</span><span class="pln">test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.scss$/</span><span class="pun">,</span><span class="pln"> loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'style!css?sourceMap!resolve-url!sass?sourceMap'</span><span class="pun">}</span></pre>

<p>
	إن الخاصية <code>test</code> تُخبر الأداة <code>Webpack</code> أن الملفات الّتي يجب على المُحمّل التعامل معها يجب أن تحقق التعبير النمطي المُمرر لهذه الخاصية. في حالتنا تكون <code>/\.scss$/</code>. أما الخاصية <code>loader</code> فهي تُحدد المُحمّل الّذي يجب أن نستخدمه. هنا نحدد المُحملات <code>style</code> و <code>css</code> و <code>resolve-url</code> و <code>sass</code> والّتي ستُنفذ بترتيب عكسي.
</p>

<p>
	يجب علي أن أعترف بأنني لست ماهرًا في بناء جملة تحديد المُحملات <code>loader3!loader2!loader1</code>. ولكن متى يجب علينا قراءة شيفرة برمجية من اليمين إلى اليسار؟ عدا هذا السطر. على الرغم من ذلك تعدّ المُحمّلات ميزة قوية جدًا لمُجمّع الحزم <code>Webpack</code>. في الحقيقة يسمح لنا المُحمّل الّذي استخدمته للتو باستيراد ملفات <code>SASS</code> بداخل الشيفرة البرمجية للجافاسكربت. فعلى سبيل المثال يمكننا استيراد ملفات التنسيق المخصّصة العامة الّتي عملنا عليها في المشروع في ملف الّذي حددنا كنقطة دخول وهو <code>index.js</code> وسيكون شكله كما يلي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_3117_89" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">render</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'react-dom'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="typ">Router</span><span class="pun">,</span><span class="pln"> browserHistory</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'react-router'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> routes </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./routes'</span><span class="pun">;</span><span class="pln">
</span><span class="com">// CSS imports</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'../node_modules/bootstrap/dist/css/bootstrap.css'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'../node_modules/font-awesome/css/font-awesome.css'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./styles/global.scss'</span><span class="pun">;</span><span class="pln">

render</span><span class="pun">(&lt;</span><span class="typ">Router</span><span class="pln"> history</span><span class="pun">={</span><span class="pln">browserHistory</span><span class="pun">}</span><span class="pln"> routes</span><span class="pun">={</span><span class="pln">routes</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;,</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'app'</span><span class="pun">));</span></pre>

<p>
	وبطريقة مشابهة في مكوّن الترويسة يمكننا استيراد الملف <code>import './Header.scss'</code> المرتبط بهذا المكون، وهذا وبكل تأكيد ينطبق على كافة المكونات الأخرى في التطبيق.
</p>

<p>
	<strong>من وجهة نظري الشخصية أرى بأن هذا الأمر يعدُّ تغييرًا ثوريًا في عالم التطوير بلغة جافاسكربت</strong>. إذ لا داعي للقلق بشأن تجميع ملفات التنسيق وتصغيرها وحتى خرائط الشيفرة البرمجية المحوّلة (source maps) أيضًا، وذلك لأن مُحمّلنا يتعامل مع كلّ هذه الأمور عوضًا عنا. حتى إعادة التحميل النشط للوحدات (hot module reloading) ستعمل على ملفات التنسيق خاصتنا، ومن ثمّ فإن القدرة على التعامل مع تعليمات الاستيراد من داخل ملف الجافاسكربت سيجعل من عملية التطوير أبسط من الناحية المفاهيمية، وأكثر تناسقًا، وتقلل من تبديل السياق في التطوير (وذلك عند استخدام أدوات مساعدة مختلفة)، والمنطق البرمجي أسهل في التفكير.
</p>

<p>
	لإعطاء ملخص موجز حول كيفية عمل هذه الميّزة مع مُجمّع الحزم <code>Webpack</code> وكيف سيضع ملفات التنسيق في ملف جافاسكربت المُجمّع. في الحقيقة لا يقتصر الأمر على ملفات التنسيق وحسب بل يمكنه أيضًا فعل ذلك من أجل الصور والخطوط.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_91" style=""><span class="pun">{</span><span class="pln">test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(svg|png|jpe?g|gif)(\?\S*)?$/</span><span class="pun">,</span><span class="pln"> loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'url?limit=100000&amp;name=img/[name].[ext]'</span><span class="pun">},</span><span class="pln">
</span><span class="pun">{</span><span class="pln">test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(eot|woff|woff2|ttf)(\?\S*)?$/</span><span class="pun">,</span><span class="pln"> loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'url?limit=100000&amp;name=fonts/[name].[ext]'</span><span class="pun">}</span></pre>

<p>
	إن الشيفرة البرمجية السابقة توجّه المُحمّل إلى تضمين الصور والخطوط بداخل الملف المُجمّع إن كان حجمهم لا يتعدى 100 كيلوبايت، وإلا فسيعرضهم كملفات منفصلة، كما يمكننا في أي وقت تغيير حجم المسموح به، ليصبح مثلًا 10 كيلوبايت.
</p>

<p>
	هذا هو باختصار طريقة إعداد مُجمّع الحزم <code>Webpack</code> وفي الحقيقة إنه يتطلب قدرًا لا بأس به من الإعداد. ولكن فوائد استخدامه رائعة للغاية. على الرغم من أن مُجمّع الحزم <code>Browserify</code> لديه أيضًا ملحقاته الجميلة وتحويلاته ولكنه لا يستطيع أن ينافس محملات <code>Webpack</code> من ناحية الخصائص الوظيفية المُضافة.
</p>

<h2>
	3. طريقة Webpack + NPM Scripts
</h2>

<p>
	في هذه الطريقة من الإعداد سنستخدم <code>npm scripts</code> مباشرة بدلًا من الاعتماد على Gulp لأتمتة مهامنا.
</p>

<p>
	سيكون ملف <code>package.json</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3117_93" style=""><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"start"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm-run-all --parallel lint:watch test:watch build"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"start:prod"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm-run-all --parallel lint test build:prod"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"clean-dist"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"rimraf ./dist &amp;&amp; mkdir dist"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"clean-build"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"rimraf ./build &amp;&amp; mkdir build"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"clean"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm-run-all clean-dist clean-build"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"mocha ./app/**/*.test.js --compilers js:babel-core/register"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"test:watch"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm run test -- --watch"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"lint"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"esw ./app/**/*.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"lint:watch"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm run lint -- --watch"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"server"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"nodemon server.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"server:prod"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"cross-env NODE_ENV=production nodemon server.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"build-html"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"node tools/buildHtml.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"build-html:prod"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"cross-env NODE_ENV=production node tools/buildHtml.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"prebuild"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm-run-all clean-dist build-html"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"postbuild"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm run server"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"prebuild:prod"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm-run-all clean-build build-html:prod"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"build:prod"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"cross-env NODE_ENV=production webpack"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"postbuild:prod"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"npm run server:prod"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لتشغيل طريقة البناء من أجل وضع التطوير أو وضع النشر يمكنك كتابة الأوامر <code>npm start</code> و <code>npm run start:prod</code> على التتالي.
</p>

<p>
	نلاحظ أن هذا الملف أفضل وبكل تأكيد من ملف الإعداد <code>gulpfile</code> الّذي بنيناه سابقًا في هذا المقال. إذ اختصرنا الكثير من الشيفرات البرمجية لتكون عدد التعليمات بدلًا من 150 أو 99 تعليمة أصبحت 19 تعليمة (12 تعليمة إذا استثنينا التعليمات الخاصة بوضع النشر لأن معظمها مشابهة للتعليمات في وضع التطوير الّتي في بيئة <code>Node.js</code> لتصبح في وضع النشر). إن العيب الوحيد هو أن التعليمات مبهمة إلى حدٍ ما بالموازنة مع التعليمات الخاصة للأداة Gulp وليست معبرة مثلها. فعلى سبيل المثال لا توجد (على حسب اطلاعي على الأقل) بوجود تعليمة واحدة تشغّل <code>npm script</code> لينفذ تعليمات معينة تنفيذًا متسلسلًا وتعليمات أخرى ليُنفذها تنفيذًا متوازيًا. وإنما تعليمة واحدة لكل منهما.
</p>

<p>
	ومع ذلك هنالك الكثير من الميّزات الكبيرة لهذا المنهج. بالإضافة إلى أنه يوجد حزمة مقابلة لكل مكتبة من المكتبات الّتي كنا نستخدمها في السابق مع أداة Gulp.
</p>

<p>
	فبدلًا من استخدام المكتبات الموجودة في Gulp مثل:
</p>

<ul>
	<li>
		gulp-eslint
	</li>
	<li>
		gulp-mocha
	</li>
	<li>
		gulp-nodemon
	</li>
	<li>
		etc
	</li>
</ul>

<p>
	يمكننا استخدام الحزم التالية في Webpack:
</p>

<ul>
	<li>
		eslint
	</li>
	<li>
		mocha
	</li>
	<li>
		nodemon
	</li>
	<li>
		etc
	</li>
</ul>

<p>
	نقلًا عن كوري هاوس في <a href="https://www.freecodecamp.org/news/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8/" rel="external nofollow">مقالته</a> "لماذا تركت أداة Gulp و Grunt وتوجهت إلى NPM Scripts":
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		كنت من أشد المعجبين بأداة Gulp، ولكن في مشروعي الأخير أنتهى بي المطاف بكتابة مئات الأسطر من الشيفرات البرمجية في ملف الإعداد <code>gulpfile</code> واستخدام حوالي 12 مُلحق من ملحقات Gulp. كما أنني كافحت من أجل دمج <code>Webpack</code> مع <code>Browsersync</code> مع <code>hot reloading</code> مع <code>Mocha</code> والكثير من الشيفرات Gulp. ولكن لماذا توجهت إلى <code>NPM Scripts</code>؟ لأن بعض التوثيقات الرسمية للملحقات لم تحتوي على المعلومات الكافية الّتي ستُغطي طريقة استخدامي لها في مشروعي. كما كشفت بعض الملحقات عن جزء من واجهة برمجة التطبيقات (<abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>) الّتي احتجتها. ومن مكان أخر تظهر مشكلة غريبة والّذي من المفترض أن لا أشاهد سوى ملفات خفيفة وبسيطة. بالإضافة إلى ذلك أزيلت الألون المخصصة للأوامر عندما عملت مع سطر الأوامر.
	</p>
</blockquote>

<p>
	يحدد كوري هاوس ثلاث مشاكل رئيسية وهي:
</p>

<ol>
	<li>
		مشكلة الاعتماد على منشئي الملحقات.
	</li>
	<li>
		مشكلة تنقيح الأخطاء المرهقة.
	</li>
	<li>
		مشكلة التوثيقات الرسمية المفككة والضعيفة.
	</li>
</ol>

<p>
	ومن وجهة نظري أتفق مع جميع هذه المشاكل.
</p>

<h3>
	1. مشكلة الاعتماد على منشئي الملحقات
</h3>

<p>
	مثلًا عندما تُحدّث مكتبة مثل: <code>eslint</code>، ستحتاج المكتبة المرتبطة بها <code>gulp-eslint</code> إلى تحديث مناظر لكي يتوافقوا مع بعضهم بعضًا. فإذا فقد المشرف الاهتمام بالعمل على تعديل وصيانة المكتبة، فإن الإصدار المخصص لأداة gulp من هذه المكتبة لن يتزامن مع النسخة الأصلية للمكتبة. وينطبق نفس الشيء عند إنشاء مكتبة جديدة. فعلى سبيل المثال إذا قام شخص ما بإنشاء مكتبة "xyz" وأطلقها، واحتجت فجأة إلى نسخة مخصصة من المكتبة للعمل مع أداة Gulp مثل: <code>gulp-xyz</code>.
</p>

<p>
	بمعنى آخر، هذا النهج غير قابل للتوسّع. ومن الناحية المثالية، نريد نهجًا مثل Gulp ولكن بإمكانه استخدام المكتبات الأصلية مباشرة.
</p>

<h3>
	2. مشكلة تنقيح الأخطاء المرهقة
</h3>

<p>
	على الرغم من أن المكتبات مثل <code>gulp-plumber</code> تساعد في تخفيف هذه المشكلة تخفيفًا كبيرًا، إلا أنه من المعروف أن الإبلاغ عن الأخطاء في <code>gulp</code> ليس مفيدًا جدًا. فإذا كان هناك تعليمة واحدة خاطئة في الأنبوب فإنها سترمي استثناءً غير قابل للمُعالجة، وعندما تلاحق المشكلة في المكدس ستظهر مشكلة تبدو غير مرتبطة تمامًا بالسبب الحقيقي للمشكلة الّتي ظهرت في الشيفرة البرمجية. لهذا السبب يمكن أن يجعل من علمية تنقيح الأخطاء كابوسًا في بعض الحالات. ومهما بحثت عن حلول لهذه المشاكل سواءً على محركات البحث مثل: <code>Google</code> أو موقع <code>Stack Overflow</code> فلن يساعدك هذا البحث فعليًا إذا كان الخطأ مبهمً أو مضللًا.
</p>

<h3>
	3. مشكلة الوثائق الرسمية المفككة والضعيفة
</h3>

<p>
	في كثير من الأحيان ألاحظ أن أحد مكتبات <code>gulp</code> الصغيرة يكون توثيقها الرسمي محدودًا للغاية. أظن بأن هذا الأمر يرجعُ من أن المُنشئ عادة لهذه المكتبة يكون هدفه الأساسي استخداماته الخاصة. بالإضافة إلى ذلك من الشائع أن تنّظر إلى التوثيق الرسمي لكلّ من الملحقات الإضافية للأداة Gulp والمكتبة الأصلية (الّتي تعتمد عليها تلك الملحقات)، مما يعني الكثير من تبديل السياق وزيادة كمية القراءة إلى الضعف بسبب ذلك.
</p>

<h2>
	الخاتمة
</h2>

<p>
	يبدو لي أنه من الواضح جدًا أن <code>Webpack</code> أفضل من <code>Browserify</code> وأن <code>NPM scripts</code> أفضل من <code>Gulp</code>، على الرغم من أن كلّ خيارٍ منهم له فوائده وعيوبه. ومن المؤكد أيضًا أن تعابير وتعليمات <code>Gulp</code> أكثر مقروئية وملاءمة للاستخدام من <code>NPM scripts</code>، ولكنك ستدفع الثمن غاليًا في جميع عمليات التعقيد المضافة.
</p>

<p>
	قد لا تكون كلّ طريقة من طرق الإعداد السابقة مثالية لتطبيقك، ولكن إذا رغبت في تجنب عدد هائل من تبعيات التطوير وتجربة تنقيح الأخطاء المحبطة، فإن <code>Webpack</code> مع <code>NPM scripts</code> هي الطريقة المناسبة لك. آمل أن تجد هذه المقالة مفيدة في اختيار الأدوات المناسبة لمشروعك القادم.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.toptal.com/front-end/webpack-browserify-gulp-which-is-better" rel="external nofollow">Webpack or Browserify &amp; Gulp: Which Is Better?</a> لصاحبه Eric Grosse
</p>
]]></description><guid isPermaLink="false">867</guid><pubDate>Tue, 28 Apr 2020 18:08:00 +0000</pubDate></item><item><title>&#x62F;&#x644;&#x64A;&#x644; Webpack &#x627;&#x644;&#x634;&#x627;&#x645;&#x644;</title><link>https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_04/WebPack-Tutorial.jpg.63ba867d581da33c77b3690da525f723.jpg" /></p>

<p>
	شهد مجال تطوير الوِب في السنوات الأخيرة تطورات هائلة على جميع المستويات والذي أدى إلى تحسنيات كبيرة على مستوى السرعة والجودة وقابلية الصيانة. ليس ذلك فحسب بل تعدى الأمر إلى إمكانية أتمتة بعض المهام الروتينية والتركيز على جودة العمل المُسلم ورفع الإنتاجية وذلك باستخدام أدوات مساعدة مثل Webpack، والتي تعدّ من أشهر الأدوات المساعدة بل إنها أصبحت عنصرًا أساسيًا في أي مشروع يُبنى بلغة جافاسكربت.
</p>

<p>
	حدثت بعض هذه التطورات على صعيد تطوير الواجهات الخلفية (Back-end) مثل ظهور Node.js والّتي تستخدم استخدامًا كبيرًا في هذه الأيام، وبعضها الآخر كان في تطوير الواجهات الأمامية (front-end)، ومن أبرز هذه التطورات هي النقلة النوعية للغة جافاسكربت ودعمها للعديد من المميزات والخصائص، والتي أدت في نهاية المطاف لجعلها واحدةً من أبرز الخيارات القوية في برمجة الوِب. بل وحتى إنها احتلت المرتبة الأولى في ترتيب أشهر لغات البرمجة شعبية لعام 2019 وذلك بحسب <a href="https://insights.stackoverflow.com/survey/2019" rel="external nofollow">إحصائية</a> موقع Stackoverflow.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36354" href="https://academy.hsoub.com/uploads/monthly_2020_04/most_popular_technologies.png.eda38b2262bf6976e5517e90dd5608c4.png" rel=""><img alt="most_popular_technologies.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36354" data-unique="id5nusf0m" src="https://academy.hsoub.com/uploads/monthly_2020_04/most_popular_technologies.thumb.png.671f9c28a92163c758061c90e4da1399.png"></a>
</p>

<p>
	وكما <a href="https://w3techs.com/technologies/overview/client_side_language" rel="external nofollow">أعلنت</a> شركة Web3Techs أن لغة جافاسكربت مُستخدمة من قِبل حوالي 95% من مواقع العالم قاطبةً، وهي أيضًا على رأس قائمة "أكثر لغة مشهورة في برمجة الواجهات الأمامية" بحسب نفس الشركة.
</p>

<p>
	ولكن كيف جرت هذه التحولات السريعة والقوية بنفس الوقت لهذه اللغة؟ وما هي المراحل الأساسية الّتي مرت بها؟ وما هي الأدوات المختلفة الّتي ظهرت بالتزامن مع تطور هذه اللغة؟ وما هي أدوات البناء (مثل Gulp)؟ وما هو مجمع الوحدات (مثل Webpack)؟ ولماذا ظهر؟ وما هي مميزاته؟
</p>

<p>
	سنحاول في هذا الدليل مرافقتكم في رحلة استكشافية للإجابة على هذه الأسئلة، ولنستعرض فيها أيضًا التاريخ العريق لهذه اللغة، ومعرفة الاسباب الرئيسية لتطورها وكيف احتلت بهذه السرعة المكانة العالية الّتي هي عليها اليوم. فهل أنت مستعد لمشاركتنا في هذه الرحلة؟
</p>

<h2>
	لمحة تاريخية
</h2>

<p>
	بالعودة إلى زمن ما قبل الأدوات الحديثة كيف كان الحال آنذاك؟ في الحقيقة كانت الأمور بسيطة وسهلةً جدًا. سنبدأ رحلتنا بالتعرف على طرق استخدام لغة جافاسكربت في ملفات <code>HTML</code>.
</p>

<h3>
	طرق استخدام لغة جافاسكربت
</h3>

<p>
	يوجد طرقٌ عديدة يمكننا لتضمين الشيفرات البرمجية للغة جافاسكربت في ملفات <code>HTML</code>، يعتمد اختيارنا للطريقة بحسب حجم المشروع الّذي سنعمل عليه وسنذكر أهم الطرق المستخدمة من البداية وحتى يومنا الحالي.
</p>

<h4>
	الشيفرة السطرية
</h4>

<p>
	بدأت لغة جافاسكربت في كتابة شيفرتها البرمجية بهذه الطريقة البسيطة إذ أن الشيفرة البرمجية مكتوبة مباشرة في ملف <code>HTML</code> داخل وسم <code>&lt;script&gt;</code>. لا بدّ بأن معظمنا كتبَ شيفرة برمجية بهذه الطريقة في بداية تعلمه لهذه اللغة وذلك من كونها أسهل الطرق لتطبيق شيفرة برمجية معينة.
</p>

<p>
	في المثال التالي يحتوي الملف <code>index.html</code> على شيفرة جافاسكربت سطرية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_6" style="">
<span class="pln">    </span><span class="tag">&lt;html&gt;</span><span class="pln">
      </span><span class="tag">&lt;head&gt;</span><span class="pln">
        </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
        </span><span class="tag">&lt;title&gt;</span><span class="pln">Inline Example</span><span class="tag">&lt;/title&gt;</span><span class="pln">
      </span><span class="tag">&lt;/head&gt;</span><span class="pln">
      </span><span class="tag">&lt;body&gt;</span><span class="pln">
        </span><span class="tag">&lt;h1&gt;</span><span class="pln">
          The Answer is
          </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"answer"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
        </span><span class="tag">&lt;/h1&gt;</span><span class="pln">

        </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="tag">&gt;</span><span class="pln">
          </span><span class="kwd">function</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          </span><span class="kwd">function</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> iteratee</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">var</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
              length </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
              memo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">
            </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> length</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
              memo </span><span class="pun">=</span><span class="pln"> iteratee</span><span class="pun">(</span><span class="pln">memo</span><span class="pun">,</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">])</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> memo</span><span class="pun">;</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          </span><span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">){</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> add</span><span class="pun">);</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          </span><span class="com">/* Main Function */</span><span class="pln">
          </span><span class="kwd">var</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pln"> </span><span class="pun">];</span><span class="pln">
          </span><span class="kwd">var</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln">
          document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"answer"</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> answer</span><span class="pun">;</span><span class="pln">
        </span><span class="tag">&lt;/script&gt;</span><span class="pln">
      </span><span class="tag">&lt;/body&gt;</span><span class="pln">
    </span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	هذه الطريقة جيدة جدًا للبدء إذ لا يوجد أي ملفات خارجية أو تبعية ناشئة بين الملفات، ولكن هذه هي الطريقة المثالية لبناء شيفرة برمجية غير قابلة للصيانة وذلك بسبب المشاكل التالية:
</p>

<ol>
<li>
		<p>
			<strong>عدم إمكانية إعادة استخدام الشيفرة البرمجية</strong>: فإذا احتجنا إلى إضافة صفحة أخرى وهذه الصفحة تحتاج إلى بعض الوظائف المحققة في الشيفرة البرمجية الّتي كتبناها سابقًا في الملف السابق فيجب علينا حينها نسخ الشيفرة البرمجية المطلوبة ولصقها في الصفحة المراد تحقيق الوظائف فيها.
		</p>
	</li>
	<li>
		<p>
			<strong>عدم وجود ثبات في التبعية بين الشيفرة البرمجية</strong>: أنت مسؤول عن تنسيق التبعية بين الدوال المستخدمة على سبيل المثال إن كان هنالك دالة معينة تعتمد في وظيفتها على نتيجة دالة أخرى محققة قبلها فيجب علينا حينها الانتباه لهذا الأمر وعدم تغيير ترتيب تواجد الدوال في الشيفرة البرمجية.
		</p>
	</li>
	<li>
		<p>
			<strong>التضارب في المتغيرات العامة (Global Variables)</strong>: جميع الدوال والمتغيرات ستكون موجودة على نطاق عام (Global Scope) وهذا بدوره سيؤدي إلى تضارب في المتحولات، ويحدث هذا عند ازدياد ضخامة المشروع وكتابة شيفرة برمجية كبيرة مما يجعل إمكانية إعادة استخدام نفس أسماء المتحولات أمر وارد الحدوث بقوة، وخصيصًا إن كان هنالك أكثر من مبرمج يعمل على نفس المشروع.
		</p>
	</li>
</ol>
<h4>
	استخدام الملفات الخارجية
</h4>

<p>
	الطريقة الأخرى المستخدمة في كتابة الشيفرة البرمجية للغة جافاسكربت هي استخدام ملفات منفصلة إذ سنجزّء الشيفرة البرمجية إلى أجزاء صغيرة ونحملها تباعًا في ملف <code>index.html</code>. وبهذه الطريقة يمكننا استدعاء مكتبات منفصلة والّتي تقدم لنا وظائف إضافية مثل مكتبة moment (والتي تتيح لنا التلاعب بالتواريخ وإمكانية إجراء تحويلات معينة من نمط تاريخٍ معين إلى نمطٍ آخر).
</p>

<p>
	وبالإضافة إلى ذلك أدى استخدام الملفات المنفصلة في الشيفرة البرمجية إلى إتاحة الفرصة لنا بإعادة استخدام الشيفرة البرمجية أكثر من مرة ومن أي مكان نريد، وبذلك نكون تخلصنا من فكرة نسخ الشيفرة البرمجية من مكان ما ولصقها في مكان آخر متى ما أردنا استخدام هذه الشيفرة.
</p>

<p>
	الملف <code>index.html</code> هو الملف الّذي سيستدعي جميع التبعيات (ملفات جافاسكربت) الّتي سيحتاجها ويكون محتواه على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_8" style="">
<span class="tag">&lt;html&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">External File Example</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="com">&lt;!-- الملف الرئيسي الّذي سيستدعي ملفات جافاسكربت --&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">
      The Answer is
      </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"answer"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./add.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./reduce.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./sum.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./main.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	إن هذا المثال يجمع عناصر المصفوفة الموجودة في الملف <code>main.js</code> ويظهرها في صفحة <code>index.html</code> ويكون محتوى الملفات على الشكل التالي:
</p>

<ul>
<li>
		<strong>الملف <code>add.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_10" style="">
<span class="kwd">function</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<strong>الملف <code>reduce.js</code></strong>
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_14" style="">
<span class="kwd">function</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> iteratee</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
	</span><span class="kwd">var</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    length </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
    memo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">
    index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pun">(;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> length</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
	    memo </span><span class="pun">=</span><span class="pln"> iteratee</span><span class="pun">(</span><span class="pln">memo</span><span class="pun">,</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">])</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> memo</span><span class="pun">;</span><span class="pln"> 
</span><span class="pun">}</span></pre>

<ul>
<li>
		<strong>الملف <code>sum.js</code></strong>
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_16" style="">
<span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">){</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> add</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<strong>الملف <code>main.js</code></strong>
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_18" style="">
<span class="kwd">var</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pln"> </span><span class="pun">];</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"answer"</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> answer</span><span class="pun">;</span></pre>

<p>
	ولكن هذا الحل لم يكُ مثاليًا بل واجه المشاكل التالية:
</p>

<ol>
<li>
		عدم وجود ثبات في التبعية بين الشيفرة البرمجية.
	</li>
	<li>
		التضارب في المتغيرات العامة مازالت موجودة.
	</li>
	<li>
		تحديث المكتبات أو الملفات المستدعاة سيكون يدويًا في حال ظهور نسخة جديدة للمكتبة (أو الملف).
	</li>
	<li>
		زيادة عدد طلبات HTTP بين المخدم والعميل: يؤدي زيادة الملفات المتضمنة في إلى زيادة الحمل على المخدم. فعلى سبيل المثال في الشيفرة السابقة سيحتاج كلّ ملف من الملفات الأربع طلب HTTP منفصل (أي أربع رحلات ذهاب وعودة من المخدم وإلى جهاز العميل) وبناءً عليه سيكون من الأفضل لو استخدمنا ملفًا واحدًا بدلًا من أربع ملفات منفصلة.
	</li>
</ol>
<p>
	كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_20" style="">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">External File Example</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="com">&lt;!-- الملف الرئيسي الّذي سيستدعي ملفات جافاسكربت --&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">
      The Answer is
      </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"answer"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
  </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"/dist/bundle.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<h4>
	استخدام كائن الوحدات (Module Object) ونمط الوحدات (Module Pattern)
</h4>

<p>
	باستخدام نمط الوحدات (والذي تحدثنا عنه في <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%81%D9%8A-javascript-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-r47/" rel="">مقال</a> مفصّل) يمكننا أن نقلل التضارب في المتغيّرات العامة. إذ إننا سنكشف عن كائن واحد للمجال العام (Global Scope) وهذا الكائن سيحتوي على كافة الدوال والقيم الّتي سنحتاج إليها في تطبيق الوِب خاصتنا.
</p>

<p>
	في المثال التالي سنكشف عن كائن واحد وهو <code>myApp</code> للمجال العام (Global Scope)، وكلّ الدوال ستكون متاحة عن طريق هذا الكائن.
</p>

<p>
	والمثال التالي سيُوضح الفكرة:
</p>

<ul>
<li>
		<strong>الملف <code>my-app.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_22" style="">
<span class="kwd">var</span><span class="pln"> myApp </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span></pre>

<ul>
<li>
		<strong>الملف <code>add.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_24" style="">
<span class="pun">(</span><span class="kwd">function</span><span class="pun">(){</span><span class="pln">
	myApp</span><span class="pun">.</span><span class="pln">add </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
	</span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">})();</span></pre>

<ul>
<li>
		<strong>الملف <code>reduce.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_30" style="">
<span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    myApp</span><span class="pun">.</span><span class="pln">reduce </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> iteratee</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">var</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
            length </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
            memo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">
        index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> length</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            memo </span><span class="pun">=</span><span class="pln"> iteratee</span><span class="pun">(</span><span class="pln">memo</span><span class="pun">,</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">])</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> memo</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">})();</span></pre>

<ul>
<li>
		<strong>الملف <code>sum.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_35" style="">
<span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    myApp</span><span class="pun">.</span><span class="pln">sum </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">arr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> myApp</span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> myUtil</span><span class="pun">.</span><span class="pln">add</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">})();</span></pre>

<ul>
<li>
		<strong>الملف <code>main.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_37" style="">
<span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">app</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">var</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> app</span><span class="pun">.</span><span class="pln">sum</span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"answer"</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> answer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">})(</span><span class="pln">myApp</span><span class="pun">);</span></pre>

<ul>
<li>
		<strong>الملف <code>index.html</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_39" style="">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">JS Modules</span><span class="tag">&lt;/title&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">
        The Answer is
        </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"answer"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
    </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./my-app.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./add.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./reduce.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./sum.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./main.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	<strong>لاحظ</strong> أن كلّ ملف من الملفات غُلّف بطريقة نمط الوحدات ما عدا <code>my-app.js</code> إذ أن طريقة التغليف العامة المتبعة في نمط الوحدات هي على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_41" style="">
<span class="pun">(</span><span class="kwd">function</span><span class="pun">(){</span><span class="pln"> </span><span class="com">/*... your code goes here ...*/</span><span class="pln"> </span><span class="pun">})();</span></pre>

<p>
	بهذه الطريقة نجد أن كلّ المتحولات المحلية ستبقى ضمن مجال الدالة المستخدمة فيه. وبذلك لن يكون هنالك أي احتمالية لحدوث تضارب في المتغيرات العامة. وهكذا نكون حللنا مشكلة التضارب في المتغيرات العامة.
</p>

<p>
	سيكون الوصول إلى الدوال المعرفة في الملفات مثل (add و reduce ..إلخ) عن طريق ربط اسم الدالة مع الكائن <code>myApp</code> كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_43" style="">
<span class="pln">myApp</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">);</span><span class="pln">  
myApp</span><span class="pun">.</span><span class="pln">sum</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="lit">2</span><span class="pun">,</span><span class="lit">3</span><span class="pun">,</span><span class="lit">4</span><span class="pun">]);</span><span class="pln">  
myApp</span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">(</span><span class="pln">add</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span></pre>

<p>
	كما يمكننا أيضًا تمرير الكائن <code>myApp</code> كوسيط مثل ما فعلنا في ملف <code>main.js</code> وذلك من أجل تصغير أسم الكائن وجعل الشيفرة البرمجية أصغر حجمًا.
</p>

<p>
	تعدّ هذه الطريقة تحسنًا ممتازًا بالموازنة مع المثال السابق وفي الحقيقة إن أشهر مكتبات جافاسكربت مثل <code>jQuery</code> تستخدم هذه الطريقة؛ إذ أنها فقط تكشف عن متغير عام وهو <code>$</code> وكل التوابع تُستدعى عن طريق هذا الكائن.
</p>

<p>
	في الحقيقة إننا إلى هذه اللحظة لم نصل إلى الحل النهائي إذ أن الحل السابق لا يزال يعاني من بعض المشاكل مثل:
</p>

<ol>
<li>
		<strong>عدم وجود ثبات في التبعية بين الشيفرة البرمجية</strong>: نلاحظ في ملف <code>index.html</code> الملفات لا تزال بحاجة إلى وضعها وفق ترتيب معين يحافظ على التبعية بين الملفات إذ أن الملف <code>myApp.js</code> يجب أن يأتي قبل أي ملف وملف <code>main.js</code> يجب أن يأتي بعد كلّ الملفات.
	</li>
	<li>
		<strong>التضارب في المتغيّرات العامة</strong>: صحيح أننا قللنا عدد المتغيّرات العامة إلى الواحد ولكنها لا تزال موجودة.
	</li>
	<li>
		<strong>تحديث المكتبات</strong>: إن تحديث المكتبات (أو التبعيات أو أي ملف عمومًا) المستدعاة سيكون يدويًا في حال ظهور نسخة جديدة للمكتبة أو للتبعية.
	</li>
	<li>
		<strong>زيادة عدد طلبات HTTP بين الخادم والعميل</strong>: وذلك بعد زيادة عدد الملفات المستدعاة في ملف <code>index.html</code>.
	</li>
</ol>
<h3>
	ظهور CommonJS
</h3>

<p>
	جرت نقاشات عديدة في عام 2009 حول إمكانية جلب لغة جافاسكربت إلى الواجهات الخلفية (استخدامها من جانب الخادم) وفعلًا كان ذلك على يد مهندسٍ شاب يعمل لدى شركة موزيلا ويُدعى كيفن دانجور (Kevin Dangoor).
</p>

<p>
	قدمت <code>CommonJS</code> طريقة لتعريف الوحدات لحل مشكلة المجال في جافاسكربت من خلال التأكد من تنفيذ كلّ وحدة في فضاء الأسماء (namespace) الخاص بها وذلك بإجبار الوحدات على تصدير تلك المتغيّرات صراحةً (الذين نريد عرضهم واستخدامهم في المجال العام) وأيضًا تعريف تلك الوحدات المطلوبة للعمل بالشكل الصحيح مع بعضها بعضًا.
</p>

<p>
	بالإضافة إلى ذلك قدمت <code>CommonJS</code> واجهة برمجية مخصصة للجافاسكربت هذه المميزات فتحت أبصار العالم لإمكانيات هذه اللغة العظيمة، وجذبت اهتمام كبير من المطورين والذي انعكس في سرعة تطورها وتأقلمها مع جميع التغيّرات والمشاكل الّتي واجهتها.
</p>

<p>
	إن <code>CommonJS</code> ليست مكتبة جافاسكربت وإنما هي معايير تنظيمية لكتابة الشيفرة البرمجية (على سبيل المثال الطريقة الّتي خصصتها لاستيراد الوحدات استيرادً منظمًا) وهذه المعايير شبيه بالّتي تطرحها منظمة ECMA ‏(European Computer Manufacturers Association).
</p>

<p>
	ولتحقيق التنظيم في طريقة استدعاء الوحدات تقدم لنا <code>CommonJS</code> الدوال والأغراض المساعدة لذلك وهي:
</p>

<ol>
<li>
		<strong>دالة <code>require()‎</code></strong>: والّتي تسمح لنا باستيراد وحدات معينة في المجال المحلي الّذي نكون فيه.
	</li>
	<li>
		<strong>كائن الوحدة <code>module.exports</code></strong>: والذي يسمح لنا بتصدير دالة ما من المجال الحالي إلى الوحدة.
	</li>
</ol>
<p>
	ولنأخذ مثلًا بسيطًا عن كيفية استخدام <code>CommonJS</code>:
</p>

<ul>
<li>
		<strong>الملف <code>moduleA.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_45" style="">
<span class="pln">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln"> value </span><span class="pun">){</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">*</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<strong>الملف <code>moduleB.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_47" style="">
<span class="kwd">var</span><span class="pln"> multiplyBy2 </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./moduleA'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> multiplyBy2</span><span class="pun">(</span><span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	لاحظ أننا لم نستخدم الاسم الكامل للملف <code>moduleA.js</code> وإنما حذفنا اللاحقة، كما أن <code>/.</code> تشير إلى أن الوحدة <code>moduleA</code> موجودة في نفس المجلد الموجود فيه <code>moduleB</code>.
</p>

<p>
	وبفضل مشروع <code>CommonJS</code> ظهر أكبر مشروع معتمدًا عليه وهو <code>Node.js</code> وهي بيئة تشغيل لغة جافاسكربت مفتوحة المصدر وتعمل على جميع أنظمة التشغيل، والّتي تستطيع تشغيل شيفرة جافاسكربت خارج المتصفحات.
</p>

<p>
	بالإضافة إلى ذلك تسمح <code>Node.js</code> للمطوري الواجهات الخلفية باستخدام جافاسكربت لكتابة برمجيات تعمل من جهة الخادم وذلك لتوليد صفحات الوِب توليدًا ديناميكيًا قبل إرسالها إلى المتصفح، وكما تستطيع أيضًا التعامل مع الملفات وقواعد البيانات، ومختلف أنظمة الشبكات وخدمات أنظمة التشغيل.
</p>

<h3>
	الوحدة غير المتزامنة AMD
</h3>

<p>
	علِمنا إن الهدف الرئيسي الّذي بنيت عليه <code>CommonJS</code> هو استخدامها في الواجهات الخلفية (من طرف المخدم) ولذلك مشكلة استدعاء الوحدات استدعاءً متزامنًا لم تكُ مشكلة في ذلك الحين، بل ولم تكُ لتظهر لولا إصرار مجتمع المطورين على جلب <code>CommonJS</code> إلى الواجهات الأمامية ولنُلخص مشكلة <code>CommonJS</code> هي عندما نستدعي الوحدة معينة في ملف ما ولتكن مثلًا <code>add</code> كما في السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_49" style="">
<span class="kwd">var</span><span class="pln"> add</span><span class="pun">=</span><span class="pln">require</span><span class="pun">(</span><span class="str">'add'</span><span class="pun">);</span></pre>

<p>
	سيتوقف النظام حتى تصبح الوحدة <code>add</code> جاهزة؛ أي أن هذا السطر البرمجي سيجمد المتصفح أثناء تحميل هذا الملف. ماذا لو كان هنالك أكثر من وحدة مطلوبة كيف ستكون الأمور عندها؟ لذلك إنها ليست أفضل طريقة لتعريف الوحدات من جانب المتصفح.
</p>

<p>
	من أجل نقلِ صياغة تعريف وحدة ما من صياغة يفهمها الخادم إلى صياغة يفهمها المتصفح قدمت لنا <code>CommonJS</code> العديد من التنسيقات لتعريف الوحدات وكانت واحدة من أشهرها هي تعريف الوحدات بالطريقة غير المتزامنة (Asynchronous Module Definition) والّتي تُعرف إختصارًا AMD وتكون طريقة تعريفها كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_51" style="">
<span class="pln">define</span><span class="pun">([‘</span><span class="pln">add</span><span class="pun">’,</span><span class="pln"> </span><span class="pun">‘</span><span class="pln">reduce</span><span class="pun">’],</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">add</span><span class="pun">,</span><span class="pln"> reduce</span><span class="pun">){</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){...};</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إن الدالة <code>define</code> تأخذ قائمة بالتبعيّات الموجودة والتي مرّرناها لها كقائمة وستُعيد النتيجة إلى الدالة المجهولة (Anonymous function) كوسطاء (الدالة المجهولة هي الدالة الّتي ليس لديها اسم ولقد فصلنا في <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%88%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B3%D9%87%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r782/" rel="">مقالٍ سابق</a> كيفية استخدمها وذلك في سلسلة <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA/" rel="">تعلم جافاسكربت</a>). وسيكون ترتيب التبعيات بنفس الترتيب الموجود في القائمة ومن ثم ستُعيد الدالة المجهولة النتيجة والتي سنُصدرها ونستخدمها في نهاية المطاف.
</p>

<p>
	هذه الطريقة تُكافئ الطريقة السابقة لاستدعاء الوحدات (التبعيات) لأنها تعطي نفس النتائج الوظيفية، إلا أن هذه الطريقة تستخدم التحميل غير المتزامن للوحدات.
</p>

<p>
	إن <code>CommonJS</code> و <code>AMD</code> حلّت آخر مشكلتين متبقيتين مع نمط الوحدات وهما على الشكل التالي:
</p>

<ol>
<li>
		عدم وجود ثبات في التبعية بين الشيفرة البرمجية.
	</li>
	<li>
		التضارب في المتغيرات العامة.
	</li>
</ol>
<p>
	نحن فقط سنهتم بالتبعيات الموجودة من أجل كلّ وحدة (أو ملف) وبالتأكيد في هذه الحالة لا يوجد هناك أي تضارب في المتغيرات العامة.
</p>

<h2>
	مُحمل الوحدات RequireJS
</h2>

<p>
	في الحقيقة إن طريقة استخدام <code>AMD</code> مع <code>CommonJS</code> كانت مفيدة جدًا، ولكن كيف يمكننا استخدامها في المتصفح وهنا جاء محمل الوحدات <code>RequireJS</code> إلى الساحة والّذي سيساعدنا لتحميل الوحدات تحميلًا غير متزامن <code>AMD</code>. والذي يعتبر من أوائل التطبيقات الفعلية لمكتبة <code>CommonJS</code> مع <code>AMD</code>.
</p>

<p>
	على الرغم من إسمها، إلا أن هدفها ليس دعم بناء جملة 'require' في <code>CommonJS</code>. إن <code>RequireJS</code> تُتيح لنا كتابة وحدات من نمط <code>AMD</code>. <strong>ملاحظة</strong>: قبل أن تطبق المثال التالي، يجب عليك <a href="https://requirejs.org/docs/download.html" rel="external nofollow">تنزيل</a> ملف <code>require.js</code> من موقعه الرسمي.
</p>

<p>
	لاحظ أن الاستدعاء الوحيد الموجود في ملف <code>index.html</code> هو:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_53" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">data-main</span><span class="pun">=</span><span class="atv">”main”</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">”require.js”</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	هذا الوسم سيُحمِّلُ محليًا مكتبة <code>require.js</code> إلى الصفحة، ومن بعدها السمة <code>data-main</code> في الوسم <code>&lt;script&gt;</code> ستخبر المكتبة من أي ملف ستبدأ. بعد أن تستدعي <code>require.js</code> ملف <code>main.js</code> سيقودنا هذا الملف إلى التبعيات المتعلقة به، وهكذا إلى أن نحمل جميع التبعيات (الملفات) المطلوبة.
</p>

<p>
	<strong>ملاحظة</strong>: استخدمنا <code>main</code> فقط بدون الإشارة إلى اللاحقة والتي هي <code>js</code> وذلك لأن هذه المكتبة تفترض بأن كلّ القيم الّتي سنُسندها لهذه الخاصية ستكون ملفات جافاسكربت.
</p>

<p>
	وسيكون تحميل التبعيات للمثال السابق كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36351" href="https://academy.hsoub.com/uploads/monthly_2020_04/dependencies.png.c346f349062b1b6125915b672c63530b.png" rel=""><img alt="dependencies.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36351" data-unique="data-unique" src="https://academy.hsoub.com/uploads/monthly_2020_04/dependencies.thumb.png.576b844de97883106a5cfcda93999a99.png"></a>
</p>

<p>
	نلاحظ أن المتصفح يحمّل الملف <code>index.html</code> والملف بدوره يحمّل المكتبة <code>require.js</code> والّذي سيتكفل ببقية الملفات الأخرى.
</p>

<p>
	وبهذا نكون حللنا جميع المشاكل الّتي واجهتنا حتى هذه اللحظة، ولكن وكما هي العادة في عالم البرمجة في كلّ محاولة لإصلاح مشكلةٍ ما تظهر مشكلةٌ أخرى، ولكن عادة ما تكون أخف من سابقتها في التأثير. ومن المشاكل الّتي ظهرت مع هذا الحل هي:
</p>

<ol>
<li>
		<strong>صياغة <code>AMD</code> طويلة ومضجرة</strong>: إذ أن كلّ شيء يجب تغليفة بالدالة <code>define</code> ولذا يوجد مسافة بادئة (يُقصد تعريف الدالة <code>define</code>) للشيفرة البرمجية وهذه المشكلة يمكن ألا تظهر مع الملفات الصغيرة ولكن ما إن يكبر حجم الملف وتزداد تفاصيله ستصبح مشكلة كبيرة.
	</li>
	<li>
		<strong>قوائم الاعتماديات الموجودة في المصفوفة</strong>: إن قائمة الاعتماديات الموجودة في المصفوفة يجب أن تتشابه تمامًا مع القائمة المررة كوسيط للدالة وإذا كان لدينا العديد من التبعيات ستزداد الأمور تعقيدًا.
	</li>
	<li>
		في النسخة الحالية من HTTP للمتصفحات تحميل العديد من الملفات الصغيرة سيخفف من الأداء (وخصيصًا بدون استخدام أي تقنيات مساعدة مثل: ذاكرة التخزين المؤقت Cache).
	</li>
</ol>
<h2>
	مجمع الوحدات أو مجمع الحزم (Module Bundler)
</h2>

<p>
	انطلاقًا من الأسباب السابقة أراد بعض الأشخاص بناء التعليمات من خلال <code>CommonJS</code> لكن هذه الأخيرة مخصصة للعمل على الخوادم وليس المتصفح وهي تدعم التحميل المتزامن أيضًا.
</p>

<p>
	بقيت هذه الإشكالية عصية على الحل حتى جاء مجمع الوحدات <code>Browserify</code> لينقذ الموقف ويقدم لنا حلولً مناسبة. وهو يعدّ أول مجمع وحدات وجد آنذاك، وكان باستطاعته تحويل الوحدات المكتوبة بصياغة <code>CommonJS</code> والتي لا يستطيع المتصفح أن يفهمها بطبيعة الحال (وذلك لأنها بُنيت بالأساس للتعامل مع الخادم) إلى صياغة يفهمها المتصفح.
</p>

<p>
	يمكننا التعامل مع مجمع الحزم <code>Browserify</code> من خلال سطر الأوامر إذ يمكنك إعطاؤه أمرًا ما وتمرير بعض الخيارات الإضافية المتاحة لكل أمر من الأوامر الموجودة. وهو يعتمد في بنيته على مدير الحزم npm وبيئة <code>Node.js</code>.
</p>

<p>
	يمكن لمُجمع الوحدات <code>Browserify</code> تجميع شجرة التبعيات للشيفرة البرمجية الخاصة بك وتحزيمها في ملف واحد. وأيضًا يحل مشكلة عدم إمكانية الوصول إلى الملفات من قبل المتصفح إذ إنه يبحث عن جميع تعليمات require (والتي لا يمكن للمتصفح أن يستدعي الملف المطلوب لأنه لا يملك الصلاحيات المناسبة لذلك) فيستبدلها بمحتوى الملف المراد تضمينه (وذلك انطلاقًا من اعتماده على <code>Node.js</code> والتي تستطيع الوصول إلى الملفات) وهكذا يصبح الملف جاهزًا للتنفيذ في المتصفح ولا يواجه أي مشكلة.
</p>

<h2>
	حلول كثيرة فأي منها سنستخدم؟
</h2>

<p>
	إذا لدينا الكثير من الخيارات المتاحة فأي واحدة منهم سنستخدم في مشروعنا القادم؟ إذ إننا حتى هذه اللحظة ناقشنا العديد من الخيارات المتاحة لكتابة الوحدات مثل: نمط الوحدات (Module Pattern) وطريقة <code>CommonJS</code> مع <code>AMD</code> وطريقة <code>RequireJS</code> فأي منهم سنعتمده لمشروعنا القادم؟ الجواب: ولا واحدة منها.
</p>

<p>
	كما نعلم بأن لغة جافاسكربت لم تكُ تدعم نظام الوحدات في أصل اللغة، ولهذا نلاحظ وجود العديد من الخيارات المتاحة لتعويض هذا النقص. ولكن هذا الأمر لم يستمر طويلًا إذ بعد تحديث المواصفات القياسية للغة جافاسكربت ES6 أصبح وأخيرًا نظام الوحدات جزءًا أساسيًا منها وهذا يكون جواب سؤالنا الرئيسي أي الخيارات سنستخدم؟ إنها طريقة ES6 (تحدثنا في <a href="https://academy.hsoub.com/tags/introduction%20to%20es6/" rel="">سلسلة</a> مقالاتٍ سابقة عن المميزات الجديدة لهذا الإصدار من لغة جافاسكربت ES6).
</p>

<p>
	تُستخدم التعليمة <code>import</code> و <code>export</code> لاستيراد وتصدير الوحدات في التحديث الجديد ES6. وإليك مثالًا عمليًا يوضح لك كيفية استخدامها:
</p>

<ul>
<li>
		<strong>الملف <code>main.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_55" style="">
<span class="kwd">import</span><span class="pln"> sum from </span><span class="str">"./sum"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> values </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pln"> </span><span class="pun">];</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> answer </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">values</span><span class="pun">);</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"answer"</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> answer</span><span class="pun">;</span></pre>

<ul>
<li>
		<strong>الملف <code>sum.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_57" style="">
<span class="kwd">import</span><span class="pln"> add from </span><span class="str">'./add'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> reduce from </span><span class="str">'./reduce'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">){</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> add</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<strong>الملف <code>add.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_59" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln">b</span><span class="pun">){</span><span class="pln">
	</span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
<li>
		<strong>الملف <code>reduce.js</code></strong>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_61" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">arr</span><span class="pun">,</span><span class="pln"> iteratee</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    length </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
    memo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">];</span><span class="pln">
    index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">for</span><span class="pun">(;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> length</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">){</span><span class="pln">
        memo </span><span class="pun">=</span><span class="pln"> iteratee</span><span class="pun">(</span><span class="pln">memo</span><span class="pun">,</span><span class="pln"> arr</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> memo</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في الحقيقة دعم جافاسكربت لنظام الوحدات بهذا الشكل المختصر قلبَ مجريات الُلعبة، بل إن نظام الوحدات هذا سيحكم عالم جافاسكربت في نهاية المطاف. إنها باختصار مستقبل لغة جافاسكربت.
</p>

<p>
	ولكن! (أعلم بأنك سئمت من هذه الكلمة ولكن يجب علينا إكمال بقية جوانب هذه الطريقة لتوضيح جميع تفاصيل المشهد لديك) هذا التحديث من اللغة لم تدعمه جميع المتصفحات المستخدمة في هذا اليوم، أو لنكن أكثر دقة ما تزال بعض المتصفحات لا تدعم هذه التحديث وخصوصًا المتصفحات القديمة وفي الحقيقة نسبة المستخدمين لهذه المتصفحات كبيرة وتؤخذ بعين الإعتبار. إذا ما الّذي يجب علينا فعله الآن؟ وكيف سنحوّل الشيفرة البرمجية من الإصدار ES6 إلى الإصدار ES5؟ هنا يأتي دور أهم أداة سنتعرف عليها اليوم ألا وهي مجمع الوحدات <code>Webpack</code>.
</p>

<h2>
	ما هي Webpack؟
</h2>

<p>
	هي عبارة عن مجمع واحدات (Module Bundler) أو أداة بناء (Build Tool) مثل أداة <code>Browserify</code> وهي تحول شجرة تبعية الملفات وتُجمعها في ملف واحد. ليس ذلك فحسب وإنما تحوي على الكثير من المميزات الأخرى نذكر منها:
</p>

<ol>
<li>
		<strong>تقسيم الشيفرة البرمجية</strong>: عندما يكون لدينا تطبيقات متعددة تتشارك نفس الوحدات يمكننا من خلال هذه الأداة تجميع الشيفرة البرمجية في ملفين أو أكثر. فمثلًا إذا كان لدينا التطبيقين التاليين <code>app1</code> و <code>app2</code> ويشترك كلاهما في العديد من الوحدات إذا استخدمنا <code>Browserify</code> سيكون لدينا <code>app1.js</code> و <code>app2.js</code> وكل ملف منهم مجمع فيه الشيفرات البرمجية الخاصة بكل تطبيق، بينما مع أداة <code>Webpack</code> يمكننا إنشاء ملف <code>app1.js</code> وملف <code>app2.js</code> وبالإضافة إلى الملف المشترك <code>shared-lib.js</code>. نعم ستضطر إلى تحميل ملفين في صفحة <code>Html</code> ولكن مع إمكانية استخدام تقنيات مساعدة مثل: التخزين في الذاكرة المؤقتة للمتصفح (Cache) واستخدام شبكة توصيل المحتوى الموزعة CDN ‏(Content Delivery Network) كلّ هذه التقنيات ستقلل من وقت التحميل الإبتدائي للصفحات.
	</li>
	<li>
		<strong>المُحملات (Loaders)</strong>: مع استخدام المُحمل المخصص يمكنك تحميل أي ملف إلى التطبيق إذ يمكنك استخدام الدالة <code>reuiqre</code> ليس فقط لتحميل ملفات جافاسكربت فقط (مثلما كانت أداة <code>Browserify</code>) وإنما ملفات التنسيقات وملفات SaSS وملفات Less بالإضافة إلى إمكانية تحميل الصور والخطوط والملفات والكثير غيرها.
	</li>
	<li>
		<strong>الملحقات (Plugins)</strong>: تقدم <code>Webpack</code> ميزة الملحقات مجموعة واسعة من المهام مثل تحسين الحزمة وإدارة الملحقات (مثل الصور) بالإضافة إلى ذلك يمكننا حتى التلاعب بالملفات قبل تحميلها وتحزيمها في الملف الهدف (على سبيل المثال إمكانية ضغط الصور قبل رفعها على المخدم سنتطرق لهذه الميزّة لاحقًا في هذا الدليل).
	</li>
	<li>
		<strong>نظام الأوضاع (Mode)</strong>: توفر لك هذه الأداة ثلاث أنواع من الأوضاع وضع التطوير ووضع الإنتاج أو بدون وضع. باختصار يستخدم كلّ وضع من الأوضاع من أجل سيناريو معين:
		<ul>
<li>
				وضع التطوير: يستخدم عند العمل في مرحلة التطوير لمشروع الوِب وتكون الشيفرة البرمجية مقروءة ومفهومة.
			</li>
			<li>
				وضع الإنتاج: يستخدم عند العمل في مرحلة عرض مشروع الوِب على الإنترنت وتكون الشيفرة البرمجية مختصرة وغير مقروءة.
			</li>
			<li>
				بدون وضع: يكون الشيفرة الّتي استخدمت في مرحلة التطوير هي ذاتها الّتي ستعرض على الإنترنت بدون أي تعديل.
			</li>
		</ul>
</li>
	<li>
		<strong>تسهل عملية تحويل الشيفرات البرمجية إلى إصدار أقدم (Transpiling)</strong>: إمكانية تحويل شيفرات جافاسكربت البرمجية الحديثة مثل ES6 والّتي لا تدعمها بعض المتصفحات إلى شيفرات جافاسكربت البرمجية القديمة مثل ES5 من خلال Babel على سبيل المثال، وبذلك نكون ضربنا عصفورين بحجر واحد أولهما أننا كتبنا التطبيق بالإصدار الحديث من اللغة، وثانيهما أننا استطعنا تشغيل التطبيق على كافة المتصفحات. أليس هذا رائعًا؟
	</li>
</ol>
<p>
	لابد بأنك متحمسٌ جدًا للتعرف على هذه الأداة والبدء في استخدامها على الفور في مشاريعك القادمة إذًا هيا بنا نبدأ لنتعرف عليها أكثر ونتعلم كيفية استخدامها.
</p>

<h3>
	مفاهيم أساسية
</h3>

<p>
	قبل الشروع في العمل مع هذه الأداة لا بدّ لنا من التعرف على بعض المفاهيم الرئيسية والتي سنستخدمها بكثرة في الأمثلة <strong>ملف الإعداد</strong> <code>webpack.config.js</code> :هو عبارة عن ملف يحدد كيفية تعامل الأداة Webpack مع بعض الخصائص للمحملات أو الملحقات وبعض خصائص الأداة نفسها. وعند إنشائنا لمشروع جديد لن نجده في مجلد المشروع وذلك بسبب ميزة التعديلات الصفرية (zero configuration) الجديدة الّتي جاءت مع الإصدار الرابع أو الأحدث من هذه الأداة والتي تمكننا من استخدام الأداة بدون الحاجة لأي تعديل في ملف الإعداد. بل يمكننا عدم إنشاؤه من الأساس.
</p>

<p>
	يمكن تخصيص هذا الملف تخصيصًا بسيطًا أو متقدمًا وذلك بحسب المشروع الّذي تعكفُ على تطويره، وهنالك بعض الخصائص الأساسية المشتركة في كلّ المشاريع، والّتي يجب التنويه إليها:
</p>

<ul>
<li>
		<strong>الخاصية Entry</strong>: تحدد هذه الخاصية ملف الأولي من الشروع الّذي سنبنيه (نجمعه) في أول التنفيذ.
	</li>
	<li>
		<strong>الخاصية Output</strong>: تحدد مجلد المشروع الّذي سنستخدمه في مرحلة الإنتاج (أو النشر) يكون فيه الشيفرة البرمجية الّتي نريد استخدامها لنشر المشروع على الإنترنت. تحتوي هذه الخاصية على بعض الخيارات نذكر منها:
		<ul>
<li>
				<strong>filename</strong>: نحدد فيها أسم الملف الّذي نريد تجميع الحزم فيه.
			</li>
			<li>
				<strong>path</strong>: نحدد فيها مسار الملف الّذي نريد تجميع الحزم فيه.
			</li>
		</ul>
</li>
	<li>
		<strong>الخاصية Loaders</strong>: تحدد المحملات الّتي نريد أن نستخدمها مع Webpack. كلّ مُحمل يحتوي على بعض الخيارات نذكر منها:
		<ul>
<li>
				<strong>test</strong>: تحدد نوع الملفات الّتي سيُحملها هذا المُحمل (يمكننا استخدام التعابير النمطية في تحديد الملفات).
			</li>
			<li>
				<strong>exclude</strong>: الملفات الّتي نريد استبعادها (والتي من الممكن أن تحقق الخاصية الأولى).
			</li>
			<li>
				<strong>use</strong>: ما هو المُحمل الّذي سنستخدمه لإجراء التغييرات على الملفات المقبولة في test ولم تُستبعد في الخاصية exclude.
			</li>
		</ul>
</li>
	<li>
		<strong>الخاصية Plugins</strong>: تحدد هذه الخاصية الملحقات الّتي نريد استخدامها وكما تحدد بعض الخيارات لهذه الملحقات.
	</li>
	<li>
		<strong>الخاصية Mode</strong>: تحدد هذه الخاصية الوضع الّذي نريد العمل عليه أي طريقة نشر المشروع إلى مجلد الخرج (output).
	</li>
</ul>
<p>
	كانت هذه أبرز المفاهيم الأساسية الّتي سنتعامل معها في الأمثلة القادمة.
</p>

<h2>
	إعداد بيئة التطوير
</h2>

<p>
	بما أن مجمعات الحزم (الوحدات) تعتمد على <code>Node.js</code> اعتمادً أساسيًا للوصول إلى نظام الملفات من أجل تجميع الاعتماديات بشكلها النهائي لذا لابد لنا من تثبيت <code>Node.js</code> في البداية وإليك الخطوات الكاملة لتثبيته على نظام التشغيل ويندوز (يمكنك الاطلاع على <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-nodejs-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r419/" rel="">مقال سابق</a> تحدثنا فيه عن كيفية تثبيت <code>Node.js</code> على نظام لينكس):
</p>

<ol>
<li>
		ندخل إلى <a href="https://nodejs.org/en/download/" rel="external nofollow">الموقع</a> الرسمي الخاص ببيئة <code>Node.js</code> ونحمل النسخة الموافقة لنظام التشغيل الخاص بك (يفضل تحميل الإصدارات ذات الدعم الطويل والتي تُدعى LTS).
	</li>
	<li>
		بمجرد إنتهاء التنزيل نفتح الملف المُنزّل.
	</li>
	<li>
		سيسألك معالج التثبيت عن موقع التثبيت والمكونات الّتي تريد تضمينها في عملية التثبيت يمكنك ترك تلك الخيارات بوضعها الافتراضي.
	</li>
	<li>
		انقر فوق تثبيت وانتظر حتى ينتهي معالج التثبيت.
	</li>
</ol>
<p>
	تهانينا أصبحت بيئة <code>Node.js</code> جاهزة للعمل.
</p>

<p>
	<strong>ملاحظة</strong>: يمكنك التأكد من تثبيت البيئة تثبيتًا صحيحًا من خلال تنفيذ الأمر التالي على سطر الأوامر (Command line)
</p>

<pre class="ipsCode">
node –v
</pre>

<p>
	يجب أن يظهر لك نسخة <code>Node.js</code> المثبتة وفي حالتنا ستظهر <code>12.16.1</code>. كما يمكنك التأكد من تثبيت مدير الحزم Node Package Manager والّذي يُعرف اختصارًا <code>npm</code> من خلال الأمر
</p>

<pre class="ipsCode">
npm –v
</pre>

<p>
	يجب أن تظهر لك نسخة <code>npm</code> المثبتة وفي حالتنا ستظهر <code>6.13.4</code>.
</p>

<p>
	نحن جاهزون الآن لإنشاء مشروع جديد وتثبيت <code>Webpack</code>!
</p>

<h3>
	إنشاء مشروع جديد وتثبيت Webpack
</h3>

<ol>
<li>
		في البداية ننشئ مجلدًا جديدًا للمشروع وندخل إليه عبر التعليمات التالية:
	</li>
</ol>
<pre class="ipsCode">
$ mkdir project; cd project
</pre>

<ol start="2">
<li>
		ثم ننشئ مشروعًا جديدًا عبر التعليمة التالية:
	</li>
</ol>
<pre class="ipsCode">
$ npm init -y
</pre>

<ol start="3">
<li>
		ثم نثبت أداة Webpack عبر التعليمة التالية:
	</li>
</ol>
<pre class="ipsCode">
$ npm install -D webpack webpack-dev-server
</pre>

<p>
	يجب أن يظهر لك نتيجة مماثلة للصورة التالية:
</p>

<p style="text-align: center;">
	<img alt="webpack-installed.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36356" data-unique="bay6sz6hr" src="https://academy.hsoub.com/uploads/monthly_2020_04/webpack-installed.png.1d4f1ec6b39f27f81bf98c50120cabbc.png"></p>

<p>
	الآن لننشئ بنية الملفات الهرمية التالية:
</p>

<pre class="ipsCode">
webpack-demo
  |- package.json
+ |- index.html
+ |- /src
+   |- index.js
</pre>

<p>
	<strong>ملاحظة</strong>: جرى عرض الملفات في هذه المقالة بأسلوب عرض التغييرات في git فالأسطر الّتي بجانبها إشارة موجبة + يجب عليك إضافتها والّتي بجانبها إشارة سالبة - يجب عليك حذفها.
</p>

<p>
	الآن نفتح ملف <code>index.js</code> الموجود في مجلد <code>src</code> ونجري عليه التعديلات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_63" style="">
<span class="pln">unction component</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> element </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'div'</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// Lodash, currently included via a script, is required for this line to work</span><span class="pln">
  element</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> _</span><span class="pun">.</span><span class="pln">join</span><span class="pun">([</span><span class="str">'Hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'webpack'</span><span class="pun">],</span><span class="pln"> </span><span class="str">' '</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> element</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">component</span><span class="pun">());</span></pre>

<p>
	ونعدل الملف <code>index.html</code> الموجود في مجلد <code>webpack-demo</code> ونجري عليه التعديلات التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_65" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Getting Started</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://unpkg.com/lodash@4.16.6"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./src/index.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	كما يجب علينا أن نعدل ملف <code>package.json</code> لتفعيل الوضع الخاص كي نمنع النشر العرضي للشيفرة البرمجية الخاصة بنا ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_67" style="">
<span class="pln">  </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack-demo"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   </span><span class="str">"private"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
</span><span class="pun">-</span><span class="pln">   </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
    </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"webpack"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^4.20.2"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"webpack-cli"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^3.1.2"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p>
	لمزيد من المعلومات حول التفاصيل الداخلية لملف <code>package.json</code> ننصحك بأخذ جولة في <a href="https://docs.npmjs.com/files/package.json" rel="external nofollow">التوثيق الرسمي</a> الخاص به.
</p>

<p>
	في المثال السابق هنالك تبعيات ضمنية إذ أن الملف <code>index.js</code> يعتمد على <code>loadash</code> (وهي مكتبة تساعد المبرمجين على كتابة شيفرة برمجية أكثر إيجازًا وتزيد من قابلية الصيانة) في عمله وبناءً عليه يجب أن يكون <code>loadash</code> مُضمن قبل أن تعمل الشيفرة البرمجية للملف <code>index.js</code>. ولكن إن الملف <code>index.js</code> لم يُصرح بوضوح عن حاجته لهذه المكتبة وإنما افترض أنها موجودة ضمن المتحولات العامة.
</p>

<p>
	في الحقيقة إن إدارة المشروع بهذه الطريقة تعدّ مشكلة وذلك للأسباب التالية:
</p>

<ol>
<li>
		ليس من الواضح أن الشيفرة البرمجية للملف <code>index.js</code> تعتمد على مكتبة خارجية.
	</li>
	<li>
		إذا كانت التبعية مفقودة، أو ضُمنت في الترتيب الخطأ، فلن يعمل التطبيق بالطريقة الصحيحة.
	</li>
	<li>
		إذا ضُمنت تبعيةً ما ولكن لم تُستخدم، سيضطر المتصفح إلى تنزيلها مع أنها غير ضرورية.
	</li>
</ol>
<p>
	لنستخدم Webpack لإدارة هذه المشروع بدلًا من الطريقة الحالية.
</p>

<h3>
	إنشاء حزمة باستخدام Webpack
</h3>

<p>
	سنعدل بنية المشروع قليلًا لفصل الشيفرة البرمجية الّتي سنعمل عليها عن الشيفرة البرمجية الّتي سننشرها. وتجدر الإشارة إلى أن الشيفرة البرمجية الّتي سننشرها هي الناتج المصغّر والمحسّن لعملية البناء الناتجة عن الشيفرة الّتي كتبناها والتي ستُحمّل في نهاية المطاف في المتصفح.
</p>

<p>
	إذًا سنضع الشيفرة البرمجية الّتي سنعمل عليها في المجلد <code>src</code> أما الّتي سننشرها ستكون في مجلد <code>dist</code> أي ستكون بهذا الشكل:
</p>

<pre class="ipsCode">
  webpack-demo
  |- package.json
+ |- /dist
+   |- index.html
- |- index.html
  |- /src
    |- index.js
</pre>

<p>
	لتحزيم مكتبة <code>loadash</code> (والّتي هي تبعية في ملف <code>index.html</code>) باستخدام الملف <code>index.js</code> سنحتاج أولًا إلى تثبيت هذه المكتبة محليًا باستخدام الأمر التالي:
</p>

<pre class="ipsCode">
npm install --save lodash
</pre>

<p>
	عند تثبيت حزمة (مكتبة) معينة والّتي سنحتاجها في عملية التحزيم النهائية قبل النشر النهائي يجب علينا إضافة <code>install --save</code> أما إذا أردت تثبيت حزمة (مكتبة) معينة لمرحلة التطوير فقط يجب عليك إضافة <code>install --save-dev</code>.
</p>

<p>
	لمزيد من المعلومات ننصحك بالإطلاع على <a href="https://docs.npmjs.com/cli/install" rel="external nofollow">التوثيق الرسمي</a> لمدير الحزم NPM.
</p>

<p>
	الآن لنضيف تعليمة استيراد الحزمة في الشيفرة البرمجية للملف <code>index.js</code> الموجود في المجلد <code>src</code> كما في الشكل التالي:
</p>

<pre class="ipsCode">
+ import _ from 'lodash';
+
  function component() {
    const element = document.createElement('div');

-   // Lodash, currently included via a script, is required for this line to work
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    return element;
  }

  document.body.appendChild(component());
</pre>

<p>
	بما أننا قمنا باستيراد مكتبة <code>loadash</code> في الملف السابق لنعدل الملف <code>index.html</code> بما يتوافق مع ذلك ليكون كما في الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_69" style="">
<span class="pln"> </span><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
  </span><span class="tag">&lt;html&gt;</span><span class="pln">
   </span><span class="tag">&lt;head&gt;</span><span class="pln">
     </span><span class="tag">&lt;title&gt;</span><span class="pln">Getting Started</span><span class="tag">&lt;/title&gt;</span><span class="pln">
-    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://unpkg.com/lodash@4.16.6"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
   </span><span class="tag">&lt;/head&gt;</span><span class="pln">
   </span><span class="tag">&lt;body&gt;</span><span class="pln">
-    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"./src/index.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
+    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"main.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
   </span><span class="tag">&lt;/body&gt;</span><span class="pln">
  </span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	<strong>ملاحظة</strong>: يتطلب ملف <code>index.js</code> صراحة وجود المكتبة (أو أي تبعية إذا أردنا تعميم الفكرة) <code>loadash</code> ويربطه كمتحول <code>_</code> (أي لا يوجد تضارب في المجال). من خلال تحديد التبعيات الّتي تحتاجها الوحدة، يمكن لمُجمع الوحدات (المقصود Webpack) استخدام هذه المعلومات لإنشاء مخطط بياني للاعتماديات (dependency graph). ثم يستخدم الرسم البياني لإنشاء حزمة محسنة والّتي ستُنفذ الاستدعاءات بالترتيب الصحيح.
</p>

<p>
	لننفذ التعليمة التالية <code>npx webpack</code> والتي ستأخذ الشيفرة البرمجية في الملف <code>index.js</code> وهو القيمة المسندة للخاصية <code>entry</code> وتنشر الخرج في ملف <code>main.js</code> والذي سيكون القيمة في المسندة للخاصية <code>output</code>. إن تعليمة <code>npx</code> والتي تأتي مع نسخة <code>8.2</code> من <code>Node.js</code> أو أحدث، و <code>5.2.0</code> من مدير الحزم <code>NPM</code> أو أحدث. إذ تعمل على أداة Webpack الثنائية الموجودة في <code>(‎./node_modules/.bin/webpack)</code> الّتي ثبتناها في البداية. ويكون ناتج التنفيذ كما يلي:
</p>

<pre class="ipsCode">
npx webpack

...
Built at: 13/06/2018 11:52:07
  Asset      Size  Chunks             Chunk Names
main.js  70.4 KiB       0  [emitted]  main
...

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
</pre>

<p>
	<strong>ملاحظة</strong>: من الممكن أن يختلف الناتج الّذي سيظهر لك بعض الشيء ولكن إذا نجحت عملية البناء فلا تقلق من رسالة التحذير فإن الأمور على ما يرام. افتح الملف <code>index.html</code> يجب أن يظهر لك <code>Hello webpack</code> فإذا ظهرت لديك تهانينا هذا يدلّ على أنك أنجزت جميع الخطوات بنجاح.
</p>

<p>
	إذا ظهرت لديك رسالة خطأ في صياغة ملف جافاسكربت المصغّرة (minified) وذلك عند فتحك لملف <code>index.html</code> فعيّن وضع التطوير ومن ثم شغل الأمر <code>npx webpack</code> من جديد. هذا الخطأ يتعلق بتشغيل حزمة الوٍب <code>npx</code> على أحدث نسخة من <code>Node.js</code> (الإصدار <code>12.5</code> أو الأحدث) بدلًا من الإصدار ذو الدعم الطويل <code>LTS</code>.
</p>

<h3>
	الوحدات
</h3>

<p>
	في المواصفات القياسية لنسخة جافاسكربت ES6 دُعمت تعليمتي <code>import</code> و<code>export</code> إذ أن معظم المتصفحات تدعمها في الوقت الحالي (ولكن يوجد بعض المتصفحات لا تدعمها) و<code>Webpack</code> ليست استثناءً بل إنها تدعمها دعمًا متميزًا.
</p>

<p>
	تدعم <code>Webpack</code> هذه الخاصية من خلال تحويل (transpiles) الشيفرة البرمجية المقابلة للتعليمتين <code>import</code> و<code>export</code> إلى نسخة أقدم من ES6 مثل ES5 وبذلك تؤمن فهم المتصفح ما تعنيه هذه التعليمتين باللغة الّتي يفهمها. إضافةً إلى ذلك يدعم <code>Webpack</code> العديد من صيغ الوحدات الأخرى لمزيد من المعلومات يمكنك زيارة <a href="https://webpack.js.org/api/module-methods" rel="external nofollow">التوثيق الرسمي</a>.
</p>

<p>
	<strong>ملاحظة</strong>: إن <code>Webpack</code> لن يحوّل أي تعليمة برمجية عدا تعليمتي <code>import</code> و<code>export</code> ولذلك في حال كنت تستخدم مميزات أخرى من المواصفات القياسية ES6 فعندها يجب عليك استخدام محولات المخصصة لذلك مثل <code>Babel</code> أو <code>Bublé</code>.
</p>

<h3>
	استخدام ملف الإعداد
</h3>

<p>
	إن جميع الإصدارات الّتي جاءت بعد الإصدار الرابع من <code>Webpack</code> تدعم ميزة التعديلات الصفرية الّتي تحدثنا عنها سابقًا في هذا الدليل، ولذا إذا أردنا أن نخصص بعض الإعدادات في المشروع لا بد لنا من إنشاء ملف الإعداد إنشاءً يدويًا. إذًا سنضيف ملف الإعداد على بنية الملفات وستصبح البنية الهرمية للمشروع على الشكل التالي:
</p>

<pre class="ipsCode">
 webpack-demo
  |- package.json
+ |- webpack.config.js
  |- /dist
    |- index.html
  |- /src
    |- index.js
</pre>

<p>
	وسنُضيف إليه بعض التعليمات المهمة والتي سنستخدمها كثير في المشروع وبذلك ستتحسن إنتاجيتنا أكثر من ذي قبل. وتكون الإضافة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_71" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">
module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pun">,</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">),</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أما الآن لنجرب تنفيذ التعليمة التالية:
</p>

<pre class="ipsCode">
npx webpack --config webpack.config.js

...
  Asset      Size  Chunks             Chunk Names
main.js  70.4 KiB       0  [emitted]  main
...

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
</pre>

<p>
	<strong>ملاحظة</strong>: في حالة وجود أكثر من ملف للإعداد تلتقط التعليمة <code>Webpack</code> ذلك إلتقاطًا إفتراضيًا. ونستخدم خيار <code>‎--config</code> وبعده اسم الملف لتوضيح فكرة أنه بإمكانك تمرير أسم أي ملف تريده تمريرًا يدويًا، وهذا سيكون مفيدًا جدًا لملفات الإعداد الأكثر تعقيدًا والتي سنحتاج لتقسيمها إلى ملفات متعددة.
</p>

<p>
	يتيح ملف التكوين مرونة أكبر بكثير من استخدام سطر الأوامر CLI ‏(Command Line Interface) البسيط. إذ يمكننا تحديد وتثبيت قواعد معينة للمُحمل (Loader) والملحقات (Plugin) وتخصيص الخيارات بالإضافة للعديد من التحسينات الأخرى. لمزيد من المعلومات يمكنك الإطلاع على <a href="https://webpack.js.org/configuration" rel="external nofollow">التوثيق الرسمي</a> لملف الإعداد.
</p>

<h3>
	إنشاء إختصار لتعليمة البناء
</h3>

<p>
	يمكننا استخدام مميزات <code>Webpack</code> لجعل الأمور أسهل من خلال وضع بعض اللمسات الجمالية مثل اختصار بعض الخطوات، وذلك من خلال كتابة الأوامر في ملف <code>package.json</code> وتحديدًا في <code>scripts</code>. وبذلك نختصر على أنفسنا عناء كتابة بعض التعليمات الطويلة والمُملة. إذًا لنعدل ملف <code>package.json</code> ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_73" style="">
<span class="pln">  </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack-demo"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">-</span><span class="pln">      </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"webpack"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
    </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"webpack"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^4.20.2"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"webpack-cli"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^3.1.2"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"lodash"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^4.17.5"</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p>
	يمكننا الآن استخدام الأمر <code>npm run build</code> بدلًا من الأمر <code>npx</code> الّذي استخدمنا سابقًا. لاحظ بأنه يمكننا دائمًا الإشارة إلى الحزم المثبتة محليًا في مدير الحزم npm في السمة <code>scripts</code> في الملف <code>package.json</code> بنفس الطريقة الّتي استخدمناها مع التعليمة <code>npx</code>. هذا الطريقة هي متعارف عليها في معظم المشاريع القائمة على مدير الحزم npm لأنه يسمح لجميع المساهمين لاستخدام نفس التعليمات المشتركة (وحتى مع بعض الخيارات مثل <code>‎--config</code>).
</p>

<p>
	<strong>ملاحظة</strong>: يمكنك تمرير الخيارات الخاصة لتعليمة ما من خلال إضافة شرطتين -- بين الأمر <code>npm run build</code> والخيارات الّتي نريد تخصيصها مثل <code>npm run build -- --colors</code>.
</p>

<pre class="ipsCode">
npm run build

...
  Asset      Size  Chunks             Chunk Names
main.js  70.4 KiB       0  [emitted]  main
...

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/.
</pre>

<p>
	أما الآن وبعد أن تعلمنا الأساسيات عن كيفية استخدام <code>Webpack</code> لننتقل إلى الاستخدامات الأهم لهذه الأداة مثل إدارة الصور والخطوط ..إلخ. إذا نفذت جميع التعليمات السابقة تنفيذًا صحيحًا يجب أن يكون البنية الهرمية للمشروع على الشكل التالي:
</p>

<pre class="ipsCode">
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
  |- main.js
  |- index.html
|- /src
  |- index.js
|- /node_modules
</pre>

<p>
	<strong>ملاحظة</strong>: إذا كنت تستخدم الإصدار الخامس من مدير الحزم <code>npm</code> من الممكن أن ترى ملفًا إضافيًا اسمه <code>package-lock.json</code>.
</p>

<h2>
	استخدام Webpack لإدارة الملحقات
</h2>

<p>
	بعد أن تعلمنا أساسيات <code>Webpack</code> وإنشأنا مشروعًا صغيرًا يعرض "Hello Webpack"، سنحاول الآن أن نركز أكثر على الأمور المهمة والتي تخدمنا بها هذه الأداة مثل إدارة ملفات التنسيق والصور وسنرى بالضبط كيف تتعامل هذه الأداة مع كلٍّ منهم.
</p>

<p>
	يستخدم مطورو الواجهات الأمامية أدوات بناء مثل: Grunt و <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-gulp-r663/" rel="">Glup</a> لمعالجة هذه الملحقات ونقلها من مجلد <code>src</code> إلى مجلد <code>dist</code> أو حتى لإنشاء مجلد جديد لهذه الملحقات، تستخدم <code>Webpack</code> نفس الفكرة للتعامل مع الوحدات، بالإضافة إلى أن <code>Webpack</code> تجمع كلّ التبعيات ديناميكيًا وتنشئ ما يعرف بمخطط التبعيات وهذا أمر رائع لأن كلّ وحدة (Module) توضح صراحة ما هي تبعياتها وبذلك نتجنب تجميع الوحدات غير المستخدمة في المشروع.
</p>

<p>
	واحدة من أروع مميزات <code>Webpack</code> هي إعطاؤك إمكانية تضمين أي نوع آخر من الملفات، إلى جانب جافاسكربت، ولهذه الفكرة تحديدًا أنشئ المحمل (Loader). أي يمكن تطبيق نفس المزايا المذكورة أعلاه (التبعيات المستخدمة من قبل الوحدات) على كلّ شيء مستخدم في إنشاء المواقع أو تطبيقات الوِب أيضًا.
</p>

<h3>
	إدارة ملفات التنسيق CSS
</h3>

<p>
	في البداية سنعدل قليلًا في بنية الملفات وبعض الملفات قبل أن ندير ملفات التنسيق.
</p>

<p>
	سنعدل الملف <code>dist/index.html</code> ليصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5305_75" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
  </span><span class="tag">&lt;html&gt;</span><span class="pln">
    </span><span class="tag">&lt;head&gt;</span><span class="pln">
-    </span><span class="tag">&lt;title&gt;</span><span class="pln">Getting Started</span><span class="tag">&lt;/title&gt;</span><span class="pln">
+    </span><span class="tag">&lt;title&gt;</span><span class="pln">Asset Management</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;/head&gt;</span><span class="pln">
    </span><span class="tag">&lt;body&gt;</span><span class="pln">
-     </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"main.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
+     </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"bundle.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;/body&gt;</span><span class="pln">
  </span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وكما سنعدل أيضًا ملف الإعداد <code>webpack.config.js</code> ليصبح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_77" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

  module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
    output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">-</span><span class="pln">     filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
      path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">};</span></pre>

<p>
	وبذلك يُصبح كلّ شيء جاهز لنبدأ. أولًا لنثبت محمل ملفات التنسيق <code>css-loader</code> الّذي سيساعدنا في معالجة تعليمة الاستيراد <code>import</code> الّتي سنستخدمها أيضًا من أجل استيراد ملفات التنسيق ومحمل التنسيق <code>style-loader</code> الّذي سيساعدنا في أخذ ملف التنسيق ووضعه في ملف (أو ملفات) تنسيق منفصلة. إذًا لنُنفذ الأمر التالي:
</p>

<pre class="ipsCode">
npm install --save-dev style-loader css-loader
</pre>

<p>
	ولنُعدل ملف الإعداد <code>webpack.config.js</code> ليحوي المعلومات اللازمة لحزم المُحمل (Loader) الّتي ثبتناها للتو وسيصبح الملف على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_79" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

  module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
    output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
      path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.css$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">           </span><span class="str">'style-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">           </span><span class="str">'css-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         </span><span class="pun">],</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     </span><span class="pun">],</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">};</span></pre>

<p>
	<strong>ملاحظة</strong>: تستخدم <code>Webpack</code> التعابير النمطية (Regular Expression) لتحديد الملفات الّتي يجب البحث عنها وتقديمها إلى مُحمل (Loader) محدد. في الحالة السابقة نلاحظ أنه ستُقدم جميع ملفات التنسيق (ذات اللاحقة <code>css</code>) إلى المُحمل <code>style-loader</code> والمُحمل <code>css-loader</code>.
</p>

<p>
	وهذا بدوره سيمكنك من استيراد ملف التنسيق الّذي سيكون مثل هذا <code>import './style.css'</code> إلى الملف الّذي يعتمد على ملف التنسيق هذا. عند تشغيل مجمع الحزم <code>Webpack</code> سيُضاف ملف تنسيق مخصص في وسم <code>&lt;style&gt;</code> وذلك في داخل الوسم <code>&lt;head&gt;</code>.
</p>

<p>
	لنجرب ذلك من خلال إضافة ملف تنسيق جديد <code>style.css</code> إلى مشروعنا واستيراده في الملف <code>index.js</code>: لننشئ أولًا ملف التنسيق <code>style.css</code> في مشروعنا لتصبح البنية الهرمية للمشروع على الشكل التالي:
</p>

<pre class="ipsCode">
webpack-demo
  |- package.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- style.css
    |- index.js
  |- /node_modules
</pre>

<p>
	ومن ثم لنُضف بعض الخصائص إلى ملف <code>style.css</code> ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5305_82" style="">
<span class="pun">.</span><span class="pln">hello </span><span class="pun">{</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ولنستدعي ملف التنسيق <code>style.css</code> في ملف <code>index.js</code> ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_84" style="">
<span class="kwd">import</span><span class="pln"> _ from </span><span class="str">'lodash'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./style.css'</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> component</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> element </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'div'</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Lodash, now imported by this script</span><span class="pln">
    element</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> _</span><span class="pun">.</span><span class="pln">join</span><span class="pun">([</span><span class="str">'Hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'webpack'</span><span class="pun">],</span><span class="pln"> </span><span class="str">' '</span><span class="pun">);</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   element</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">'hello'</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> element</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">component</span><span class="pun">());</span></pre>

<p>
	ومن ثم نُنفذ أمر البناء التالي:
</p>

<pre class="ipsCode">
npm run build

...
    Asset      Size  Chunks             Chunk Names
bundle.js  76.4 KiB       0  [emitted]  main
Entrypoint main = bundle.js
</pre>

<p>
	لنفتح الآن ملف <code>index.html</code> ولنرى ما هي التغييرات الّتي جرت عليه. لاحظ أن كلمة "Hello Webpack" أصبحت الآن باللون الأحمر. لمعرفة ما الّذي فعلته <code>Webpack</code> افحص الصفحة من خلال أدوات المطوّر (المُقدمة من متصفح جوجل كروم على سبيل المثال)، ولكن لا تعرض مصدر الصفحة لأنها لن تقدم لك النتيجة الّتي نود الإشارة إليها. إذ أن الوسم <code>&lt;style&gt;</code> سيُنشئ ديناميكيًا من قِبل جافاسكربت. لذلك افحصها حصرًا من خلال أدوات المطوّر وافحص تحديدًا الوسم <code>&lt;head&gt;</code> يجب أن يحتوي على الوسم <code>&lt;style&gt;</code> والذي استوردناه في ملف <code>index.js</code>.
</p>

<p>
	<strong>ملاحظة</strong>: سابقًا كان علينا تصغير (Minimize) ملفات التنسيق تصغيرًا يدويًا قدر الإمكان وذلك بحذف المساحات الفارغة بين الخصائص الموجودة في الملف من أجل زيادة سرعة تحميل الصفحة، ولكن مع الإصدار الرابع من مجمع الحزم <code>4 Webpack</code> أو الأحدث منه أصبح تصغير ملفات التنسيق خطوة افتراضية في الأداة، وبذلك أضافت لنا هذه الأداة بُعدًا آخر لتحسين الشيفرة البرمجية وسبب وجيّه لزيادة محبتنا لها.
</p>

<h3>
	إدارة ملفات SASS
</h3>

<p>
	تتيح لنا <code>Webpack</code> إمكانية التعامل مع ملفات SASS وتحويلها إلى ملفات تنسيق عادية. ولنأخذ مثالًا عمليًا نطبق فيه كيفية تحويل ملفات SASS إلى ملفات تنسيق عادية. سنركز في هذا المثال على دعم الملفات SCSS مثل: (<code>your-styles.scss</code>) و الملفات SCSS modules مثل: (<code>your-component.module.scss</code>).
</p>

<p>
	تجنبًا لتعقيد الأمور أكثر من اللازم سنبدأ بمشروع جديد ننفذ فيه هذه الفكرة. سنفترض أنك أنشأت مشروعًا جديدًا وثبتّ أداة <code>Webpack</code>، لننتقل الآن لتثبيت الإضافات والمُحملات اللازمة لهذا المشروع. سنبدأ أولًا بتثبيت الحزم التالية:
</p>

<pre class="ipsCode">
npm install --save-dev node-sass sass-loader style-loader css-loader mini-css-extract-plugin
</pre>

<p>
	ستكون مهمة كلّ حزمة من الحزم على الشكل التالي:
</p>

<ul>
<li>
		<code>node-sass</code>: ستوفر هذه الحزمة ربط <code>Node.js</code> مع <code>LibSass</code> وهذا الأخير هو مترجم <code>SASS</code>.
	</li>
	<li>
		<code>sass-loader</code>: هو مُحمّل ملفات <code>SASS</code> إلى مشروعنا.
	</li>
	<li>
		<code>css-loader</code>: تستخدم هذه الحزمة لتفسير التعليمة <code>@import</code> و <code>@url()</code> بما تشير إليه في ملفات التنسيق.
	</li>
	<li>
		<code>style-loader</code>: تستخدم هذه الحزمة لإسناد الخصائص الموجودة في ملفات التنسيق إلى الوسوم الفعلية في شجرة DOM الافتراضية.
	</li>
	<li>
		<code>mini-css-extract-plugin</code>: هذه الحزمة تستخرج الخصائص الموجودة في ملفات التنسيق إلى ملفات منفصلة. إذ أنها تنشئ لكل ملف جافاسكربت ملف تنسيق الخاص به وهو لدعم ميّزة التحميل عند الطلب (On-Demand-Loading) لملفات التنسيق و خرائط الشيفرة البرمجية المحوّلة Source Maps (لمزيد من المعلومات عنها يمكنك الإطلاع على <a href="https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map" rel="external nofollow">المقال</a> التالي).
	</li>
</ul>
<p>
	سنحتاج إلى إضافة مُحملين، أحدهما للتنسيقات العامة والأخرى للتنسيقات المركبة. واللّذان يشار إليهما بملفات (SCSS modules). إذ أن هذه الأخيرة تعمل جيدًا مع المكتبات أو أطر العمل المُعتمدة على المكونات (component-based) مثل: React.
</p>

<p>
	سنضيف هذه الحزمة الملحقة إلى ملف الإعداد <code>webpack.config.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_86" style="">
<span class="pun">+</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">MiniCssExtractPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'mini-css-extract-plugin'</span><span class="pun">)</span><span class="pln">
module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">    </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MiniCssExtractPlugin</span><span class="pun">({</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      filename</span><span class="pun">:</span><span class="pln"> isDevelopment </span><span class="pun">?</span><span class="pln"> </span><span class="str">'[name].css'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'[name].[hash].css'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      chunkFilename</span><span class="pun">:</span><span class="pln"> isDevelopment </span><span class="pun">?</span><span class="pln"> </span><span class="str">'[id].css'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'[id].[hash].css'</span><span class="pln">
</span><span class="pun">+</span><span class="pln">    </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ أننا أضفنا خاصية أسماء الملفات المجزأة من أجل زيادة فعالية وسهولة خرق ذاكرة التخزين المؤقّت (<a href="https://www.keycdn.com/support/what-is-cache-busting" rel="external nofollow">Cache Bustring</a> وهي تحِلُّ مشكلة التخزين المؤقت في المتصفح باستخدام إصدار معرف فريد للملف يميزه وذلك من أجل أخباره في حال وجود نسخة جديدة من الملف الذي حفظه فعلًا في هذه الذاكرة، وذلك من أجل أن يحملها تلقائيًا إلى ذاكرة التخزين المؤقت بدلًا من نسخة الملف القديمة الموجودة فيه)، والآن سنضيف الخواص المناسبة لملف الإعداد <code>webpack.config.js</code> من أجل عملية التحويل المناسبة للملفات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_88" style="">
<span class="pln">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.module\.s(a|c)ss$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        loader</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          isDevelopment </span><span class="pun">?</span><span class="pln"> </span><span class="str">'style-loader'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">MiniCssExtractPlugin</span><span class="pun">.</span><span class="pln">loader</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">            loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'css-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">            options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">              modules</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">              sourceMap</span><span class="pun">:</span><span class="pln"> isDevelopment
</span><span class="pun">+</span><span class="pln">            </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">            loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'sass-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">            options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">              sourceMap</span><span class="pun">:</span><span class="pln"> isDevelopment
</span><span class="pun">+</span><span class="pln">            </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        </span><span class="pun">]</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.s(a|c)ss$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        exclude</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.module.(s(a|c)ss)$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        loader</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          isDevelopment </span><span class="pun">?</span><span class="pln"> </span><span class="str">'style-loader'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">MiniCssExtractPlugin</span><span class="pun">.</span><span class="pln">loader</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="str">'css-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">            loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'sass-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">            options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">              sourceMap</span><span class="pun">:</span><span class="pln"> isDevelopment
</span><span class="pun">+</span><span class="pln">            </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">          </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">        </span><span class="pun">]</span><span class="pln">
</span><span class="pun">+</span><span class="pln">      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">]</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  resolve</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">-</span><span class="pln">    extensions</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'.js'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'.jsx'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">+</span><span class="pln">    extensions</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'.js'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'.jsx'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'.scss'</span><span class="pun">]</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ أن القاعدة الأولى المطبقة في الشيفرة السابقة ستُطبق على الملفات ذات الامتدادات <code>.module.scss</code> أو <code>.module.sass</code> إذ في البداية ستُحوّلُ ملفات SASS إلى CSS من خلال <code>sass-loader</code> ومن ثم ستُمرّر إلى الحزمة <code>css-loader</code> لمعالجة التعليمات <code>‎@import()‎</code> و <code>url()‎</code> ..إلخ. ومن ثمّ ستُسندُ الحزمة <code>style-loader</code> الخصائص المناسبة في DOM أو ستسند من خلال الحزمة <code>Mini CSS Extract Plugin</code> وذلك لإخراج ملفات التنسيق أثناء وضع النشر.
</p>

<p>
	القاعدة الثانية مشابهة جدًا للقاعدة الأولى باستثناء أننا لا نحوّل أسماء الأصناف. الآن أصبح ملف الإعداد جاهز نحتاج الآن لإنشاء ملف تنسيقات <code>SASS</code> للتأكد من أن كل شيء يعمل مثلما خطط له. أنشئ مجلد <code>src</code> ثم أنشئ بداخله ملفًا جديدًا باسم <code>app.module.scss</code> وأضف الشيفرة البرمجية التالية:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5305_90" style="">
<span class="pun">.</span><span class="pln">red </span><span class="pun">{</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أي عنصر سيأخذ الصنف <code>red</code> سيكون لونه أحمر.
</p>

<p>
	افتح الملف <code>app.js</code> واستدعي الملف السابق على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_92" style="">
<span class="kwd">import</span><span class="pln"> styles from </span><span class="str">'./app.module'</span></pre>

<p>
	نلاحظ أن اسم الملف لا يحتوي على اللاحقة <code>.scss</code> وذلك لأننا سبق وأخبرنا <code>Webpack</code> بأن يأخذ بعين الاعتبار هذه اللاحقة وذلك في ملف الإعداد. ثم لنُضيف الآن الدالة التالية في نفس الملف <code>app.js</code> ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_94" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">h2 className</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">red</span><span class="pun">}&gt;</span><span class="typ">This</span><span class="pln"> is our </span><span class="typ">React</span><span class="pln"> application</span><span class="pun">!&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أليست سهلة جدًا؟ بغض النظر عن كيفية تحويل الحزمة <code>css-loader</code> اسم الصنف الّذي بنيناه (red) والّذي سيصبح مثل: <code>‎_1S0lDPmyPNEJpMz0dtrm3F</code> أو شيء من هذا القبيل، ولكنها ساعدتنا في مهمتنا مساعدةً كبيرة.
</p>

<p>
	لنُضيف الآن بعض التنسيقات العمومية. لننشئ ملف جديد وليكن <code>global.scss</code> في المجلد <code>src</code> ولِنفتحه ونُضيف بداخله الشيفرة البرمجية التالية:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5305_96" style="">
<span class="pln">body </span><span class="pun">{</span><span class="pln">
  background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> yellow</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا الملف سيجعل لون خلفية الصفحة الرئيسية لتطبيق الوِب خاصتنا أصفر. ولكن يجب أن نفتح الملف <code>index.js</code> ونستدعي ملف التنسيق السابق في بداية الملف. مثلما هو موضح في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_98" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="str">'./global'</span></pre>

<p>
	واخيرًا سنحصل على الخرج التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36355" href="https://academy.hsoub.com/uploads/monthly_2020_04/webpack-4-css-modules.png.1e45d2926168fc1dc24f3e86e2b18c6c.png" rel=""><img alt="webpack-4-css-modules.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36355" data-unique="o7r6t3d2f" src="https://academy.hsoub.com/uploads/monthly_2020_04/webpack-4-css-modules.thumb.png.05b51bc234da1e8f101be20f6d6e3f1d.png"></a>
</p>

<p>
	نلاحظ أن استخدام وحدات SCSS يتطلب منا جهدًا لا بأس به، ولكن بالموازنة مع كمية الفائدة الّتي تقدمها وحدات SCSS من سهولة في الصيانة لتطبيق الوِب في المستقبل فأعتقد أن الأمر يستحق هذا الجهد.
</p>

<p>
	بالإضافة إلى ذلك توفر أداة Webpack حزم أُخرى مخصصة لأي نكهة من نكهات ملفات التنسيق مثل الحزمة: <a href="https://webpack.js.org/loaders/postcss-loader" rel="external nofollow">postcss</a> لتوفير دعم Postcss أو الحزمة <a href="https://webpack.js.org/loaders/less-loader" rel="external nofollow">Less</a> لدعم ملفات التنسيق من نوع Less أيضًا.
</p>

<h3>
	إدارة الصور
</h3>

<p>
	تتيح <code>Webpack</code> لنا إمكانية إدارة الصور مثل: الخلفيات والأيقونات وذلك من خلال مُحمل الملفات. في البداية لنثبت أولًا مُحمل الملفات من خلال الأمر التالي:
</p>

<pre class="ipsCode">
npm install --save-dev file-loader
</pre>

<p>
	ومن ثم سنضيف إلى ملف الإعداد <code>webpack.config.js</code> المعلومات اللازمة لعمل هذا المُحمل كما في الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_100" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

  module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
    output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
      path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
          test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.css$/</span><span class="pun">,</span><span class="pln">
          use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'style-loader'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'css-loader'</span><span class="pln">
          </span><span class="pun">],</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(png|svg|jpg|gif)$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">           </span><span class="str">'file-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         </span><span class="pun">],</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">],</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">};</span></pre>

<p>
	عند استيراد لصورة معينة ولتكن <code>import MyImage from './my-image.png'</code> ستضاف هذه الصورة إلى مجلد الخرج (output) الّذي حددناه في ملف الإعداد وسيحمل المتغير <code>MyImage</code> عنوان الرابط التشعبي الخاص بالصورة (URL) بعد معالجتها.
</p>

<p>
	عندما استخدمنا محمل ملفات التنسيق <code>css-loader</code> جرى نفس السيناريو السابق. أي أنه سيكون الخاصية التالية في ملفات التنسيق <code>url('./my-image.png')</code> والمُحمل سيلاحظ أن الملف هذا موجود محليًا وبذلك سيُحول <code>'‎./my-image.png'</code> إلى المسار النهائي المُسند للخاصية (output) في ملف الإعداد <code>webpack.config.js</code> ومُحمل ملفات html‏ (<a href="https://webpack.js.org/loaders/html-loader" rel="external nofollow">html-loader</a>) سيتعامل مع <code>&lt;img src="./my-image.png" /</code>‎<code>&gt;</code> بنفس الطريقة.
</p>

<p>
	لنُضف الآن الصور اللازمة في مشروعنا. وهنا يمكنك استخدام أي صورة تريدها. لننسخ الصورة الّتي سنعمل عليها ولتكن <code>icon.png</code> إلى مشروعنا. لتصبح البنية الهرمية للمشروع على الشكل التالي:
</p>

<pre class="ipsCode">
webpack-demo
  |- package.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- icon.png
    |- style.css
    |- index.js
  |- /node_modules
</pre>

<p>
	ومن ثم لنستورد هذه الصورة في ملف <code>src/index.js</code>، ولنُضيفها إلى وسم <code>&lt;div&gt;</code> المتواجدة فيه، ولتصبح الشيفرة البرمجية للملف <code>src/index.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_102" style="">
<span class="kwd">import</span><span class="pln"> _ from </span><span class="str">'lodash'</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./style.css'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Icon</span><span class="pln"> from </span><span class="str">'./icon.png'</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> component</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> element </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'div'</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Lodash, now imported by this script</span><span class="pln">
    element</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> _</span><span class="pun">.</span><span class="pln">join</span><span class="pun">([</span><span class="str">'Hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'webpack'</span><span class="pun">],</span><span class="pln"> </span><span class="str">' '</span><span class="pun">);</span><span class="pln">
    element</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">'hello'</span><span class="pun">);</span><span class="pln">

</span><span class="pun">+</span><span class="pln">   </span><span class="com">// Add the image to our existing div.</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   </span><span class="kwd">const</span><span class="pln"> myIcon </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">();</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   myIcon</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Icon</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   element</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">myIcon</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> element</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">component</span><span class="pun">());</span></pre>

<p>
	ومن ثم سنعدل ملف التنسيق من أجل تضمين الصور الّتي نعمل عليها ليصبح بذلك ملف التنسيق <code>src/style.css</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5305_104" style="">
<span class="pln">  </span><span class="pun">.</span><span class="pln">hello </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   background</span><span class="pun">:</span><span class="pln"> url</span><span class="pun">(</span><span class="str">'./icon.png'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p>
	ولننفذ الآن أمر البناء التالي:
</p>

<pre class="ipsCode">
npm run build

...
                               Asset      Size  Chunks                    Chunk Names
da4574bb234ddc4bb47cbe1ca4b20303.png  3.01 MiB          [emitted]  [big]
                           bundle.js  76.7 KiB       0  [emitted]         main
Entrypoint main = bundle.js
...
</pre>

<p>
	إذا نفذت جميع الخطوات السابقة تنفيذًا صحيحًا يجب أن ترى الأيقونة (الصورة) مكررة في الخلفية، بالإضافة إلى وسم <code>&lt;img&gt;</code> بجوار النص "Hello webpack". وإذا فحصت الصورة ستجد أن اسمها الفعلي تغيّر إلى شيء شبيه بهذا الاسم <code>5c999da72346a995e7e2718865d019c8.png</code> هذا يعني أن <code>Webpack</code> عثرت على الملف في مجلد <code>src</code> وعالجته.
</p>

<p>
	الخطوة المنطقية التالية الّتي سننفذها هي تصغير حجم الصور الّتي سنستخدمها في المشروع وتحسينها وذلك من خلال الحزم المساعدة المتوفرة مع الأداة <code>Webpack</code> مثل الحزمة <code>Imagemin</code>.
</p>

<h3>
	ضغط الصور باستخدام Imagemin
</h3>

<p>
	إن طريقة ضغط الصور الّتي سنستخدمها في المشروع هي باستخدام حزمة <code>Imagemin</code> إذ تعدّ هذه الحزمة خيارًا ممتازًا وذلك لأنها تدعم مجموعة متنوعة من أنواع الصور وسهلة التكامل مع الشيفرة البرمجية لأدوات البناء أو مجمع الحزم (الوحدات). سنعمل على مشروع جديد تجنبًا للمشاكل التي من الممكن أن تظهر لنا في حال أكملنا العمل على المشروع السابق. سنفرض أنك أنشأت مشروعًا جديدًا وثبّت الأداة <code>Webpack</code> تثبيتًا صحيحًا.
</p>

<p>
	في البداية لنثبت هذه الحزم عبر التعليمة التالية:
</p>

<pre class="ipsCode">
npm install imagemin-webpack-plugin copy-webpack-plugin --save-dev
</pre>

<p>
	<strong>ملاحظة</strong>: أن الحزمة copy-webpack-plugin ستساعدنا على نسخ الصور من مجلد <code>images/</code> إلى مجلد <code>dist/</code> وهو مجلد النشر وهو اختصار لكلمة distribution.
</p>

<p>
	لننشئ ملف الإعداد <code>webpack.config.js</code> ولنعدله ليتناسب مع الحزم الجديدة ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_106" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">ImageminPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'imagemin-webpack-plugin'</span><span class="pun">).</span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">CopyWebpackPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'copy-webpack-plugin'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
     </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CopyWebpackPlugin</span><span class="pun">([{</span><span class="pln">
       from</span><span class="pun">:</span><span class="pln"> </span><span class="str">'img/**/**'</span><span class="pun">,</span><span class="pln">
       to</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">)</span><span class="pln">
     </span><span class="pun">}]),</span><span class="pln">
     </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ImageminPlugin</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تعدّ هذه الطريقة من أسهل الطرق لإعداد لهذه الحزمة. ولنضغط الصور الّتي في المجلد <code>images/</code> وننسخها إلى المجلد <code>dist/</code> من خلال تنفيذ الأمر التالي:
</p>

<pre class="ipsCode">
webpack --config webpack.config.js --mode development
</pre>

<p>
	نلاحظ من التعليمة السابقة أن وضع التنفيذ الحالي هو وضع التطوير، ولكن ما الّذي سيحدث إذا نفذنا التعليمة في وضع النشر؟ لنرى ما الّذي سيحدث.
</p>

<pre class="ipsCode">
webpack --config webpack.config.js --mode production
</pre>

<p>
	ستُظهر لنا في هذه المرة أداة <code>Webpack</code> تحذيرًا يخبرك بأن الصور ذات النوعية PNG الخاصة بك لا تزال تتجاوز الحد الأقصى للحجم الموصى به، على الرغم من بعض الضغط. ولذلك سنحتاج إلى ضبط اليدوي لعملية الضغط لهذا النوع من الصور لتصبح أفضل مما قبل.
</p>

<p>
	لنعدل قيمة الضغط للصور ذات النوعية PNG لتصبح 50% وسيكون ملف الإعداد على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_108" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">ImageminPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'imagemin-webpack-plugin'</span><span class="pun">).</span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">CopyWebpackPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'copy-webpack-plugin'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CopyWebpackPlugin</span><span class="pun">([{</span><span class="pln">
        from</span><span class="pun">:</span><span class="pln"> </span><span class="str">'img/**/**'</span><span class="pun">,</span><span class="pln">
        to</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}]),</span><span class="pln">
    </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ImageminPlugin</span><span class="pun">({</span><span class="pln">
      pngquant</span><span class="pun">:</span><span class="pln"> </span><span class="pun">({</span><span class="pln">quality</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">0.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">]}),</span><span class="pln">
      </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ أننا مررنا للغرض <code>Pngquant</code> مجالًا لقيمة جودة الصور من 0.5 إلى 0.5 أي من الحدّ الأدنى إلى الحدّ الأعلى لجودة الصورة. إذ أن الحدّ الأعلى الأفتراضي 1 والحدّ الأدنى الافتراضي 0.
</p>

<p>
	ولكن ماذا لو أردنا أن نضبط الجودة (نسبة الضغط) المستخدمة لبقية الأنواع من الصور بنفس الطريقة السابقة؟ في الحقيقة يوجد بعض الملحقات الإضافية المساعدة لهذا الغرض تحديدًا والّتي سنستعرضها في هذا الجدول:
</p>

<table>
<thead><tr>
<th>
				نوع الصور
			</th>
			<th>
				ضغط مع خسارة بعض معلومات الصورة
			</th>
			<th>
				ضغط بدون خسارة بعض معلومات الصورة
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				JPEG
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-mozjpeg" rel="external nofollow">imagemin-mozjpeg</a>
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-jpegtran" rel="external nofollow">imagemin-jpegtran</a>
			</td>
		</tr>
<tr>
<td>
				PNG
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-pngquant" rel="external nofollow">imagemin-pngquant</a>
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-optipng" rel="external nofollow">imagemin-optipng</a>
			</td>
		</tr>
<tr>
<td>
				GIF
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-giflossy" rel="external nofollow">imagemin-giflossy</a>
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-gifsicle" rel="external nofollow">imagemin-gifsicle</a>
			</td>
		</tr>
<tr>
<td>
				SVG
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-svgo" rel="external nofollow">Imagemin-svgo</a>
			</td>
			<td>
				 
			</td>
		</tr>
<tr>
<td>
				WebP
			</td>
			<td>
				<a href="https://www.npmjs.com/package/imagemin-webp" rel="external nofollow">imagemin-webp</a>
			</td>
			<td>
				 
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	نلاحظ أن لدينا نوعين من طرق ضغط الصور وهما كالتالي:
</p>

<ol>
<li>
		<strong>ضغط مع خسارة بعض معلومات الصورة (Lossy)</strong>: يؤدي استخدام هذا النوع إلى تصغير حجم الصورة تصغيرًا ملحوظًا، ولكن مع فقدان بعض معلومات الصورة.
	</li>
	<li>
		<strong>ضغط بدون خسارة بعض معلومات الصورة (Lossless)</strong>: يؤدي استخدام هذا النوع إلى تصغير حجم الصورة تصغيرًا أقل من الطريقة السابقة، ولكن بدون فقدان بعض معلومات الصورة.
	</li>
</ol>
<p>
	إن طريقة الضغط الإفتراضية للحزمة <code>imagemin</code> للصور ذات النوعية JPEG هي الطريقة <code>imagemin-jpegtran</code>، سنستعرض كيفية ضغط الصور باستخدام الحزمة الأخرى وهي <code>imagemin-mozjpeg</code>.
</p>

<p>
	في البداية يجب علينا تثبيت هذه الحزمة من خلال الأمر التالي:
</p>

<pre class="ipsCode">
npm install imagemin-mozjpeg
</pre>

<p>
	ولنُعدل ملف الإعداد <code>webpack.config.js</code> لضبط طريقة ضغط الصور وفق ما نريده. ليصبح الملف على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_110" style="">
<span class="kwd">const</span><span class="pln"> imageminMozjpeg </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'imagemin-mozjpeg'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ImageminPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'imagemin-webpack-plugin'</span><span class="pun">).</span><span class="kwd">default</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">CopyWebpackPlugin</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'copy-webpack-plugin'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./index.js'</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CopyWebpackPlugin</span><span class="pun">([{</span><span class="pln">
      from</span><span class="pun">:</span><span class="pln"> </span><span class="str">'img/**/**'</span><span class="pun">,</span><span class="pln">
      to</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}]),</span><span class="pln">
    </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ImageminPlugin</span><span class="pun">({</span><span class="pln">
      pngquant</span><span class="pun">:</span><span class="pln"> </span><span class="pun">({</span><span class="pln">quality</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">0.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">]}),</span><span class="pln">
      plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">imageminMozjpeg</span><span class="pun">({</span><span class="pln">quality</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pun">})]</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ أن نسبة الضغط في هذه الحزمة من 100 إذ مررنا القيمة 50 وبذلك نخبره بأننا نريد ضغط الصور من النوع JPEG بنسبة 50%.
</p>

<p>
	ولنُنفذ الآن التعليمة التالية لنرى النتيجة:
</p>

<pre class="ipsCode">
webpack --config webpack.config.js --mode production
</pre>

<p>
	تهانينا في حال نفذّت جميع الخطوات تنفيذًا صحيحًا يجب أن تكون الصور مضغوطة وفقَ المطلوب.
</p>

<p>
	إن الأداة <code>Webpack</code> تحذرنا من الصور ذات الحجم الكبير، ولكنها لا تستطيع إخبارنا إن كانت الصور مضغوطة أم لا، ولهذا السبب سنستخدم الأداة Lighthouse للتحقق من التغييرات المُنفّذة.
</p>

<p>
	تسمح لنا الأداة Lighthouse من التحقق من ترميز الصور بكفاءة وإن كانت الصور الموجودة في صفحتك مضغوطة على نحوٍ أمثلي أم لا (لمزيد من المعلومات حول هذه الأدة ننصحك بالاطلاع على هذا <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A5%D9%84%D9%89-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D9%84%D8%A3%D8%AF%D8%A7%D8%A9-lighthouse-r691/" rel="">المقال</a> المفصّل). وستكون النتيجة مشابهة للصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36353" href="https://academy.hsoub.com/uploads/monthly_2020_04/lighthouse_passing.png.7657e764da261ca70c1d1dcb8a9a051d.png" rel=""><img alt="lighthouse_passing.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36353" data-unique="3j3xs1048" src="https://academy.hsoub.com/uploads/monthly_2020_04/lighthouse_passing.thumb.png.d9915915cf508bd5ef942c10877b96f5.png"></a>
</p>

<p>
	بنفس الطريقة يمكنك استخدام بقية الحزم لتخصيص قيمة الضغط المطلوب للأنواع الأخرى من الصور المستخدمة في مشروعك.
</p>

<h3>
	إدارة الخطوط
</h3>

<p>
	يمكننا إدارة الخطوط أيضًا مع <code>Webpack</code> من خلال مُحمل الملفات الّذي ثبتناه في إدارة الصور (سنعمل على نفس المشروع الّذي بنيناه في فقرة إدارة الصور)، والذي سيأخذ أي ملف تضعه له في ملف الإعداد ويضعه في المسار النهائي المُسند للخاصية (output) في ملف الإعداد <code>webpack.config.js</code>. أي أن مُحمل الملفات قادر على التعامل الخطوط أيضًا. لنعدل الآن ملف الإعداد <code>webpack.config.js</code> ليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_112" style="">
<span class="pln"> </span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

  module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pun">,</span><span class="pln">
    output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bundle.js'</span><span class="pun">,</span><span class="pln">
      path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
          test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.css$/</span><span class="pun">,</span><span class="pln">
          use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'style-loader'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'css-loader'</span><span class="pln">
          </span><span class="pun">],</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
          test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(png|svg|jpg|gif)$/</span><span class="pun">,</span><span class="pln">
          use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'file-loader'</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">],</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.(woff|woff2|eot|ttf|otf)$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">           </span><span class="str">'file-loader'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         </span><span class="pun">],</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">],</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">};</span></pre>

<p>
	أضف خطًا معينًا إلى مجلد المشروع لتصبح البنية الهرمية للمشروع على الشكل التالي:
</p>

<pre class="ipsCode">
 webpack-demo
  |- package.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- my-font.woff
+   |- my-font.woff2
    |- icon.png
    |- style.css
    |- index.js
  |- /node_modules
</pre>

<p>
	من خلال التعديلات الّتي أجريناها على ملف الإعداد لمُحمل الملفات يمكننا الآن جعل التعليمة الّتي تصرح بها عن المسارات للخطوط <code>‎@font-face</code> إلى الشكل التالي <code>url(...)‎</code>. سيعاد توجيه هذه المسارات إلى المسار النهائي المُسند للخاصية (output) في ملف الإعداد <code>webpack.config.js</code> تمامًا كما تعامل مع الصور.
</p>

<p>
	إذا سيصبح ملف التنسيق <code>src/style.css</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_5305_117" style="">
<span class="pun">+</span><span class="pln"> </span><span class="lit">@font</span><span class="pun">-</span><span class="pln">face </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   font</span><span class="pun">-</span><span class="pln">family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'MyFont'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   src</span><span class="pun">:</span><span class="pln">  url</span><span class="pun">(</span><span class="str">'./my-font.woff2'</span><span class="pun">)</span><span class="pln"> format</span><span class="pun">(</span><span class="str">'woff2'</span><span class="pun">),</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         url</span><span class="pun">(</span><span class="str">'./my-font.woff'</span><span class="pun">)</span><span class="pln"> format</span><span class="pun">(</span><span class="str">'woff'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">600</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   font</span><span class="pun">-</span><span class="pln">style</span><span class="pun">:</span><span class="pln"> normal</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">.</span><span class="pln">hello </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   font</span><span class="pun">-</span><span class="pln">family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'MyFont'</span><span class="pun">;</span><span class="pln">
    background</span><span class="pun">:</span><span class="pln"> url</span><span class="pun">(</span><span class="str">'./icon.png'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p>
	لنرى كيف سيتعامل <code>Webpack</code> مع الخطوط ولننفذ أمر البناء على الشكل التالي:
</p>

<pre class="ipsCode">
npm run build

...
                                 Asset      Size  Chunks                    Chunk Names
5439466351d432b73fdb518c6ae9654a.woff2  19.5 KiB          [emitted]
 387c65cc923ad19790469cfb5b7cb583.woff  23.4 KiB          [emitted]
  da4574bb234ddc4bb47cbe1ca4b20303.png  3.01 MiB          [emitted]  [big]
                             bundle.js    77 KiB       0  [emitted]         main
Entrypoint main = bundle.js
...
</pre>

<p>
	افتح ملف <code>index.html</code> وانظر إلى النص "Hello webpack" كيف تغير شكل الخط ليصبح مطابق لشكل الخط الجديد.
</p>

<p>
	أما الآن لننتقل إلى واحدٍ من أبرز استخدامات مجمع الحزم <code>Webpack</code> وهو تحويل الشيفرات البرمجية الخاصة بالإصدارات الحديثة من المواصفات القياسية للغة جافاسكربت مثل ES6 إلى ES5.
</p>

<h3>
	تحويل الشيفرة البرمجية باستخدام Babel
</h3>

<p>
	بعد أن تعرفنا كيف ندير جميع التبعيات الموجودة في شيفرات جافاسكربت البرمجية من خلال <code>Webpack</code>. لا بُدّ لنا من جعل هذه الشيفرة البرمجية تعمل على جميع المتصفحات وذلك لكي نضمن أنه مهما يكن المتصفح الّذي سيستخدمه العميل (أو المستخدم) ستظهر النتيجة المطلوبة تمامًا مثل ما نريده.
</p>

<p>
	<strong>عملية التحويل (Transpiling)</strong>: هي عملية تغيير الشيفرة البرمجية من إصدار معين إلى إصدار أقدم وذلك لضمان عمل هذه الشيفرة البرمجية على المتصفحات كلها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36352" href="https://academy.hsoub.com/uploads/monthly_2020_04/ES5_and_ES6_support.png.7567a993e12bc5d6c2351498d70ca651.png" rel=""><img alt="ES5_and_ES6_support.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36352" data-unique="gopg8pcd7" src="https://academy.hsoub.com/uploads/monthly_2020_04/ES5_and_ES6_support.thumb.png.e416f4afe4b97a7078b2811a84d02295.png"></a>
</p>

<p>
	مقتطف من <a href="http://kangax.github.io/compat-table/es6/" rel="external nofollow">المخطط التفصيلي</a> لدعم المتصفحات للمميزات الإصدار ES5 والإصدار ES6 من لغة جافاسكربت.
</p>

<p>
	سننشئ مشروعًا بسيطًا لشرح طريقة استخدام هذا المُحول:
</p>

<pre class="ipsCode">
mkdir webpack-demo2
cd webpack-demo2
npm init -y
npm install webpack webpack-cli --save-dev
</pre>

<p>
	<strong>ملاحظة</strong>: لن نتطرق للتفاصيل نظرًا لأنها شُرحت في بداية هذا الدليل.
</p>

<p>
	ستكون البنية الهرمية للمشروع على الشكل التالي:
</p>

<pre class="ipsCode">
webpack-demo2
  |- package.json
+ |- index.html
+ |- /src
+   |- index.js
</pre>

<p>
	في البداية سنحتاج لتثبيت ثلاث حزم للتعامل مع المحول <code>Babel</code> وستكون على الشكل التالي:
</p>

<pre class="ipsCode">
npm install babel-core babel-loader babel-preset-env --save-dev
</pre>

<ul>
<li>
		<strong>الحزمة babel-core</strong>: تحتوي على الشيفرة البرمجية للنواة الأساسية للمُحول <code>Babel</code>.
	</li>
	<li>
		<strong>الحزمة babel-preset-env</strong>: تحتوي على الشيفرة البرمجية الّتي ستمكن النواة الأساسية للمحول Babel من تحويل الشيفرة البرمجية للجافاسكربت ذات الإصدار 6ES إلى الإصدار ES5.
	</li>
	<li>
		<strong>الحزمة babel-loader</strong>: أو (مُحمل المحول) وهي الّتي ستستخدمها <code>Webpack</code> في تحميل المحول والتعامل معه في عملية التحزيم (Bundling).
	</li>
</ul>
<p>
	أما الآن سننشئ ملف <code>babelrc.</code> والذي سيطلب من المحول <code>Babel</code> أن يستخدم الحزمة <code>babel-preset-env</code> أثناء عملية التحويل وسيكون الملف <code>babelrc.</code> على الشكل التالي:
</p>

<pre class="ipsCode">
+{ "presets":  ["env"]  }
</pre>

<p>
	ومن ثم سننشئ ملف الإعداد <code>webpack.config.js</code> ونطلب من <code>Webpack</code> الإستعانة بالمحمل <code>Babel</code> أثناء تحزيمه. وسيكون ملف الإعداد <code>webpack.config.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_119" style="">
<span class="pun">+</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">

</span><span class="pun">+</span><span class="pln"> module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   entry</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> main</span><span class="pun">:</span><span class="pln"> </span><span class="str">'./src/index.js'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'dist'</span><span class="pun">),</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">'main.js'</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   </span><span class="pun">},</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   module</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     rules</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         exclude</span><span class="pun">:</span><span class="pln"> </span><span class="str">/node_modules/</span><span class="pun">,</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         use</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">+</span><span class="pln">           loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pln">
</span><span class="pun">+</span><span class="pln">         </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">       </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln">     </span><span class="pun">]</span><span class="pln">
</span><span class="pun">+</span><span class="pln">   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	سنضع الآن في ملف <code>src/index.js</code> شيفرة برمجية من الإصدار ES6، وليصبح على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_121" style="">
<span class="pun">+</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> fn </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"Arrow functions're Working!"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">fn</span><span class="pun">());</span></pre>

<p>
	ومن ثم ننفذ الأمر التالي:
</p>

<pre class="ipsCode">
npm run dev
</pre>

<p>
	ومن ثم لنفتح ملف <code>dist/main.js</code> نلاحظ أن الدالة السهمية (Arrow function) تُحوّلُ إلى دالة عادية وذلك وفق الطريقة المتبعة في الإصدار ES5 من لغة جافاسكربت.
</p>

<p>
	وسيكون محتوى الملف <code>dist/main.js</code> على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5305_123" style="">
<span class="str">'use strict'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">var</span><span class="pln"> fn </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> fn</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Arrow functions're Working!"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">fn</span><span class="pun">());</span></pre>

<p>
	وبذلك استطعنا استخدام مميزات الإصدار الحديث من لغة جافاسكربت ES6 وتحويله إلى إصدار أقدم ES5 لتفهمه المتصفحات القديمة.
</p>

<h2>
	هل من المخاطرة تعلم Webpack؟
</h2>

<p>
	بعد أن تعرفنا سريعًا على <code>Webpack</code> يمكن لسائل أن يسأل <strong>ماذا لو أنني تعلمت العمل على <code>Webpack</code> وبعدها ظهرت أداة جديدة أفضل منها أو تغطي مجال أوسع في العمل؟ أليس ذلك هدرًا لوقتي ولجهدي؟</strong>
</p>

<p>
	صراحة، الشيء الوحيد الّذي استطيع تأكيده لك هو أن عجلة التحديث في مجال الواجهات الأمامية سريع الدوران، وما إن تظهر مشكلة ما حتى يتسارع المطورون لحلها فمن الممكن أن تظهر أداة أقوى منها (وهذا لا اتوقعه أن يحدث قريبًا وخصيصًا مع الدعم الكبير الّتي تشهده هذه الأداة من الشركات العملاقة مثل فيسبوك) هذا وارد الحدوث، ولكن هذا الأمر لا يُلغي فكرة أهمية تعلمك العمل مع هذه الأداة إذ أن معظم الأدوات الّتي تظهر لحل مشكلة معينة تكون متشابهة في البنية وطريقة العمل مع بعض الإختلافات. أي إن تعلمك العمل مع هذه الأداة سيجهزك للعمل مع الأدوات الأخرى المستقبلية. ومن يعلم ربما تكون أنت صاحب فكرة الأداة الجديدة الّتي ستتفوق على <code>Webpack</code>!
</p>

<p>
	والسؤال الأخر الّذي من الممكن يخطر في أذهاننا أيضًا <strong>هل تستحق هذه الأداة الوقت المبذول لتعلمها؟</strong> أو بتعبير أخر <strong>هل النتائج الّتي سأجنيها من استخدام هذه الأداة ستغطي على الوقت المبذول لتعلمها؟</strong> في الحقيقة لا أبالغ أن قُلت أنه من الواجب عليك تعلمها فورًا وبدون تردد وخصيصًا إن كُنت تتخذُ من مهنة تطوير الواجهات الأمامية (أو مطور برمجيات عمومًا) مصدرًا أساسيًا للدخل في حياتك! لأنها ستعزز كثيرًا من إنتاجيتك وسرعتك وجودة العمل المُسلّم وهذا ما ستشعر به من أول شهر من استخدامك لها.
</p>

<p>
	في الحقيقة إن مجيئ <code>Webpack</code> بكل هذه المميزات وقابلية التخصيص والإمكانيات أطاح بجميع المنافسين لديها مثل <code>Browserify</code>، ليس ذلك فحسب بل تخطّت إمكانياتها حدود الفكرة التي أُنشأت من أجلها لتهدد أدواتٍ مساعدةٍ أخرى كأدوات البناء مثل <code>Gulp</code> (والتي تحدثنا عنها في <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A9-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-gulp-r663/" rel="">مقالٍ سابق</a>) إذ أنه يمكننا من خلال <code>NPM Script</code> أن نعوّض عمل <code>Gulp</code> ولكن هذا سيكون على حساب الصعوبة إذ أن العمل مع <code>Webpack</code> ليس بسهولة العمل مع <code>Gulp</code>. ننصحك بالانتقال بعد الانتهاء من هذا المقال إلى مقال، <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D9%8A%D9%87%D9%85%D8%A7-%D8%A3%D9%81%D8%B6%D9%84-%D9%83%D8%A3%D8%AF%D8%A7%D8%A9-%D9%85%D8%B3%D8%A7%D8%B9%D8%AF%D8%A9-webpack-%D8%A3%D9%85-browserify-%D9%85%D8%B9-gulp%D8%9F-r867/" rel="">أيهما أفضل كأداة مساعدة Webpack أم Browserify مع Gulp؟</a> الذي يشرح الفروقات بين كل هذه الأدوات وحالات استخدامها وغيرها من التفاصيل المفيدة.
</p>

<p>
	بالإضافة إلى ذلك وفرت لنا <code>Webpack</code> إمكانية التكامل مع أدوات مساعدة أُخرى مثل أداة السقالة <code>Yeoman</code> (والتي تحدثنا عنها في <a href="https://academy.hsoub.com/programming/workflow/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D9%88%D8%A8%D9%86%D8%A7%D8%A4%D9%87-%D8%B9%D8%A8%D8%B1-yeoman-r761/" rel="">مقالٍ سابق</a>) وبذلك نحصل على فوائد كِلتا الأداتين ونختصر وقتنا اختصارًا ملحوظًا. وبذلك يمكننا القول بأن المرونة الموجودة لدى <code>Webpack</code> وملحقاتها الرائعة والدعم المتميز من مجتمعها ومستخدميها جعلها من أقوى الخيارات الموجودة في الساحة حاليًا.
</p>

<h2>
	الخاتمة
</h2>

<p>
	بدأنا هذا الدليل ونحن لا نعرف شيئًا عن <code>Webpack</code>، ثم حفرنا عميقًا في تاريخها لنفهم تمامًا ما هي الأسباب الّتي أدت لظهور أداةٍ قوية كهذه الأداة، ثم بدأنا بتجهز المتطلبات اللازمة لتشغيلها، وفصلّنا في جميع المفاهيم الأساسية لها، وتعلمنا كيفية استخدامها في إدارة ملفات التنسيق والصور والخطوط ، وكيفية استخدامها لضغط الصور قبل رفعها وتعلمنا طريقة استخدامها لتحويل الشيفرة البرمجية من إصدار حديث إلى إصدار أقدم منه. وأخيرًا ناقشنا أهمية تعلمها وفوائده.
</p>

<p>
	وختامًا إن هذا الدليل لم يغطي كافة المميزات والجوانب الموجودة في أداة <code>Webpack</code> وإنما فقط أردنا من هذا الدليل أن يلفت نظرك لأهمية هذه الأداة وقدراتها المتميزة في مساعدتك في العمل على المشاريع وخصوصًا الكبيرة منها. بل وجعلها يدك اليمنى في أي مشروع جديد وبالتأكيد إن كلّ هذه المعلومات لن تُشبع فضول المطوّر المحترف ولذلك يمكنك دومًا الاطلاع على <a href="https://webpack.js.org/guides/" rel="external nofollow">التوثيق الرسمي</a> للأداة لمزيد من المعلومات.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		<a href="https://webpack.js.org" rel="external nofollow">التوثيق الرسمي</a> لأداة Webpack.
	</li>
	<li>
		<a href="https://www.npmjs.com" rel="external nofollow">التوثيق الرسمي</a> لمدير الحزم npm.
	</li>
	<li>
		المقال <a href="https://web.dev/codelab-imagemin-webpack/" rel="external nofollow">Using Imagemin with webpack</a> لصاحبته Katie Hempenius.
	</li>
	<li>
		المقال <a href="https://developerhandbook.com/webpack/how-to-configure-scss-modules-for-webpack/" rel="external nofollow">How to configure SCSS modules for Webpack</a> لصاحبه Jon Preece.
	</li>
	<li>
		المقال <a href="https://medium.com/sungthecoder/javascript-module-module-loader-module-bundler-es6-module-confused-yet-6343510e7bde" rel="external nofollow">Brief history of JavaScript Modules</a> لصاحبه SungTheCoder.
	</li>
</ul>
]]></description><guid isPermaLink="false">866</guid><pubDate>Tue, 21 Apr 2020 18:04:00 +0000</pubDate></item><item><title>&#x62F;&#x644;&#x64A;&#x644;&#x643; &#x627;&#x644;&#x643;&#x627;&#x645;&#x644; &#x625;&#x644;&#x649; &#x646;&#x638;&#x627;&#x645; &#x627;&#x644;&#x628;&#x646;&#x627;&#x621; Gradle</title><link>https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D9%83%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-gradle-r865/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2020_04/Gradle.jpg.a002c2fc1d3820a3665211c32158ce29.jpg" /></p>

<h2>
	1. مقدمة عن نظام بناء Gradle
</h2>

<h3>
	1.1. ما هو نظام بناء Gradle
</h3>

<p>
	<em>Gradle</em> هو نظام بناء إداري للأغراض العامة. يدعم <em>Gradle</em> التنزيل والضبط التلقائي للتبعيات Dependencies أو المكتبات الأخرى. ويدعم مستودعات Maven و Ivy لاستعادة هذه التبعيات.
</p>

<p>
	يدعم Gradle المشروع المُتعدد والبناءات ذات النواتج المتعددة.
</p>

<h3>
	1.2. المشاريع والمهام في Gradle
</h3>

<p>
	يتم وصف بناءات جرادل (Gradle Builds) عبر ملف واحد أو عدة ملفات build.gradle متعددة. يوجد عادةً (ملف بناء Build File) واحد على الأقل في المجلد الأصلي للمشروع. يُعرِّف كل ملف بناء مشروعًا ومهامًا لهذا المشروع.
</p>

<p>
	تنقسم المشروعات إلى نوعين:
</p>

<ul>
<li>
		شيء يجب بناؤه
	</li>
	<li>
		شيء يجب القيام به
	</li>
</ul>
<p>
	ويتكون المشروع من مهام Tasks. تمثل المهمة Task جزءًا من العمل الذي تؤديه أحد الأبنية Builds، على سبيل المثال، تجميع التعليمات البرمجية المصدر (Compile the source code) أو إنشاء Javadoc. تستند ملفات البناء هذه إلى لغة ذات مجال مخصص (Domain Specific Language - DSL).في هذا الملف، يمكنك استخدام مجموعة من التعليمات التصريحية والضرورية (declarative and imperative statements).
</p>

<p>
	تستطيع كتابة كود Groovy أو Kotlin متى احتجت إلى ذلك. يمكنك أيضًا إنشاء المهام وتوسيعها ديناميكيًا في وقت التشغيل Runtime.
</p>

<p>
	تمثل القائمة التالية ملف بناء بسيط للغاية.
</p>

<pre class="ipsCode">
task hello {
    doLast {
        println 'Hello Gradle'
    }
}
</pre>

<p>
	لتنفيذ المهمة <code>hello</code> في ملف البناء، اكتب <code>gradle hello</code> في سطر الأوامر في مجلد ملف البناء. إذا اختفى ناتج جرادل Gradle Output، يُمكنك استخدام <code>q-</code> كـ (quiet parameter).
</p>

<pre class="ipsCode">
gradle hello

# alternative add the -q flag
gradle -q hello
</pre>

<h3>
	1.3 التعليقات في ملفات بناء Gradle
</h3>

<p>
	يُمكنك استخدام التعليقات الفردية والمتعددة الأسطر في ملفات بناء Gradle كما يلي:
</p>

<pre class="ipsCode">
// تعليق على سطر واحد

/*
 تعليق
على أسطر
متعددة
*/
</pre>

<h3>
	1.4. إعدادات المشروع والوصف
</h3>

<p>
	بشكل افتراضي، يستخدم Gradle اسم ال (Directory Name) كاسم مشروع. يمكنك تغيير هذا عن طريق إنشاء ملف settings.gradle في الدليل الذي يُحدد اسم المشروع.
</p>

<pre class="ipsCode">
rootProject.name ='com.vogella.gradle.first'
</pre>

<p>
	يمكنك أيضًا إضافة وصف لمشروعك عبر ملف <code>build.gradle</code>:
</p>

<pre class="ipsCode">
description ="""
Example project for a Gradle build

Project name: ${project.name}

More detailed information here... """

task hello {
    doLast {
        println 'Hello Gradle'
    }
}
</pre>

<p>
	استخدم الأمر <code>gradle project</code> للحصول على معلومات حول مشروعك. تظهر القائمة التالية الناتج.
</p>

<pre class="ipsCode">
:projects

------------------------------------------------------------
Root project -
Example project for a Gradle build

Project name: com.vogella.gradle.first

More detailed information here...
------------------------------------------------------------

Root project 'com.vogella.gradle.first' -
Example project for a Gradle build

Project name: com.vogella.gradle.first

More detailed information here...
No sub-projects

To see a list of the tasks of a project, run gradle &lt;project-path&gt;:tasks
For example, try running gradle :tasks

BUILD SUCCESSFUL

Total time: 1.048 secs
</pre>

<h2>
	2. مكونات Gradle الإضافية (Gradle Plug-ins)
</h2>

<p>
	يستخدم نظام بناء Gradle مكونات إضافية أو إضافات (Plug-ins) لزيادة وظائفه الأساسية. المكوّن الإضافي هو توسيع لعمل لـ Gradle يضيف عادة بعض المهام المُعدّة مسبقًا. يأتي Gradle مع عدد من المكونات الإضافية، ويمكنك تطوير مكونات إضافية مخصصة.
</p>

<p>
	مثال على ذلك هو مُكوِّن جافا الإضافي (Java Plug-in). يُضيف هذا المكوّن الإضافي مهامًا إلى مشروعك والتي تسمح بترجمة شيفرة جافا المصدرية (Java Source Code)، وتشغيل اختبارات الوحدة وإنشاء ملف JAR. يتواجد المكون الإضافي في ملف <code>build.gradle</code> مع التعليمة البرمجية <code>'apply plugin:'pluginname</code> .
</p>

<p>
	على سبيل المثال، الأمر التالي يجعل المكون الإضافي لنظام Android متاحًا لإنشاء Gradle:
</p>

<pre class="ipsCode" id="ips_uid_9559_9">
apply plugin: 'com.android.application'</pre>

<p>
	يوفر Gradle أيضًا سجلًا للمكونات الإضافية عبر المكوِّن الإضافي الخاص بالبحث في Gradle والمُسمى بـ <a href="http://plugins.gradle.org/" rel="external nofollow">Gradle Plugin Search</a>.
</p>

<h3>
	2.1. دعم بيئة تطوير متكاملة IDE لـ Gradle.
</h3>

<p>
	تقوم شركة Gradleware بتطوير برنامج Eclipse Gradle التعليمي عبر مشروع Eclipse Buildship. تتضمن البيئات التطويرية المتكاملة الأخرى IDEs مثل IntelliJ و Android Studio دعمًا جيدًا لـ Gradle.
</p>

<h2>
	تثبيت وتهيئة Gradle
</h2>

<h3>
	2.2. المتطلبات
</h3>

<p>
	يتطلب استخدام Gradle تثبيت عدّة تطوير جافا (JDK (Java Development Kit.
</p>

<h3>
	2.3. تحميل واستخراج Gradle
</h3>

<p>
	يمكن العثور على أحدث إصدار من Gradle على صفحة تنزيل جرادل <a href="http://gradle.org/gradle-download" rel="external nofollow">Gradle Download Page</a>. قم بتنزيل أحدث توزيع متكامل.
</p>

<p>
	إنها gradle-${version}-all.zip، حيث تعبّر {version}$ عن الإصدار الحالي.
</p>

<p>
	استخرج محتويات الملف المضغوط الذي قمت بتنزيله في مجلد جديد.
</p>

<h3>
	2.4. تثبيت Gradle على الويندوز
</h3>

<p>
	أضف هذا المُجلد الجديد، الذي استخرجت فيه محتويات Gradle في الخطوة السابقة، إلى مُتغير البيئة <code>PATH</code>.
</p>

<p>
	بالضغط على Win + Pause، يمكنك فتح إعدادات النظام.
</p>

<p style="text-align: center;">
	<img alt="2-SetupStep1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36320" data-unique="y24mkbiyn" src="https://academy.hsoub.com/uploads/monthly_2020_04/2-SetupStep1.png.ef55ba6e69933d31d96ff4476cc1dad9.png"></p>

<p>
	أولًا قم باختيار Advanced System Settings، ثم اضغط على زر Environment Variables كما هو موضّح.
</p>

<p style="text-align: center;">
	<img alt="3-SetupStep2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36321" data-unique="2km8wg03b" src="https://academy.hsoub.com/uploads/monthly_2020_04/3-SetupStep2.png.f0f868feff4a108d501b4447b2581465.png"></p>

<ol>
<li>
		<p>
			في نافذة متغيرات البيئة، يجب تعيين متغيرات المستخدم JAVA_HOME و GRADLE_HOME.
		</p>
	</li>
	<li>
		<p>
			بعد ذلك، اختر المدخل PATH في (متغيرات النظام System Variables). ثم اضغط على زر التعديل Edit لإضافة ملف bin الخاص بتثبيت Gradle إلى المسار PATH.
		</p>
	</li>
</ol>
<p style="text-align: center;">
	<img alt="4-SetupStep3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="36322" data-unique="oi0lywzna" src="https://academy.hsoub.com/uploads/monthly_2020_04/4-SetupStep3.png.2a303bfd8f6c4911544a622169878be4.png"></p>

<h3>
	2.5. تثبيت Gradle على لينكس/ماك
</h3>

<h4>
	2.5.1 التثبيت اليدوي
</h4>

<p>
	يجب أن يشير متغير <code>JAVA_HOME</code> إلى jdk مناسب ويجب أن يكون <code>JAVA_HOME/bin$</code> جزءًا من متغير بيئة PATH.
</p>

<p>
	أضف Gradle إلى المسار عن طريق تنفيذ السطر الآتي في Terminal:
</p>

<pre class="ipsCode">
export PATH=/usr/local/gradle/FOLDER_TO_WHICH_YOU_EXTRACTED_GRADLE/bin:$PATH
</pre>

<h4>
	2.5.2 التثبيت مع SDKMAN!
</h4>

<p>
	SDKMAN! هي أداة سطر أوامر تتيح لك تثبيت إصدارات Gradle المتعددة والتبديل بينها. يتم تشغيله على أي نظام تشغيل يستند إلى UNIX.
</p>

<h5>
	تثبيت SDKMAN!
</h5>

<p>
	يمكنك تثبيته من سطر الأوامر. إذا كنت قد قمت بالفعل بتثبيت SDKMAN! يمكنك تخطي هذه الخطوة.
</p>

<pre class="ipsCode">
curl -s "https://get.sdkman.io" | bash
</pre>

<p>
	بعد تثبيت SDKMAN! يجب عليك إعادة تشغيل الجهاز قبل استخدامه.
</p>

<h5>
	تثبيت Gradle وإعداد الإصدار الافتراضي
</h5>

<pre class="ipsCode">
sdk install gradle 6.2.1
sdk default gradle 6.2.1
gradle -v
</pre>

<h5>
	تبديل إصدار Gradle
</h5>

<pre class="ipsCode">
sdk install gradle 6.2.1
# use 6.2.1 for current terminal session
sdk use gradle 6.2.1
gradle -v
</pre>

<h4>
	2.5.3. تحقق من نجاح تثبيت Gradle
</h4>

<p>
	افتح سطر أوامر واكتب gradle، وسيتم تشغيل المهمة المساعدة الخاصة بـ Gradle بشكل افتراضي.
</p>

<p>
	<strong>تنبيه</strong>: قد يختلف الإصدار المتوافر حاليًا أثناء قراءتك للمقال عن الإصدار المستعمل أثناء ترجمة المقال، لذا نرجو التأكد من هذه النقطة من موقع Gradle الرسمي قبل الاستمرار، فقد يكون هنالك بعض الإختلافات والتحديثات التي أضيفت على أحدث إصدار Gradle عن الإصدارات السابقة.
</p>

<h4>
	2.6. استخدام البرنامج الخفي Gradle لتحسين وقت بدء التشغيل
</h4>

<p>
	يسمح Gradle لنفسه بالبدء كـبرنامج خفي لتجنب بدء تشغيل جهاز جافا الظاهري Java Virtual Machine لكل بنية. لتكوين ذلك:
</p>

<ul>
<li>
		أنشئ ملف يسمى gradle.properties في <code>HOME}/.gradle}$</code> وإضافة السطر التالي إليه:
	</li>
</ul>
<pre class="ipsCode">
org.gradle.daemon=true
</pre>

<p>
	يمكنك أيضًا وضع ملف gradle.properties في المجلد الأصلي لمشروعك وإلزامه بنظام التحكم في الإصدار Control Version System.
</p>

<p>
	إذا لم يتم استخدام Gradle لبضع ساعات ، فإن البرنامج الخفي يتوقف تلقائيًا.
</p>

<p>
	بتنفيذ Gradle مع الوسيط <code>daemon--</code> في سطر الأوامر يبدأ البرنامج الخفي gradle. لإيقاف البرنامج الخفي بشكل تفاعلي، استخدم الأمر <code>gradle --stop</code>.
</p>

<h4>
	2.7. حدد إعدادات JVM المخصصة لـ Gradle
</h4>

<p>
	يوفر متغير البيئة GRADLE_OPTS الفرصة لتعيين خيارات JVM محددة لـ Gradle. في استخدام البرنامج الخفي Gradle لتحسين وقت بدء التشغيل، تم تحسين أداء بدء التشغيل JVM، لكن (قاتل الأداء Performance Killer) الآخر للبنيات الكبيرة يمكن أن يكون الحد الأقصى لـ(مساحة كومة heap space) صغيرة جدا. لذلك من المنطقي زيادته في Gradle.
</p>

<p>
	يُعرِّف <code>export GRADLE_OPTS=-Xmx1024m</code> أن Gradle يمكنه استخدام 1 غيغابايت كحد أقصى لحجم الكومة.
</p>

<p>
	في نظام التشغيل Windows، عادةً ما يتم تعريف متغيرات البيئة عن طريق واجهة مستخدم خاصية النظام.
</p>

<p>
	إذا كنت ترغب في ضبط إعدادات JVM على المستوى العالمي ولكن على أساس كل مشروع، أضف إلى الملف ‎<your app="" folder="">/gradle.properties السطر التالي:</your></p>

<pre class="ipsCode">
org.gradle.jvmargs=-Xms2g -Xmx4g -XX\:MaxHeapSize\=3g
</pre>

<h4>
	2.8. ملف ‎.gitignore النموذجي لمشاريع Gradle
</h4>

<p>
	إذا كنت تستخدم Git كنظام للتحكم في الإصدار، فيمكنك استخدام ملف gitignore. التالي كقالب لمشروع Gradle.
</p>

<pre class="ipsCode">
# Android built artifacts
*.apk
*.ap_
*.dex

# Java build artifacts class files
*.class

# other generated files
bin/
gen/
build/

# local configuration file (for Android sdk path, etc)
local.properties

# OSX files
.DS_Store

# Eclipse project files
.classpath
.project

# Android Studio
*.iml
.idea
.gradle


#NDK
obj/
</pre>

<h2>
	3. تمرين: إنشاء مشروع Java باستخدام سطر أوامر Gradle
</h2>

<p>
	يوفر Gradle دعمًا <a href="https://academy.hsoub.com/programming/workflow/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D9%88%D8%A8%D9%86%D8%A7%D8%A4%D9%87-%D8%B9%D8%A8%D8%B1-yeoman-r761/" rel="">للسقالات Scaffolding Support</a> لإنشاء مشاريع تستند إلى Gradle عبر سطر الأوامر.
</p>

<p>
	قم بإنشاء مجلد جديد new directory على نظام الملفات الخاص بك، وانتقل إليه وقم بتشغيل الأمر التالي.
</p>

<pre class="ipsCode">
gradle init --type java-library --test-framework junit-jupiter
</pre>

<p>
	يؤدي هذا إلى إنشاء مشروع Java يستند إلى Gradle والذي يستخدم JUnit Jupiter لاختبار الوحدة Unit Testing.
</p>

<p>
	يمكنك تشغيل البناء build عبر:
</p>

<pre class="ipsCode">
gradle build
</pre>

<p>
	والاختبار الناتج عن هذا البناء عبر:
</p>

<pre class="ipsCode">
gradle test
</pre>

<p>
	سيقوم Gradle بإنشاء تقرير اختبار في مجلد <code>/build/reports/tests/test</code>
</p>

<h2>
	4. تمرين: تكوين خصائص Gradle
</h2>

<p>
	عند استخدام أمر gradle للمرة الأولى، يتم إنشاء مجلد gradle. في الدليل {USER_HOME}$.
</p>

<p>
	عادة ما يكون home/${yourUserName}/.gradle/ على نظام Linux.
</p>

<p>
	بينما يكون C:\Users\${yourUserName}.gradle على نظام Windows. أما على نظام MAC فيكون /Users/${yourUserName}/.gradle.
</p>

<p>
	داخل مجلد gradle. هذا، يجب إنشاء ملف gradle.properties مع المحتويات التالية:
</p>

<pre class="ipsCode">
org.gradle.warning.mode=all
</pre>

<p>
	الآن سيتم تجاوز الافتراض لخاصية org.gradle.warning.mode (الافتراض هو الملخص).
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يمكن تعيين الخصائص الشائعة أو السرية أو استبدالها هنا، على سبيل المثال، افتراضيات Gradle أو رموز الوصول للنشر- access tokens for deployment.
	</p>
</blockquote>

<h2>
	5. إدارة التبعية لمشاريع جافا
</h2>

<h3>
	5.1. إدارة التبعيات باستخدام Gradle
</h3>

<p>
	يسمح Gradle بإدارة classpath من المشاريع الخاصة بك. يمكنه إضافة ملفات JAR أو أدلة أو مشاريع أخرى إلى مسار إنشاء التطبيق الخاص بك. كما يدعم التنزيل التلقائي لتبعيات مكتبة Java. ما عليك سوى تحديد التبعية في ملف بناء Gradle الخاص بك. مما يجعل Gradle يقوم بتنزيل المكتبة بما في ذلك التبعيات العابرة لها أثناء الإنشاء.
</p>

<p>
	يتم تحديد مكتبة Java بواسطة Gradle عبر <code>groupId:artifactId:version</code> الخاص بمشروعها (ويُعرف أيضًا باسم GAV في Maven).
</p>

<p>
	تحدد هذه الـGAV مكتبة بشكل فريد في إصدار معين.
</p>

<p>
	يمكنك استخدام <a href="http://plugins.gradle.org/" rel="external nofollow">موقع Maven </a> للبحث عن GAV المكتبة في Maven Central.
</p>

<p>
	لإضافة تبعية، قم بإضافة إدخال إلى قسم التبعية في ملف build.gradle الخاص بك كما هو موضح في القائمة التالية.
</p>

<pre class="ipsCode">
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.squareup.okhttp:okhttp:2.5.0'
    testCompile 'junit:junit:4.12'

}
</pre>

<h3>
	5.2. تحديد المستودعات للبحث عن التبعيات
</h3>

<p>
	في ملف البناء الخاص بك، يمكنك تحديد المستودعات البعيدة للبحث عن التبعيات. يدعم Gradle مستودعات Maven و Ivy للبحث عن التبعيات. توضح القائمة التالية كيفية تكوين Maven central كمصدر تبعية.
</p>

<pre class="ipsCode">
repositories {
    mavenCentral()
}
</pre>

<p>
	من الممكن أيضًا تكوين الهدف كعنوان URL:
</p>

<pre class="ipsCode">
repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}
</pre>

<p>
	توضح القائمة التالية كيفية تحديد تبعية Ivy:
</p>

<pre class="ipsCode">
repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
    }
}
</pre>

<p>
	يمكنك أيضًا إضافة مستودعات مختلفة مرة واحدة.
</p>

<pre class="ipsCode">
repositories {
    maven ("https://repository-achartengine.forge.cloudbees.com/snapshot/")
    jcenter {
        url "http://jcenter.bintray.com/"
    }
}
</pre>

<p>
	يمكنك أيضًا الرجوع إلى الملفات التي ينتجها المشروع من نظام الملفات.
</p>

<pre class="ipsCode">
apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
    runtime files('libs/library1.jar', 'libs/library2.jar')
    runtime fileTree(dir: 'libs', include: '*.jar')
    compile fileTree(dir: "${System.properties['user.home']}/libs/cargo", include: '*.jar')
}
}
</pre>

<h3>
	5.3. إظهار تبعيات المشروع (التبعيات العابرة أيضًا)
</h3>

<p>
	يعرض الأمر التالي جميع التبعيات العابرة لمشروع Gradle:
</p>

<pre class="ipsCode">
gradle dependencies
</pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يسمح <a href="https://mvnrepository.com/" rel="external nofollow">mvnrepository.com</a> أيضا بالبحث عن الناتج ويظهر التبعيات مباشرة.
	</p>
</blockquote>

<h3>
	5.4. ذاكرة التخزين المؤقت لـ Gradle وحذف ذاكرة التخزين المؤقت
</h3>

<p>
	يمكنك تحديث التبعيات في ذاكرة التخزين المؤقت مع خيار سطر الأوامر <code>refresh-dependencies--</code>.
</p>

<p>
	يمكنك أيضًا حذف الملفات المخزنة مؤقتًا ضمن gradle/cache./~ مع البناء القادم (next build)، يحاول Gradle تنزيل التبعيات مرة أخرى.
</p>

<h3>
	5.5.استثناء التبعيات المتعدية
</h3>

<p>
	في بعض الأحيان يكون لديك تبعيات على الحزم Packages التي تحدد التبعيات العابرة المتعارضة. أحد الحلول هو استبعاد التبعية من وحدة نمطية محددة:
</p>

<pre class="ipsCode">
compile 'org.springframework:spring-web:4.3.10.RELEASE' {
    exclude group: 'com.google.code.gson', module: 'gson'
}
</pre>

<p>
	إذا كان لديك تبعية متعددة تحدد تبعية تريد استبعادها، فيمكنك القيام بذلك على مستوى المشروع:
</p>

<pre class="ipsCode">
configurations.all {
    exclude group: 'com.google.code.gson', module: 'gson'
}
</pre>

<p>
	باتباع نفس النهج، يمكننا استبعاد التبعية فقط أثناء وقت التشغيل runtim:
</p>

<pre class="ipsCode">
configurations.runtime {
    exclude group: 'com.google.code.gson', module: 'gson'
}
</pre>

<h3>
	5.6. فرض إصدار محدد من تبعية متعدية
</h3>

<p>
	من الممكن إجبار gradle على اختيار إصدار معين عندما يواجه التبعيات العابرة المتعارضة. ضع في اعتبارك أنه قد تضطر إلى تحديث هذا الإصدار يدويًا عندما تقوم بتحديث الحزم التي تعتمد عليه.
</p>

<pre class="ipsCode">
configurations.all {
    resolutionStrategy.force 'com.google.code.gson:gson:2.8.1'
}
</pre>

<h2>
	6. تشغيل بناء Running a build
</h2>

<p>
	عند بدء إنشاء Gradle عبر سطر الأوامر، تبحث أداة أمر <code>gradle</code> عن ملف يسمى <code>build.gradle</code> في الدليل الحالي.
</p>

<p>
	يدعم Gradle أيضًا اختصار المهام، على سبيل المثال، لبدء تشغيل المهمة <code>lars</code>، فإنّ استخدام أمر <code>gradle l</code> يكفي. يجب على الاختصار أن يحدد المهمة بشكل فريد، وإلا سيُظهر Gradle رسالة خطأ، توضح أن الاختصار غامض. يمكن أيضًا استخدام CamelCase للاختصار، على سبيل المثال، يمكن استدعاء المهمة <code>gradle vogellaCompany</code> باستخدام الأمر <code>gradle vC</code>.
</p>

<p>
	يمكن تشغيل بناء Gradle عبر الأمر <code>gradle</code> أو <code>gradle -q</code>. الوسائط <code>q-</code> أو <code>quiet--</code> تجعل تنفيذ Gradle أقل طولًا. يمكن معالجة مهمة محددة كالتالي: <code>gradle -q other</code>، والتي تنفذ المهمة "الأخرى.
</p>

<p>
	يمكنك بالطبع أيضًا استخدام السكربت المجمع the Gradle wrapper script، إذا كان ذلك متاحًا.
</p>

<p>
	لتحديد ملف بناء مختلف، يمكن استخدام الخيار <code>b buildFileName-</code>.
</p>

<p>
	في السيناريوهات التي لا يتوفر فيها اتصال بشبكة، يمكن استخدام المعامل<code>offline--</code>. يعمل هذا على تشغيل Gradle build دون الاتصال بالإنترنت، مما يعني أن Gradle لا يحاول الوصول إلى الموارد من الشبكة أثناء الإنشاء. على سبيل المثال، للتبعيات من مستودع إنتاج مثل Maven Central أو Bintray.
</p>

<p>
	للحصول على ناتج تفصيلي لما يقوم به Gradle، يمكنك تحديد المعامل <code>info--</code>.
</p>

<h2>
	7. مهام Gradle
</h2>

<h3>
	7.1. مهام Gradle الافتراضية
</h3>

<p>
	يقدم Gradle أيضًا مهامًا لاستكشاف Gradle نفسه، حيث يمكنك تحليل مشروع Gradle باستخدام مهام Gradle الافتراضية.
</p>

<p>
	مثال جيد هو مهمة المهام، والتي تعرض المهام المتاحة للمشروع. عند كتابة <code>gradle -q tasks</code>، يتم عرض قائمة بالمهام. يسرد هذا الأمر المهام الأساسية حتى بدون ملف <code>build.gradle</code>.
</p>

<p style="text-align: center;">
	<img alt="5-DefaultGradleTask.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36323" data-unique="8j7t8nhvx" src="https://academy.hsoub.com/uploads/monthly_2020_04/5-DefaultGradleTask.PNG.160f9831a4158b8c131ec35e3f1b6ab0.PNG"></p>

<p>
	يحاول Gradle أيضًا إعطاء بعض الإرشادات لاستخدام المهام التي تم استدعاؤها، كما هو موضح في الجزء السفلي من إخراج وحدة التحكم. سيقوم الأمر <code>gradle tasks --all</code> أيضًا بسرد المهام التابعة، والتي يتم استدعاؤها قبل المهمة الفعلية.
</p>

<p>
	عند تشغيل <code>gradle tasks --all</code> يبدو الناتج مشابهًا تمامًا للملف السابق، باستثناء مهمة <code>init</code> التي تعتمد على المهمة <code>wrapper</code>.
</p>

<h3>
	7.2. إنشاء مهام Gradle مخصصة
</h3>

<p>
	في جزء gradle_runbuild_buildfile، تم إنشاء أول مهمة بسيطة في ملف <code>build.gradle</code>
</p>

<pre class="ipsCode">
task hello {
    doLast {
        println 'Hello Gradle'
    }
}
</pre>

<p>
	عند تشغيل مهمة مهام <code>gradle -q tasks</code> مع ملف <code>build.gradle</code> هذا، سيتم سرد مهمة <code>hello</code> ضمن "مهام أخرى Other Tasks".
</p>

<p style="text-align: center;">
	<img alt="6-customeGradleTasks.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36324" data-unique="s08xb5d1j" src="https://academy.hsoub.com/uploads/monthly_2020_04/6-customeGradleTasks.PNG.98f11ef5a989fa5a3604656996416823.PNG"></p>

<p>
	تعتبر المهام بدون مجموعة مهام خاصة. على سبيل المثال، لا تُظهر طريقة العرض Gradle Task View الخاصة بـ Eclipse Gradle Plug-in مثل هذه المهام. ولكن يمكن إظهارها عن طريق تنشيط الإدخال الصحيح في قائمة العرض.
</p>

<p>
	يمكن تطبيق المجموعات عن طريق خاصية المجموعة <code>group</code> ويمكن تطبيق وصف باستخدام خاصية الوصف<code>description</code>. في حالة وجود المجموعة بالفعل، تتم إضافة مهمة <code>hello</code> إليها. إذا كانت المجموعة غير موجودة، فهي موجودة مُسبقًا.
</p>

<pre class="ipsCode">
task hello {
     group 'vogella'
     description 'The hello task greets Gradle by saying "Hello Gradle"'

     doFirst {
        println 'Hello Gradle'
     }
     doLast {
        println 'Bye bye Gradle'
     }
}
</pre>

<h3>
	7.3. هيكل المهمة Task Structure
</h3>

<p>
	Gradle له مراحل مختلفة، عند العمل مع المهام. بادئ ذي بدء، هناك مرحلة التكوين، حيث يتم تنفيذ الكود، المحدد مباشرة في إغلاق المهمة. يتم تنفيذ كتلة التكوين لكل مهمة متوفرة وليس فقط لتلك المهام، والتي يتم تنفيذها فعليًا فيما بعد.
</p>

<p>
	بعد مرحلة التكوين، تقوم مرحلة التنفيذ بعد ذلك بتشغيل الكود داخل عمليات إغلاق <code>doFirst</code> أو <code>doLast</code> لتلك المهام، والتي تم تنفيذها بالفعل.
</p>

<pre class="ipsCode">
task onlySpecifiesCodeForConfigurationPhase {
    group 'vogella'
    description 'Configuration phase task example.'

    println 'I always get printed even though, I am not invoked'
}

task anotherUnrelatedTask {
    doLast {
        println 'I am in the doLast execution phase'
    }
}
</pre>

<p>
	عند تنفيذ <code>gradle -q anotherUnrelatedTask</code>، تتم طباعة ما يلي:
</p>

<pre class="ipsCode">
I always get printed even though, I am not invoked
I am in the doLast execution phase
</pre>

<p>
	تأتي العبارة الأولى من مرحلة التكوين التي يتم فيها تقييم تعريف المهمة <code>onlySpecifiesCodeForConfigurationPhase</code>.
</p>

<h3>
	7.4. تبعيات مهمة Task Dependencies
</h3>

<p>
	يسمح Gradle بتعريف المهام الافتراضية في ملف البناء. يتم تنفيذ هذه، إذا لم يتم تحديد مهام أخرى. يمكن للمهام أيضًا تحديد تبعياتها. كلا الإعدادات موضحة في ملف البناء التالي.
</p>

<pre class="ipsCode">
defaultTasks 'clean', 'compile'

task clean {
    doLast {
        println 'Executing the clean task'
    }
}

task compile {
    doLast {
        println 'Executing the compile task'
    }
}

task other(dependsOn: 'compile') {
    doLast {
        println "I'm not a default task!"
    }
}

task cleanOther {
    doLast {
        println "I want to clean up before running!"
    }
}

cleanOther.dependsOn clean, compile
</pre>

<p>
	يمكن أيضًا تنفيذ ارتباطات تنفيذ المهام المحددة مسبقًا للمهام الافتراضية أو المهام من المكونات الإضافية Plug-ins باستخدام طريقة <code>dependOn</code>.
</p>

<p>
	على سبيل المثال، عندما يَتعيّن القيام ببعض الأشياء مباشرة بعد تجميع كود جافا:
</p>

<pre class="ipsCode">
apply plugin: 'java'

task invokedAfterCompileJava(dependsOn: 'compileJava')  {
    doLast {
        println 'This will be invoked right after the compileJava task is done'
    }
}
</pre>

<p>
	كبديل لإنشاء مهمة جديدة، والتي تعتمد على مهمة <code>'compileJava'</code>، يمكن أيضًا تطبيق كتلة تنفيذ جديدة مباشرة على مهمة موجودة، على سبيل المثال، المهمة <code>'compileJava'</code>:
</p>

<pre class="ipsCode">
apply plugin: 'java'

compileJava.doFirst { println 'Another action applied to the "compileJava" task' }
compileJava.doLast { println 'Another doLast action is also applied' }
</pre>

<p>
	عند تشغيل المهمة <code>'javaCompile'</code>، يتم تنفيذ جميع الإجراءات، التي تم تطبيقها على المهمة javaCompile، واحدة تلو الأخرى وفقًا للترتيب الذي تم تطبيقه على المهمة.
</p>

<h3>
	7.5. تخطي مهام Skipping Tasks
</h3>

<p>
	يمكن تخطي المهام عن طريق تمرير إغلاق مُسند predicate closure إلى طريقة <code>onlyIf</code> الخاصة بالمهمة أو عن طريق طرح <code>StopExecutionException</code>:
</p>

<pre class="ipsCode">
task eclipse {
    doLast {
        println 'Hello Eclipse'
    }
}

// #1st approach - closure returning true, if the task should be executed, false if not.
eclipse.onlyIf {
    project.hasProperty('usingEclipse')
}

// #2nd approach - alternatively throw an StopExecutionException() like this
eclipse.doFirst {
    if(!usingEclipse) {
        throw new StopExecutionException()
    }
}
</pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		سيتم تشغيل المهام التي تعتمد على مهمة Eclipse حتى إذا تم طرح <code>StopExecutionException</code>.
	</p>
</blockquote>

<h4>
	7.5.1. الوصول إلى متغيرات النظام مثل دليل المستخدم الرئيسي
</h4>

<p>
	يمكنك الوصول إلى متغيرات النظام. على سبيل المثال، للحصول على دليل المستخدم الرئيسي، استخدم ما يلي:
</p>

<pre class="ipsCode">
def homePath = System.properties['user.home']
</pre>

<h2>
	8. تمرين: مهام Gradle
</h2>

<h3>
	8.1. استخدام مهمة المهام في Gradle
</h3>

<p>
	الهدف من هذا التمرين هو الحصول على نظرة عامة حول المهام الافتراضية، والتي يتم تسليمها افتراضيًا.
</p>

<p>
	افتح سطر الأوامر وقم بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode">
gradle -q tasks
</pre>

<h3>
	8.2. استخدام المهمة help
</h3>

<p>
	الهدف من هذا التمرين هو الاستفادة من المهمة <code>help</code> المساعدة للحصول على مزيد من المعلومات حول المهام الأخرى، مثل مهمة <code>init</code>.
</p>

<pre class="ipsCode">
gradle -q help --task init
</pre>

<h3>
	8.3. إنشاء مشروع Groovy
</h3>

<p>
	التمرين السابق على علم باستخدام المهمة init:
</p>

<pre class="ipsCode">
gradle -q init --type groovy-library
</pre>

<h3>
	8.4. اختياري - إدخال مشروع Groovy جديد
</h3>

<p>
	يمكن استخدام Eclipse Buildship لإدخال المشروع إلى Eclipse IDE.
</p>

<h3>
	8.5 . استخدام مهمة التبعيات
</h3>

<p>
	من أجل رؤية تبعيات المشروع (بما في ذلك التبعيات)، يجب التذرع بمهمة التبعيات.
</p>

<pre class="ipsCode">
./gradlew dependencies
</pre>

<p>
	إذا تم تنفيذ تمرين الإدخال Buildship الاختياري، فيمكن أيضًا استدعاء مهمة التبعيات باستخدام طريقة العرض Gradle Tasks.
</p>

<h2>
	9. استخدام Gradle Wrapper
</h2>

<p>
	يتيح Gradle Wrapper إمكانية قيام المستخدم بتشغيل البناء باستخدام إصدار مُحدَّد مُسبقًا واستخدام إعدادات Gradle دون تثبيت Gradle محليًا. هذا المُغلِّفWrapper هو برنامج يتكون من سكربتات مرقِّعة-Patch Script على ويندوز، وسكربت للصدفة-Shell Script لأنظمة التشغيل الأخرى مثل لينكس. عند بدء تشغيل Gradle build عبر المجمع، يتم تنزيل الإصدار المحدد من Gradle تلقائيًا ويستخدم لتشغيل البناء.
</p>

<p>
	يعد الـWrapper الطريقة المفضلة لبدء إنشاء Gradle، حيث يجعل تنفيذ البناء مستقلًا عن إصدار Gradle المثبت. يمكن إنشاء البرنامج النصي المجمع عن طريق <code>gradle wrapper</code>.
</p>

<p>
	نتيجًة لذلك، يمكنك العثور على <em>gradlew</em> للأنظمة المستندة إلى يونكس (أنظمة التشغيل مفتوحة المصدر) و <em>gradlew.bat</em> لأنظمة الويندوز. يمكن استخدام هذه الملفات بدلاً من ذلك لأمر <code>gradle</code>، وإذا لم يتم تثبيت Gradle على الجهاز، فسيتم تنزيل Gradle وتثبيته تلقائيًا.
</p>

<p>
	من الممكن أيضًا تحديد مهمة تُحدِّد إصدار البرنامج. إذا تم تنفيذ هذه المهمة، فإنها تنشئ الـWrapper وتُحمِّل الإصدار الصحيح من Gradle.
</p>

<pre class="ipsCode">
wrapper {
    gradleVersion = '4.9'
}
</pre>

<p>
	يمكن أيضًا تعريف إصدار Gradle Wrapper، عند إنشائه عبر سطر الأوامر الآتي:
</p>

<pre class="ipsCode">
gradle wrapper --gradle-version 4.9
</pre>

<p>
	بدون هذا الإصدار الصريح، ستقوم Gradle تلقائيًا باختيار أحدث إصدار.
</p>

<h3>
	9.1. تكوين GRADLE_OPTS لبرنامج Gradle Wrapper
</h3>

<p>
	يمكن أيضًا تحديد GRADLE_OPTS داخل ملف <em>gradlew</em> أو <em>gradlew.bat</em>.
</p>

<pre class="ipsCode">
#!/usr/bin/env bash

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Add default JVM options here.
# You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="-Xmx1024m"

#... {more lines}
</pre>

<pre class="ipsCode">
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

@rem Add default JVM options here. You can also use JAVA_OPTS
# and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=-Xmx1024m

@rem ... {more lines}
</pre>

<h2>
	10. تمرين: تكوين مهمة الـ Wrapper
</h2>

<p>
	تتوفر مهام مثل مهام Gradle Wrapper افتراضيًا ويمكن تعيين خصائص معينة منها، على سبيل المثال، <em>gradleVersion</em> مثل هذا:
</p>

<pre class="ipsCode">
wrapper {
    gradleVersion = '4.9'
}
</pre>

<h2>
	11. تمرين - إنشاء مهام Gradle مخصصة
</h2>

<h3>
	11.1. تمرين: مهمة Hello Gradle
</h3>

<p>
	قم بإنشاء مهمة <em>helloGradle</em>، والتي تطبع <em>Hello Gradle</em> مع ورشة عمل المجموعة والوصف المناسب.
</p>

<p>
	ثم استخدم أمر <code>gradlew tasks/.</code> لرؤية مهمة <em>helloGradle</em> الجديدة في وحدة التحكم أو استخدام Buildship في Eclipse IDE. ثم استدعِ مهمة <code>helloGradle</code> عن طريق استدعاء <code>gradlew hG/.</code> أو مرة أخرى استخدم Buildship في Eclipse IDE.
</p>

<h3>
	11.2. تمرين: التبعيات بين المهام
</h3>

<p>
	قم بإنشاء مهمتين جديدتين الأولى تدعى <code>LearnGroovy</code>، تطبع <em>Learn Groovy</em>، والثانية <code>learnGradle</code>، تطبع 'Learn Gradle'. يجب أن يكون لهذه المهام تبعيات معقولة.
</p>

<h3>
	11.3. تمرين: عمل doFirst للمهام
</h3>

<p>
	قم بتعديل مهمة <code>learnGroovy</code> بحيث تقوم بطباعة *Install Eclipse IDE with Buildship * قبل أن تقوم بطباعة Learn Groovy.
</p>

<h2>
	12. تمرين: إنشاء مهمة نسخ
</h2>

<p>
	يمكن أيضًا إنشاء مهام جديدة واستخلاصها من مهمة أخرى وتحديد خصائص معينة لها. يمكن استخدام نوع المهمة <code>Copy</code> لتحديد هذه المهمة، والتي تكون قادرة على نسخ الملفات.
</p>

<p>
	إنشاء مشروع جديد باستخدام ملف build.gradle التالي:
</p>

<pre class="ipsCode">
task copyFile(type: Copy) {
    from 'source'
    into 'destination'
}
</pre>

<p>
	قم بإنشاء مجلد مصدر داخل هذا المشروع وأضف ملف نصي إلى هذا المجلد. عند تشغيل مهمة <code>copyFile</code>، تقوم بنسخ الملف النصي إلى مجلد وجهة جديد.
</p>

<h2>
	13. تمرين: تحديد مهمة مخصصة في ملف gradle آخر
</h2>

<p>
	إنشاء مشروع Gradle جديد، والذي يحتوي على الهيكل التالي.
</p>

<p style="text-align: center;">
	<img alt="7-CustomeGradle.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36325" data-unique="yuxqdeeqr" src="https://academy.hsoub.com/uploads/monthly_2020_04/7-CustomeGradle.PNG.4b41ab7fd8d2833c991ed85056e98512.PNG"></p>

<p>
	تبدو الفئة <em>CheckWebsite.groovy</em> كما يلي:
</p>

<pre class="ipsCode">
package com.example

import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

class CheckWebsite extends DefaultTask {

    String url = 'http://www.vogella.com'

    @TaskAction
    void checkWebsite() {
        // check the given website by using the url
    try {
        Document doc = Jsoup.connect(url).get();
        String title = doc.title();
        println title
        println url
    } catch (IOException e) {
        e.printStackTrace();
    }
    }
}
</pre>

<p>
	نظرًا لأن هذا الصنف-Class بها تبعيات خارجية لـ jsoup، يجب إنشاء ملف build.gradle لها. لذلك فإن <em>build.gradle</em> داخل مجلد <em>buildSrc</em>، المسؤول عن بناء فئة <code>CheckWebsite</code>، يبدو كما يلي:
</p>

<pre class="ipsCode">
plugins {
    id 'groovy'
}

repositories {
   jcenter()
}

dependencies {
    compile 'org.jsoup:jsoup:1.8.3'
}
</pre>

<p>
	أخيرًا، يستخدم ملف <em>build.gradle</em> الرئيسي في المجلد الرئيسي نوع مهام جديدة مثل <code>com.example.CheckWebsite</code>.
</p>

<pre class="ipsCode">
task defaultWebsiteCheck(type: com.example.CheckWebsite)

task checkGradleWebsite(type: com.example.CheckWebsite) {
    url = 'https://docs.gradle.org/'
}

wrapper {
    gradleVersion = '4.9'
}
</pre>

<h2>
	14. تمرين: بناء Gradle Trigger من كود جافا
</h2>

<p>
	يصف هذا التمرين كيفية تشغيل إنشاء gradle من تعليمات كود جافا.
</p>

<h3>
	14.1. إنشاء مشاريع جديدة لـGradle
</h3>

<p>
	قم بإنشاء مشروعين gradle جديدين بأسماء <em>BaseProject</em> (يبدأ هذا المشروع إنشاء gradle) و <em>TargetProject</em> (تم بناء هذا المشروع بواسطة BaseProject). تأكد من أن BaseProject يطبق البرنامج المساعد جافا Java blugin.
</p>

<h3>
	14.2. إضافة التبعيات
</h3>

<p>
	أضِف التبعية التالية إلى BaseProject.
</p>

<pre class="ipsCode">
compile 'org.gradle:gradle-tooling-api:4.0-rc-2'
</pre>

<h3>
	14.3. إنشاء TargetProject
</h3>

<p>
	أُنشئ Class تحت إسم Application بطريقة <code>static main</code> كما يلي:
</p>

<pre class="ipsCode">
import java.io.File;

import org.gradle.tooling.BuildLauncher;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;

public class Application {

    public static void main(String[] args) {
        ProjectConnection connection = GradleConnector.newConnector().forProjectDirectory(new File("path/to/targetproject")).connect();
        try {
            BuildLauncher build = connection.newBuild();
            build.forTasks("build");
            build.run();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connection.close();
        }
    }
}
</pre>

<p>
	تقوم هذه الطريقة أولاً بإنشاء ProjectConnection للمشروع الذي يجب إنشاؤه ويتصل به. تأكد من استبدال path/to/targetproject بمسار TargetProject. من project connection، يمكن الحصول على BuildLauncher جديد. بمساعدة الدالة <code>()forTasks</code> يمكنك تحديد مهام gradle التي يجب تنفيذها. يوفر BuildLauncher أيضًا بعض الطرق الأخرى لتكوين البناء. يمكنك، على سبيل المثال، تعيين عوامل بناء gradle أو تغيير إصدار Java لإنشاء المشروع بها. وعن طريق استدعاء الدالة <code>()run</code> يتم تنفيذ البناء أخيرًا. تأكد من إغلاق الاتصال في البلوك الأخير.
</p>

<h2>
	15.بناء مشاريع جافا
</h2>

<h3>
	15.1. البريمج التكميلي جافا Java plug-in
</h3>

<p>
	يوفر البريمج التكميلي Java مهامًا لتجميع كود Java وتشغيل اختبارات الوحدة وإنشاء Javadoc وإنشاء ملف JAR.
</p>

<h3>
	15.2. تخطيط المشروع الافتراضي لمشاريع Java
</h3>

<p>
	تفترض هذه المكونات الإضافية إعدادًا معينًا لمشروع Java الخاص بك (على غرار Maven).
</p>

<ul>
<li>
		src/main/java يحتوي على كود جافا الأساسي.
	</li>
	<li>
		src/test/java يحتوي على اختبارات جافا.
	</li>
</ul>
<p>
	إذا اتبعت هذا الإعداد، فسيكون ملف البناء التالي كافياً لتجميع مشروع Java واختباره وحزمه.
</p>

<pre class="ipsCode">
apply plugin: 'java'
</pre>

<p>
	لبدء التنفيذ، اكتب <code>gradle build</code> في سطر الأوامر.
</p>

<p>
	يمكن استخدام <code>SourceSets</code> لتحديد بنية مشروع مختلفة، على سبيل المثال، يتم تخزين المصادر في مجلد src بدلاً من src/main/java.
</p>

<pre class="ipsCode">
apply plugin: 'java'

sourceSets {
    main {
         java {
              srcDir 'src'
              }
         }
    test {
         java {
              srcDir 'test'
              }
         }
}
</pre>

<h3>
	15.3. إنشاء مشروع Java باستخدام مهمة init
</h3>

<p>
	لا يدعم Gradle بعد قوالب مشاريع متعددة (تسمى النماذج الأولية archetypes) مثل Maven. لكنه يوفر مهمة <code>init</code> لإنشاء هيكل مشروع Gradle جديد. بدون عوامل إضافية، تنشئ هذه المهمة مشروع Gradle، والذي يحتوي على ملفات <em>gradle wrapper</em> وملف <em>build.gradle</em> و <em>settings.gradle</em>.
</p>

<p>
	عند إضافة المعامل <code>type--</code> مع <code>'java-library'</code> كقيمة، يتم تنفيذ بناء مشروع java ويحتوي ملف <em>build.gradle</em> على قالب Java معين مع JUnit.
</p>

<p style="text-align: center;">
	<img alt="8-JavaProjectUsingInitFunction.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36326" data-unique="h297qdudn" src="https://academy.hsoub.com/uploads/monthly_2020_04/8-JavaProjectUsingInitFunction.PNG.1d195b0534b7e199df0efcfb2be04a01.PNG"></p>

<p>
	سيبدو ملف build.gradle مشابهًا لهذا:
</p>

<pre class="ipsCode">
/*
 * ... deleted the generated text for brevity
 */

// Apply the java plugin to add support for Java
apply plugin: 'java'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> at compile time
    compile 'org.slf4j:slf4j-api:1.7.12'

    // Declare the dependency for your favourite test framework you want to use
    // TestNG is also supported by the Gradle Test task. Just change the
    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
    // 'test.useTestNG()' to your build script.
    testCompile 'junit:junit:4.12'
}
</pre>

<p>
	يوفر المشروع المستضاف على Github والذي يُدعى <a href="https://github.com/townsfolk/gradle-templates" rel="external nofollow">Gradle-Templates Project</a> مزيدًا من القوالب تتجاوز مهمة <code>init</code>. يعمل فريق Gradle أيضًا على موضوع النموذج/القالب هذا.
</p>

<p>
	عادةً ما يحتوي مشروع Java على إصدار و JRE مُستهدف يتم تجميعه عليه. يمكن تعيين خاصية <code>version</code> و <code>sourceCompatibility</code> في ملف <em>build.gradle</em>.
</p>

<pre class="ipsCode">
version = 0.1.0
sourceCompatibility = 1.8
</pre>

<p>
	عند تعيين خاصية الإصدار، سيتم تغيير اسم الناتج وفقًا لذلك، على سبيل المثال، my-lib-name} -0.1.0.jar}
</p>

<p>
	إذا كان الناتج عبارة عن تطبيق جافا قابل للتنفيذ، فيجب أن يكون ملف MANIFEST.MF على دراية بالصنف Class باستخدام الدالة main.
</p>

<pre class="ipsCode">
apply plugin: 'java'

jar {
    manifest {
        attributes 'Main-Class': 'com.example.main.Application'
    }
}
</pre>

<h2>
	16. بناء مشاريع Groovy
</h2>

<h3>
	16.1. البريمج التكميلي جروفي Groovy Plugin
</h3>

<p>
	يعمل البريمج التكميلي Groovy في Gradle على تمديد البريمج التكميلي Java ويوفر مهامًا لبرامج Groovy.
</p>

<pre class="ipsCode">
apply plugin: 'groovy'

repositories {
  mavenCentral()
}
dependencies {
    implementation 'org.codehaus.groovy:groovy-all:2.4.5'
    testImplementation 'junit:junit:4.12'
}
</pre>

<p>
	لكي تبدأ البناء، اكتب <code>gradle build</code> في سطر الأوامر.
</p>

<h3>
	16.2. الشكل الافتراضي لمشاريع Groovy
</h3>

<p>
	تفترض هذه البريمجات التكميلية
</p>

<ul>
<li>
		src/main/groovy يحتوي على الكود الأصلي لـ Groovy.
	</li>
	<li>
		src/test/groovy يحتوي على اختبارات Groovy.
	</li>
	<li>
		src/main/java يحتوي على الكود الأصلي لـ Java.
	</li>
	<li>
		src/test/java يحتوي على اختبارات Java.
	</li>
</ul>
<p>
	إذا تتبّعت هذا الإعداد، يكون ملف البناء التالي كافياً لتجميع مشروع Groovy واختباره وتعبئته.
</p>

<h2>
	17. اختبارات مع Gradle
</h2>

<h3>
	17.1. تنفيذ 5 اختبارات JUnit مع Gradle
</h3>

<p>
	لاستخدام 5 اختبارات جافا، أضف ما يلي إلى نهاية الـ <code>dependencies</code> في ملف 'build.gradle` الخاص بك. استخدم Gradle 6.0 على الأقل لهذا لتجنب المشكلات التي تم إصلاحها بالفعل.
</p>

<pre class="ipsCode">
dependencies {

    // more stuff
    testImplementation(enforcedPlatform("org.junit:junit-bom:5.4.0")) // JUnit 5 BOM
    testImplementation("org.junit.jupiter:junit-jupiter")
}
</pre>

<h3>
	17.2. اختبار اصطلاحات التسمية لـ Gradle
</h3>

<p>
	تفحص مهمة "test" في Gradle جميع الفئات المترجمة في المجلد المصدر للمشروع، على سبيل المثال،
</p>

<p>
	src/test/java أو /src/test/groovy/. يتم تحديد أصناف JUnit بواسطة:
</p>

<ul>
<li>
		<p>
			Class أو Super Class يوسِّع TestCase أو GroovyTestCase.
		</p>
	</li>
	<li>
		<p>
			الـ Class أو Super Class يُرمز أليهم بـ RunWith@.
		</p>
	</li>
	<li>
		<p>
			يحتوي الـ Class أو Super Class على دالة يُرمز إليها بـ Test@.
		</p>
	</li>
</ul>
<p>
	يمكنك تعيين خاصية <code>scanForTestClasses</code> إلى false، إذا كنت لا تريد الكشف التلقائي عن فئة الاختبار. في هذه الحالة، إذا لم يتم تحديد أنماط تضمين/استبعاد إضافية، فإن الإعدادات الافتراضية للفئات المضمنة هي "Tests.class”, “/*Test.class/” والفئات المُستثناة الافتراضية هي"Abstract * .class /*".
</p>

<h3>
	17.3. تضمين واستبعاد اختبارات معينة
</h3>

<p>
	يتم وصف تكوين الاختبار بشكل عام في وصف مهام اختبار جرادل <a href="https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html" rel="external nofollow">Gradle Test Tasks Description</a>.
</p>

<p>
	فئة الاختبار <code>teast</code> Class لديها دوال <code>include</code> و<code>exclude</code>. يمكن استخدام هذه الدوال لتحديد الاختبارات التي يجب تشغيلها بالفعل.
</p>

<p>
	تشغيل الاختبارات المضمنة included tests فقط:
</p>

<pre class="ipsCode">
test {
    include '**my.package.name/*'
}
</pre>

<p>
	تخطي الاختبارات المستبعدة excluded tests:
</p>

<pre class="ipsCode">
test {
    exclude '**my.package.name/*'
}
</pre>

<h3>
	17.4. إظهار كل نواتج الاختبار في الطرفية Terminal
</h3>

<p>
	بشكل افتراضي، لا يقوم Gradle بطباعة الناتج القياسي لاختباراتك على الطرفية Terminal. لمشاهدة جميع مخرجات اختباراتك، أضف هذا إلى ملف البناء الخاص بك:
</p>

<pre class="ipsCode">
test {
    testLogging.showStandardStreams = true
}
</pre>

<h2>
	18. بناء مشاريع متعددة مع Gradle
</h2>

<h3>
	18.1. إنشاء هيكل بناء مشروع متعدد
</h3>

<p>
	عادةً لا يتألف تطبيق الأعمال من مشروع / وحدة واحدة فقط ، ولكن يحتوي على العديد من المشاريع، والتي يجب أن يتم بناؤها.
</p>

<p>
	لدى Gradle مفهوم المشروع الأساسي The root project، والذي يمكن أن يحتوي على العديد من المشاريع الفرعية. يتم تحديد المشروع الأساسي بواسطة ملف <em>build.gradle</em>، مثل المشاريع الفردية من قبل. لتحديد، ما هي المشاريع التي تنتمي إلى البناء يتم استخدام ملف <em>settings.gradle</em>.
</p>

<p>
	على سبيل المثال، قد يكون هناك هيكل المشروع هذا:
</p>

<ul>
<li>
		<p>
			root_project
		</p>
	</li>
	<li>
		<p>
			core
		</p>
	</li>
	<li>
		<p>
			ui
		</p>
	</li>
	<li>
		<p>
			util
		</p>
	</li>
	<li>
		<p>
			settings.gradle
		</p>
	</li>
</ul>
<p>
	وجود بنية المشروع هذا، سيبدو ملف الإعدادات settings.gradle هكذا:
</p>

<pre class="ipsCode">
include 'core', 'ui', 'util'

# altenative way would be
#include 'core'
#include 'ui'
#include 'util'
</pre>

<p>
	إلى جانب المهمة <code>tasks</code>، يوفر Gradle أيضًا المهمة المساعدة <code>projects</code>، والتي يمكن تنفيذها في المجلد <em>root_project</em>.
</p>

<pre class="ipsCode">
&gt; gradle projects
</pre>

<p style="text-align: center;">
	<img alt="9-RootProject.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36327" data-unique="476a4oe0e" src="https://academy.hsoub.com/uploads/monthly_2020_04/9-RootProject.PNG.3246e0df8d0c93be1ff31c67808b516e.PNG"></p>

<h3>
	18.2. تحديد تكوين بناء عام
</h3>

<p>
	في ملف <em>build.gradle</em> في التكوينات العامة root_project يمكن تطبيقها على جميع المشاريع أو على المشروعات الفرعية فقط.
</p>

<pre class="ipsCode">
allprojects {
    group = 'com.example.gradle'
    version = '0.1.0'
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'eclipse'
}
</pre>

<p>
	يحدد هذا مجموعة <code>com.example.gradle</code> مشتركة وإصدار <code>0.1.0</code> لجميع المشاريع.
</p>

<p>
	تُطبِّق نهاية المشروعات الفرعية تكوينات شائعة على جميع المشروعات الفرعية، ولكن ليس على المشروع الأصلي، كما هو الحال في نهاية <code>all projects</code>.
</p>

<h3>
	18.3. تكوينات محددة المشروع والتبعيات
</h3>

<p>
	يمكن أن يكون للـ core و ui و util (هيكل الـ root project في المشروع كما وضحنا ذلك في الجزء 19.1) الخاصين بالمشروعات الفرعية ملف build.gradle خاص بها أيضًا. إذا كانت لديهم احتياجات محددة، والتي لم يتم تطبيقها بالفعل عن طريق التكوين العام للمشروع الأصلي.
</p>

<p>
	على سبيل المثال، عادةً ما يكون لمشروع واجهة المستخدم ui تبعية للمشروع الأساسي. لذا يحتاج مشروع واجهة المستخدم إلى ملف <em>build.gradle</em> الخاص به لتحديد هذه التبعية.
</p>

<pre class="ipsCode">
dependencies {
    compile project(':core')
    compile 'log4j:log4j:1.2.17'
}
</pre>

<p>
	يتم تحديد تبعيات المشروع مع دالة المشروع project method.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		الـ <code>:</code> قبل أن يكون مرجع المشروع الأساسي فهو خاص بـ Gradle ويعمل إلى حد كبير مثل شرطة مائلة (<code>/</code>) ، عند الرجوع إلى هيكل المجلد.
	</p>
</blockquote>

<p>
	بدلاً من ذلك، يمكنك أيضًا تحديد تبعيات مشروع في ملف build.gradle الأصلي. ولكن يعتبر ممارسة جيدة لتحديد التبعيات في ملفات build.gradle المحدّدة الخاصة بالمشروع، وبالتالي يتم تضمين النهج التالي فقط لغرض العرض التوضيحي.
</p>

<pre class="ipsCode">
allprojects {
 apply plugin: 'java'

 repositories {
  mavenCentral()
 }

}

project(':com.example.core').dependencies {
    compile project(':com.example.model')
    compile 'log4j:log4j:1.2.17'
}
</pre>

<h2>
	19. النشر مع Gradle
</h2>

<h3>
	19.1. كيفية النشر باستخدام Gradle
</h3>

<p>
	يوفر Gradle عدة طرق لنشر عناصر البناء في مستودعات الإنتاج، مثل <a href="https://www.jfrog.com/open-source" rel="external nofollow">Artifactory</a> أو <a href="http://www.sonatype.org/nexus" rel="external nofollow">Sonatyp Nexus</a>.
</p>

<h3>
	19.2. باستخدام المافن نشر البريمج التكميلي Using the maven-publish plugin
</h3>

<p>
	الطريقة الأكثر شيوعًا هي استخدام المكون الإضافي <code>maven-publish</code>، والذي يوفره Gradle افتراضيًا.
</p>

<pre class="ipsCode">
// other plug-ins
apply plugin: 'maven-publish'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }

    repositories {
        maven {
            url "$buildDir/repo"
        }
    }
}
</pre>

<p>
	هناك العديد من خيارات النشر ، عندما يتم تطبيق <code>java</code> والمكون الإضافي <code>maven-publish</code>.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36328" href="https://academy.hsoub.com/uploads/monthly_2020_04/10-GradleDeployment.PNG.764cf10818c220bbc4e43851169ff8b3.PNG" rel=""><img alt="10-GradleDeployment.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36328" data-unique="u8pfzbz97" src="https://academy.hsoub.com/uploads/monthly_2020_04/10-GradleDeployment.thumb.PNG.fbb9bcccd03964d100d6aff7620eafa2.PNG"></a>
</p>

<p>
	يمكن إجراء النشر إلى مستودع منفصل مثل هذا:
</p>

<pre class="ipsCode">
apply plugin: 'groovy'
apply plugin: 'maven-publish'

group 'workshop'
version = '1.0.0'

publishing {
    publications {
        mavenJava(MavenPublication) { from components.java }
    }

    repositories {
        maven {
            // default credentials for a nexus repository manager
            credentials {
                username 'admin'
                password 'admin123'
            }
            // url to the releases maven repository
            url "http://localhost:8081/nexus/content/repositories/releases/"
        }
    }
}
</pre>

<p>
	يمكن العثور على مزيد من المعلومات حول النشر إلى مستودع Maven للإنتاج هنا: <a href="https://docs.gradle.org/current/userguide/publishing_maven.html" rel="external nofollow">Publish to Maven repository with Gradle</a>.
</p>

<h2>
	20. التكامل مع Ant
</h2>

<p>
	يدعم Gradle تشغيل مهام Ant عبر برنامج Groovy AntBuilder.
</p>

<h2>
	21. تحويل مشاريع Groovy إلى Gradle
</h2>

<p>
	يوفر Gradle مهمة <code>init</code> المحتضنة، والتي تساعد في إنشاء مشاريع Gradle جديدة. يمكن لهذه المهمة أيضًا تحويل ملفات Apache Maven pom.xml إلى ملفات بناء Gradle، إذا كانت جميع المكونات الإضافية المستخدمة في Maven معروفة لهذه المهمة.
</p>

<p>
	في هذا الجزء، سيتم تحويل Groovy pom.xml التالي إلى مشروع Gradle.
</p>

<pre class="ipsCode">
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;com.example.app&lt;/groupId&gt;
    &lt;artifactId&gt;example-app&lt;/artifactId&gt;
    &lt;packaging&gt;jar&lt;/packaging&gt;
    &lt;version&gt;1.0.0-SNAPSHOT&lt;/version&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;4.11&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/project&gt;
</pre>

<p>
	يؤدي تشغيل <code>gradle init --type pom</code> في سطر الأوامر إلى تكوين Gradle الآتي.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36329" href="https://academy.hsoub.com/uploads/monthly_2020_04/11-ConvertMavenToGradle.PNG.064d534cd856f15fda728821e348a1f9.PNG" rel=""><img alt="11-ConvertMavenToGradle.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36329" data-unique="mv3xtocfl" src="https://academy.hsoub.com/uploads/monthly_2020_04/11-ConvertMavenToGradle.thumb.PNG.fa9551b18d62a16cae5bf99ec9a4bad4.PNG"></a>
</p>

<p>
	تعتمد مهمة <code>init</code> على مهمة Wrapper بحيث يتم أيضًا إنشاء Gradle Wrapper.
</p>

<p>
	يشبه ناتج ملف build.gradle الآتي:
</p>

<pre class="ipsCode">
apply plugin: 'java'
apply plugin: 'maven'

group = 'com.example.app'
version = '1.0.0-SNAPSHOT'

description = """"""

sourceCompatibility = 1.5
targetCompatibility = 1.5

repositories {
     maven { url "http://repo.maven.apache.org/maven2" }
}

dependencies {
    testImplementation group: 'junit', name: 'junit', version:'4.11'
}
</pre>

<h2>
	22. تطوير مكونات Gradle الإضافية Gradle Plug-ins
</h2>

<h3>
	22.1. لماذا نُنشيء مكونات Gradle الإضافية؟
</h3>

<p>
	كقاعدة عامة، من المفيد أن يكون هناك بناءً تصريحيًا declarative build بأكبر قدر ممكن لأن هذا يُبسِّط التعديلات المستقبلية. لذلك يُنصح بتجنب التعليمات البرمجية المعقدة في ملف بناء Gradle الخاص بك. إذا كنت بحاجة إلى منطق مخصص، فيجب وضعه في مكون إضافي مخصص لـ Gradle.
</p>

<h3>
	22.2. Gradle DSL
</h3>

<p>
	يأتي كل مكون إضافي من Gradle مزودًا بخدمة DSL. لمشاهدة جميع خصائص كائن Gradle، يمكنك استخدام مقتطف التالي من الكود:
</p>

<pre class="ipsCode">
println variants.properties
            .sort{it.key}
            .collect{it}
            .findAll{!filtered.contains(it.key)}
            .join('\n')
</pre>

<p>
	على سبيل المثال، لتحديد المهام التي تعرض جميع خصائص <code>android.applicationVariants</code> (في مشروع أندرويد)، استخدم:
</p>

<pre class="ipsCode">
task showAndoidVariantsInformation {
    doLast {
        android.applicationVariants.all { variants -&gt;
            println variants.properties
            .sort{it.key}
            .collect{it}
            .findAll{!filtered.contains(it.key)}
            .join('\n')
        }
    }
}
</pre>

<h2>
	23. تمرين: إنشاء برنامجًا مساعدًا بسيطًا Simple Gradle Plugin
</h2>

<p>
	تعمل الأداة الإضافية java-gradle-plugin على تبسيط إنشاء مكونات إضافية مخصصة لـ Gradle. هذا البرنامج المساعد في طور الإعداد حاليًا. يفعل ما يأتي:
</p>

<ul>
<li>
		تتم إضافة التابع ()gradleApi تلقائيًا.
	</li>
	<li>
		تتم إضافة التابع ()gradleTestKit تلقائيًا.
	</li>
	<li>
		تتم إضافة ملفات واصف المكونات الإضافية الضرورية تلقائيًا.
	</li>
</ul>
<h3>
	23.1. إنشاء مشروع Gradle
</h3>

<p>
	حدِّد Gradle Project &lt; Gradle &lt; Other &lt; new &lt; File في Eclipse لإنشاء مشروع Gradle جديد. ضع <em>com.vogella.gradleplugin</em> ليكون اسم المشروع كما هو موضّح.
</p>

<p style="text-align: center;">
	<img alt="12-NewGradleProject.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36330" data-unique="frp50675m" src="https://academy.hsoub.com/uploads/monthly_2020_04/12-NewGradleProject.PNG.2998884fc13d51e261438c33c4334363.PNG"></p>

<p>
	التزم بالإعدادات الافتراضية للمعالج وقم بإنشاء المشروع.
</p>

<h3>
	23.2. قم بتطبيق المكون الإضافي "java-gradle-plugin"
</h3>

<p>
	غيِّر ملف <code>build.gradle</code> إلى ما يلي:
</p>

<pre class="ipsCode">
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        vogellaPlugin {
            id = 'com.vogella.gradleplugin'
            implementationClass = 'com.vogella.gradleplugin.MyPlugin'
        }
    }
}

repositories {
    jcenter()
}

dependencies {
    // No need to add gradleApi() here, because it is applied by the 'java-gradle-plugin' plug-in

    // We want to merge and parse SpotBugs xml files with XSLT
    compile('net.sf.saxon:Saxon-HE:9.8.0-12')
    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}

wrapper {
    gradleVersion = '4.9'
}
</pre>

<p>
	في مجلد src/main/java/، قم بإنشاء الصنفين Classes التاليتين.
</p>

<pre class="ipsCode">
package com.vogella.gradleplugin;

import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;

public class MyTask extends DefaultTask {

    @TaskAction
    public void myTask() {
        System.out.println("Hello from vogella task");
    }
}
</pre>

<pre class="ipsCode">
package com.vogella.gradleplugin;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

public class MyPlugin implements Plugin&lt;Project&gt; {

    @Override
    public void apply(Project project) {
        project.getTasks().create("myTask", MyTask.class);
    }

}
</pre>

<p>
	قم بتنفيذ المهمة <code>build</code> لإنشاء مكون إضافي وانظر التدريبات التالية حول كيفية نشر المكون الإضافي واستخدامه.
</p>

<h2>
	24. تمرين: قم بنشر المكونات الإضافية المخصصة لـ Gradle في مستودع Maven المحلي
</h2>

<p>
	أضف المكوّن الإضافي Gradle-plugin ومغلِّف النشر publishing closur إلى ملف build.gradle.
</p>

<pre class="ipsCode">
plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
}

group = 'com.vogella'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

// ... more code

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}
</pre>

<p>
	تتوفر الآن مهام نشر إضافية ويمكن استخدام مهمة publishToMavenLocal لإضافة مكون Gradle الإضافي إلى مستودع Maven المحلي.
</p>

<pre class="ipsCode">
./gradlew pTML
</pre>

<h2>
	25. تمرين: استخدام المكون الإضافي الجديد
</h2>

<p>
	لاستخدام مكون Gradle الجديد، يجب تحديد تبعية له. إذا دفعت مكونك الإضافي إلى مستودع Maven المحلي، فيجب على Gradle العثور عليه وإتاحته.
</p>

<pre class="ipsCode">
buildscript {
    repositories {
        mavenLocal()
    }
    dependencies {
        classpath 'com.vogella:com.vogella.gradleplugin:0.0.1-SNAPSHOT'
    }
 }

apply plugin: 'com.vogella.gradleplugin'
</pre>

<p>
	الآن المهمة الجديدة من com.vogella.gradleplugin يجب أن تكون متاحة:
</p>

<pre class="ipsCode">
./gradlew tasks

./gradlew myTask
</pre>

<h2>
	26. تمرين: -اختياري- نشر المكوّن الإضافي في مدخل المكوّن الإضافي جرادل Gradle Plug-in Portal
</h2>

<p>
	لنشر مكون إضافي من نوع Gradle في مدخل Gradle Plug-in، يمكن استخدام <em>com.gradle.plugin-publish</em>.
</p>

<p>
	قبل تحميل مكونات Gradle الإضافية إلى البوابة ، يجب عليك التسجيل على <a href="https://plugins.gradle.org/user/register" ipsnoembed="false" rel="external nofollow">https://plugins.gradle.org/user/register</a> والحصول على مفاتيح api من ملفك الشخصي.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="36331" href="https://academy.hsoub.com/uploads/monthly_2020_04/13-PublishingGradlePluginToGradlePluginPortal.PNG.6faf1c295701768757c89e35e4b9f1c7.PNG" rel=""><img alt="13-PublishingGradlePluginToGradlePluginPortal.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36331" data-unique="7sm0bkjd6" src="https://academy.hsoub.com/uploads/monthly_2020_04/13-PublishingGradlePluginToGradlePluginPortal.thumb.PNG.1e93c6b656a23bd72a6d1abe21f34d85.PNG"></a>
</p>

<p>
	يجب إضافة خصائص <em>gradle.publish.key</em> و <em>gradle.publish.secret</em> إلى <em>gradle.properties</em>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		عادةً ما يكون ملف <em>gradle.properties</em> موجودًا في (usually <em>/${user.home}/.gradle</em>)
	</p>
</blockquote>

<p>
	بعد ذلك ، يجب تعديل ملف <em>build.gradle</em> ليتمكن من تحميل مكونات Gradle الإضافية.
</p>

<pre class="ipsCode">
plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
    id 'com.gradle.plugin-publish' version '0.9.10'
}

// more code ...

pluginBundle {
    website = '${Web site for your plugin}'
    vcsUrl = 'https://github.com/${your-repo}'

    plugins {
        vogellaPlugin {
            id = 'com.vogella.gradleplugin'
            displayName = 'Vogella Sample Plug-in'
            description = 'Vogella Sample Plug-in for trying the '
            tags = ['Vogella','Training','Gradle','Sample']
            // Gradle's plug-in portal does not support SNAPSHOTs
            version = '0.0.1'
        }
    }
}
</pre>

<p>
	يمكن بعد ذلك استخدام المهمة التالية لتحميل المكوّن الإضافي Gradle.
</p>

<pre class="ipsCode">
./gradlew publishPlugins
</pre>

<p>
	عند نشر المكون الإضافي، يمكن استخدام نهايات المكونات الإضافية للاستفادة من مكون Gradle الإضافي.
</p>

<pre class="ipsCode">
plugins {
  id "com.vogella.gradleplugin" version "0.0.1"
}
</pre>

<p>
	لذلك يمكن حذف النهايات المطوّلة بشكل أكبر وأيضًا دالة المكوِّن الإضافي apply من الفصول السابقة.
</p>

<p>
	المزيد من التفاصيل تجدها <a href="https://guides.gradle.org/publishing-plugins-to-gradle-plugin-portal/" rel="external nofollow">هنا</a>.
</p>

<h2>
	27. تصحيح إضافات غرادل Gradle Plug-ins
</h2>

<h3>
	27.1. تنشيط تصحيح الأخطاء عن بُعد
</h3>

<p>
	يجب تحديد الخصائص التالية في ملف gradle.properties لتمكين تصحيح الأخطاء عن بُعد:
</p>

<pre class="ipsCode">
org.gradle.daemon=true
org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -Xmx4g -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006
</pre>

<p>
	عند تشغيل بناء Gradle محليًا باستخدام هذه الإعدادات، يمكن الوصول إلى تصحيح الأخطاء عن بُعد عبر مضيف محلي localhost على المنفذ 5006.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		عادةً ما يكون ملف gradle.properties موجودًا في gradleHomeDir (عادةً <em>user.home}/.gradle}$/</em>)
	</p>
</blockquote>

<h3>
	27.2. تصحيح الأخطاء عن بُعد في Eclipse IDE
</h3>

<p>
	يجب عليك إدخال مكون إضافي معين إلى Eclipse IDE باستخدام أدوات Buildship. بعد ذلك، يمكنك إضافة نقاط التوقف break points إلى الملفات الأساسية للمكون الإضافي.
</p>

<p>
	بعد ذلك، افتح تكوين تصحيح الأخطاء Debug Configuration وانقر بزر الفأرة الأيمن فوق Remote Java Application لإنشاء Debug Configuration جديد باستخدام الإعدادات التالية:
</p>

<p style="text-align: center;">
	<img alt="14-RemoteDebuggingInEclipse.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36332" data-unique="3euf8nkmr" src="https://academy.hsoub.com/uploads/monthly_2020_04/14-RemoteDebuggingInEclipse.PNG.210d9fa0a76f182a0017746f925de2c8.PNG"></p>

<p>
	اضغط على الزر Debug لتشغيل مصحح الأخطاء عن بُعد remote debugger.
</p>

<p style="text-align: center;">
	<img alt="15-Debug.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36333" data-unique="zfdktkcou" src="https://academy.hsoub.com/uploads/monthly_2020_04/15-Debug.PNG.53ea11426b20a44398c08f0ce45684ca.PNG"></p>

<p>
	بعد ذلك، يمكن تشغيل بناء Gradle، الذي يستخدم مكون Gradle الإضافي المطلوب، إما باستخدام طريقة عرض مهام Gradle لأداة Buildship داخل Eclipse IDE أو استدعاء بناء Gradle من سطر الأوامر.
</p>

<p>
	عند الوصول إلى نقطة توقف أثناء مهمة Gradle، فإن Eclipse IDE سوف يتوقف عند هذه النقطة.
</p>

<p style="text-align: center;">
	<img alt="16-BreakPoint.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="36334" data-unique="d4blcsxw3" src="https://academy.hsoub.com/uploads/monthly_2020_04/16-BreakPoint.PNG.6c45d0c23c293a5bcb2f0eef1de9c1ea.PNG"></p>

<h2>
	28. استخدام أدوات تحليل الكود
</h2>

<p>
	يوفر Gradle عدة مكونات إضافية لتحليل قاعدة الكود لمشروع Gradle.
</p>

<p>
	الجدول 1. أدوات تحليل كود Gradle
</p>

<table>
<thead><tr>
<th style="text-align:center">
				الأداة
			</th>
			<th style="text-align:center">
				Plug-in
			</th>
			<th style="text-align:center">
				الوصف
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:center">
				<a href="https://docs.gradle.org/current/userguide/checkstyle_plugin.html" rel="external nofollow">Checkstyle</a>
			</td>
			<td style="text-align:center">
				checkstyle
			</td>
			<td style="text-align:center">
				التحقق من صحة قواعد checkstyle، والتي يتم تطبيقها على المشروع.
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<a href="https://docs.gradle.org/current/userguide/jacoco_plugin.html" rel="external nofollow">Jacoco</a>
			</td>
			<td style="text-align:center">
				jacoco
			</td>
			<td style="text-align:center">
				يتحقق من تغطية الاختبار للكود الذي يجري بناؤه.
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<a href="https://docs.gradle.org/current/userguide/findbugs_plugin.html" rel="external nofollow">FindBugs</a>
			</td>
			<td style="text-align:center">
				findbugs
			</td>
			<td style="text-align:center">
				تحليل الكود الثابت للجافا.
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<a href="https://docs.gradle.org/current/userguide/codenarc_plugin.html" rel="external nofollow">CodeNarc</a>
			</td>
			<td style="text-align:center">
				codenarc
			</td>
			<td style="text-align:center">
				تحليل الكود الثابت لـGroovy.
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<a href="https://docs.gradle.org/current/userguide/pmd_plugin.html" rel="external nofollow">PMD</a>
			</td>
			<td style="text-align:center">
				pmd
			</td>
			<td style="text-align:center">
				يضيف اختبارات جودة الكود لعدة لغات برمجة.
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<a href="https://docs.gradle.org/current/userguide/jdepend_plugin.html" rel="external nofollow">JDepend</a>
			</td>
			<td style="text-align:center">
				jdepend
			</td>
			<td style="text-align:center">
				أداة تحليل التعليمات البرمجية الأخرى لتحليل التبعيات في كود الجافا.
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
}

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<h3>
	28.1. Jcoco لمشاريع Gradle
</h3>

<p>
	لاستخدام Jacoco لتحليل مدى تغطية الاختبار-Code Coverage، يجب إضافة الكود التالي إلى ملف build.gradle ذي المستوى الأعلى.
</p>

<pre class="ipsCode">
plugins {
    id 'jacoco'
}

jacocoTestReport {
    reports {
        xml.enabled true
        html.enabled true
    }
}
</pre>

<p>
	إذا كان لديك مشروع Gradle متعدد المشروعات، فأنت بحاجة إلى إضافة jacocoTestReport والمكون الإضافي jacoco إلى قسم المشاريع الفرعية في ملف build.gradle ذي المستوى الأعلى.
</p>

<pre class="ipsCode">
plugins {
    id 'jacoco'
}

subprojects {

    apply plugin: 'jacoco'

    jacocoTestReport {
        reports {
            xml.enabled true
            html.enabled true
        }
    }
}
</pre>

<p>
	إذا كنت ترغب في الحصول على ملف xml مدمج لجميع المشروعات الفرعية، على سبيل المثال لاستخدام SonarQube، يمكنك إنشائه بالمهمة التالية في build.gradle ذي المستوى الأعلى.
</p>

<pre class="ipsCode">
task generateMergedReport(type: JacocoReport) {
    dependsOn = subprojects.test

    additionalSourceDirs.setFrom files(subprojects.sourceSets.main.allSource.srcDirs)
    sourceDirectories.setFrom files(subprojects.sourceSets.main.allSource.srcDirs)
    classDirectories.setFrom files(subprojects.sourceSets.main.output)

    executionData.setFrom project.fileTree(dir: '.', include: '**/build/jacoco/test.exec')

    reports {
        xml.enabled true
        xml.destination file("../coverage-reports/coverage.xml")
    }
}
</pre>

<p>
	ستقوم هذه المهمة بحفظ ملف XML الموحد في المستوى العلوي من المشروع ضمن تقارير التغطية.
</p>

<p>
	أخيرًا لإنشاء ملف xml مدمج، قم بتشغيل Gradle باستخدام المهمة التي تم إنشاؤها generMergedReport.
</p>

<pre class="ipsCode">
./gradle clean build generateMergedReport
</pre>

<h2>
	29. Gradle
</h2>

<ul>
<li>
		<p>
			<a href="http://mrhaki.blogspot.de/search/label/Gradle%3AGoodness" rel="external nofollow">Gradle Goodness blog series</a>
		</p>
	</li>
	<li>
		<p>
			<a href="https://www.vogella.com/tutorials/EclipseGradle/article.html" rel="external nofollow">Eclipse Gradle Tooling</a>
		</p>
	</li>
	<li>
		<p>
			<a href="https://docs.gradle.org/current/userguide/publishing_maven.html" rel="external nofollow">Publish to Maven repository</a>
		</p>
	</li>
</ul>
<h2>
	حقوق النشر والترخيص والكود
</h2>

<p>
	يتم منح الاستخدام المجاني لأمثلة البرامج بموجب شروط <a href="https://www.eclipse.org/legal/epl-2.0" rel="external nofollow">Eclipse Public License 2.0</a>. المقال مترجم وبتصرف للمقال <a href="https://www.vogella.com/tutorials/Gradle/article.html" rel="external nofollow">The Gradle build system- Tutorial</a>
</p>
]]></description><guid isPermaLink="false">865</guid><pubDate>Fri, 10 Apr 2020 05:10:48 +0000</pubDate></item></channel></rss>
