مقدمة إلى خرائط مصادر JavaScript

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

خرائط المصدر هي طريقة لربط ملف مجمّع/مصغّر مرة أخرى بحالة لم يتم إنشاؤها. عند إنشاء إصدار للنشر، بالإضافة إلى تصغير ملفات JavaScript ودمجها، يمكنك إنشاء خريطة مصدر تحتوي على معلومات عن ملفاتك الأصلية. عند إجراء طلب بحث عن رقم سطر وعمود معيّنَين في رمز JavaScript الذي تم إنشاؤه، يمكنك إجراء بحث في خريطة المصدر التي تعرض الموقع الأصلي. يمكن لأدوات المطوّرين (الإصدارات اليومية من WebKit أو Google Chrome أو Firefox 23 والإصدارات الأحدث) تحليل خريطة المصدر تلقائيًا وجعلها تبدو كما لو كنت تستخدم ملفات غير مُكثَّفة وغير مُدمَجة.

يتيح لك الإصدار التجريبي النقر بزر الماوس الأيمن في أي مكان في نصّ عادي يحتوي على المصدر الذي تم إنشاؤه. سيؤدي اختيار "الحصول على الموقع الأصلي" إلى طلب البحث في خريطة المصدر من خلال إدخال رقم السطر والعمود الذي تم إنشاؤه، وعرض الموضع في الرمز الأصلي. تأكَّد من أنّ وحدة التحكّم مفتوحة حتى تتمكّن من الاطّلاع على الإخراج.

مثال على مكتبة خرائط مصادر JavaScript من Mozilla قيد الاستخدام

الواقع

قبل الاطّلاع على عملية التنفيذ التالية لميزة "خرائط المصدر" في الحياة الواقعية، تأكَّد من تفعيل ميزة "خرائط المصدر" في Chrome Canary أو WebKit Nightly من خلال النقر على رمز الترس في لوحة "أدوات المطوّر" ووضع علامة في المربّع بجانب خيار "تفعيل خرائط المصدر".

كيفية تفعيل خرائط المصدر في أدوات المطوّرين في WebKit

في الإصدار 23 من Firefox والإصدارات الأحدث، تكون خرائط المصدر مفعَّلة تلقائيًا في أدوات المطوّرين المضمّنة.

كيفية تفعيل خرائط المصدر في أدوات المطوّرين في Firefox

لماذا يجب الاهتمام بخرائط المصدر؟

في الوقت الحالي، لا تعمل ميزة "ربط المصدر" إلا بين JavaScript غير المضغوطة/المجمّعة وJavaScript المضغوطة/غير المجمّعة، ولكن يبدو المستقبل مشرقًا مع الحديث عن لغات مُجمَّعة إلى JavaScript مثل CoffeeScript، وحتى إمكانية إضافة دعم لأدوات المعالجة المسبقة لـ CSS مثل SASS أو LESS.

في المستقبل، يمكننا استخدام أي لغة تقريبًا بسهولة كما لو كانت متاحة تلقائيًا في المتصفّح باستخدام خرائط المصادر:

  • CoffeeScript
  • ECMAScript 6 والإصدارات الأحدث
  • SASS/LESS وغيرها
  • أي لغة تُجمَّع إلى JavaScript

اطّلِع على هذا التسجيل الرقمي للشاشة الذي يعرض تصحيح أخطاء CoffeeScript في إصدار تجريبي من وحدة تحكّم Firefox:

أضافت Google Web Toolkit (GWT) مؤخرًا دعمًا لـ "خرائط المصادر". أنشأ "راي كرومويل" من فريق GWT تسجيلًا رائعًا للشاشة يعرض ميزة "دعم خرائط المصدر" أثناء استخدامها.

في مثال آخر جمعته، يتم استخدام مكتبة Traceur من Google التي تتيح لك كتابة ES6 (ECMAScript 6 أو Next) وتجميعه إلى رمز متوافق مع ES3. ينشئ مُجمِّع Traceur أيضًا خريطة مصدر. اطّلِع على هذا العرض التجريبي لسمات ES6 والفئات المستخدَمة كما لو كانت متوافقة مع المتصفّح بشكلٍ أصلي، وذلك بفضل خريطة المصدر.

