Skip to content

الوحدات: الحزم

[التاريخ]

الإصدارالتغييرات
v14.13.0, v12.20.0إضافة دعم لأنماط "exports".
v14.6.0, v12.19.0إضافة حقل "imports" للحزمة.
v13.7.0, v12.17.0إلغاء وضع علامة على عمليات التصدير الشرطية.
v13.7.0, v12.16.0إزالة خيار --experimental-conditional-exports. في الإصدار 12.16.0، لا تزال عمليات التصدير الشرطية خلف علامة --experimental-modules.
v13.6.0, v12.16.0إلغاء وضع علامة على الإشارة الذاتية إلى حزمة باستخدام اسمها.
v12.7.0تقديم حقل "exports" في package.json كبديل أقوى للحقل الكلاسيكي "main".
v12.0.0إضافة دعم لوحدات ES باستخدام امتداد الملف .js عبر حقل "type" في package.json.

مقدمة

الحزمة عبارة عن شجرة مجلدات موصوفة بواسطة ملف package.json. تتكون الحزمة من المجلد الذي يحتوي على ملف package.json وجميع المجلدات الفرعية حتى المجلد التالي الذي يحتوي على ملف package.json آخر، أو مجلد باسم node_modules.

توفر هذه الصفحة إرشادات لمؤلفي الحزم الذين يكتبون ملفات package.json بالإضافة إلى مرجع لحقول package.json التي تحددها Node.js.

تحديد نظام الوحدات

مقدمة

ستتعامل Node.js مع ما يلي كوحدات ES عند تمريرها إلى node كمدخل أولي، أو عند الإشارة إليها بواسطة عبارات import أو تعبيرات import():

  • الملفات ذات الامتداد .mjs.
  • الملفات ذات الامتداد .js عندما يحتوي أقرب ملف package.json أصلي على حقل "type" ذي مستوى أعلى بقيمة "module".
  • السلاسل التي يتم تمريرها كوسيطة إلى --eval، أو يتم تمريرها إلى node عبر STDIN، مع العلامة --input-type=module.
  • الكود الذي يحتوي على بناء جملة يتم تحليله بنجاح فقط كوحدات ES، مثل عبارات import أو export أو import.meta، بدون علامة صريحة لكيفية تفسيرها. العلامات الصريحة هي امتدادات .mjs أو .cjs، وحقول "type" في package.json بقيم "module" أو "commonjs"، أو العلامة --input-type. يتم دعم تعبيرات import() الديناميكية في وحدات CommonJS أو ES ولن تجبر ملفًا على أن يتم التعامل معه كوحدة ES. انظر اكتشاف بناء الجملة.

ستتعامل Node.js مع ما يلي كوحدات CommonJS عند تمريرها إلى node كمدخل أولي، أو عند الإشارة إليها بواسطة عبارات import أو تعبيرات import():

  • الملفات ذات الامتداد .cjs.
  • الملفات ذات الامتداد .js عندما يحتوي أقرب ملف package.json أصلي على حقل "type" ذي مستوى أعلى بقيمة "commonjs".
  • السلاسل التي يتم تمريرها كوسيطة إلى --eval أو --print، أو يتم تمريرها إلى node عبر STDIN، مع العلامة --input-type=commonjs.
  • الملفات ذات الامتداد .js بدون ملف package.json أصلي أو حيث يفتقر أقرب ملف package.json أصلي إلى حقل type، وحيث يمكن تقييم الكود بنجاح كوحدة CommonJS. بمعنى آخر، تحاول Node.js تشغيل هذه الملفات "الغامضة" كوحدة CommonJS أولاً، وستعيد محاولة تقييمها كوحدات ES إذا فشل التقييم كوحدة CommonJS لأن المحلل اللغوي وجد بناء جملة وحدة ES.

يتحمل كتابة بناء جملة وحدة ES في الملفات "الغامضة" تكلفة أداء، وبالتالي يتم تشجيع المؤلفين على أن يكونوا صريحين قدر الإمكان. على وجه الخصوص، يجب على مؤلفي الحزم دائمًا تضمين حقل "type" في ملفات package.json الخاصة بهم، حتى في الحزم التي تكون فيها جميع المصادر عبارة عن وحدات CommonJS. إن كونك صريحًا بشأن نوع الحزمة سيؤدي إلى حماية الحزمة في المستقبل في حالة تغير النوع الافتراضي لـ Node.js، وسيسهل أيضًا على أدوات البناء ومحملات تحديد كيفية تفسير الملفات الموجودة في الحزمة.

كشف البنية

[التاريخ]

الإصدارالتغييرات
الإصدار 22.7.0تم تمكين كشف البنية افتراضيًا.
الإصداران 21.1.0 و 20.10.0تمت إضافته في: الإصدار 21.1.0، الإصدار 20.10.0

[مستقر: 1 - تجريبي]

مستقر: 1 الاستقرار: 1.2 - مرشح الإصدار

سوف يقوم Node.js بفحص كود المصدر للإدخال الغامض لتحديد ما إذا كان يحتوي على بنية وحدة ES؛ إذا تم الكشف عن هذه البنية، فسوف يتم التعامل مع الإدخال كوحدة ES.

