Skip to content

ما هو دور الملف package-lock.json في مشاريع Node.js ؟

ما هو دور الملف package-lock.json في مشاريع Node.js ؟

26 أبريل 2023 | 13:30

يعتبر ملف package-lock.json من الملفات المهمة التي لا يخلو منها أي مشروع Node.js مهما كان بسيطا. فما من شك أنك صادفته أو لاحظته موجودا ضمن ملفات مشروعك وربما تساءلتَ للوهلة الأولى عن مدى أهميته، وفيما إذا كان عليك إضافته إلى Git أو لاَ.

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

القصة تبدأ من الإصدارات الدلالية أو Semantic versioning

كلنا تعاملنا مع حزم واعتماديات برمجية ونحن نشتغل في مشاريعنا، بغض النظر عن التقنيات المستخدمة. قد تكون PHP ،Node.js أو أي لغة برمجة أخرى.

ومنا المعلوم أن كل حزمة برمجية يتم تطويرها وصيانتها باستمرار، إما لإضافة مزايا (Features) جديدة إليها أو لتصحيح الأخطاء (Bugs) التي ظهرت بعد نشرها.

في كل مرة يتم نشر نسخة جديدة من تلك الحزمة مع الإضافات الجديدة، وكل نسخة تحمل رقم إصدار يعرف بِ Version.

تم الإتفاق بين أوساط المبرمجين على اتباع نمط معين لترقيم تلك الإصدارات أو النسخ بشكل دلالي يجعل الأطراف الأخرى (مبرمجين وكذا مدراء الحزم مثل npm أو composer بالنسبة ل PHP وغيرهما) قادرة على التنبؤ بنوعية التعديلات التي أدخلت على تلك الحزمة من إصدار لآخر.

اصطُلِح على تسمية هذا النمط في ترقيم الإصدارات بِ Semantic Versioning أو الإصدارات الدلالية، وقرأت في بعض المقالات أنهم استخدموا “الترقيم الدلالي” كترجمة إلى العربية. ويمكن قبول كلتا الترجمتين على حسب رأيي.

كل إصدار أو نسخة دلالية تكون على الشكل التالي:

كل إصدار أو نسخة دلالية تكون على شكل x.y.z، بحيث أن:

كل مشرف (Maintainer) على حزمة برمجية موصى بالتزام اصطلاحات Semantic Versioning بشكل دقيق عند ترقيم إصداراته حتى لا يربك المطورين من مستعلمي تلك الحزمة وكذلك مدراء الإعتماديات (Dependencies managers).

إلى هنا أعتقد أنه هذا كل ما يجب عليك معرفته بخصوص الإصدارات الدلالية لكي نعود إلى موضوعنا الأساسي package-lock.json وعلاقته بكل ما قلناه.

مالذي يحدث عندما نقوم بتثبيت حزمة بواسطة npm ؟

عندما نضيف حزمة جديدة إلى مشروعنا عن طريق npm install فإنها تُضاف بشكل أوتوماتيكي إلى قائمة اعتماديات المشروع (Dependencies) في الملف package.json كالتالي:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.21"
  }
}

لا حظ أن النسخة التي تم تثبيتها هي 4.17.21 (لا حظ نمط semantic versioning)، ولكن تمت إضافة العلامة ^ قبلها 🤔

ما الذي تعنيه هذه العلامة بالنسبة لمدير الإعتماديات npm ؟

نسيت أن أقول لك بأنه عندما ثبتنا lodash عن طريق npm install lodash تم توليد ملف اسمه package-lock.json بشكل أوتوماتيكي. وبما أن مشروعنا بسيط للغاية فهذا الملف سيكون بدوره بسيطا وشبيها بما يلي:

{
  "name": "test",
  "version": "1.0.0",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "": {
      "name": "test",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "lodash": "^4.17.21"
      }
    },
    "node_modules/lodash": {
      "version": "4.17.21",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
    }
  },
  "dependencies": {
    "lodash": {
      "version": "4.17.21",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
    }
  }
}

الآن لندع هذا الملف كما هو، ولنعد إلى العلامة ^ 😄

هذه العلامة تخبر npm أن بإمكانه تحميل آخر إصدار Patch أو Minor من تلك الإعتمادية (lodash في حالتنا) عندما يتم تنفيذ الأمر npm install في المشروع.

هنا تكمن حساسية الموضوع ويبرز دور الملف package-lock.json ولماذا من الضروري إضافته إلى Git.

