دليل شامل لواجهة برمجة مُجمِّع TypeScript، يغطي أشجار البنية المجردة (AST)، وتحليل التعليمات البرمجية، والتحويل، والتوليد للمطورين الدوليين.
واجهة برمجة مُجمِّع TypeScript: إتقان معالجة AST وتحويل التعليمات البرمجية
توفر واجهة برمجة مُجمِّع TypeScript واجهة قوية لتحليل معالجة وإنشاء تعليمات TypeScript و JavaScript. يكمن في قلبها شجرة البنية المجردة (AST)، وهي تمثيل مُهيكل لتعليماتك المصدرية. إن فهم كيفية العمل مع AST يفتح إمكانيات لبناء أدوات متقدمة، مثل أدوات فحص التعليمات البرمجية، ومُنسِّقات التعليمات البرمجية، وأدوات التحليل الثابتة، ومُنشئات التعليمات البرمجية المخصصة.
ما هي واجهة برمجة مُجمِّع TypeScript؟
واجهة برمجة مُجمِّع TypeScript هي مجموعة من واجهات ووظائف TypeScript التي تكشف عن الأعمال الداخلية لمُجمِّع TypeScript. إنها تسمح للمطورين بالتفاعل برمجيًا مع عملية التجميع، متجاوزةً مجرد تجميع التعليمات البرمجية. يمكنك استخدامها لـ:
- تحليل التعليمات البرمجية: افحص هيكل التعليمات البرمجية، وحدد المشكلات المحتملة، واستخرج المعلومات الدلالية.
- تحويل التعليمات البرمجية: عدّل التعليمات البرمجية الموجودة، وأضف ميزات جديدة، أو أعد هيكلة التعليمات البرمجية تلقائيًا.
- إنشاء التعليمات البرمجية: أنشئ تعليمات برمجية جديدة من البداية بناءً على القوالب أو المدخلات الأخرى.
تُعد واجهة برمجة التطبيقات هذه ضرورية لبناء أدوات تطوير متطورة تعمل على تحسين جودة التعليمات البرمجية وأتمتة المهام المتكررة وتعزيز إنتاجية المطورين.
فهم شجرة البنية المجردة (AST)
AST هو تمثيل يشبه الشجرة لهيكل التعليمات البرمجية الخاصة بك. تمثل كل عقدة في الشجرة بناءً نحويًا، مثل إعلان متغير أو استدعاء دالة أو عبارة تحكم في التدفق. توفر واجهة برمجة مُجمِّع TypeScript أدوات لتجاوز AST وفحص عُقده وتعديلها.
ضع في اعتبارك تعليمات TypeScript البرمجية البسيطة هذه:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
سيمثل AST لهذه التعليمات البرمجية تعريف الدالة، وعبارة الإرجاع، والحرف الحرفي للقالب، واستدعاء console.log، وعناصر أخرى من التعليمات البرمجية. قد يكون تصور AST أمرًا صعبًا، ولكن يمكن أن تساعد أدوات مثل مستكشف AST (astexplorer.net). تسمح لك هذه الأدوات بإدخال التعليمات البرمجية ورؤية AST المقابلة لها بتنسيق سهل الاستخدام. سيساعدك استخدام AST Explorer على فهم نوع هيكل التعليمات البرمجية التي ستتعامل معها.
أنواع عقد AST الرئيسية
تعرّف واجهة برمجة مُجمِّع TypeScript أنواعًا مختلفة من عقد AST، يمثل كل منها بناءً نحويًا مختلفًا. فيما يلي بعض أنواع العقد الشائعة:
- SourceFile: يمثل ملف TypeScript بأكمله.
- FunctionDeclaration: يمثل تعريف الدالة.
- VariableDeclaration: يمثل إعلان متغير.
- Identifier: يمثل معرفًا (مثل اسم متغير، اسم دالة).
- StringLiteral: يمثل سلسلة حرفية.
- CallExpression: يمثل استدعاء دالة.
- ReturnStatement: يمثل عبارة إرجاع.
لكل نوع عقدة خصائص توفر معلومات حول عنصر التعليمات البرمجية المقابل. على سبيل المثال، قد تحتوي عقدة `FunctionDeclaration` على خصائص لاسمها ومعلماتها ونوع الإرجاع والجسم.
البدء باستخدام واجهة برمجة المُجمِّع
لبدء استخدام واجهة برمجة المُجمِّع، ستحتاج إلى تثبيت TypeScript والحصول على فهم أساسي لبنية TypeScript. إليك مثال بسيط يوضح كيفية قراءة ملف TypeScript وطباعة AST الخاص به:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015, // Target ECMAScript version
true // SetParentNodes: true to retain parent references in the AST
);
function printAST(node: ts.Node, indent = 0) {
const indentStr = " ".repeat(indent);
console.log(`${indentStr}${ts.SyntaxKind[node.kind]}`);
node.forEachChild(child => printAST(child, indent + 1));
}
printAST(sourceFile);
توضيح:
- استيراد الوحدات: يستورد وحدة `typescript` ووحدة `fs` لعمليات نظام الملفات.
- قراءة ملف المصدر: يقرأ محتوى ملف TypeScript المسمى `example.ts`. ستحتاج إلى إنشاء ملف `example.ts` لكي ينجح هذا.
- إنشاء SourceFile: ينشئ كائن `SourceFile`، والذي يمثل جذر AST. تقوم الدالة `ts.createSourceFile` بتحليل التعليمات البرمجية المصدر وإنشاء AST.
- طباعة AST: تحدد دالة تكرارية `printAST` تتجاوز AST وتطبع نوع كل عقدة.
- استدعاء printAST: يستدعي `printAST` لبدء طباعة AST من عقدة الجذر `SourceFile`.
لتشغيل هذه التعليمات البرمجية، احفظها كملف `.ts` (على سبيل المثال، `ast-example.ts`)، وأنشئ ملف `example.ts` ببعض تعليمات TypeScript البرمجية، ثم قم بتجميع التعليمات البرمجية وتشغيلها:
tsc ast-example.ts
node ast-example.js
سيؤدي هذا إلى طباعة AST لملف `example.ts` الخاص بك على وحدة التحكم. سيعرض الإخراج تسلسل العقد وأنواعها. على سبيل المثال، قد يعرض `FunctionDeclaration` و`Identifier` و`Block` وأنواع العقد الأخرى.
تجاوز AST
توفر واجهة برمجة المُجمِّع طرقًا متعددة لتجاوز AST. أبسطها هو استخدام طريقة `forEachChild`، كما هو موضح في المثال السابق. تزور هذه الطريقة كل عقدة تابعة لعقدة معينة.
للحصول على سيناريوهات تجاوز أكثر تعقيدًا، يمكنك استخدام نمط `Visitor`. الزائر هو كائن يحدد طرقًا ليتم استدعاؤها لأنواع عقد معينة. يتيح لك هذا تخصيص عملية التجاوز وتنفيذ إجراءات بناءً على نوع العقدة.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
class IdentifierVisitor {
visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
console.log(`Found identifier: ${node.text}`);
}
ts.forEachChild(node, n => this.visit(n));
}
}
const visitor = new IdentifierVisitor();
visitor.visit(sourceFile);
توضيح:
- فئة IdentifierVisitor: تحدد فئة `IdentifierVisitor` ذات طريقة `visit`.
- طريقة الزيارة: تتحقق طريقة `visit` مما إذا كانت العقدة الحالية هي `Identifier`. إذا كان الأمر كذلك، فإنها تطبع نص المُعرّف. ثم تستدعي `ts.forEachChild` بشكل متكرر لزيارة العقد التابعة.
- إنشاء زائر: ينشئ مثيلاً لـ `IdentifierVisitor`.
- بدء التجاوز: يستدعي طريقة `visit` على `SourceFile` لبدء التجاوز.
يوضح هذا المثال كيفية العثور على جميع المعرّفات في AST. يمكنك تكييف هذا النمط للعثور على أنواع عقد أخرى وتنفيذ إجراءات مختلفة.
تحويل AST
يكمن جوهر قوة واجهة برمجة المُجمِّع في قدرتها على تحويل AST. يمكنك تعديل AST لتغيير هيكل وسلوك التعليمات البرمجية الخاصة بك. هذا هو أساس أدوات إعادة هيكلة التعليمات البرمجية ومُنشئات التعليمات البرمجية وغيرها من الأدوات المتقدمة.
لتحويل AST، ستحتاج إلى استخدام الدالة `ts.transform`. تأخذ هذه الدالة قائمة `SourceFile` وقائمة من وظائف `TransformerFactory`. `TransformerFactory` عبارة عن دالة تأخذ `TransformationContext` وتعيد دالة `Transformer`. تكون دالة `Transformer` مسؤولة عن زيارة العقد وتحويلها في AST.
إليك مثال بسيط يوضح كيفية إضافة تعليق إلى بداية ملف TypeScript:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
const transformerFactory: ts.TransformerFactory = context => {
return transformer => {
return node => {
if (ts.isSourceFile(node)) {
// Create a leading comment
const comment = ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
" This file was automatically transformed ",
true // hasTrailingNewLine
);
return node;
}
return node;
};
};
};
const { transformed } = ts.transform(sourceFile, [transformerFactory]);
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed
});
const result = printer.printFile(transformed[0]);
fs.writeFileSync("example.transformed.ts", result);
توضيح:
- TransformerFactory: تحدد دالة `TransformerFactory` تُرجع دالة `Transformer`.
- Transformer: تتحقق دالة `Transformer` مما إذا كانت العقدة الحالية هي `SourceFile`. إذا كان الأمر كذلك، فإنها تضيف تعليقًا رئيسيًا إلى العقدة باستخدام `ts.addSyntheticLeadingComment`.
- ts.transform: يستدعي `ts.transform` لتطبيق التحويل على `SourceFile`.
- الطابعة: تنشئ كائن `Printer` لإنشاء تعليمات برمجية من AST المحول.
- الطباعة والكتابة: تطبع التعليمات البرمجية المحولة وتكتبها في ملف جديد باسم `example.transformed.ts`.
يوضح هذا المثال تحويلاً بسيطًا، ولكن يمكنك استخدام نفس النمط لتنفيذ تحويلات أكثر تعقيدًا، مثل إعادة هيكلة التعليمات البرمجية، أو إضافة عبارات تسجيل، أو إنشاء المستندات.
تقنيات التحويل المتقدمة
فيما يلي بعض تقنيات التحويل المتقدمة التي يمكنك استخدامها مع واجهة برمجة المُجمِّع:
- إنشاء عقد جديدة: استخدم وظائف `ts.createXXX` لإنشاء عقد AST جديدة. على سبيل المثال، يقوم `ts.createVariableDeclaration` بإنشاء عقدة إعلان متغير جديد.
- استبدال العقد: استبدل العقد الموجودة بعقد جديدة باستخدام الدالة `ts.visitEachChild`.
- إضافة العقد: أضف عقدًا جديدة إلى AST باستخدام وظائف `ts.updateXXX`. على سبيل المثال، يقوم `ts.updateBlock` بتحديث عبارة كتلة بعبارات جديدة.
- إزالة العقد: قم بإزالة العقد من AST عن طريق إرجاع `undefined` من دالة المُحوِّل.
توليد التعليمات البرمجية
بعد تحويل AST، ستحتاج إلى إنشاء تعليمات برمجية منها. توفر واجهة برمجة المُجمِّع كائن `Printer` لهذا الغرض. يأخذ `Printer` AST وينشئ تمثيلاً نصيًا للتعليمات البرمجية.
تقوم الدالة `ts.createPrinter` بإنشاء كائن `Printer`. يمكنك تكوين الطابعة بخيارات مختلفة، مثل حرف السطر الجديد الذي سيتم استخدامه وما إذا كنت تريد إصدار التعليقات.
تأخذ طريقة `printer.printFile` `SourceFile` وتعيد تمثيلاً نصيًا للتعليمات البرمجية. يمكنك بعد ذلك كتابة هذه السلسلة في ملف.
التطبيقات العملية لواجهة برمجة المُجمِّع
تحتوي واجهة برمجة مُجمِّع TypeScript على العديد من التطبيقات العملية في تطوير البرامج. فيما يلي بعض الأمثلة:
- أدوات فحص التعليمات البرمجية: قم بإنشاء أدوات فحص التعليمات البرمجية المخصصة لفرض معايير الترميز وتحديد المشكلات المحتملة في التعليمات البرمجية الخاصة بك.
- مُنسِّقات التعليمات البرمجية: قم بإنشاء مُنسِّقات تعليمات برمجية لتنسيق التعليمات البرمجية الخاصة بك تلقائيًا وفقًا لدليل نمط معين.
- أدوات التحليل الثابت: قم بتطوير أدوات تحليل ثابتة للكشف عن الأخطاء ونقاط الضعف الأمنية واختناقات الأداء في التعليمات البرمجية الخاصة بك.
- مُنشئات التعليمات البرمجية: قم بإنشاء تعليمات برمجية من القوالب أو المدخلات الأخرى، مما يؤدي إلى أتمتة المهام المتكررة وتقليل التعليمات البرمجية النموذجية. على سبيل المثال، إنشاء عملاء واجهة برمجة التطبيقات أو مخططات قواعد البيانات من ملف وصف.
- أدوات إعادة الهيكلة: قم بإنشاء أدوات إعادة الهيكلة لإعادة تسمية المتغيرات تلقائيًا أو استخراج الدوال أو نقل التعليمات البرمجية بين الملفات.
- أتمتة التدويل (i18n): استخرج تلقائيًا السلاسل القابلة للترجمة من تعليمات TypeScript البرمجية الخاصة بك وإنشاء ملفات ترجمة للغات مختلفة. على سبيل المثال، يمكن لأداة فحص التعليمات البرمجية بحثًا عن السلاسل التي تم تمريرها إلى دالة `translate()` وإضافتها تلقائيًا إلى ملف موارد الترجمة.
مثال: إنشاء أداة فحص تعليمات برمجية بسيطة
لنقم بإنشاء أداة فحص تعليمات برمجية بسيطة تتحقق من المتغيرات غير المستخدمة في تعليمات TypeScript البرمجية. ستحدد أداة فحص التعليمات البرمجية هذه المتغيرات التي تم الإعلان عنها ولكن لم يتم استخدامها مطلقًا.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
function findUnusedVariables(sourceFile: ts.SourceFile) {
const usedVariables = new Set();
function visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
usedVariables.add(node.text);
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
const unusedVariables: string[] = [];
function checkVariableDeclaration(node: ts.Node) {
if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
const variableName = node.name.text;
if (!usedVariables.has(variableName)) {
unusedVariables.push(variableName);
}
}
ts.forEachChild(node, checkVariableDeclaration);
}
checkVariableDeclaration(sourceFile);
return unusedVariables;
}
const unusedVariables = findUnusedVariables(sourceFile);
if (unusedVariables.length > 0) {
console.log("Unused variables:");
unusedVariables.forEach(variable => console.log(`- ${variable}`));
} else {
console.log("No unused variables found.");
}
توضيح:
- دالة findUnusedVariables: تحدد الدالة `findUnusedVariables` التي تأخذ `SourceFile` كمدخلات.
- مجموعة usedVariables: تنشئ `Set` لتخزين أسماء المتغيرات المستخدمة.
- دالة visit: تحدد الدالة التكرارية `visit` التي تتجاوز AST وتضيف أسماء جميع المعرّفات إلى مجموعة `usedVariables`.
- دالة checkVariableDeclaration: تحدد الدالة التكرارية `checkVariableDeclaration` التي تتحقق مما إذا كان إعلان متغير غير مستخدم. إذا كان الأمر كذلك، فإنه يضيف اسم المتغير إلى صفيف `unusedVariables`.
- إرجاع unusedVariables: إرجاع صفيف يحتوي على أسماء أي متغيرات غير مستخدمة.
- الإخراج: يطبع المتغيرات غير المستخدمة على وحدة التحكم.
يوضح هذا المثال أداة فحص تعليمات برمجية بسيطة. يمكنك توسيعه للتحقق من معايير الترميز الأخرى وتحديد المشكلات المحتملة الأخرى في التعليمات البرمجية الخاصة بك. على سبيل المثال، يمكنك التحقق من الواردات غير المستخدمة أو الوظائف المعقدة للغاية أو نقاط الضعف الأمنية المحتملة. المفتاح هو فهم كيفية تجاوز AST وتحديد أنواع العقد المحددة التي تهمك.
أفضل الممارسات والاعتبارات
- فهم AST: استثمر الوقت في فهم هيكل AST. استخدم أدوات مثل مستكشف AST لتصور AST للتعليمات البرمجية الخاصة بك.
- استخدام حراس الأنواع: استخدم حراس الأنواع (`ts.isXXX`) للتأكد من أنك تعمل بأنواع العقد الصحيحة.
- النظر في الأداء: يمكن أن تكون تحويلات AST مكلفة حسابيًا. قم بتحسين التعليمات البرمجية الخاصة بك لتقليل عدد العقد التي تزورها وتحويلها.
- التعامل مع الأخطاء: تعامل مع الأخطاء بأناقة. يمكن أن تتسبب واجهة برمجة المُجمِّع في حدوث استثناءات إذا حاولت إجراء عمليات غير صحيحة على AST.
- الاختبار بدقة: اختبر التحويلات الخاصة بك بدقة للتأكد من أنها تنتج النتائج المرجوة ولا تقدم أخطاء جديدة.
- استخدام المكتبات الموجودة: ضع في اعتبارك استخدام المكتبات الموجودة التي توفر تجريدات عالية المستوى على واجهة برمجة المُجمِّع. يمكن لهذه المكتبات تبسيط المهام الشائعة وتقليل كمية التعليمات البرمجية التي تحتاج إلى كتابتها. تتضمن الأمثلة `ts-morph` و`typescript-eslint`.
الخلاصة
تعد واجهة برمجة مُجمِّع TypeScript أداة قوية لإنشاء أدوات تطوير متقدمة. من خلال فهم كيفية العمل مع AST، يمكنك إنشاء أدوات فحص التعليمات البرمجية، ومُنسِّقات التعليمات البرمجية، وأدوات التحليل الثابت، والأدوات الأخرى التي تعمل على تحسين جودة التعليمات البرمجية وأتمتة المهام المتكررة وتعزيز إنتاجية المطورين. في حين أن واجهة برمجة التطبيقات يمكن أن تكون معقدة، إلا أن فوائد إتقانها كبيرة. يوفر هذا الدليل الشامل أساسًا لاستكشاف واجهة برمجة المُجمِّع واستخدامها بفعالية في مشاريعك. تذكر الاستفادة من أدوات مثل AST Explorer، والتعامل بعناية مع أنواع العقد، واختبار التحويلات الخاصة بك بدقة. مع الممارسة والتفاني، يمكنك فتح الإمكانات الكاملة لواجهة برمجة مُجمِّع TypeScript وإنشاء حلول مبتكرة لمشهد تطوير البرامج.
المزيد من الاستكشاف:
- وثائق واجهة برمجة مُجمِّع TypeScript: [https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API)
- مستكشف AST: [https://astexplorer.net/](https://astexplorer.net/)
- مكتبة ts-morph: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)