يتم تعريف الإدخال الغامض على النحو التالي:

  • الملفات ذات الامتداد .js أو بدون امتداد؛ وإما عدم وجود ملف package.json متحكم أو ملف يفتقر إلى حقل type.
  • إدخال السلسلة (--eval أو STDIN) عندما لا يتم تحديد --input-type.

يتم تعريف بنية وحدة ES على أنها بنية ستطرح خطأ عند تقييمها كوحدة CommonJS. وهذا يشمل ما يلي:

  • عبارات import (ولكن ليس تعبيرات import()، وهي صالحة في CommonJS).
  • عبارات export.
  • مراجع import.meta.
  • await في المستوى الأعلى من الوحدة النمطية.
  • إعادة التصريحات المعجمية لمتغيرات برنامج تضمين CommonJS (require، module، exports، __dirname، __filename).

مُحمّلات الوحدات

لدى Node.js نظامان لحل المحدد وتحميل الوحدات.

هناك مُحمّل وحدة CommonJS:

  • إنه متزامن تمامًا.
  • إنه مسؤول عن معالجة استدعاءات require().
  • يمكن إجراء تعديلات عليه (monkey patchable).
  • إنه يدعم المجلدات كوحدات.
  • عند حل المحدد، إذا لم يتم العثور على تطابق تام، فسيحاول إضافة امتدادات (.js و .json وأخيرًا .node) ثم يحاول حل المجلدات كوحدات.
  • إنه يعامل .json كملفات نص JSON.
  • يتم تفسير ملفات .node على أنها وحدات إضافية مُجمّعة يتم تحميلها باستخدام process.dlopen().
  • إنه يعامل جميع الملفات التي تفتقر إلى امتدادات .json أو .node كملفات نص JavaScript.
  • لا يمكن استخدامه إلا لتحميل وحدات ECMAScript من وحدات CommonJS إذا كان مخطط الوحدة متزامنًا (أي لا يحتوي على await على المستوى الأعلى). عند استخدامه لتحميل ملف نص JavaScript ليس وحدة ECMAScript، فسيتم تحميل الملف كوحدة CommonJS.

هناك مُحمّل وحدة ECMAScript:

  • إنه غير متزامن، إلا إذا كان يستخدم لتحميل الوحدات لـ require().
  • إنه مسؤول عن معالجة عبارات import وتعبيرات import().
  • لا يمكن إجراء تعديلات عليه، ويمكن تخصيصه باستخدام خطافات التحميل.
  • إنه لا يدعم المجلدات كوحدات، يجب تحديد فهارس الدليل (مثل './startup/index.js') بالكامل.
  • إنه لا يقوم بالبحث عن الامتدادات. يجب توفير امتداد الملف عندما يكون المحدد عبارة عن عنوان URL للملف النسبي أو المطلق.
  • يمكنه تحميل وحدات JSON، ولكن يلزم وجود سمة نوع الاستيراد.
  • إنه يقبل فقط امتدادات .js و .mjs و .cjs لملفات نص JavaScript.
  • يمكن استخدامه لتحميل وحدات JavaScript CommonJS. يتم تمرير هذه الوحدات من خلال cjs-module-lexer لمحاولة تحديد الصادرات المسماة، والتي تكون متاحة إذا كان من الممكن تحديدها من خلال التحليل الثابت. يتم تحويل عناوين URL الخاصة بوحدات CommonJS المستوردة إلى مسارات مطلقة ثم يتم تحميلها عبر مُحمّل وحدة CommonJS.

package.json وامتدادات الملفات

داخل الحزمة، يحدد حقل "type" في package.json الطريقة التي يجب أن تفسر بها Node.js ملفات .js. إذا لم يكن لملف package.json حقل "type"، فسيتم التعامل مع ملفات .js على أنها CommonJS.

تشير قيمة "type" في package.json التي تساوي "module" إلى Node.js لتفسير ملفات .js داخل تلك الحزمة على أنها تستخدم صيغة وحدة ES.

ينطبق الحقل "type" ليس فقط على نقاط الدخول الأولية (node my-app.js) ولكن أيضًا على الملفات المشار إليها بواسطة عبارات import وتعبيرات import().

js
// my-app.js، يتم التعامل معه كوحدة ES لأن هناك ملف package.json
// في نفس المجلد مع "type": "module".

import './startup/init.js';
// تم التحميل كوحدة ES نظرًا لأن ./startup لا يحتوي على ملف package.json،
// وبالتالي يرث قيمة "type" من مستوى أعلى.

import 'commonjs-package';
// تم التحميل كـ CommonJS نظرًا لأن ./node_modules/commonjs-package/package.json
// يفتقر إلى حقل "type" أو يحتوي على "type": "commonjs".

import './node_modules/commonjs-package/index.js';
// تم التحميل كـ CommonJS نظرًا لأن ./node_modules/commonjs-package/package.json
// يفتقر إلى حقل "type" أو يحتوي على "type": "commonjs".

يتم دائمًا تحميل الملفات التي تنتهي بـ .mjs كوحدات ES بغض النظر عن أقرب package.json أصل.

يتم دائمًا تحميل الملفات التي تنتهي بـ .cjs كـ CommonJS بغض النظر عن أقرب package.json أصل.