تتيح لك أيضًا منطقة النص في العرض الترويجي كتابة ES6 التي سيتم تجميعها على الفور وإنشاء خريطة مصدر بالإضافة إلى رمز ES3 المكافئ.

تصحيح أخطاء Traceur ES6 باستخدام خرائط المصدر

عرض توضيحي: كتابة ES6 وتصحيح الأخطاء فيها وعرض ربط المصدر أثناء التنفيذ

كيف تعمل خريطة المصدر؟

إنّ أداة التحويل أو التصغير الوحيدة لـ JavaScript التي تتيح حاليًا إنشاء خرائط المصدر هي أداة التحويل Closure compiler. (سأشرح كيفية استخدامها لاحقًا). بعد دمج JavaScript وتصغيره، سيظهر بجانبه ملف خريطة مصدر.

في الوقت الحالي، لا يضيف "مجمّع Closure" التعليق الخاص في النهاية المطلوب لإعلام أدوات مطوّري المتصفّحات بتوفر خريطة مصدر:

//# sourceMappingURL=/path/to/file.js.map

يتيح ذلك لأدوات المطوّرين ربط المكالمات بموقعها في ملفات المصدر الأصلية. في السابق، كان التعليق البرمجي هو //@، ولكن بسبب بعض المشاكل المتعلقة به وتعليقات الترجمة المجمّعة الشَرطية في Internet Explorer، تم اتخاذ القرار بتغييره إلى //#. يتيح حاليًا Chrome Canary وWebKit Nightly وFirefox 24 والإصدارات الأحدث استخدام التعليقات البرمجية الجديدة. يؤثر تغيير البنية هذا أيضًا في sourceURL.

إذا لم تعجبك فكرة التعليق الغريب، يمكنك بدلاً من ذلك ضبط عنوان خاص في ملف JavaScript المجمّع:

X-SourceMap: /path/to/file.js.map

مثل التعليق، سيُعلم هذا المستخدِم بخريطة المصدر مكان البحث عن خريطة المصدر المرتبطة بملف JavaScript. يحلّ هذا العنوان أيضًا مشكلة الإشارة إلى خرائط المصدر بلغات لا تتيح التعليقات ذات السطر الواحد.

مثال على أدوات مطوري البرامج في WebKit لتشغيل خرائط المصدر وإيقافها

لن يتم تنزيل ملف خريطة المصدر إلا إذا كانت خرائط المصادر مفعّلة وأدوات المطوّرين مفتوحة. ستحتاج أيضًا إلى تحميل ملفاتك الأصلية حتى تتمكّن أدوات المطوّرين من الإشارة إليها وعرضها عند الضرورة.

كيف يمكنني إنشاء خريطة مصدر؟

ستحتاج إلى استخدام مجمّع Closure لتصغير ملفات JavaScript ودمجها وإنشاء خريطة مصدر لها. الأمر على النحو التالي:

java -jar compiler.jar \
--js script.js \
--create_source_map ./script-min.js.map \
--source_map_format=V3 \
--js_output_file script-min.js

علامتا الأمر المهمتان هما --create_source_map و--source_map_format. هذا الإجراء مطلوب لأنّ الإصدار التلقائي هو الإصدار 2 ونريد العمل مع الإصدار 3 فقط.

بنية خريطة المصدر

لفهم خريطة المصدر بشكل أفضل، سنأخذ مثالاً صغيرًا على ملف خريطة مصدر سينشئه "مجمّع Closure"، وسنوضّح بالتفصيل آلية عمل قسم "عمليات الربط". يختلف المثال التالي قليلاً عن مثال مواصفات الإصدار 3.

{
    version : 3,
    file: "out.js",
    sourceRoot : "",
    sources: ["foo.js", "bar.js"],
    names: ["src", "maps", "are", "fun"],
    mappings: "AAgBC,SAAQ,CAAEA"
}