في وقت كتابتي لهذه التدوينة، آخر إصدار من مكتبة lodash هو 4.17.21 ولكن لنفترض أنه بعد مدة من الزمن تم نشر نسخ جديدة وأصبح آخر إصدار هو 4.19.7 على سبيل المثال، وكان على أحد أعضاء الفريق العمل على نفس المشروع. ولنفرض بأنني تجاهلت package-lock.json ولم أضفه لمدير النسخ Git، وبالتالي عندما قام زميلي بسحب المشروع كان لديه فقط ملف package.json إضافة إلى ملفات المشروع الأخرى بطبيعة الحال.

لكي يقوم بتشغيل المشروع يجب عليه أولا تثبيت الإعتماديات عن طريق تنفيذ الأمر السطري npm install.

خمن معي ماذا ستكون النتيجة ؟ 🤔

النتيجة هي أن npm سيقوم بتحميل وتثبيت النسخة 4.19.7 الجديدة رغم أنه في package.json موجودة النسخة 4.17.21، ولكن مسبوقة بالعلامة ^. عندما يراها npm يقول: حسناً، بما أنه لدي العلامة ^ فمن حقي أن أقوم بتحميل آخر إصدار Minor وآخر إصدار Patch من هذه الإعتمادية طالما ليس هناك ملف package-lock.json يفرض علي نسخة بحد ذاتها.

لذلك سيصبح لدينا سيناريو يكون فيه لدى مختلف أعضاء الفريق إصدارات مختلفة، ورغم أن الإختلافات لن تكون أساسية وجوهرية مادامت متعلقة فقط بال Minor و Patch، إلا أن ذلك من الممكن أن يسبب مشاكل غير متوقعة خاصة في التوافق والتناغم مع مكتبات واعتماديات أخرى للمشروع. وهذا النوع من المشاكل يصعب تتبعه وتشخيصه.

هناك طرق أخرى لإخبار npm بكيفية إيجاد وتحديد الإصدارات المقبولة، ونذكر بعضا منها في هذا الجدول:

النمط معناه
~version تقريبا نفس الإصدار، إذ تُقبل فقط الإصدارات الجديدة من نوع Patch.
^version يقبل الإصدارات المتوافقة، وهي كما شرحنا سابقا كل إصدارات Patch و Minor الأحدث إذا تم احترام نمط SemVer 😃
version لا يتم قبول وتثبيت إلا هذا الإصدار بالذات.
>version كل الإصدارت الأكبر (الأحدث) بالمقارنة مع الإصدار الذي يأتي بعد الرمز <.
>=version الإصدارات الأكبر من أو تساوي.
<version كل الإصدارت الأصغر (الأقدم) بالمقارنة مع الإصدار الذي يأتي بعد الرمز >.
<=version الإصدارات الأقل من أو تساوي.

كما رأينا آنفاً، يقوم مدير الإعتماديات npm باستخدام النمط ^version افتراضيا عند تثبيت أي حزمة.

الحل هو package-lock.json

إذن الحل هو في الملف package-lock.json الذي يجبر كل متدخل في المشروع على تثبيت نفس إصدارات الإعتماديات. ولذلك من الضروري تتبعه بواسطة Git وإضافته للمستودع (Repo) حتى يكون بمقدور كل شخص سحبه مع ملفات المشروع. بعد ذلك إذا قمنا بتنفيذ npm install فإنه سيتم تحميل نفس الإصدار الموجود في package.json. الأخير يجب أن يكون هو مصدر الحقيقة، وكل تحديث ل package-lock.json يجب أن يمر عبره.

حذاري أن تقوم بالتعديل على package-lock.json مباشرة 📛

إذا أردنا مثلا تحديث lodash إلى إصدار أحدث أو أقدم، فيجب المرور عبر npm install بهذه الطريقة:

npm install lodash@latest

أو

npm install lodash@<version>

بعد ذلك سيتم توليد package-lock.json من جديد، ومن ثم عليك إضافته إلى Git وعمل git push لإرساله إلى المستودع ليصبح التحديث في متناول زملائك.

جميع مديري الحزم والإعتماديات الذي تعاملت معهم مثل composer ،yarn و npm يستيعنون بملف Lock لمساعدتهم على تحديد النسخ والإصدارات التي عليهم تثبيتها.

في الختام

أتمنى أن موضوع أهمية package-lock.json أصبح أكثر وضوحا بالنسبة لك الآن، وكيف أن مسألة تتبعه من طرف Git مسألة مهمة في كل مشروع لتفادي أي نوع من المفاجآت غير السارة، مع ضمان توحيد الإصدارات المثبتة لكل حزمة بين أعضاء الفريق الواحد.

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