js
import './legacy-file.cjs';
// تم التحميل كـ CommonJS نظرًا لأنه يتم دائمًا تحميل .cjs كـ CommonJS.

import 'commonjs-package/src/index.mjs';
// تم التحميل كوحدة ES نظرًا لأنه يتم دائمًا تحميل .mjs كوحدة ES.

يمكن استخدام امتدادات .mjs و .cjs لخلط الأنواع داخل نفس الحزمة:

  • داخل حزمة "type": "module"، يمكن توجيه Node.js لتفسير ملف معين على أنه CommonJS عن طريق تسميته بامتداد .cjs (نظرًا لأنه يتم التعامل مع كل من ملفات .js و .mjs كوحدات ES داخل حزمة "module").
  • داخل حزمة "type": "commonjs"، يمكن توجيه Node.js لتفسير ملف معين على أنه وحدة ES عن طريق تسميته بامتداد .mjs (نظرًا لأنه يتم التعامل مع كل من ملفات .js و .cjs على أنها CommonJS داخل حزمة "commonjs").

علم --input-type

أُضيف في: الإصدار 12.0.0

تُعامل السلاسل النصية التي يتم تمريرها كوسيطة إلى علم --eval (أو -e)، أو التي يتم تمريرها عبر الأنابيب إلى node عبر STDIN، كوحدات ES modules عند تعيين علم --input-type=module.

bash
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module

لإكمال الصورة، يوجد أيضًا علم --input-type=commonjs، لتشغيل الإدخال كسلسلة نصية كوحدة CommonJS بشكل صريح. هذا هو السلوك الافتراضي إذا لم يتم تحديد علم --input-type.

تحديد مدير الحزم

[مستقر: 1 - تجريبي]

مستقر: 1 الاستقرار: 1 - تجريبي

بينما يُتوقع أن تكون جميع مشاريع Node.js قابلة للتثبيت بواسطة جميع مديري الحزم بمجرد نشرها، غالبًا ما يُطلب من فرق التطوير الخاصة بهم استخدام مدير حزم معين. لتسهيل هذه العملية، تأتي Node.js مع أداة تسمى Corepack تهدف إلى جعل جميع مديري الحزم متاحين بشفافية في بيئتك - بشرط أن يكون لديك Node.js مثبتًا.

افتراضيًا، لن يفرض Corepack أي مدير حزم معين وسيستخدم الإصدارات العامة "الأخيرة الجيدة المعروفة" المرتبطة بكل إصدار من Node.js، ولكن يمكنك تحسين هذه التجربة عن طريق تعيين حقل "packageManager" في package.json الخاص بمشروعك.

نقاط دخول الحزمة

في ملف package.json الخاص بالحزمة، يمكن لحقلين تحديد نقاط دخول للحزمة: "main" و "exports". ينطبق كلا الحقلين على نقاط دخول وحدة ES ووحدة CommonJS.

الحقل "main" مدعوم في جميع إصدارات Node.js، لكن قدراته محدودة: فهو يحدد فقط نقطة الدخول الرئيسية للحزمة.

يوفر "exports" بديلاً حديثًا لـ "main" مما يسمح بتحديد نقاط دخول متعددة، ودعم دقة الدخول الشرطية بين البيئات، و منع أي نقاط دخول أخرى بخلاف تلك المحددة في "exports". يسمح هذا التغليف لمؤلفي الوحدات النمطية بتحديد الواجهة العامة لحزمتهم بوضوح.

بالنسبة للحزم الجديدة التي تستهدف الإصدارات المدعومة حاليًا من Node.js، يوصى باستخدام الحقل "exports". بالنسبة للحزم التي تدعم Node.js 10 والإصدارات الأقدم، يلزم وجود الحقل "main". إذا تم تحديد كل من "exports" و "main"، فإن الحقل "exports" له الأسبقية على "main" في الإصدارات المدعومة من Node.js.

يمكن استخدام عمليات التصدير الشرطية داخل "exports" لتحديد نقاط دخول حزمة مختلفة لكل بيئة، بما في ذلك ما إذا كانت الحزمة مشار إليها عبر require أو عبر import. لمزيد من المعلومات حول دعم كل من وحدات CommonJS ووحدات ES في حزمة واحدة، يرجى الرجوع إلى قسم حزم CommonJS/ES المزدوجة.

ستمنع الحزم الحالية التي تقدم الحقل "exports" مستهلكي الحزمة من استخدام أي نقاط دخول غير محددة، بما في ذلك package.json (على سبيل المثال require('your-package/package.json')). من المحتمل أن يكون هذا تغييرًا جذريًا.

لجعل تقديم "exports" غير جذري، تأكد من تصدير كل نقطة دخول مدعومة سابقًا. من الأفضل تحديد نقاط الدخول بشكل صريح بحيث تكون واجهة برمجة التطبيقات العامة للحزمة محددة جيدًا. على سبيل المثال، يمكن للمشروع الذي قام سابقًا بتصدير main و lib و feature و package.json استخدام package.exports التالي:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}