يمكنك في ما يلي الاطّلاع على أنّ خريطة المصدر هي عنصر حرفي يحتوي على الكثير من المعلومات المفيدة:

  • رقم الإصدار الذي تستند إليه خريطة المصادر
  • اسم ملف الرمز الذي تم إنشاؤه (ملف الإنتاج المُدمَج/المُصغَّر)
  • يسمح لك sourceRoot بإضافة بنية مجلد إلى المصادر، وهي طريقة أخرى لتوفير المساحة.
  • تحتوي مصادر على جميع أسماء الملفات التي تم دمجها.
  • تحتوي أسماء على جميع أسماء المتغيّرات/الطرق التي تظهر في رمزك البرمجي.
  • أخيرًا، عنصر "عمليات الربط" هو المكان الذي يحدث فيه السحر باستخدام قيم VLQ بترميز Base64. يتم توفير المساحة الحقيقية هنا.

Base64 VLQ والاحتفاظ بحجم خريطة المصادر صغيرًا

في الأصل، كانت مواصفات خريطة المصدر تتضمّن مخرجات مفصّلة جدًا لجميع عمليات الربط، ما أدّى إلى أن يكون حجم خريطة المصدر أكبر بحوالي 10 أضعاف حجم الرمز الذي تم إنشاؤه. قلّل الإصدار الثاني من ذلك بنسبة% 50 تقريبًا، وقلّل الإصدار الثالث من ذلك مرة أخرى بنسبة %50، لذا إذا كان لديك ملف بحجم 133 كيلوبايت، سيتوفّر لديك خريطة مصدر بحجم 300 كيلوبايت تقريبًا.

كيف تم تقليل الحجم مع الحفاظ على عمليات الربط المعقدة؟

يتم استخدام VLQ (الكمية ذات الطول المتغيّر) مع ترميز القيمة إلى قيمة Base64. سمة "عمليات الربط" هي سلسلة كبيرة جدًا. تتضمّن هذه السلسلة فاصلة منقوطة (؛) تمثّل رقم سطر ضمن الملف الذي تم إنشاؤه. تتضمّن كل سطر فواصل (,) تمثّل كل قسم ضمن ذلك السطر. كلّ قسم من هذه الأقسام هو إما 1 أو 4 أو 5 في حقول الطول المتغيّر. قد تبدو بعض المقاطع أطول، ولكنّها تحتوي على أجزاء متسلسلة. يستند كل جزء إلى الجزء السابق، ما يساعد في تقليل حجم الملف لأنّ كل جزء يرتبط بالأجزاء السابقة.

تفاصيل مقطع ضمن ملف JSON الخاص بخريطة المصدر

كما ذكرنا أعلاه، يمكن أن يكون طول كلّ مقطع 1 أو 4 أو 5 بقيم متغيّرة. يُعتبر هذا المخطّط البياني بطول متغيّر من أربعة بتات مع بت متابعة واحد (g). سنشرح هذا المقطع ونوضّح لك كيفية تحديد الخريطة المصدر للموقع الجغرافي الأصلي.

القيم المعروضة أعلاه هي القيم التي تم فك ترميزها باستخدام Base64 فقط، وهناك بعض المعالجة الإضافية للحصول على قيمها الفعلية. يحدِّد كلّ قسم عادةً خمسة عناصر:

  • العمود الذي تم إنشاؤه
  • الملف الأصلي الذي ظهر فيه هذا الخطأ
  • رقم السطر الأصلي
  • العمود الأصلي
  • الاسم الأصلي، إن توفّر

لا يتضمّن كلّ جزء اسمًا أو اسم طريقة أو وسيطة، لذا ستتبدّل الأجزاء على مدار الوقت بين أربعة وخمسة أطوال متغيّرة. قيمة g في مخطّط الشريحة أعلاه هي ما يُعرف باسم بت المتابعة، ما يسمح بإجراء المزيد من التحسينات في مرحلة فك ترميز Base64 VLQ. تتيح لك وحدات المتابعة إضافة قيمة إلى الفاصل حتى تتمكّن من تخزين أعداد كبيرة بدون الحاجة إلى تخزين عدد كبير، وهي تقنية ذكية للغاية لتوفير المساحة تعود جذورها إلى تنسيق MIDI.

