استكشاف مكونات المترجم الداخلية في TypeScript

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

1. عملية تجميع TypeScript

يتبع مُجمِّع TypeScript سلسلة من الخطوات لتحويل TypeScript إلى JavaScript. وفيما يلي نظرة عامة عالية المستوى على هذه العملية:

  1. تحليل ملفات المصدر إلى شجرة نحوية مجردة (AST).
  2. ربط وفحص نوع AST.
  3. إصدار كود JavaScript والإعلانات الناتجة.

دعونا نستكشف هذه الخطوات بمزيد من التفصيل.

2. تحليل كود TypeScript

الخطوة الأولى في عملية التجميع هي تحليل كود TypeScript. يأخذ المترجم ملفات المصدر ويحللها إلى AST ويجري تحليلاً معجميًا.

فيما يلي عرض مبسط لكيفية الوصول إلى AST ومعالجتها باستخدام واجهة برمجة التطبيقات الداخلية لـ TypeScript:

import * as ts from 'typescript';

const sourceCode = 'let x: number = 10;';
const sourceFile = ts.createSourceFile('example.ts', sourceCode, ts.ScriptTarget.Latest);

console.log(sourceFile);

تُستخدم الدالة createSourceFile لتحويل كود TypeScript الخام إلى AST. يحتوي الكائن sourceFile على البنية المحللة للكود.

3. الربط والتحقق من النوع

بعد التحليل، تكون الخطوة التالية هي ربط الرموز في AST وإجراء فحص النوع. تضمن هذه المرحلة ربط جميع المعرفات بإعلاناتها الخاصة وتتحقق مما إذا كان الكود يتبع قواعد النوع الخاصة بـ TypeScript.

يتم إجراء فحص النوع باستخدام فئة TypeChecker. فيما يلي مثال لكيفية إنشاء برنامج واسترجاع معلومات النوع:

const program = ts.createProgram(['example.ts'], {});
const checker = program.getTypeChecker();

// Get type information for a specific node in the AST
sourceFile.forEachChild(node => {
    if (ts.isVariableStatement(node)) {
        const type = checker.getTypeAtLocation(node.declarationList.declarations[0]);
        console.log(checker.typeToString(type));
    }
});

في هذا المثال، يتحقق TypeChecker من نوع إعلان المتغير ويسترد معلومات النوع من AST.

4. إصدار الكود

بمجرد اكتمال فحص النوع، ينتقل المترجم إلى مرحلة الإصدار. وهنا يتم تحويل كود TypeScript إلى JavaScript. يمكن أن يتضمن الإخراج أيضًا ملفات إعلان وخرائط مصدر، حسب التكوين.

فيما يلي مثال بسيط لكيفية استخدام المترجم لإرسال كود JavaScript:

const { emitSkipped, diagnostics } = program.emit();

if (emitSkipped) {
    console.error('Emission failed:');
    diagnostics.forEach(diagnostic => {
        const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
        console.error(message);
    });
} else {
    console.log('Emission successful.');
}

تولد الدالة program.emit مخرجات JavaScript. إذا كانت هناك أي أخطاء أثناء الإرسال، يتم التقاطها وعرضها.

5. رسائل التشخيص

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

فيما يلي كيفية استرداد التشخيصات وعرضها من المترجم:

const diagnostics = ts.getPreEmitDiagnostics(program);

diagnostics.forEach(diagnostic => {
    const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
    console.log(`Error ${diagnostic.code}: ${message}`);
});

في هذا المثال، يتم استخراج التشخيصات من البرنامج وطباعتها على وحدة التحكم.

6. تحويل TypeScript باستخدام واجهات برمجة التطبيقات للمترجم

تتيح واجهة برمجة تطبيقات مُجمِّع TypeScript للمطورين إنشاء تحويلات مخصصة. يمكنك تعديل AST قبل إصدار التعليمات البرمجية، مما يتيح تخصيصات قوية وأدوات إنشاء التعليمات البرمجية.

فيما يلي مثال على تحويل بسيط يعيد تسمية جميع المتغيرات إلى newVar:

const transformer = (context: ts.TransformationContext) => {
    return (rootNode: T) => {
        function visit(node: ts.Node): ts.Node {
            if (ts.isVariableDeclaration(node)) {
                return ts.factory.updateVariableDeclaration(
                    node,
                    ts.factory.createIdentifier('newVar'),
                    node.type,
                    node.initializer
                );
            }
            return ts.visitEachChild(node, visit, context);
        }
        return ts.visitNode(rootNode, visit);
    };
};

const result = ts.transform(sourceFile, [transformer]);
console.log(result.transformed[0]);

يقوم هذا التحويل بزيارة كل عقدة في AST وإعادة تسمية المتغيرات حسب الحاجة.

خاتمة

إن استكشاف تفاصيل المترجم الداخلي لـ TypeScript يوفر فهمًا أعمق لكيفية معالجة كود TypeScript وتحويله. سواء كنت تبحث عن بناء أدوات مخصصة أو تحسين معرفتك بكيفية عمل TypeScript، فإن التعمق في تفاصيل المترجم الداخلي يمكن أن يكون تجربة مفيدة.