بدلاً من ذلك، يمكن للمشروع اختيار تصدير مجلدات كاملة مع مسارات فرعية موسعة وبدونها باستخدام أنماط التصدير:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/*": "./lib/*.js",
    "./lib/*.js": "./lib/*.js",
    "./feature": "./feature/index.js",
    "./feature/*": "./feature/*.js",
    "./feature/*.js": "./feature/*.js",
    "./package.json": "./package.json"
  }
}

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

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
}

نقطة تصدير الإدخال الرئيسية

عند كتابة حزمة جديدة، يُوصى باستخدام حقل "exports":

json
{
  "exports": "./index.js"
}

عند تحديد حقل "exports"، يتم تغليف جميع المسارات الفرعية للحزمة ولم تعد متاحة للمستوردين. على سبيل المثال، require('pkg/subpath.js') يطرح خطأ ERR_PACKAGE_PATH_NOT_EXPORTED.

يوفر هذا التغليف للتصديرات ضمانات أكثر موثوقية حول واجهات الحزمة للأدوات وعند التعامل مع ترقيات semver للحزمة. إنه ليس تغليفًا قويًا نظرًا لأن طلبًا مباشرًا لأي مسار فرعي مطلق للحزمة مثل require('/path/to/node_modules/pkg/subpath.js') سيظل يقوم بتحميل subpath.js.

تدعم جميع الإصدارات المدعومة حاليًا من Node.js وأدوات الإنشاء الحديثة حقل "exports". بالنسبة للمشاريع التي تستخدم إصدارًا أقدم من Node.js أو أداة إنشاء ذات صلة، يمكن تحقيق التوافق عن طريق تضمين حقل "main" بجانب "exports" يشير إلى نفس الوحدة النمطية:

json
{
  "main": "./index.js",
  "exports": "./index.js"
}

تصدير المسارات الفرعية

تمت الإضافة في: v12.7.0

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

json
{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}

الآن يمكن للمستهلك استيراد المسار الفرعي المحدد فقط في "exports":

js
import submodule from 'es-module-package/submodule.js';
// Loads ./node_modules/es-module-package/src/submodule.js

بينما المسارات الفرعية الأخرى ستؤدي إلى حدوث خطأ:

js
import submodule from 'es-module-package/private-module.js';
// Throws ERR_PACKAGE_PATH_NOT_EXPORTED

الامتدادات في المسارات الفرعية

يجب على مؤلفي الحزمة توفير مسارات فرعية ذات امتداد (import 'pkg/subpath.js') أو بدون امتداد (import 'pkg/subpath') في صادراتهم. يضمن هذا وجود مسار فرعي واحد فقط لكل وحدة نمطية مُصدَّرة بحيث يستورد جميع التابعين نفس المحدد المتسق، مما يحافظ على وضوح عقد الحزمة للمستهلكين وتبسيط إكمال المسار الفرعي للحزمة.

تقليديًا، كانت الحزم تميل إلى استخدام النمط بدون امتداد، والذي يتميز بفوائد قابلية القراءة وإخفاء المسار الحقيقي للملف داخل الحزمة.

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

اختصارات التصدير

تمت الإضافة في: الإصدار 12.11.0

إذا كان تصدير "." هو التصدير الوحيد، فإن حقل "exports" يوفر اختصارًا لهذه الحالة بحيث يكون قيمة حقل "exports" المباشرة.

json
{
  "exports": {
    ".": "./index.js"
  }
}

يمكن كتابته:

json
{
  "exports": "./index.js"
}

استيرادات المسار الفرعي

تمت الإضافة في: الإصدار 14.6.0، الإصدار 12.19.0

بالإضافة إلى حقل "exports"، يوجد حقل "imports" للحزمة لإنشاء تعيينات خاصة تنطبق فقط على مُعرّفات الاستيراد من داخل الحزمة نفسها.

يجب أن تبدأ الإدخالات في حقل "imports" دائمًا بـ # للتأكد من تمييزها عن مُعرّفات الحزمة الخارجية.

على سبيل المثال، يمكن استخدام حقل الاستيراد للحصول على فوائد التصدير المشروط للوحدات النمطية الداخلية:

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

حيث لا يحصل import '#dep' على حل الحزمة الخارجية dep-node-native (بما في ذلك الصادرات الخاصة بها بدورها)، وبدلاً من ذلك يحصل على الملف المحلي ./dep-polyfill.js بالنسبة للحزمة في البيئات الأخرى.

على عكس حقل "exports"، يسمح حقل "imports" بالتعيين إلى حزم خارجية.

قواعد الحل لحقل الاستيراد مماثلة بخلاف ذلك لحقل التصدير.

أنماط المسار الفرعي

[السجل]

الإصدارالتغييرات
الإصدار 16.10.0، الإصدار 14.19.0دعم الملحقات النمطية في حقل "imports".
الإصدار 16.9.0، الإصدار 14.19.0دعم الملحقات النمطية.
الإصدار 14.13.0، الإصدار 12.20.0تمت الإضافة في: الإصدار 14.13.0، الإصدار 12.20.0

بالنسبة للحزم التي تحتوي على عدد قليل من الصادرات أو الواردات، نوصي بسرد كل إدخال مسار فرعي للصادرات بشكل صريح. ولكن بالنسبة للحزم التي تحتوي على أعداد كبيرة من المسارات الفرعية، قد يتسبب ذلك في تضخم package.json ومشاكل الصيانة.

بالنسبة لحالات الاستخدام هذه، يمكن استخدام أنماط تصدير المسار الفرعي بدلاً من ذلك:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}

تعرض * الخرائط المسارات الفرعية المتداخلة لأنها مجرد بناء جملة لاستبدال السلاسل فقط.

سيتم بعد ذلك استبدال جميع مثيلات * على الجانب الأيمن بهذه القيمة، بما في ذلك إذا كانت تحتوي على أي فواصل /.

js
import featureX from 'es-module-package/features/x.js';
// يتم تحميل ./node_modules/es-module-package/src/features/x.js

import featureY from 'es-module-package/features/y/y.js';
// يتم تحميل ./node_modules/es-module-package/src/features/y/y.js

import internalZ from '#internal/z.js';
// يتم تحميل ./node_modules/es-module-package/src/internal/z.js

هذا تطابق واستبدال ثابت مباشر بدون أي معالجة خاصة لامتدادات الملفات. إن تضمين "*.js" على كلا جانبي التعيين يقيد صادرات الحزمة المكشوفة بملفات JS فقط.

يتم الحفاظ على خاصية الصادرات القابلة للتعداد بشكل ثابت مع أنماط الصادرات حيث يمكن تحديد الصادرات الفردية لحزمة ما عن طريق التعامل مع النمط الهدف للجانب الأيمن باعتباره ** glob مقابل قائمة الملفات الموجودة داخل الحزمة. نظرًا لأن مسارات node_modules محظورة في أهداف التصدير، فإن هذا التوسع يعتمد فقط على ملفات الحزمة نفسها.

لاستبعاد المجلدات الفرعية الخاصة من الأنماط، يمكن استخدام أهداف null:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
}
js
import featureInternal from 'es-module-package/features/private-internal/m.js';
// يطرح: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js';
// يتم تحميل ./node_modules/es-module-package/src/features/x.js

الصادرات الشرطية

[التاريخ]

الإصدارالتغييرات
v13.7.0، v12.16.0إلغاء علامة الصادرات الشرطية.
v13.2.0، v12.16.0تمت إضافتها في: v13.2.0، v12.16.0

توفر الصادرات الشرطية طريقة للربط بمسارات مختلفة بناءً على شروط معينة. وهي مدعومة لكل من استيراد CommonJS ووحدات ES النمطية.

على سبيل المثال، يمكن كتابة حزمة ترغب في توفير تصديرات وحدات ES نمطية مختلفة لـ require() و import:

json
// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}

تطبق Node.js الشروط التالية، المدرجة بترتيب من الأكثر تحديدًا إلى الأقل تحديدًا كما يجب تعريف الشروط:

  • "node-addons" - مشابهة لـ "node" وتطابق أي بيئة Node.js. يمكن استخدام هذا الشرط لتوفير نقطة دخول تستخدم إضافات C++ الأصلية بدلاً من نقطة دخول أكثر عالمية ولا تعتمد على الإضافات الأصلية. يمكن تعطيل هذا الشرط عبر علامة --no-addons.
  • "node" - تطابق أي بيئة Node.js. يمكن أن تكون ملف CommonJS أو وحدة ES نمطية. في معظم الحالات، لا تكون الإشارة صراحةً إلى منصة Node.js ضرورية.
  • "import" - تطابق عندما يتم تحميل الحزمة عبر import أو import()، أو عبر أي عملية استيراد أو تحليل ذات مستوى أعلى بواسطة محمل وحدة ECMAScript النمطية. تنطبق بغض النظر عن تنسيق الوحدة النمطية للملف الهدف. دائمًا ما تكون حصرية بشكل متبادل مع "require".
  • "require" - تطابق عندما يتم تحميل الحزمة عبر require(). يجب أن يكون الملف المشار إليه قابلاً للتحميل باستخدام require() على الرغم من أن الشرط يطابق بغض النظر عن تنسيق الوحدة النمطية للملف الهدف. تتضمن التنسيقات المتوقعة CommonJS و JSON والإضافات الأصلية ووحدات ES النمطية. دائمًا ما تكون حصرية بشكل متبادل مع "import".
  • "module-sync" - تطابق بغض النظر عما إذا تم تحميل الحزمة عبر import أو import() أو require(). من المتوقع أن يكون التنسيق هو وحدات ES النمطية التي لا تحتوي على انتظار ذي مستوى أعلى في رسمها البياني للوحدة النمطية - إذا كان الأمر كذلك، فسيتم طرح ERR_REQUIRE_ASYNC_MODULE عند طلب الوحدة النمطية باستخدام require().
  • "default" - الاحتياطي العام الذي يطابق دائمًا. يمكن أن يكون ملف CommonJS أو وحدة ES نمطية. يجب أن يأتي هذا الشرط دائمًا في النهاية.

داخل كائن "exports"، يكون ترتيب المفاتيح مهمًا. أثناء مطابقة الشروط، تتمتع الإدخالات السابقة بأولوية أعلى ولها الأسبقية على الإدخالات اللاحقة. القاعدة العامة هي أن الشروط يجب أن تكون من الأكثر تحديدًا إلى الأقل تحديدًا في ترتيب الكائن.

يمكن أن يؤدي استخدام الشروط "import" و "require" إلى بعض المخاطر، والتي تم شرحها بالتفصيل في قسم حزم CommonJS/ES النمطية المزدوجة.

يمكن استخدام الشرط "node-addons" لتوفير نقطة دخول تستخدم إضافات C++ الأصلية. ومع ذلك، يمكن تعطيل هذا الشرط عبر علامة --no-addons. عند استخدام "node-addons"، يوصى بمعاملة "default" كتحسين يوفر نقطة دخول أكثر عالمية، على سبيل المثال، استخدام WebAssembly بدلاً من إضافة أصلية.

يمكن أيضًا تمديد الصادرات الشرطية إلى مسارات فرعية للتصدير، على سبيل المثال:

json
{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}

يعرف حزمة حيث يمكن أن توفر require('pkg/feature.js') و import 'pkg/feature.js' تطبيقات مختلفة بين Node.js وبيئات JS الأخرى.

عند استخدام فروع البيئة، قم دائمًا بتضمين شرط "default" حيثما أمكن ذلك. يضمن توفير شرط "default" أن أي بيئات JS غير معروفة قادرة على استخدام هذا التنفيذ العالمي، مما يساعد على تجنب اضطرار بيئات JS هذه إلى التظاهر بأنها بيئات موجودة لدعم الحزم ذات الصادرات الشرطية. لهذا السبب، يفضل عادةً استخدام فروع الشروط "node" و "default" على استخدام فروع الشروط "node" و "browser".

الشروط المتداخلة

بالإضافة إلى التعيينات المباشرة، تدعم Node.js أيضًا كائنات الشروط المتداخلة.

على سبيل المثال، لتعريف حزمة تحتوي فقط على نقاط دخول ذات وضع مزدوج للاستخدام في Node.js ولكن ليس المتصفح:

json
{
  "exports": {
    "node": {
      "import": "./feature-node.mjs",
      "require": "./feature-node.cjs"
    },
    "default": "./feature.mjs"
  }
}

تستمر مطابقة الشروط بالترتيب كما هو الحال مع الشروط المسطحة. إذا لم يكن للشرط المتداخل أي تعيين، فسيستمر في التحقق من الشروط المتبقية للشرط الأصل. بهذه الطريقة، تتصرف الشروط المتداخلة بشكل مماثل لعبارات if JavaScript المتداخلة.

حل شروط المستخدم

أُضيف في: v14.9.0, v12.19.0

عند تشغيل Node.js، يمكن إضافة شروط مستخدم مخصصة باستخدام علامة --conditions:

bash
node --conditions=development index.js

والتي ستحل بعد ذلك الشرط "development" في عمليات استيراد وتصدير الحزمة، مع حل الشروط الموجودة "node" و "node-addons" و "default" و "import" و "require" حسب الاقتضاء.

يمكن تعيين أي عدد من الشروط المخصصة باستخدام علامات متكررة.

يجب أن تحتوي الشروط النموذجية على أحرف أبجدية رقمية فقط، باستخدام ":" أو "-" أو "=" كفواصل إذا لزم الأمر. أي شيء آخر قد يواجه مشاكل توافق خارج Node.

في Node، توجد قيود قليلة جدًا على الشروط، ولكن على وجه التحديد تشمل هذه:

تعريفات شروط المجتمع

يتم تجاهل سلاسل الشروط بخلاف الشروط "import" و "require" و "node" و "module-sync" و "node-addons" و "default" المنفذة في نواة Node.js افتراضيًا.

قد تنفذ الأنظمة الأساسية الأخرى شروطًا أخرى ويمكن تمكين شروط المستخدم في Node.js عبر علامة --conditions / -C.

نظرًا لأن شروط الحزمة المخصصة تتطلب تعريفات واضحة لضمان الاستخدام الصحيح، يتم توفير قائمة بشروط الحزمة المعروفة الشائعة وتعريفاتها الصارمة أدناه للمساعدة في تنسيق النظام البيئي.

  • "types" - يمكن استخدامه بواسطة أنظمة الكتابة لحل ملف الكتابة للتصدير المحدد. يجب دائمًا تضمين هذا الشرط أولاً.
  • "browser" - أي بيئة متصفح ويب.
  • "development" - يمكن استخدامه لتحديد نقطة دخول بيئة تطوير فقط، على سبيل المثال لتوفير سياق تصحيح إضافي مثل رسائل خطأ أفضل عند التشغيل في وضع التطوير. يجب أن يكون دائمًا حصريًا بشكل متبادل مع "production".
  • "production" - يمكن استخدامه لتحديد نقطة دخول بيئة الإنتاج. يجب أن يكون دائمًا حصريًا بشكل متبادل مع "development".

بالنسبة إلى أوقات التشغيل الأخرى، يتم الاحتفاظ بتعريفات مفاتيح الشروط الخاصة بالنظام الأساسي بواسطة WinterCG في مواصفات اقتراح Runtime Keys.

يمكن إضافة تعريفات شروط جديدة إلى هذه القائمة عن طريق إنشاء طلب سحب إلى وثائق Node.js لهذا القسم. متطلبات إدراج تعريف شرط جديد هنا هي:

  • يجب أن يكون التعريف واضحًا ولا لبس فيه لجميع المنفذين.
  • يجب تبرير حالة الاستخدام لسبب الحاجة إلى الشرط بوضوح.
  • يجب أن يكون هناك استخدام كافٍ للتنفيذ الحالي.
  • يجب ألا يتعارض اسم الشرط مع تعريف شرط آخر أو شرط قيد الاستخدام على نطاق واسع.
  • يجب أن يوفر إدراج تعريف الشرط فائدة تنسيق للنظام البيئي لا يمكن تحقيقها بخلاف ذلك. على سبيل المثال، لن يكون هذا هو الحال بالضرورة بالنسبة للشروط الخاصة بالشركة أو الشروط الخاصة بالتطبيق.
  • يجب أن يكون الشرط بحيث يتوقع مستخدم Node.js وجوده في وثائق نواة Node.js. الشرط "types" هو مثال جيد: لا ينتمي حقًا إلى اقتراح Runtime Keys ولكنه مناسب تمامًا هنا في وثائق Node.js.

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

الإشارة الذاتية إلى حزمة باستخدام اسمها

[التاريخ]

الإصدارالتغييرات
الإصدار v13.6.0، v12.16.0إلغاء الإشارة إلى حزمة ذاتيًا باستخدام اسمها.
الإصدار v13.1.0، v12.16.0تمت الإضافة في: v13.1.0، v12.16.0

داخل الحزمة، يمكن الإشارة إلى القيم المحددة في حقل "exports" الخاص بـ package.json الخاص بالحزمة عبر اسم الحزمة. على سبيل المثال، بافتراض أن package.json هو:

json
// package.json
{
  "name": "a-package",
  "exports": {
    ".": "./index.mjs",
    "./foo.js": "./foo.js"
  }
}

بعد ذلك، يمكن لأي وحدة داخل تلك الحزمة الإشارة إلى تصدير في الحزمة نفسها:

js
// ./a-module.mjs
import { something } from 'a-package'; // يستورد "something" من ./index.mjs.

تتوفر الإشارة الذاتية فقط إذا كان package.json يحتوي على "exports"، وسيسمح باستيراد ما تسمح به "exports" (في package.json). وبالتالي، فإن الكود أدناه، بالنظر إلى الحزمة السابقة، سيؤدي إلى حدوث خطأ في وقت التشغيل:

js
// ./another-module.mjs

// يستورد "another" من ./m.mjs. يفشل لأن
// حقل "exports" في "package.json"
// لا يوفر تصديرًا باسم "./m.mjs".
import { another } from 'a-package/m.mjs';

تتوفر الإشارة الذاتية أيضًا عند استخدام require، سواء في وحدة ES أو في وحدة CommonJS. على سبيل المثال، سيعمل هذا الكود أيضًا:

js
// ./a-module.js
const { something } = require('a-package/foo.js'); // يتم التحميل من ./foo.js.

أخيرًا، تعمل الإشارة الذاتية أيضًا مع الحزم ذات النطاق. على سبيل المثال، سيعمل هذا الكود أيضًا:

json
// package.json
{
  "name": "@my/package",
  "exports": "./index.js"
}
js
// ./index.js
module.exports = 42;
js
// ./other.js
console.log(require('@my/package'));
bash
$ node other.js
42

حزم CommonJS/ES المزدوجة

راجع مستودع أمثلة الحزم للحصول على التفاصيل.

تعريفات حقول package.json في Node.js

يصف هذا القسم الحقول المستخدمة بواسطة وقت تشغيل Node.js. تستخدم أدوات أخرى (مثل npm) حقولًا إضافية تتجاهلها Node.js ولا يتم توثيقها هنا.

تُستخدم الحقول التالية في ملفات package.json في Node.js:

  • "name" - ذات صلة عند استخدام عمليات الاستيراد المسماة داخل الحزمة. يتم استخدامه أيضًا بواسطة مديري الحزم كاسم للحزمة.
  • "main" - الوحدة الافتراضية عند تحميل الحزمة، إذا لم يتم تحديد exports، وفي إصدارات Node.js قبل إدخال exports.
  • "packageManager" - مدير الحزم الموصى به عند المساهمة في الحزمة. يتم الاستفادة منه بواسطة Corepack shims.
  • "type" - نوع الحزمة الذي يحدد ما إذا كان سيتم تحميل ملفات .js كوحدات CommonJS أو ES.
  • "exports" - عمليات تصدير الحزمة وعمليات التصدير الشرطية. عند وجودها، تحد من الوحدات الفرعية التي يمكن تحميلها من داخل الحزمة.
  • "imports" - عمليات استيراد الحزمة، للاستخدام بواسطة الوحدات داخل الحزمة نفسها.

"name"

[تاريخ]

الإصدارالتغييرات
الإصدار v13.6.0, v12.16.0تمت إزالة الخيار --experimental-resolve-self.
الإصدار v13.1.0, v12.16.0تمت إضافته في: v13.1.0, v12.16.0
json
{
  "name": "اسم الحزمة"
}

يحدد حقل "name" اسم الحزمة الخاصة بك. يتطلب النشر إلى سجل npm اسمًا يفي بمتطلبات معينة.

يمكن استخدام حقل "name" بالإضافة إلى حقل "exports" لـ الإشارة الذاتية إلى حزمة باستخدام اسمها.

"main"

تمت إضافته في: الإصدار v0.4.0

json
{
  "main": "./index.js"
}

يحدد حقل "main" نقطة دخول الحزمة عند استيرادها بالاسم عبر بحث node_modules. قيمته عبارة عن مسار.

عندما تحتوي الحزمة على حقل "exports"، فسيكون له الأسبقية على حقل "main" عند استيراد الحزمة بالاسم.

كما أنه يحدد البرنامج النصي الذي يتم استخدامه عند تحميل دليل الحزمة عبر require().

js
// سيؤدي هذا إلى الحل إلى ./path/to/directory/index.js.
require('./path/to/directory');

"packageManager"

تمت إضافته في: الإصدار v16.9.0, v14.19.0

[مستقر: 1 - تجريبي]

مستقر: 1 الاستقرار: 1 - تجريبي

json
{
  "packageManager": "<اسم مدير الحزم>@<الإصدار>"
}

يحدد حقل "packageManager" مدير الحزم المتوقع استخدامه عند العمل على المشروع الحالي. يمكن تعيينه على أي من مديري الحزم المدعومين، وسيضمن أن تستخدم فرقك نفس إصدارات مدير الحزم بالضبط دون الحاجة إلى تثبيت أي شيء آخر بخلاف Node.js.

هذا الحقل تجريبي حاليًا ويحتاج إلى الاشتراك فيه؛ تحقق من صفحة Corepack للحصول على تفاصيل حول الإجراء.

"type"

[السجل]

الإصدارالتغييرات
v13.2.0, v12.17.0إلغاء علامة --experimental-modules.
v12.0.0تمت الإضافة في: v12.0.0

يحدد الحقل "type" تنسيق الوحدة النمطية الذي يستخدمه Node.js لجميع ملفات .js التي تحتوي على ملف package.json هذا كأقرب أصل لها.

يتم تحميل الملفات التي تنتهي بـ .js كوحدات ES النمطية عندما يحتوي ملف package.json الأصل الأقرب على حقل المستوى الأعلى "type" بقيمة "module".

يتم تعريف package.json الأصل الأقرب على أنه أول package.json يتم العثور عليه عند البحث في المجلد الحالي، وأصل هذا المجلد، وهكذا حتى يتم الوصول إلى مجلد node_modules أو جذر وحدة التخزين.

json
// package.json
{
  "type": "module"
}
bash
# في نفس المجلد مثل package.json السابق {#in-same-folder-as-preceding-packagejson}
node my-app.js # يتم التشغيل كوحدة ES النمطية

إذا كان package.json الأصل الأقرب يفتقر إلى حقل "type"، أو يحتوي على "type": "commonjs"، فسيتم التعامل مع ملفات .js على أنها CommonJS. إذا تم الوصول إلى جذر وحدة التخزين ولم يتم العثور على package.json، فسيتم التعامل مع ملفات .js على أنها CommonJS.

تتم معاملة عبارات import لملفات .js كوحدات ES النمطية إذا كان package.json الأصل الأقرب يحتوي على "type": "module".

js
// my-app.js، جزء من نفس المثال أعلاه
import './startup.js'; // تم التحميل كوحدة ES النمطية بسبب package.json

بغض النظر عن قيمة الحقل "type"، يتم دائمًا التعامل مع ملفات .mjs على أنها وحدات ES النمطية ويتم دائمًا التعامل مع ملفات .cjs على أنها CommonJS.

"exports"

[السجل]

الإصدارالتغييرات
v14.13.0, v12.20.0إضافة دعم لأنماط "exports".
v13.7.0, v12.17.0إلغاء علامة الصادرات الشرطية.
v13.7.0, v12.16.0تنفيذ ترتيب الصادرات الشرطية المنطقية.
v13.7.0, v12.16.0إزالة الخيار --experimental-conditional-exports. في 12.16.0، لا تزال الصادرات الشرطية خلف --experimental-modules.
v13.2.0, v12.16.0تنفيذ الصادرات الشرطية.
v12.7.0تمت الإضافة في: v12.7.0
json
{
  "exports": "./index.js"
}

يسمح الحقل "exports" بتحديد نقاط الإدخال لحزمة عند استيرادها بالاسم المحمل إما عبر بحث node_modules أو إشارة ذاتية إلى اسمها الخاص. يتم دعمه في Node.js 12+ كبديل لـ "main" التي يمكن أن تدعم تحديد صادرات المسار الفرعي والصادرات الشرطية مع تغليف الوحدات النمطية الداخلية غير المصدرة.

يمكن أيضًا استخدام الصادرات الشرطية داخل "exports" لتحديد نقاط إدخال حزمة مختلفة لكل بيئة، بما في ذلك ما إذا كان يتم الرجوع إلى الحزمة عبر require أو عبر import.

يجب أن تكون جميع المسارات المحددة في "exports" عناوين URL للملفات النسبية التي تبدأ بـ ./.

"imports"

أضيف في: الإصدار v14.6.0، الإصدار v12.19.0

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

يجب أن تكون الإدخالات في حقل الاستيراد عبارة عن سلاسل تبدأ بـ #.

تسمح استيرادات الحزمة بالتعيين إلى حزم خارجية.

يحدد هذا الحقل عمليات استيراد المسار الفرعي للحزمة الحالية.