ما هي MagicString وهل هذه الخطوط سحرية للغاية؟

MagicString هي مكتبة غير معروفة. على الرغم من ذلك ، فإنه يحل واحدة من أكثر المشاكل إلحاحًا - تغيير التعليمات البرمجية المصدر باستخدام هيكلها (AST - شجرة بناء الجملة المجردة).

في هذه المقالة سوف نتعلم ما هي MagicString وما إذا كانت هذه السطور هي حقا "سحر". سيساعدنا ذلك على فهم المقالة التالية التي سأشرح فيها كيف تمكنا من ترجمة الوثائق الزاويّة بسرعة ، وكيف سيساعد ذلك في إنشاء مترجم عالمي لكل من Markdown وأي تنسيق ملف آخر.





منذ أسبوعين ، أصدرت وثائق Angular ( angular24.ru ) باللغة الروسية . خلال هذا الوقت ، تمت إضافة 35 مشكلة مع التصحيحات في النص وطلبين للسحب. لقد شككت بصدق في أن النظام الذي تحدد فيه النص ، وتقديم الترجمة وإصداره تلقائيًا على GitHub سيعمل. ولكن يعمل التعهيد الجماعي! :) يمكنك معرفة المزيد عن هذا من هذه المقالة .

بعد الإصدار ، كان أحد الأسئلة الأكثر شيوعًا هو: "لماذا؟". السؤال صحيح تمامًا ، ولكن للإجابة عنه ، يجب عليك أولاً أن تفهم ما هي MagicString وكيف تعمل وكيف تكون مفيدة.

لنفترض أن لدينا شفرة مصدر بسيطة:

const a = 1;

نريد استبدال الثابت بـ var . الحل الأبسط هو استبدال const بـ var بـ String.prototype.replace المعتاد . وبالنسبة لهذه المهمة ، هذا على الأرجح الحل الأكثر صحة. ولكن ماذا لو احتجنا إلى استبدال const بـ var فقط في النطاق العالمي؟ ولكن لا تستبدلهم داخل الوظائف؟ يمكنك ، بالطبع ، التوصل إلى انتظام أكثر تعقيدًا أو كتابة رمز صعب ، ولكن هناك طريقة أكثر مرونة وقابلة للتطوير.

يمكننا استخدام المحللون للحصول على AST - Abstract Syntax Tree. إذا كنت مهتمًا بما هو AST ، فانتقل إلى astexplorer.net . في الجوهر ، إنها شجرة تعرض بدقة بنية التعليمات البرمجية الخاصة بك.

علاوة على ذلك ، كل عقد في AST لها بدايةو تنتهي المؤشرات تشير إلى مواقف هذه العناصر في شفرة المصدر. بمعرفة هذه الإحداثيات ووجود هيكل الوثيقة في متناول اليد ، يمكننا إجراء عمليات استبدال وتبديل معقدة مع الحفاظ على بنية المستند.



عادة ، يتم الاستبدال باستخدام تصميم نمط الزائر والعديد من المساعدين ، الذين عادة ما يلتفون في مكتبة واحدة ، والتي يمكن تسميتها "واجهة برمجة تطبيقات المحولات". يحتوي كل محلل على "API المحول" الخاص به.

هذه المكتبات سهلة الاستخدام للغاية ، ولكن لديها العديد من المشاكل. واحد منهم هو الأداء.

نظرًا لأن كل عقدة (جيدًا ، تقريبًا) في شجرة AST تحتوي على إحداثيات ، عند تغيير عقدة واحدة ، نحتاج غالبًا إلى تحديث الإحداثيات لبقية الشجرة. هنا يمكنك القول أنه يمكنك القيام بقليل من الدم - لا تقم بتحديث الإحداثيات في كل مكان ، ولكن ببساطة قم بإعادة AST إلى النص بناءً على البنية. ولكن هناك مشكلة واحدة: ستفقد على الفور تنسيق النص الأصلي ، والذي يتعارض مع مهمتنا - لاستبدال الثابت بـ var في السطر الحالي. في الواقع ، نحصل على سطر جديد بتنسيق جديد. وإذا لم تكن هذه مشكلة لخط صغير ، فتخيل ملفًا من 1000 سطر تم فيه تغيير التنسيق تمامًا بسبب استبدال الثابت بـ var . هذا لا يبدو جيدًا جدًا.



وهنا يأتي سحر MagicString. علمت لأول مرة بوجودهم من مشروع ريتش هاريس ، الذي كان يسمى بزبدة الفخار . Butternut عبارة عن أداة تصغير لجافا سكريبت. وزعم Butternutt أن يكون 3 مرات أسرع من UglifyJS و 10-15 مرات أسرع من بابل . سأمضي قدما وأقول أن المشروع كان مغطى بحوض من النحاس قبل 3 سنوات على الأقل. ولكن حتى ذلك الحين ، كنت مفتونًا بسر أدائها. لقد كان MagicString.

دعونا نلقي نظرة على العمل مع MagicString:

var MagicString = require( 'magic-string' );
var s = new MagicString( 'const a = 1; const b = 2;' );

s.overwrite( 0, 5, 'var' );
s.toString(); // 'var a = 1; const b = 2;'

//  

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

  1. استبدلنا const بـ var ، بدءًا من المؤشر 0 وانتهاءً بالمؤشر 5
  2. نحن نعلم أن جميع البدائل اللاحقة يجب أن تحتوي على فهرس أقل من 2 (يختلف أقل من const بحرفين ، سطر أقصر)
  3. نقوم بتحديث إحداثيات جميع العمليات
  4. نطبق العملية التالية ، إلخ.




كل شيء يبدو بسيطًا جدًا. لكن لماذا MagicString أسرع؟ الجواب بسيط للغاية: عدد العمليات التي نقوم بها على شجرتنا أقل بكثير من عدد عقد AST. ناهيك عن مقدار الذاكرة اللازمة لـ AST وحقيقة أن Tree Traversal (السفر عبر شجرة) ليست عملية مجانية ، ولكن O (n + m)



وإذا كنت على استعداد للانتظار نصف ساعة إضافية؟ وهنا يأتي الإصدار الثاني من MagicString. يخترع كل محلل API للتحويل الخاص به. وهذا لا يزال جيدًا جدًا ، إذا كان هناك مثل API (لا يوفره كل محلل) ، فعادة ما يتم تركنا بدون القدرة على استبدال النص المصدر عادةً باستخدام AST. لكن MagicString هي واجهة برمجة تطبيقات عالمية واحدة لتغيير سلسلة المصدر. لا يهم أي محلل أو مجموعة من المحللات التي استخدمتها. مع MagicString ، يمكنك العمل بشكل جيد على قدم المساواة مع أي AST.



آمل أن تكون مهتمًا بـ MagicString. في المقالة التالية سأتحدث عن MagicString المزدوجة وكيفية عمل مترجم عالمي لمستندات Markdown.

اشترك في قناة Telegram الخاصة بي obenjiro_notes و Twitter obenjiroحتى لا تفوتك المقالات التالية حول الموضوع والعديد من الأشياء الأخرى المثيرة للاهتمام.

All Articles