الوحدات: واجهة برمجة التطبيقات node:module
أضيفت في: v0.3.7
الكائن Module
توفر أساليب الأداة المساعدة العامة عند التفاعل مع مثيلات Module
، ومتغير module
الذي غالبًا ما يُرى في وحدات CommonJS. يتم الوصول إليه عبر import 'node:module'
أو require('node:module')
.
module.builtinModules
[السجل]
الإصدار | التغييرات |
---|---|
v23.5.0 | تحتوي القائمة الآن أيضًا على وحدات ذات بادئة فقط. |
v9.3.0, v8.10.0, v6.13.0 | أضيفت في: v9.3.0, v8.10.0, v6.13.0 |
قائمة بأسماء جميع الوحدات التي توفرها Node.js. يمكن استخدامه للتحقق مما إذا كانت الوحدة النمطية يتم الاحتفاظ بها بواسطة طرف ثالث أم لا.
module
في هذا السياق ليس هو نفس الكائن الذي توفره أداة تغليف الوحدة. للوصول إليه، اطلب وحدة Module
:
// module.mjs
// في وحدة ECMAScript
import { builtinModules as builtin } from 'node:module';
// module.cjs
// في وحدة CommonJS
const builtin = require('node:module').builtinModules;
module.createRequire(filename)
أضيفت في: v12.2.0
filename
<string> | <URL> اسم الملف المراد استخدامه لإنشاء وظيفة الطلب. يجب أن يكون كائن عنوان URL للملف، أو سلسلة عنوان URL للملف، أو سلسلة مسار مطلق.- Returns: <require> وظيفة الطلب
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
// sibling-module.js هي وحدة CommonJS.
const siblingModule = require('./sibling-module');
module.findPackageJSON(specifier[, base])
أضيفت في: v23.2.0
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1 1 - تطوير نشط
specifier
<string> | <URL> محدد الوحدة التي سيتم استردادpackage.json
الخاصة بها. عند تمرير محدد مجرد، يتم إرجاعpackage.json
في جذر الحزمة. عند تمرير محدد نسبي أو محدد مطلق، يتم إرجاع أقربpackage.json
أصل.base
<string> | <URL> الموقع المطلق (سلسلة عنوان URLfile:
أو مسار نظام الملفات) للوحدة النمطية المحتوية. بالنسبة إلى CJS، استخدم__filename
(وليس__dirname
!)؛ بالنسبة إلى ESM، استخدمimport.meta.url
. لا تحتاج إلى تمريره إذا كانspecifier
عبارة عنabsolute specifier
.- Returns: <string> | <undefined> مسار إذا تم العثور على
package.json
. عندما يكونstartLocation
حزمة، فإنpackage.json
الجذر الخاص بالحزمة؛ عندما يكون نسبيًا أو غير محلول، فإن أقربpackage.json
إلىstartLocation
.
/path/to/project
├ packages/
├ bar/
├ bar.js
└ package.json // name = '@foo/bar'
└ qux/
├ node_modules/
└ some-package/
└ package.json // name = 'some-package'
├ qux.js
└ package.json // name = '@foo/qux'
├ main.js
└ package.json // name = '@foo'
// /path/to/project/packages/bar/bar.js
import { findPackageJSON } from 'node:module';
findPackageJSON('..', import.meta.url);
// '/path/to/project/package.json'
// نفس النتيجة عند تمرير محدد مطلق بدلاً من ذلك:
findPackageJSON(new URL('../', import.meta.url));
findPackageJSON(import.meta.resolve('../'));
findPackageJSON('some-package', import.meta.url);
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// عند تمرير محدد مطلق، قد تحصل على نتيجة مختلفة إذا
// كانت الوحدة النمطية التي تم حلها داخل مجلد فرعي يحتوي على `package.json` متداخلة.
findPackageJSON(import.meta.resolve('some-package'));
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'
findPackageJSON('@foo/qux', import.meta.url);
// '/path/to/project/packages/qux/package.json'
// /path/to/project/packages/bar/bar.js
const { findPackageJSON } = require('node:module');
const { pathToFileURL } = require('node:url');
const path = require('node:path');
findPackageJSON('..', __filename);
// '/path/to/project/package.json'
// نفس النتيجة عند تمرير محدد مطلق بدلاً من ذلك:
findPackageJSON(pathToFileURL(path.join(__dirname, '..')));
findPackageJSON('some-package', __filename);
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// عند تمرير محدد مطلق، قد تحصل على نتيجة مختلفة إذا
// كانت الوحدة النمطية التي تم حلها داخل مجلد فرعي يحتوي على `package.json` متداخلة.
findPackageJSON(pathToFileURL(require.resolve('some-package')));
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'
findPackageJSON('@foo/qux', __filename);
// '/path/to/project/packages/qux/package.json'
module.isBuiltin(moduleName)
تمت الإضافة في: v18.6.0, v16.17.0
import { isBuiltin } from 'node:module';
isBuiltin('node:fs'); // true
isBuiltin('fs'); // true
isBuiltin('wss'); // false
module.register(specifier[, parentURL][, options])
[السجل]
الإصدار | التغييرات |
---|---|
v20.8.0, v18.19.0 | إضافة دعم لمثيلات WHATWG URL. |
v20.6.0, v18.19.0 | تمت الإضافة في: v20.6.0, v18.19.0 |
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1.2 - مرشح الإصدار
specifier
<string> | <URL> خطافات تخصيص ليتم تسجيلها؛ يجب أن يكون هذا هو نفس السلسلة التي سيتم تمريرها إلىimport()
، باستثناء أنه إذا كان نسبيًا ، فسيتم حله بالنسبة إلىparentURL
.parentURL
<string> | <URL> إذا كنت تريد حلspecifier
بالنسبة إلى عنوان URL أساسي، مثلimport.meta.url
، يمكنك تمرير عنوان URL هذا هنا. الافتراضي:'data:'
options
<Object>parentURL
<string> | <URL> إذا كنت تريد حلspecifier
بالنسبة إلى عنوان URL أساسي، مثلimport.meta.url
، يمكنك تمرير عنوان URL هذا هنا. يتم تجاهل هذه الخاصية إذا تم توفيرparentURL
كوسيطة ثانية. الافتراضي:'data:'
data
<any> أي قيمة JavaScript عشوائية وقابلة للاستنساخ ليتم تمريرها إلى الخطافinitialize
.transferList
<Object[]> كائنات قابلة للنقل ليتم تمريرها إلى الخطافinitialize
.
قم بتسجيل وحدة تصدّر خطافات تقوم بتخصيص سلوك حل وحدة Node.js وتحميلها. راجع خطافات التخصيص.
module.registerHooks(options)
تمت الإضافة في: الإصدار v23.5.0
[ثابت: 1 - تجريبي]
ثابت: 1 الاستقرار: 1.1 - تطوير نشط
options
<Object>load
<Function> | <undefined> راجع خطاف التحميل. افتراضي:undefined
.resolve
<Function> | <undefined> راجع خطاف الحل. افتراضي:undefined
.
تسجيل الخطافات التي تخصص سلوك حل وتحميل وحدة Node.js. راجع خطافات التخصيص.
module.stripTypeScriptTypes(code[, options])
تمت الإضافة في: الإصدار v23.2.0
[ثابت: 1 - تجريبي]
ثابت: 1 الاستقرار: 1.1 - تطوير نشط
code
<string> الكود الذي سيتم إزالة تعليقات النوع منه.options
<Object>mode
<string> افتراضي:'strip'
. القيم المحتملة هي:'strip'
إزالة تعليقات النوع فقط دون إجراء تحويل لميزات TypeScript.'transform'
إزالة تعليقات النوع وتحويل ميزات TypeScript إلى JavaScript.sourceMap
<boolean> افتراضي:false
. فقط عندما يكونmode
هو'transform'
، إذا كانtrue
، فسيتم إنشاء خريطة مصدر للكود المحول.sourceUrl
<string> يحدد عنوان URL المصدر المستخدم في خريطة المصدر.
الإرجاع: <string> الكود مع إزالة تعليقات النوع.
module.stripTypeScriptTypes()
يزيل تعليقات النوع من كود TypeScript. يمكن استخدامه لإزالة تعليقات النوع من كود TypeScript قبل تشغيله باستخدامvm.runInContext()
أوvm.compileFunction()
. بشكل افتراضي، سيطرح خطأ إذا كان الكود يحتوي على ميزات TypeScript تتطلب التحويل مثلEnums
، راجع إزالة النوع لمزيد من المعلومات. عندما يكون الوضع هو'transform'
، فإنه يحول أيضًا ميزات TypeScript إلى JavaScript، راجع تحويل ميزات TypeScript لمزيد من المعلومات. عندما يكون الوضع هو'strip'
، لا يتم إنشاء خرائط المصدر، لأن المواقع محفوظة. إذا تم توفيرsourceMap
، عندما يكون الوضع هو'strip'
، فسيتم طرح خطأ.
تحذير: لا ينبغي اعتبار مخرج هذه الدالة ثابتًا عبر إصدارات Node.js، بسبب التغييرات في محلل TypeScript.
import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a = 1;
const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a = 1;
إذا تم توفير sourceUrl
، فسيتم استخدامه وإلحاقه كتعليق في نهاية الإخراج:
import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
console.log(strippedCode);
// Prints: const a = 1\n\n//# sourceURL=source.ts;
const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
console.log(strippedCode);
// Prints: const a = 1\n\n//# sourceURL=source.ts;
عندما يكون mode
هو 'transform'
، يتم تحويل الكود إلى JavaScript:
import { stripTypeScriptTypes } from 'node:module';
const code = `
namespace MathUtil {
export const add = (a: number, b: number) => a + b;
}`;
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true });
console.log(strippedCode);
// Prints:
// var MathUtil;
// (function(MathUtil) {
// MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...
const { stripTypeScriptTypes } = require('node:module');
const code = `
namespace MathUtil {
export const add = (a: number, b: number) => a + b;
}`;
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true });
console.log(strippedCode);
// Prints:
// var MathUtil;
// (function(MathUtil) {
// MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...
module.syncBuiltinESMExports()
أُضيف في: v12.12.0
تقوم الطريقة module.syncBuiltinESMExports()
بتحديث جميع الروابط الحية لـ وحدات ES Modules المدمجة لتتوافق مع خصائص الصادرات CommonJS. إنها لا تضيف أو تزيل أسماء مصدّرة من وحدات ES Modules.
const fs = require('node:fs');
const assert = require('node:assert');
const { syncBuiltinESMExports } = require('node:module');
fs.readFile = newAPI;
delete fs.readFileSync;
function newAPI() {
// ...
}
fs.newAPI = newAPI;
syncBuiltinESMExports();
import('node:fs').then((esmFS) => {
// تقوم بمزامنة الخاصية readFile الموجودة بالقيمة الجديدة
assert.strictEqual(esmFS.readFile, newAPI);
// تمت إزالة readFileSync من fs المطلوبة
assert.strictEqual('readFileSync' in fs, false);
// syncBuiltinESMExports() لا تزيل readFileSync من esmFS
assert.strictEqual('readFileSync' in esmFS, true);
// syncBuiltinESMExports() لا تضيف أسماء
assert.strictEqual(esmFS.newAPI, undefined);
});
ذاكرة التخزين المؤقت لتجميع الوحدات النمطية
[السجل]
الإصدار | التغييرات |
---|---|
v22.8.0 | أضف واجهات JavaScript API الأولية للوصول في وقت التشغيل. |
v22.1.0 | أُضيف في: v22.1.0 |
يمكن تمكين ذاكرة التخزين المؤقت لتجميع الوحدات النمطية إما باستخدام الطريقة module.enableCompileCache()
أو متغير البيئة NODE_COMPILE_CACHE=dir
. بعد تمكينها، متى قام Node.js بتجميع CommonJS أو ECMAScript Module، فإنه سيستخدم ذاكرة التخزين المؤقت لرمز V8 الموجودة على القرص والمخزنة في الدليل المحدد لتسريع التجميع. قد يؤدي هذا إلى إبطاء التحميل الأول لرسم بياني للوحدات النمطية، ولكن قد تحصل عمليات التحميل اللاحقة لنفس الرسم البياني للوحدات النمطية على تسريع كبير إذا لم تتغير محتويات الوحدات النمطية.
لتنظيف ذاكرة التخزين المؤقت للتجميع التي تم إنشاؤها على القرص، ما عليك سوى إزالة دليل ذاكرة التخزين المؤقت. سيتم إعادة إنشاء دليل ذاكرة التخزين المؤقت في المرة التالية التي يتم فيها استخدام نفس الدليل لتخزين ذاكرة التخزين المؤقت للتجميع. لتجنب ملء القرص بذاكرة تخزين مؤقت قديمة، يوصى باستخدام دليل ضمن os.tmpdir()
. إذا تم تمكين ذاكرة التخزين المؤقت للتجميع عن طريق استدعاء module.enableCompileCache()
دون تحديد الدليل، فسيستخدم Node.js متغير البيئة NODE_COMPILE_CACHE=dir
إذا تم تعيينه، أو سيتم الافتراضي إلى path.join(os.tmpdir(), 'node-compile-cache')
بخلاف ذلك. لتحديد موقع دليل ذاكرة التخزين المؤقت للتجميع المستخدم بواسطة مثيل Node.js قيد التشغيل، استخدم module.getCompileCacheDir()
.
حاليًا عند استخدام ذاكرة التخزين المؤقت للتجميع مع تغطية كود JavaScript V8، قد تكون التغطية التي تجمعها V8 أقل دقة في الوظائف التي يتم إلغاء تسلسلها من ذاكرة التخزين المؤقت للتعليمات البرمجية. يوصى بإيقاف تشغيل هذا عند تشغيل الاختبارات لإنشاء تغطية دقيقة.
يمكن تعطيل ذاكرة التخزين المؤقت لتجميع الوحدات النمطية التي تم تمكينها بواسطة متغير البيئة NODE_DISABLE_COMPILE_CACHE=1
. يمكن أن يكون هذا مفيدًا عندما تؤدي ذاكرة التخزين المؤقت للتجميع إلى سلوكيات غير متوقعة أو غير مرغوب فيها (على سبيل المثال، تغطية اختبار أقل دقة).
لا يمكن إعادة استخدام ذاكرة التخزين المؤقت للتجميع التي تم إنشاؤها بواسطة إصدار واحد من Node.js بواسطة إصدار مختلف من Node.js. سيتم تخزين ذاكرة التخزين المؤقت التي تم إنشاؤها بواسطة إصدارات مختلفة من Node.js بشكل منفصل إذا تم استخدام نفس الدليل الأساسي لتخزين ذاكرة التخزين المؤقت، بحيث يمكن أن تتعايش.
في الوقت الحالي، عندما يتم تمكين ذاكرة التخزين المؤقت للتجميع ويتم تحميل وحدة نمطية حديثًا، يتم إنشاء ذاكرة التخزين المؤقت للتعليمات البرمجية من التعليمات البرمجية المجمعة على الفور، ولكن سيتم كتابتها على القرص فقط عندما يكون مثيل Node.js على وشك الخروج. هذا عرضة للتغيير. يمكن استخدام الطريقة module.flushCompileCache()
للتأكد من مسح ذاكرة التخزين المؤقت للتعليمات البرمجية المتراكمة إلى القرص في حالة رغبة التطبيق في إنشاء مثيلات Node.js أخرى والسماح لها بمشاركة ذاكرة التخزين المؤقت قبل فترة طويلة من خروج الأصل.
module.constants.compileCacheStatus
أُضيف في: v22.8.0
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1.1 - تطوير نشط
يتم إرجاع الثوابت التالية كحقل status
في الكائن الذي يتم إرجاعه بواسطة module.enableCompileCache()
للإشارة إلى نتيجة محاولة تمكين ذاكرة التخزين المؤقت لتجميع الوحدة.
ثابت | الوصف |
---|---|
ENABLED | تمكن Node.js من تمكين ذاكرة التخزين المؤقت للتجميع بنجاح. سيتم إرجاع الدليل المستخدم لتخزين ذاكرة التخزين المؤقت للتجميع في الحقل directory في الكائن الذي تم إرجاعه. |
ALREADY_ENABLED | تم بالفعل تمكين ذاكرة التخزين المؤقت للتجميع من قبل، إما عن طريق استدعاء سابق لـ module.enableCompileCache() ، أو عن طريق متغير البيئة NODE_COMPILE_CACHE=dir . سيتم إرجاع الدليل المستخدم لتخزين ذاكرة التخزين المؤقت للتجميع في الحقل directory في الكائن الذي تم إرجاعه. |
FAILED | فشل Node.js في تمكين ذاكرة التخزين المؤقت للتجميع. يمكن أن يكون هذا بسبب عدم وجود إذن لاستخدام الدليل المحدد، أو أنواع مختلفة من أخطاء نظام الملفات. سيتم إرجاع تفاصيل الفشل في الحقل message في الكائن الذي تم إرجاعه. |
DISABLED | لا يمكن لـ Node.js تمكين ذاكرة التخزين المؤقت للتجميع لأن متغير البيئة NODE_DISABLE_COMPILE_CACHE=1 قد تم تعيينه. |
module.enableCompileCache([cacheDir])
أُضيف في: v22.8.0
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1.1 - تطوير نشط
cacheDir
<string> | <undefined> مسار اختياري لتحديد الدليل حيث سيتم تخزين/استرجاع ذاكرة التخزين المؤقت للتجميع.- الإرجاع: <Object>
status
<integer> أحدmodule.constants.compileCacheStatus
message
<string> | <undefined> إذا لم يتمكن Node.js من تمكين ذاكرة التخزين المؤقت للتجميع، فسيحتوي هذا على رسالة الخطأ. يتم تعيينه فقط إذا كانstatus
هوmodule.constants.compileCacheStatus.FAILED
.directory
<string> | <undefined> إذا تم تمكين ذاكرة التخزين المؤقت للتجميع، فسيحتوي هذا على الدليل حيث يتم تخزين ذاكرة التخزين المؤقت للتجميع. يتم تعيينه فقط إذا كانstatus
هوmodule.constants.compileCacheStatus.ENABLED
أوmodule.constants.compileCacheStatus.ALREADY_ENABLED
.
تمكين ذاكرة التخزين المؤقت لتجميع الوحدة في مثيل Node.js الحالي.
إذا لم يتم تحديد cacheDir
، فسيستخدم Node.js إما الدليل المحدد بواسطة متغير البيئة NODE_COMPILE_CACHE=dir
إذا تم تعيينه، أو سيستخدم path.join(os.tmpdir(), 'node-compile-cache')
بخلاف ذلك. بالنسبة لحالات الاستخدام العامة، يوصى باستدعاء module.enableCompileCache()
دون تحديد cacheDir
، بحيث يمكن تجاوز الدليل بواسطة متغير البيئة NODE_COMPILE_CACHE
عند الضرورة.
نظرًا لأن ذاكرة التخزين المؤقت للتجميع من المفترض أن تكون تحسينًا صامتًا غير مطلوب للتطبيق ليكون فعالاً، فقد تم تصميم هذه الطريقة بحيث لا تطرح أي استثناء عند تعذر تمكين ذاكرة التخزين المؤقت للتجميع. بدلاً من ذلك، ستُرجع كائنًا يحتوي على رسالة خطأ في الحقل message
للمساعدة في تصحيح الأخطاء. إذا تم تمكين ذاكرة التخزين المؤقت للتجميع بنجاح، فسيحتوي الحقل directory
في الكائن الذي تم إرجاعه على المسار إلى الدليل حيث يتم تخزين ذاكرة التخزين المؤقت للتجميع. سيكون الحقل status
في الكائن الذي تم إرجاعه أحد قيم module.constants.compileCacheStatus
للإشارة إلى نتيجة محاولة تمكين ذاكرة التخزين المؤقت لتجميع الوحدة.
تؤثر هذه الطريقة فقط على مثيل Node.js الحالي. لتمكينه في سلاسل عمليات العامل الفرعية، إما استدعاء هذه الطريقة في سلاسل عمليات العامل الفرعية أيضًا، أو تعيين قيمة process.env.NODE_COMPILE_CACHE
إلى دليل ذاكرة التخزين المؤقت للتجميع بحيث يمكن وراثة السلوك في العمال الفرعيين. يمكن الحصول على الدليل إما من الحقل directory
الذي تم إرجاعه بواسطة هذه الطريقة، أو باستخدام module.getCompileCacheDir()
.
module.flushCompileCache()
أُضيف في: v23.0.0
تفريغ ذاكرة التخزين المؤقت لتجميع الوحدات المتراكمة من الوحدات النمطية المحملة بالفعل في مثيل Node.js الحالي إلى القرص. يعود هذا بعد انتهاء جميع عمليات نظام الملفات المتدفقة، بغض النظر عما إذا كانت ناجحة أم لا. في حالة وجود أي أخطاء، فسيفشل هذا بصمت، لأن فقدان ذاكرة التخزين المؤقت للتجميع يجب ألا يتعارض مع التشغيل الفعلي للتطبيق.
module.getCompileCacheDir()
أُضيف في: v22.8.0
- الإرجاع: <string> | <undefined> مسار إلى دليل ذاكرة التخزين المؤقت لتجميع الوحدات إذا كان ممكّنًا، أو
undefined
بخلاف ذلك.
خطافات التخصيص
[تاريخ]
الإصدار | التغييرات |
---|---|
v23.5.0 | إضافة دعم للخطافات المتزامنة وفي سلسلة العمليات. |
v20.6.0, v18.19.0 | إضافة خطاف initialize لاستبدال globalPreload . |
v18.6.0, v16.17.0 | إضافة دعم لتسلسل أدوات التحميل. |
v16.12.0 | إزالة getFormat وgetSource وtransformSource وglobalPreload ؛ إضافة خطاف load وخطاف getGlobalPreload . |
v8.8.0 | أُضيف في: v8.8.0 |
[مستقر: 1 - تجريبي]
مستقر: 1 مستقر: 1.2 - مرشح الإصدار (إصدار غير متزامن) الاستقرار: 1.1 - تطوير نشط (إصدار متزامن)
يوجد نوعان من خطافات تخصيص الوحدات النمطية المدعومة حاليًا:
التمكين
يمكن تخصيص تحليل الوحدات النمطية وتحميلها عن طريق:
يمكن تسجيل الخطافات قبل تشغيل كود التطبيق باستخدام العلامة --import
أو العلامة --require
:
node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js
// register-hooks.js
// لا يمكن طلب هذا الملف باستخدام require() إلا إذا لم يكن يحتوي على await من المستوى الأعلى.
// استخدم module.register() لتسجيل الخطافات غير المتزامنة في سلسلة عمليات مخصصة.
import { register } from 'node:module';
register('./hooks.mjs', import.meta.url);
// register-hooks.js
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
// استخدم module.register() لتسجيل الخطافات غير المتزامنة في سلسلة عمليات مخصصة.
register('./hooks.mjs', pathToFileURL(__filename));
// استخدم module.registerHooks() لتسجيل الخطافات المتزامنة في سلسلة العمليات الرئيسية.
import { registerHooks } from 'node:module';
registerHooks({
resolve(specifier, context, nextResolve) { /* implementation */ },
load(url, context, nextLoad) { /* implementation */ },
});
// استخدم module.registerHooks() لتسجيل الخطافات المتزامنة في سلسلة العمليات الرئيسية.
const { registerHooks } = require('node:module');
registerHooks({
resolve(specifier, context, nextResolve) { /* implementation */ },
load(url, context, nextLoad) { /* implementation */ },
});
يمكن أن يكون الملف الذي تم تمريره إلى --import
أو --require
أيضًا عبارة عن تصدير من تبعية:
node --import some-package/register ./my-app.js
node --require some-package/register ./my-app.js
حيث يحتوي some-package
على حقل "exports"
يحدد التصدير /register
لتعيينه إلى ملف يستدعي register()
، مثل مثال register-hooks.js
التالي.
يضمن استخدام --import
أو --require
تسجيل الخطافات قبل استيراد أي ملفات تطبيق، بما في ذلك نقطة دخول التطبيق وأي سلاسل عمليات عاملة بشكل افتراضي أيضًا.
بدلاً من ذلك، يمكن استدعاء register()
وregisterHooks()
من نقطة الدخول، على الرغم من أنه يجب استخدام import()
الديناميكي لأي كود ESM يجب تشغيله بعد تسجيل الخطافات.
import { register } from 'node:module';
register('http-to-https', import.meta.url);
// نظرًا لأن هذا `import()` ديناميكي، فسيتم تشغيل خطافات `http-to-https`
// للتعامل مع `./my-app.js` وأي ملفات أخرى يستوردها أو يطلبها.
await import('./my-app.js');
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
register('http-to-https', pathToFileURL(__filename));
// نظرًا لأن هذا `import()` ديناميكي، فسيتم تشغيل خطافات `http-to-https`
// للتعامل مع `./my-app.js` وأي ملفات أخرى يستوردها أو يطلبها.
import('./my-app.js');
سيتم تشغيل خطافات التخصيص لأي وحدات نمطية يتم تحميلها لاحقًا عن التسجيل والوحدات النمطية التي تشير إليها عبر import
وrequire
المضمن. لا يمكن تخصيص دالة require
التي أنشأها المستخدمون باستخدام module.createRequire()
إلا عن طريق الخطافات المتزامنة.
في هذا المثال، نقوم بتسجيل خطافات http-to-https
، ولكنها ستكون متاحة فقط للوحدات النمطية المستوردة لاحقًا - في هذه الحالة، my-app.js
وأي شيء يشير إليه عبر import
أو require
المضمن في تبعيات CommonJS.
إذا كان import('./my-app.js')
بدلاً من ذلك عبارة عن import './my-app.js'
ثابت، فسيتم تحميل التطبيق بالفعل قبل تسجيل خطافات http-to-https
. وذلك بسبب مواصفات وحدات ES، حيث يتم تقييم عمليات الاستيراد الثابتة من أوراق الشجرة أولاً، ثم الرجوع إلى الجذع. يمكن أن تكون هناك عمليات استيراد ثابتة داخل my-app.js
، والتي لن يتم تقييمها حتى يتم استيراد my-app.js
ديناميكيًا.
إذا تم استخدام الخطافات المتزامنة، فسيتم دعم كل من import
وrequire
وrequire
المستخدم الذي تم إنشاؤه باستخدام createRequire()
.
import { registerHooks, createRequire } from 'node:module';
registerHooks({ /* implementation of synchronous hooks */ });
const require = createRequire(import.meta.url);
// تؤثر الخطافات المتزامنة على دالة import و require() و require() الخاصة بالمستخدم
// التي تم إنشاؤها من خلال createRequire().
await import('./my-app.js');
require('./my-app-2.js');
const { register, registerHooks } = require('node:module');
const { pathToFileURL } = require('node:url');
registerHooks({ /* implementation of synchronous hooks */ });
const userRequire = createRequire(__filename);
// تؤثر الخطافات المتزامنة على دالة import و require() و require() الخاصة بالمستخدم
// التي تم إنشاؤها من خلال createRequire().
import('./my-app.js');
require('./my-app-2.js');
userRequire('./my-app-3.js');
أخيرًا، إذا كان كل ما تريد فعله هو تسجيل الخطافات قبل تشغيل تطبيقك ولا تريد إنشاء ملف منفصل لهذا الغرض، فيمكنك تمرير عنوان URL data:
إلى --import
:
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("http-to-https", pathToFileURL("./"));' ./my-app.js
التسلسل
من الممكن استدعاء register
أكثر من مرة:
// entrypoint.mjs
import { register } from 'node:module';
register('./foo.mjs', import.meta.url);
register('./bar.mjs', import.meta.url);
await import('./my-app.mjs');
// entrypoint.cjs
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const parentURL = pathToFileURL(__filename);
register('./foo.mjs', parentURL);
register('./bar.mjs', parentURL);
import('./my-app.mjs');
في هذا المثال، ستشكل الخطافات المسجلة سلاسل. تعمل هذه السلاسل بأسلوب آخر ما يدخل أول ما يخرج (LIFO). إذا كان كل من foo.mjs
و bar.mjs
يعرّفان خطاف resolve
، فسيتم استدعاؤهما على النحو التالي (لاحظ من اليمين إلى اليسار): الافتراضي الخاص بـ node ← ./foo.mjs
← ./bar.mjs
(بدءًا بـ ./bar.mjs
، ثم ./foo.mjs
، ثم الافتراضي الخاص بـ Node.js). وينطبق الشيء نفسه على جميع الخطافات الأخرى.
تؤثر الخطافات المسجلة أيضًا على register
نفسها. في هذا المثال، سيتم حل bar.mjs
وتحميله عبر الخطافات المسجلة بواسطة foo.mjs
(لأن خطافات foo
ستكون قد أضيفت بالفعل إلى السلسلة). يتيح ذلك أشياء مثل كتابة الخطافات بلغات غير JavaScript، طالما أن الخطافات المسجلة سابقًا تحول إلى JavaScript.
لا يمكن استدعاء طريقة register
من داخل الوحدة النمطية التي تحدد الخطافات.
يعمل تسلسل registerHooks
بالمثل. إذا تم خلط الخطافات المتزامنة وغير المتزامنة، فسيتم دائمًا تشغيل الخطافات المتزامنة أولاً قبل بدء تشغيل الخطافات غير المتزامنة، أي في آخر خطاف متزامن يتم تشغيله، يتضمن خطافه التالي استدعاء الخطافات غير المتزامنة.
// entrypoint.mjs
import { registerHooks } from 'node:module';
const hook1 = { /* implementation of hooks */ };
const hook2 = { /* implementation of hooks */ };
// hook2 run before hook1.
registerHooks(hook1);
registerHooks(hook2);
// entrypoint.cjs
const { registerHooks } = require('node:module');
const hook1 = { /* implementation of hooks */ };
const hook2 = { /* implementation of hooks */ };
// hook2 run before hook1.
registerHooks(hook1);
registerHooks(hook2);
التواصل مع خطافات تخصيص الوحدة النمطية
تعمل الخطافات غير المتزامنة على سلسلة رسائل مخصصة، منفصلة عن سلسلة الرسائل الرئيسية التي تقوم بتشغيل كود التطبيق. هذا يعني أن تغيير المتغيرات العامة لن يؤثر على سلسلة الرسائل الأخرى (سلاسل الرسائل)، ويجب استخدام قنوات الرسائل للتواصل بين سلاسل الرسائل.
يمكن استخدام طريقة register
لتمرير البيانات إلى خطاف initialize
. قد تتضمن البيانات التي تم تمريرها إلى الخطاف كائنات قابلة للتحويل مثل المنافذ.
import { register } from 'node:module';
import { MessageChannel } from 'node:worker_threads';
// يوضح هذا المثال كيف يمكن استخدام قناة رسائل
// للتواصل مع الخطافات، عن طريق إرسال `port2` إلى الخطافات.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
console.log(msg);
});
port1.unref();
register('./my-hooks.mjs', {
parentURL: import.meta.url,
data: { number: 1, port: port2 },
transferList: [port2],
});
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');
// يعرض هذا المثال كيف يمكن استخدام قناة رسائل
// للتواصل مع الخطافات، عن طريق إرسال `port2` إلى الخطافات.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
console.log(msg);
});
port1.unref();
register('./my-hooks.mjs', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
});
يتم تشغيل خطافات الوحدة النمطية المتزامنة على نفس سلسلة الرسائل حيث يتم تشغيل كود التطبيق. يمكنهم تغيير المتغيرات العامة للسياق الذي يتم الوصول إليه بواسطة سلسلة الرسائل الرئيسية مباشرة.
الخطافات
خطافات غير متزامنة مقبولة بواسطة module.register()
يمكن استخدام طريقة register
لتسجيل وحدة نمطية تصدر مجموعة من الخطافات. الخطافات هي وظائف يتم استدعاؤها بواسطة Node.js لتخصيص عملية حل الوحدة النمطية وتحميلها. يجب أن يكون للوظائف المصدرة أسماء وتوقيعات محددة، ويجب تصديرها كصادرات مسماة.
export async function initialize({ number, port }) {
// يتلقى البيانات من `register`.
}
export async function resolve(specifier, context, nextResolve) {
// خذ محدد `import` أو `require` وقم بحله إلى عنوان URL.
}
export async function load(url, context, nextLoad) {
// خذ عنوان URL تم حله وأرجع الكود المصدري المراد تقييمه.
}
يتم تشغيل الخطافات غير المتزامنة في سلسلة رسائل منفصلة، معزولة عن سلسلة الرسائل الرئيسية حيث يتم تشغيل كود التطبيق. هذا يعني أنه نطاق مختلف. قد يتم إنهاء سلسلة رسائل الخطافات بواسطة سلسلة الرسائل الرئيسية في أي وقت، لذلك لا تعتمد على العمليات غير المتزامنة (مثل console.log
) لإكمالها. يتم توريثها إلى العاملين الفرعيين افتراضيًا.
خطافات متزامنة مقبولة بواسطة module.registerHooks()
تمت إضافتها في: الإصدار 23.5.0
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1.1 - تطوير نشط
تقبل طريقة module.registerHooks()
وظائف الخطاف المتزامنة. initialize()
غير مدعوم ولا ضروري، حيث يمكن لمُنفِّذ الخطاف ببساطة تشغيل كود التهيئة مباشرةً قبل استدعاء module.registerHooks()
.
function resolve(specifier, context, nextResolve) {
// خذ مُعرّف `import` أو `require` وحلّه إلى عنوان URL.
}
function load(url, context, nextLoad) {
// خذ عنوان URL محلولًا وأرجع الكود المصدري ليتم تقييمه.
}
يتم تشغيل الخطافات المتزامنة في نفس مؤشر الترابط ونفس المجال حيث يتم تحميل الوحدات النمطية. على عكس الخطافات غير المتزامنة، لا يتم توريثها افتراضيًا في سلاسل عمليات العامل الفرعية، على الرغم من أنه إذا تم تسجيل الخطافات باستخدام ملف تم تحميله مسبقًا بواسطة --import
أو --require
، يمكن لسلاسل عمليات العامل الفرعية أن ترث البرامج النصية التي تم تحميلها مسبقًا عبر وراثة process.execArgv
. راجع وثائق Worker
للحصول على التفاصيل.
في الخطافات المتزامنة، يمكن للمستخدمين توقع اكتمال console.log()
بنفس الطريقة التي يتوقعون بها اكتمال console.log()
في كود الوحدة النمطية.
اصطلاحات الخطافات
تعتبر الخطافات جزءًا من سلسلة، حتى لو كانت هذه السلسلة تتكون من خطاف مخصص واحد فقط (مقدم من المستخدم) والخطاف الافتراضي، الموجود دائمًا. تتداخل وظائف الخطاف: يجب على كل وظيفة دائمًا إرجاع كائن عادي، ويحدث التسلسل نتيجة لاستدعاء كل وظيفة next\<hookName\>()
، وهو مرجع إلى خطاف المحمل اللاحق (بترتيب LIFO).
يتسبب الخطاف الذي يُرجع قيمة تفتقر إلى خاصية مطلوبة في حدوث استثناء. كما يتسبب الخطاف الذي يُرجع دون استدعاء next\<hookName\>()
ودون إرجاع shortCircuit: true
في حدوث استثناء أيضًا. تهدف هذه الأخطاء إلى المساعدة في منع الانقطاعات غير المقصودة في السلسلة. قم بإرجاع shortCircuit: true
من خطاف للإشارة إلى أن السلسلة تنتهي عمدًا عند خطافك.
initialize()
تمت إضافتها في: الإصدار v20.6.0، v18.19.0
data
<any> البيانات منregister(loader, import.meta.url, { data })
.
يتم قبول الخطاف initialize
فقط بواسطة register
. registerHooks()
لا يدعمها ولا يحتاجها لأن التهيئة التي تتم للخطافات المتزامنة يمكن تشغيلها مباشرة قبل استدعاء registerHooks()
.
يوفر الخطاف initialize
طريقة لتعريف دالة مخصصة يتم تشغيلها في سلسلة عمليات الخطافات عند تهيئة وحدة الخطافات. تحدث التهيئة عندما يتم تسجيل وحدة الخطافات عبر register
.
يمكن لهذا الخطاف تلقي بيانات من استدعاء register
، بما في ذلك المنافذ والكائنات الأخرى القابلة للتحويل. يمكن أن تكون القيمة المرجعة لـ initialize
عبارة عن <Promise>، وفي هذه الحالة سيتم انتظارها قبل استئناف تنفيذ سلسلة عمليات التطبيق الرئيسية.
رمز تخصيص الوحدة:
// path-to-my-hooks.js
export async function initialize({ number, port }) {
port.postMessage(`increment: ${number + 1}`);
}
رمز المتصل:
import assert from 'node:assert';
import { register } from 'node:module';
import { MessageChannel } from 'node:worker_threads';
// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
assert.strictEqual(msg, 'increment: 2');
});
port1.unref();
register('./path-to-my-hooks.js', {
parentURL: import.meta.url,
data: { number: 1, port: port2 },
transferList: [port2],
});
const assert = require('node:assert');
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');
// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();
port1.on('message', (msg) => {
assert.strictEqual(msg, 'increment: 2');
});
port1.unref();
register('./path-to-my-hooks.js', {
parentURL: pathToFileURL(__filename),
data: { number: 1, port: port2 },
transferList: [port2],
});
resolve(specifier, context, nextResolve)
[السجل]
الإصدار | التغييرات |
---|---|
v23.5.0 | إضافة دعم للخطافات المتزامنة وداخل السلسلة. |
v21.0.0, v20.10.0, v18.19.0 | تم استبدال الخاصية context.importAssertions بـ context.importAttributes . لا يزال استخدام الاسم القديم مدعومًا وسيصدر تحذيرًا تجريبيًا. |
v18.6.0, v16.17.0 | إضافة دعم لربط خطافات الحل. يجب على كل خطاف إما استدعاء nextResolve() أو تضمين خاصية shortCircuit مضبوطة على true في إرجاعه. |
v17.1.0, v16.14.0 | إضافة دعم لتأكيدات الاستيراد. |
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1.2 - مرشح الإصدار (الإصدار غير المتزامن) الاستقرار: 1.1 - تطوير نشط (الإصدار المتزامن)
specifier
<string>context
<Object>conditions
<string[]> شروط التصدير لـpackage.json
ذات الصلةimportAttributes
<Object> كائن تمثل أزواج قيمه الرئيسية السمات الخاصة بالوحدة المراد استيرادهاparentURL
<string> | <undefined> الوحدة النمطية التي تستورد هذه الوحدة، أو غير معرف إذا كانت هذه هي نقطة إدخال Node.js
nextResolve
<Function> خطافresolve
اللاحق في السلسلة، أو خطافresolve
الافتراضي لـ Node.js بعد آخر خطافresolve
مقدم من المستخدمالإرجاع: <Object> | <Promise> يأخذ الإصدار غير المتزامن إما كائنًا يحتوي على الخصائص التالية، أو
Promise
سيؤدي إلى حل هذا الكائن. يقبل الإصدار المتزامن فقط كائنًا يتم إرجاعه بشكل متزامن.format
<string> | <null> | <undefined> تلميح لخطاف التحميل (قد يتم تجاهله)'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
importAttributes
<Object> | <undefined> سمات الاستيراد المراد استخدامها عند تخزين الوحدة النمطية مؤقتًا (اختياري؛ إذا تم استبعاده فسيتم استخدام الإدخال)shortCircuit
<undefined> | <boolean> إشارة إلى أن هذا الخطاف يعتزم إنهاء سلسلة خطافاتresolve
. افتراضي:false
url
<string> عنوان URL المطلق الذي يحل إليه هذا الإدخال
سلسلة خطاف resolve
مسؤولة عن إخبار Node.js بمكان العثور على عبارة أو تعبير import
معين وكيفية تخزينه مؤقتًا، أو استدعاء require
. يمكنه اختياريًا إرجاع تنسيق (مثل 'module'
) كتلميح إلى خطاف load
. إذا تم تحديد تنسيق، فإن خطاف load
هو المسؤول في النهاية عن توفير قيمة format
النهائية (وهو حر في تجاهل التلميح المقدم من resolve
)؛ إذا قدم resolve
format
، يلزم وجود خطاف load
مخصص حتى لو كان ذلك فقط لتمرير القيمة إلى خطاف load
الافتراضي لـ Node.js.
تعد سمات نوع الاستيراد جزءًا من مفتاح ذاكرة التخزين المؤقت لحفظ الوحدات النمطية التي تم تحميلها في ذاكرة التخزين المؤقت للوحدة النمطية الداخلية. خطاف resolve
مسؤول عن إرجاع كائن importAttributes
إذا كان يجب تخزين الوحدة النمطية مؤقتًا بسمات مختلفة عما كانت موجودة في التعليمات البرمجية المصدر.
الخاصية conditions
في context
عبارة عن مصفوفة من الشروط التي ستستخدم لمطابقة شروط تصدير الحزمة لطلب الحل هذا. يمكن استخدامها للبحث عن تعيينات شرطية في مكان آخر أو لتعديل القائمة عند استدعاء منطق الحل الافتراضي.
توجد دائمًا شروط تصدير الحزمة الحالية في مصفوفة context.conditions
التي تم تمريرها إلى الخطاف. لضمان سلوك حل محدد الوحدة النمطية الافتراضي لـ Node.js عند استدعاء defaultResolve
، يجب أن تتضمن مصفوفة context.conditions
التي تم تمريرها إليه جميع عناصر مصفوفة context.conditions
التي تم تمريرها في الأصل إلى خطاف resolve
.
// الإصدار غير المتزامن المقبول بواسطة module.register().
export async function resolve(specifier, context, nextResolve) {
const { parentURL = null } = context;
if (Math.random() > 0.5) { // بعض الشروط.
// بالنسبة لبعض المحددات أو كلها، قم ببعض المنطق المخصص للحل.
// قم دائمًا بإرجاع كائن بالشكل {url: <string>}.
return {
shortCircuit: true,
url: parentURL ?
new URL(specifier, parentURL).href :
new URL(specifier).href,
};
}
if (Math.random() < 0.5) { // شرط آخر.
// عند استدعاء `defaultResolve`، يمكن تعديل الوسائط. في هذه الحالة
// تتم إضافة قيمة أخرى لمطابقة الصادرات الشرطية.
return nextResolve(specifier, {
...context,
conditions: [...context.conditions, 'another-condition'],
});
}
// قم بتأجيل إلى الخطاف التالي في السلسلة، والذي سيكون
// الحل الافتراضي لـ Node.js إذا كان هذا هو آخر محمل محدد من قبل المستخدم.
return nextResolve(specifier);
}
// الإصدار المتزامن المقبول بواسطة module.registerHooks().
function resolve(specifier, context, nextResolve) {
// مشابه لـ resolve() غير المتزامن أعلاه، نظرًا لأن هذا الإصدار لا يحتوي على
// أي منطق غير متزامن.
}
load(url, context, nextLoad)
[السجل]
الإصدار | التغييرات |
---|---|
v23.5.0 | إضافة دعم للإصدار المتزامن وفي سلسلة العمليات. |
v20.6.0 | إضافة دعم لـ source بتنسيق commonjs . |
v18.6.0, v16.17.0 | إضافة دعم لربط خطافات التحميل. يجب على كل خطاف إما استدعاء nextLoad() أو تضمين خاصية shortCircuit مضبوطة على true في إرجاعه. |
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1.2 - مرشح الإصدار (إصدار غير متزامن) الاستقرار: 1.1 - تطوير نشط (إصدار متزامن)
url
<string> عنوان URL الذي تم إرجاعه بواسطة سلسلةresolve
context
<Object>conditions
<string[]> شروط التصدير لملفpackage.json
ذي الصلةformat
<string> | <null> | <undefined> التنسيق المقدم اختياريًا بواسطة سلسلة خطافresolve
importAttributes
<Object>
nextLoad
<Function> خطافload
اللاحق في السلسلة، أو خطافload
الافتراضي لـ Node.js بعد آخر خطافload
مقدم من المستخدمالإرجاع: <Object> | <Promise> يأخذ الإصدار غير المتزامن إما كائنًا يحتوي على الخصائص التالية، أو
Promise
سيتم حله إلى مثل هذا الكائن. يقبل الإصدار المتزامن فقط كائنًا يتم إرجاعه بشكل متزامن.format
<string>shortCircuit
<undefined> | <boolean> إشارة إلى أن هذا الخطاف يعتزم إنهاء سلسلة خطافاتload
. افتراضي:false
source
<string> | <ArrayBuffer> | <TypedArray> مصدر Node.js لتقييمه
يوفر خطاف load
طريقة لتحديد طريقة مخصصة لتحديد كيفية تفسير عنوان URL واسترجاعه وتحليله. وهو مسؤول أيضًا عن التحقق من صحة سمات الاستيراد.
يجب أن تكون القيمة النهائية لـ format
واحدة مما يلي:
format | الوصف | الأنواع المقبولة لـ source التي تم إرجاعها بواسطة load |
---|---|---|
'builtin' | تحميل وحدة Node.js مدمجة | غير قابل للتطبيق |
'commonjs' | تحميل وحدة Node.js CommonJS | { string , ArrayBuffer , TypedArray , null , undefined } |
'json' | تحميل ملف JSON | { string , ArrayBuffer , TypedArray } |
'module' | تحميل وحدة ES | { string , ArrayBuffer , TypedArray } |
'wasm' | تحميل وحدة WebAssembly | { ArrayBuffer , TypedArray } |
يتم تجاهل قيمة source للنوع 'builtin' لأنه لا يمكن حاليًا استبدال قيمة وحدة Node.js (أساسية) مدمجة. |
تحذير في خطاف load
غير المتزامن
عند استخدام خطاف load
غير المتزامن، فإن حذف أو توفير source
لـ 'commonjs'
له تأثيرات مختلفة جدًا:
- عند توفير
source
، سيتم معالجة جميع استدعاءاتrequire
من هذه الوحدة بواسطة محمل ESM مع خطافاتresolve
وload
المسجلة؛ سيتم معالجة جميع استدعاءاتrequire.resolve
من هذه الوحدة بواسطة محمل ESM مع خطافاتresolve
المسجلة؛ ستكون مجموعة فرعية فقط من واجهة برمجة تطبيقات CommonJS متاحة (على سبيل المثال، لا يوجدrequire.extensions
، ولا يوجدrequire.cache
، ولا يوجدrequire.resolve.paths
) ولن يتم تطبيق التصحيح المؤقت على محمل وحدة CommonJS. - إذا كان
source
غير معرف أوnull
، فسيتم التعامل معه بواسطة محمل وحدة CommonJS ولن تمر استدعاءاتrequire
/require.resolve
عبر الخطافات المسجلة. هذا السلوك لـsource
القيمة الفارغة مؤقت - في المستقبل، لن يتم دعمsource
القيمة الفارغة.
لا تنطبق هذه التحذيرات على خطاف load
المتزامن، وفي هذه الحالة تكون المجموعة الكاملة من واجهات برمجة تطبيقات CommonJS متاحة لوحدات CommonJS المخصصة، و require
/require.resolve
تمر دائمًا عبر الخطافات المسجلة.
يقوم تطبيق load
غير المتزامن الداخلي لـ Node.js، وهو قيمة next
للخطاف الأخير في سلسلة load
، بإرجاع null
لـ source
عندما يكون format
هو 'commonjs'
للتوافق مع الإصدارات السابقة. إليك مثال على خطاف من شأنه أن يختار استخدام السلوك غير الافتراضي:
import { readFile } from 'node:fs/promises';
// Asynchronous version accepted by module.register(). This fix is not needed
// for the synchronous version accepted by module.registerSync().
export async function load(url, context, nextLoad) {
const result = await nextLoad(url, context);
if (result.format === 'commonjs') {
result.source ??= await readFile(new URL(result.responseURL ?? url));
}
return result;
}
لا ينطبق هذا أيضًا على خطاف load
المتزامن، وفي هذه الحالة يحتوي source
الذي تم إرجاعه على رمز المصدر الذي تم تحميله بواسطة الخطاف التالي، بغض النظر عن تنسيق الوحدة.
- كائن
ArrayBuffer
المحدد هوSharedArrayBuffer
. - كائن
TypedArray
المحدد هوUint8Array
.
إذا لم تكن قيمة المصدر لتنسيق قائم على النص (أي 'json'
و 'module'
) سلسلة، فسيتم تحويلها إلى سلسلة باستخدام util.TextDecoder
.
يوفر خطاف load
طريقة لتحديد طريقة مخصصة لاسترداد رمز المصدر لعنوان URL الذي تم حله. سيسمح هذا للمحمل بتجنب قراءة الملفات من القرص. يمكن استخدامه أيضًا لتعيين تنسيق غير معترف به إلى تنسيق مدعوم، على سبيل المثال yaml
إلى module
.
// Asynchronous version accepted by module.register().
export async function load(url, context, nextLoad) {
const { format } = context;
if (Math.random() > 0.5) { // Some condition
/*
For some or all URLs, do some custom logic for retrieving the source.
Always return an object of the form {
format: <string>,
source: <string|buffer>,
}.
*/
return {
format,
shortCircuit: true,
source: '...',
};
}
// Defer to the next hook in the chain.
return nextLoad(url);
}
// Synchronous version accepted by module.registerHooks().
function load(url, context, nextLoad) {
// Similar to the asynchronous load() above, since that one does not have
// any asynchronous logic.
}
في سيناريو أكثر تقدمًا، يمكن استخدام هذا أيضًا لتحويل مصدر غير مدعوم إلى مصدر مدعوم (انظر أمثلة أدناه).
أمثلة
يمكن استخدام خطافات تخصيص الوحدة المختلفة معًا لإنجاز تخصيصات واسعة النطاق لسلوكيات تحميل وتقييم كود Node.js.
الاستيراد من HTTPS
يقوم الخطاف أدناه بتسجيل الخطافات لتمكين الدعم الأولي لمثل هذه المحددات. على الرغم من أن هذا قد يبدو تحسنًا كبيرًا في وظائف Node.js الأساسية، إلا أن هناك عيوبًا كبيرة في استخدام هذه الخطافات فعليًا: الأداء أبطأ بكثير من تحميل الملفات من القرص، ولا يوجد تخزين مؤقت، ولا يوجد أمان.
// https-hooks.mjs
import { get } from 'node:https';
export function load(url, context, nextLoad) {
// لكي يتم تحميل JavaScript عبر الشبكة، نحتاج إلى جلبها
// وإعادتها.
if (url.startsWith('https://')) {
return new Promise((resolve, reject) => {
get(url, (res) => {
let data = '';
res.setEncoding('utf8');
res.on('data', (chunk) => data += chunk);
res.on('end', () => resolve({
// يفترض هذا المثال أن جميع JavaScript المقدمة عبر الشبكة عبارة عن وحدة ES
// كود.
format: 'module',
shortCircuit: true,
source: data,
}));
}).on('error', (err) => reject(err));
});
}
// دع Node.js يتعامل مع جميع عناوين URL الأخرى.
return nextLoad(url);
}
// main.mjs
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js';
console.log(VERSION);
مع وحدة الخطافات السابقة، فإن تشغيل node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs
يطبع الإصدار الحالي من CoffeeScript لكل وحدة في عنوان URL في main.mjs
.
التحويل البرمجي
يمكن تحويل المصادر الموجودة بتنسيقات لا تفهمها Node.js إلى JavaScript باستخدام load
hook.
هذا أقل أداءً من تحويل ملفات المصدر برمجياً قبل تشغيل Node.js؛ يجب استخدام خطافات المحول البرمجي فقط لأغراض التطوير والاختبار.
نسخة غير متزامنة
// coffeescript-hooks.mjs
import { readFile } from 'node:fs/promises';
import { dirname, extname, resolve as resolvePath } from 'node:path';
import { cwd } from 'node:process';
import { fileURLToPath, pathToFileURL } from 'node:url';
import coffeescript from 'coffeescript';
const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;
export async function load(url, context, nextLoad) {
if (extensionsRegex.test(url)) {
// يمكن أن تكون ملفات CoffeeScript إما CommonJS أو وحدات ES، لذلك نريد أن
// تتعامل Node.js مع أي ملف CoffeeScript بنفس طريقة تعاملها مع ملف .js في
// نفس الموقع. لتحديد كيف ستفسر Node.js ملف .js عشوائيًا، ابحث في نظام
// الملفات عن أقرب ملف package.json أبوي واقرأ الحقل "type" الخاص به.
const format = await getPackageType(url);
const { source: rawSource } = await nextLoad(url, { ...context, format });
// يقوم هذا الخطاف بتحويل كود مصدر CoffeeScript إلى كود مصدر JavaScript
// لجميع ملفات CoffeeScript المستوردة.
const transformedSource = coffeescript.compile(rawSource.toString(), url);
return {
format,
shortCircuit: true,
source: transformedSource,
};
}
// دع Node.js يتعامل مع جميع عناوين URL الأخرى.
return nextLoad(url);
}
async function getPackageType(url) {
// `url` هو فقط مسار ملف أثناء التكرار الأول عند تمرير عنوان url الذي تم حله
// من الخطاف load()
// سيحتوي مسار الملف الفعلي من load() على امتداد ملف كما هو مطلوب
// حسب المواصفة
// سيتحقق هذا التحقق البسيط من صحة ما إذا كانت `url` تحتوي على امتداد ملف
// لمعظم المشاريع ولكنه لا يغطي بعض الحالات الشاذة (مثل
// الملفات التي لا تحتوي على امتداد أو عنوان url ينتهي بمسافة لاحقة)
const isFilePath = !!extname(url);
// إذا كان مسار ملف، فاحصل على الدليل الموجود فيه
const dir = isFilePath ?
dirname(fileURLToPath(url)) :
url;
// قم بتركيب مسار ملف إلى package.json في نفس الدليل،
// والذي قد يكون موجودًا أو غير موجود
const packagePath = resolvePath(dir, 'package.json');
// حاول قراءة package.json الذي قد يكون غير موجود
const type = await readFile(packagePath, { encoding: 'utf8' })
.then((filestring) => JSON.parse(filestring).type)
.catch((err) => {
if (err?.code !== 'ENOENT') console.error(err);
});
// إذا كان package.json موجودًا ويحتوي على حقل `type` بقيمة، فها هو
if (type) return type;
// وإلا، (إذا لم يكن في الجذر) فاستمر في فحص الدليل التالي لأعلى
// إذا كان في الجذر، فتوقف وأرجع false
return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
}
النسخة المتزامنة
// coffeescript-sync-hooks.mjs
import { readFileSync } from 'node:fs/promises';
import { registerHooks } from 'node:module';
import { dirname, extname, resolve as resolvePath } from 'node:path';
import { cwd } from 'node:process';
import { fileURLToPath, pathToFileURL } from 'node:url';
import coffeescript from 'coffeescript';
const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;
function load(url, context, nextLoad) {
if (extensionsRegex.test(url)) {
const format = getPackageType(url);
const { source: rawSource } = nextLoad(url, { ...context, format });
const transformedSource = coffeescript.compile(rawSource.toString(), url);
return {
format,
shortCircuit: true,
source: transformedSource,
};
}
return nextLoad(url);
}
function getPackageType(url) {
const isFilePath = !!extname(url);
const dir = isFilePath ? dirname(fileURLToPath(url)) : url;
const packagePath = resolvePath(dir, 'package.json');
let type;
try {
const filestring = readFileSync(packagePath, { encoding: 'utf8' });
type = JSON.parse(filestring).type;
} catch (err) {
if (err?.code !== 'ENOENT') console.error(err);
}
if (type) return type;
return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
}
registerHooks({ load });
تشغيل الخطافات
# main.coffee {#maincoffee}
import { scream } from './scream.coffee'
console.log scream 'hello, world'
import { version } from 'node:process'
console.log "Brought to you by Node.js version #{version}"
# scream.coffee {#screamcoffee}
export scream = (str) -> str.toUpperCase()
مع وحدات الخطافات السابقة، تشغيل node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee
أو node --import ./coffeescript-sync-hooks.mjs ./main.coffee
يتسبب في تحويل main.coffee
إلى JavaScript بعد تحميل الكود المصدري الخاص به من القرص ولكن قبل تنفيذه بواسطة Node.js؛ وهكذا بالنسبة لأي ملفات .coffee
أو .litcoffee
أو .coffee.md
مشار إليها عبر عبارات import
لأي ملف تم تحميله.
خرائط الاستيراد
عرّفت الأمثلة السابقة وظائف load
. هذا مثال على وظيفة resolve
. يقوم هذا المرفق بقراءة ملف import-map.json
الذي يحدد المعرفات التي يجب تجاوزها إلى عناوين URL أخرى (هذا تنفيذ مبسط للغاية لمجموعة فرعية صغيرة من مواصفات "خرائط الاستيراد").
الإصدار غير المتزامن
// import-map-hooks.js
import fs from 'node:fs/promises';
const { imports } = JSON.parse(await fs.readFile('import-map.json'));
export async function resolve(specifier, context, nextResolve) {
if (Object.hasOwn(imports, specifier)) {
return nextResolve(imports[specifier], context);
}
return nextResolve(specifier, context);
}
الإصدار المتزامن
// import-map-sync-hooks.js
import fs from 'node:fs/promises';
import module from 'node:module';
const { imports } = JSON.parse(fs.readFileSync('import-map.json', 'utf-8'));
function resolve(specifier, context, nextResolve) {
if (Object.hasOwn(imports, specifier)) {
return nextResolve(imports[specifier], context);
}
return nextResolve(specifier, context);
}
module.registerHooks({ resolve });
استخدام المرفقات
مع هذه الملفات:
// main.js
import 'a-module';
// import-map.json
{
"imports": {
"a-module": "./some-module.js"
}
}
// some-module.js
console.log('some module!');
تشغيل node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js
أو node --import ./import-map-sync-hooks.js main.js
يجب أن يطبع some module!
.
دعم الخريطة المصدر v3
أضيف في: v13.7.0, v12.17.0
[مستقر: 1 - تجريبي]
مستقر: 1 الاستقرار: 1 - تجريبي
مساعدون للتفاعل مع ذاكرة التخزين المؤقت للخريطة المصدر. يتم ملء ذاكرة التخزين المؤقت هذه عند تمكين تحليل الخريطة المصدر والعثور على توجيهات تضمين الخريطة المصدر في تذييل الوحدة.
لتمكين تحليل الخريطة المصدر، يجب تشغيل Node.js بالعلامة --enable-source-maps
، أو مع تمكين تغطية التعليمات البرمجية عن طريق تعيين NODE_V8_COVERAGE=dir
.
// module.mjs
// في وحدة ECMAScript
import { findSourceMap, SourceMap } from 'node:module';
// module.cjs
// في وحدة CommonJS
const { findSourceMap, SourceMap } = require('node:module');
module.findSourceMap(path)
تمت الإضافة في: v13.7.0، v12.17.0
path
<string>- الإرجاع: <module.SourceMap> | <undefined> يُرجع
module.SourceMap
إذا تم العثور على خريطة مصدر، وundefined
بخلاف ذلك.
path
هو المسار المُحلل للملف الذي يجب جلب خريطة المصدر المقابلة له.
الفئة: module.SourceMap
تمت الإضافة في: v13.7.0، v12.17.0
new SourceMap(payload[, { lineLengths }])
{#new-sourcemappayload-{-linelengths-}}
payload
<Object>lineLengths
<number[]>
ينشئ مثيلاً جديدًا من sourceMap
.
payload
هو كائن بمفاتيح تطابق تنسيق خريطة المصدر v3:
file
: <string>version
: <number>sources
: <string[]>sourcesContent
: <string[]>names
: <string[]>mappings
: <string>sourceRoot
: <string>
lineLengths
هي مصفوفة اختيارية لطول كل سطر في الكود الذي تم إنشاؤه.
sourceMap.payload
- الإرجاع: <Object>
Getter للحمولة المستخدمة لإنشاء مثيل SourceMap
.
sourceMap.findEntry(lineOffset, columnOffset)
lineOffset
<number> إزاحة رقم السطر المفهرسة بالصفر في المصدر المُنشأcolumnOffset
<number> إزاحة رقم العمود المفهرسة بالصفر في المصدر المُنشأ- Returns: <Object>
بالنظر إلى إزاحة السطر وإزاحة العمود في ملف المصدر المُنشأ، تُرجع كائنًا يمثل نطاق SourceMap في الملف الأصلي إذا تم العثور عليه، أو كائنًا فارغًا إذا لم يتم العثور عليه.
يحتوي الكائن المُرجع على المفاتيح التالية:
- generatedLine: <number> إزاحة السطر لبداية النطاق في المصدر المُنشأ
- generatedColumn: <number> إزاحة العمود لبداية النطاق في المصدر المُنشأ
- originalSource: <string> اسم ملف المصدر الأصلي، كما هو مُبلغ عنه في SourceMap
- originalLine: <number> إزاحة السطر لبداية النطاق في المصدر الأصلي
- originalColumn: <number> إزاحة العمود لبداية النطاق في المصدر الأصلي
- name: <string>
تمثل القيمة المرجعة النطاق الخام كما يظهر في SourceMap، بناءً على الإزاحات المفهرسة بالصفر، وليس أرقام الأسطر والأعمدة المفهرسة بالواحد كما تظهر في رسائل الخطأ وكائنات CallSite.
للحصول على أرقام الأسطر والأعمدة المفهرسة بالواحد المقابلة من lineNumber و columnNumber كما يتم الإبلاغ عنها بواسطة مكدسات الأخطاء وكائنات CallSite، استخدم sourceMap.findOrigin(lineNumber, columnNumber)
sourceMap.findOrigin(lineNumber, columnNumber)
lineNumber
<number> رقم السطر ذو الفهرس 1 لموقع الاستدعاء في المصدر المُنشأcolumnNumber
<number> رقم العمود ذو الفهرس 1 لموقع الاستدعاء في المصدر المُنشأ- الإرجاع: <Object>
بالنظر إلى lineNumber
و columnNumber
بفهرس 1 من موقع استدعاء في المصدر المُنشأ، ابحث عن موقع استدعاء مماثل في المصدر الأصلي.
إذا لم يتم العثور على lineNumber
و columnNumber
المقدمين في أي خريطة مصدر، فسيتم إرجاع كائن فارغ. بخلاف ذلك، يحتوي الكائن الذي تم إرجاعه على المفاتيح التالية:
- name: <string> | <undefined> اسم النطاق في خريطة المصدر، إذا تم توفيره
- fileName: <string> اسم ملف المصدر الأصلي، كما هو مُبلغ عنه في SourceMap
- lineNumber: <number> رقم السطر ذو الفهرس 1 لموقع الاستدعاء المطابق في المصدر الأصلي
- columnNumber: <number> رقم العمود ذو الفهرس 1 لموقع الاستدعاء المطابق في المصدر الأصلي