بعد معالجة المخطّط البياني أعلاه AAgBC، سيعرض القيمة 0، 0، 32، 16، 1، حيث يمثّل الرقم 32 بت المتابعة الذي يساعد في إنشاء القيمة التالية 16. القيمة B التي تم فك تشفيرها بترميز Base64 هي 1. وبالتالي، فإنّ القيم المهمة المستخدَمة هي 0 و0 و16 و1. يُعلمنا ذلك بأنّ السطر 1 (يتم احتساب عدد الأسطر باستخدام النقطتَين المنفصلتَين) العمود 0 من الملف الذي تم إنشاؤه يرتبط بالملف 0 (صفيف الملفات 0 هو foo.js)، السطر 16 في العمود 1.

لعرض كيفية فك ترميز الشرائح، سأشير إلى مكتبة JavaScript الخاصة بخرائط المصدر من Mozilla. يمكنك أيضًا الاطّلاع على رمز تعيين المصدر الخاص بأدوات المطوّرين في WebKit، والذي كتبته أيضًا بلغة JavaScript.

لفهم كيفية الحصول على القيمة 16 من B بشكل صحيح، يجب أن يكون لدينا فهم أساسي للمشغّلات الثنائية وطريقة عمل المواصفات لربط المصدر. يتم وضع علامة على الرقم السابق، g، كبت واصل من خلال مقارنة الرقم (32) وVLQ_CONTINUATION_BIT (الثنائي 100000 أو 32) باستخدام عامل التشغيل "الجمع بين الوحدات الثنائية" (&).

32 & 32 = 32
// or
100000
|
|
V
100000

ويعرض هذا الإجراء القيمة 1 في كل موضع بت يظهر فيه كلاهما. وبالتالي، فإنّ القيمة التي تم فك ترميزها باستخدام Base64 والتي تبلغ 33 & 32 ستظهر على أنّها 32 لأنّها تشترك فقط في موضع 32 بت كما هو موضّح في المخطّط البياني أعلاه. يؤدي ذلك بعد ذلك إلى زيادة قيمة التحويل للبت بمقدار 5 لكل بت متابعة سابق. في الحالة أعلاه، تمّ نقله بمقدار 5 مرة واحدة فقط، وبالتالي تمّ نقل 1 (ب) 5 مرات إلى اليسار.

1 <<../ 5 // 32

// Shift the bit by 5 spots
______
|    |
V    V
100001 = 100000 = 32

وبعد ذلك، يتم تحويل هذه القيمة من قيمة VLQ ذات علامة من خلال إزاحة الرقم (32) بمقدار موضع واحد إلى اليمين.

32 >> 1 // 16
//or
100000
|
 |
 V
010000 = 16

إليك الطريقة التي يمكنك بها تحويل 1 إلى 16. قد تبدو هذه العملية معقّدة للغاية، ولكنّها تصبح أكثر منطقية عندما تبدأ الأرقام بالزيادة.

المشاكل المحتمَلة في XSSI

تشير المواصفة إلى مشاكل تضمين النصوص البرمجية على مستوى المواقع الإلكترونية التي قد تنشأ عن استخدام خريطة مصدر. للحدّ من حدوث ذلك، ننصحك بإضافة ")]}" في بداية السطر الأول من خريطة المصدر لإبطال JavaScript عمدًا حتى يتمّ طرح خطأ في البنية. يمكن لأدوات المطوّرين في WebKit معالجة ذلك.

if (response.slice(0, 3) === ")]}") {
    response = response.substring(response.indexOf('\n'));
}

كما هو موضّح أعلاه، يتم تقسيم الأحرف الثلاثة الأولى للتحقّق مما إذا كانت تتطابق مع خطأ البنية في المواصفات، وإذا كان الأمر كذلك، تتم إزالة جميع الأحرف التي تسبق عنصر السطر الجديد الأول (\n).

sourceURL وdisplayName في العمل: دالة Eval والدوالّ المجهولة

على الرغم من أنّ الاتفاقيتين التاليتَين ليستا جزءًا من مواصفات خريطة المصدر، إلا أنّهما تتيحان لك تسهيل عملية التطوير كثيرًا عند العمل مع وظائف التقييم والدوالّ المجهولة.

