Plugin API
تُعد plugins جزءًا أساسيًا من منظومة webpack، فهي تمنح المجتمع طريقة قوية للدخول إلى عملية compilation الخاصة بـ webpack. يستطيع plugin أن يستخدم hook في أحداث مهمة تُطلق خلال كل compilation. وفي كل مرحلة، يحصل plugin على وصول كامل إلى compiler، وإلى compilation الحالي عندما يكون ذلك مناسبًا.
لنبدأ بأداة tapable، فهي الأساس الذي بُنيت عليه واجهة plugins في webpack.
Tapable
هذه المكتبة الصغيرة أداة محورية داخل webpack، ويمكن استخدامها أيضًا خارج webpack لتوفير واجهة plugins مشابهة. كثير من الكائنات في webpack ترث من الصنف Tapable. يوفّر هذا الصنف الدوال tap وtapAsync وtapPromise، وتستخدمها plugins لإضافة خطوات build مخصصة تُنفّذ أثناء compilation.
راجع التوثيق لمعرفة المزيد. من المهم فهم دوال tap الثلاث، وكذلك أنواع hooks التي توفرها. في التوثيق ستجد الكائنات التي ترث من Tapable، مثل compiler، والـ hooks التي توفرها، ونوع كل hook، مثل SyncHook.
أنواع Plugins
بحسب hooks التي تستخدمها، وبحسب دوال tap التي تعتمد عليها، يمكن أن تعمل plugins بعدة طرق. هذا مرتبط مباشرةً بالـ hooks التي توفرها Tapable. توضح صفحة compiler hooks نوع hook الأساسي من Tapable، وهذا يحدد دوال tap المتاحة.
لذلك يختلف سلوك plugin حسب الحدث الذي تستخدم معه tap. مثلًا، عند الدخول في مرحلة compile، يمكنك استخدام tap المتزامنة فقط:
compiler.hooks.compile.tap("MyPlugin", (params) => {
console.log("Synchronously tapping the compile hook.");
});أما hook مثل run، لأنه يستخدم AsyncHook، فيمكنك معه استخدام tapAsync أو tapPromise، بالإضافة إلى tap:
compiler.hooks.run.tapAsync(
"MyPlugin",
(source, target, routesList, callback) => {
console.log("Asynchronously tapping the run hook.");
callback();
},
);
compiler.hooks.run.tapPromise("MyPlugin", (source, target, routesList) =>
new Promise((resolve) => {
setTimeout(resolve, 1000);
}).then(() => {
console.log("Asynchronously tapping the run hook with a delay.");
}),
);
compiler.hooks.run.tapPromise(
"MyPlugin",
async (source, target, routesList) => {
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
console.log("Asynchronously tapping the run hook with a delay.");
},
);الفكرة الأساسية: توجد أكثر من طريقة للدخول إلى compiler، وكل طريقة تسمح لـ plugin أن يعمل بالشكل المناسب للمرحلة التي يحتاجها.
Hooks مخصصة
إذا أردت توفير hook مخصص داخل compilation حتى تتمكن plugins أخرى من استخدام tap معه، فاتبع الخطوات التالية:
-
أنشئ
WeakMapعلى مستوى module لحفظ hooks الخاصة بكل compilation:const compilationHooks = new WeakMap<Compilation, MyHooks>(); interface MyHooks { custom: SyncHook<[number, string]>; } -
أنشئ static method داخل plugin:
static getCompilationHooks(compilation: Compilation) : MyHooks { let hooks = compilationHooks.get(compilation); if(hooks === undefined) { compilationHooks.set(compilation, hooks = { custom: new SyncHook() }); } return hooks; } -
استدعِ hooks داخل plugin بهذا الشكل:
const hooks = MyPlugin.getCompilationHooks(compilation); hooks.custom.call(1, "hello"); -
يمكن لـ plugins أخرى الوصول إلى hooks المخصصة أيضًا:
import MyPlugin from "my-plugin"; const hooks = MyPlugin.getCompilationHooks(compilation); hooks.custom.tap("OtherPlugin", (n, s) => { // magic });
مرة أخرى، راجع توثيق tapable لمعرفة أنواع hook المختلفة وطريقة عملها.
الإبلاغ عن التقدم
يمكن لـ plugins الإبلاغ عن التقدم عبر ProgressPlugin، والذي يطبع رسائل التقدم إلى stderr افتراضيًا. لتفعيل إظهار التقدم، مرّر الخيار --progress عند تشغيل webpack CLI.
يمكن تخصيص النص المطبوع بتمرير arguments مختلفة إلى الدالة reportProgress الخاصة بـ ProgressPlugin.
لكي يبلّغ plugin عن التقدم، يجب أن يستخدم tap على hook مع الخيار context: true:
compiler.hooks.emit.tapAsync(
{
name: "MyPlugin",
context: true,
},
(context, compiler, callback) => {
const reportProgress = context && context.reportProgress;
if (reportProgress) reportProgress(0.95, "Starting work");
setTimeout(() => {
if (reportProgress) reportProgress(0.95, "Done work");
callback();
}, 1000);
},
);يمكن استدعاء الدالة reportProgress بهذه arguments:
reportProgress(percentage, ...args);percentage: هذا argument غير مستخدم مباشرة؛ بدلًا من ذلك يحسبProgressPluginالنسبة بناءً على hook الحالي....args: أي عدد من النصوص، وستُمرر إلى handler الخاص بـProgressPluginليتم عرضها للمستخدم.
انتبه إلى أن جزءًا فقط من compiler hooks وcompilation hooks يدعم الدالة reportProgress. راجع ProgressPlugin للقائمة الكاملة.
التسجيل
واجهة Logging API متاحة منذ إصدار webpack 4.37. عند تفعيل logging في إعدادات stats، أو عند تفعيل infrastructure logging، يمكن لـ plugins تسجيل رسائل تُطبع بتنسيق logger المناسب، سواء stats أو infrastructure.
- يُفضّل أن تستخدم plugins الدالة
compilation.getLogger('PluginName')للتسجيل. هذا النوع من logging يُخزّن داخل Stats ويُنسّق بناءً عليها. ويمكن للمستخدم فلترته أو تصديره. - يمكن لـ plugins استخدام
compiler.getInfrastructureLogger('PluginName')للتسجيل. تسجيلinfrastructureلا يُخزّن داخل Stats، ولذلك لا يُنسّق معها. عادةً يُسجّل مباشرةً إلى console أو dashboard أو GUI. ويمكن للمستخدم فلترته. - يمكن لـ plugins استخدام fallback خاص لاكتشاف دعم logging، مثل
compilation.getLogger ? compilation.getLogger('PluginName') : console، لدعم إصدارات webpack القديمة التي لا توفر دالةgetLoggerعلى كائنcompilation.
الخطوة التالية
راجع قسم compiler hooks للحصول على قائمة مفصلة بكل compiler hooks المتاحة والـ parameters التي توفرها.