يبدو المساعد الأوّل مشابهًا جدًا للسمة //# sourceMappingURL، وقد تمّت الإشارة إليه في مواصفات الإصدار 3 من خريطة المصدر. من خلال تضمين التعليق الخاص التالي في الرمز البرمجي الذي سيتم تقييمه، يمكنك تسمية عمليات التقييم لكي تظهر كأسماء منطقية أكثر في أدوات المطوّرين. اطّلِع على عرض تجريبي بسيط باستخدام برنامج تجميع CoffeeScript:

عرض توضيحي: عرض رمز eval() كنص برمجي من خلال sourceURL

//# sourceURL=sqrt.coffee
شكل التعليق الخاص sourceURL في أدوات المطوّرين

يسمح لك المساعد الآخر بتسمية الدوال المجهولة باستخدام السمة displayName المتاحة في السياق الحالي للدالة المجهولة. يمكنك إنشاء ملف شخصي للعرض التجريبي التالي للاطّلاع على سمة displayName أثناء عملها.

btns[0].addEventListener("click", function(e) {
    var fn = function() {
        console.log("You clicked button number: 1");
    };

    fn.displayName = "Anonymous function of button 1";

    return fn();
}, false);
عرض سمة displayName أثناء استخدامها

عند إنشاء ملف تعريف لرمزك ضمن أدوات المطوّرين، سيتم عرض الموقع displayName بدلاً من (anonymous). ومع ذلك، فإنّ displayName غير متوفّر في Chrome. ولكن لا يزال هناك أمل، فقد تم اقتراح اسم أفضل بكثير يُسمى debugName.

اعتبارًا من وقت كتابة هذه المقالة، لا تتوفّر عملية تسمية eval إلا في متصفّحات Firefox وWebKit. لا تتوفّر سمة displayName إلا في الإصدارات التجريبية من WebKit.

لنتعاون معًا

هناك حاليًا مناقشة طويلة جدًا حول إتاحة خريطة المصدر التي تتم إضافتها إلى CoffeeScript. يُرجى التحقّق من المشكلة وتقديم دعمك لإضافة إنشاء خريطة المصدر إلى مُجمِّع CoffeeScript. سيكون هذا إنجازًا كبيرًا لـ CoffeeScript وعشّاقه المخلصين.

يتضمّن UglifyJS أيضًا مشكلة في خريطة المصدر يجب الاطّلاع عليها أيضًا.

تُنشئ العديد من الأدوات خرائط مصادر، بما في ذلك مُجمِّع coffeescript. أعتقد أنّ هذه نقطة مثيرة للجدل الآن.

كلما زاد عدد الأدوات المتاحة لنا التي يمكنها إنشاء خرائط مصادر، كان ذلك أفضل، لذا يُرجى طلب إتاحة استخدام خرائط المصادر في مشروعك المفضّل المفتوح المصدر أو إضافة هذه الميزة إليه.

ليست مثالية

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

يمكن حلّ هذه المشكلة بالتأكيد، ومن خلال إيلاء المزيد من الاهتمام للخرائط المصدر، يمكننا البدء في توفير بعض الميزات الرائعة وتحسين الأداء.

المشاكل

أضاف jQuery 1.9 مؤخرًا إمكانية استخدام خرائط المصادر عند عرضها خارج خدمات CDN الرسمية. وأشار أيضًا إلى خطأ غريب عند استخدام تعليقات الترجمة المشروطة في Internet Explorer ("//‎@cc_on") قبل تحميل jQuery. تم منذ ذلك الحين إجراء commit للتخفيف من هذا الأمر من خلال لفّ sourceMappingURL في تعليق مكوّن من عدة أسطر. الدرس الذي يجب تعلمه هو عدم استخدام التعليقات الشَرطية.

وقد تمّ حلّ هذه المشكلة من خلال تغيير بنية الجملة إلى //#.

الأدوات والموارد

في ما يلي بعض المراجع والأدوات الإضافية التي يجب الاطّلاع عليها:

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

ماذا تنتظر؟ ابدأ الآن في إنشاء خرائط المصدر لجميع المشاريع.