להבין את package.json ואת package-lock.json



הקובץ package.json

הקובץ package.json שומר רשימה של כל הספריות שהתקנתי והגרסאות שלהם.
בכל פעם שנריץ את הפקודה:

npm install

npm ישתמש בקובץ package.json כדי לקבוע איזה ספריות ואיזה גרסאות יותקנו.

ליד כל ספריה כתוב את הגרסה שבה השתמשתי, למשל:

"axios": "^1.6.8"

הספריה axios הותקנה עם גרסה 1.6.8.

מספרי הגרסאות

כל גרסה בנויה משלוש מספרים. השמאלית נקראת major, האמצעית minor, והימנית patch.

1.6.8 [major minor patch]

  • major - בין גרסאות major יכול להיות שינוי משמעותי ולפעמים הוא לא backward compatible
  • minor - בין גרסאות minor השינוי צריך להיות backward compatible, ואמור לעבור ללא בעיות.
  • patch - בין גרסאות patch השינוי צריך להיות backward compatible, והוא בד"כ מציין תיקון באגים.

סימנים מיוחדים

  • אם מספר הגרסה מופיע בלי סימנים מיוחדים, npm יתקין בדיוק את הגרסה שציינו.
"axios": "1.6.8"
  • הסימן ^ (נקרא Caret Symbol), למשל:

"axios": "^1.6.8"

הוא מציין שכל פעם שאני עושה npm install ה-npm ינסה לשדרג לי את גרסאות minor ו-patch אבל לא major. ולכן במקרה של 1.6.8 הוא יכול להתקין לי לדוגמה את 1.6.9, 1.7.3, 1.12.15 אבל לא 2.0.0.

  • הסימן ~ (נקרא Tilde Symbol), למשל:

"axios": "~1.6.8"

הוא מציין ש-npm ינסה לשדרג לי את גרסאות patch אבל לא minor, major. לכן הוא יכול להתקין לי 1.6.9, 1.6.11 אבל לא 1.7.0.

  • הסימן < (גדול מ-), למשל:

"axios": ">1.6.8"

 מציין ש-npm יתקין רק גרסאות גדולות מ-1.6.8.

  • הסימנים > (קטן מ-), => (קטן או שווה מ-), =< (גדול או שווה מ-), כמו שהוסבר בסעיף הקודם גם כאן הם מציינים ל-npm להתקין רק גרסאות קטנות מגרסה מסוימת, קטנות או שוות, גדולות או שוות.

  • הסימן || (OR), למשל:

"axios": "<1.2.0 || >1.6.8"

מאפשר לציין כמה טווחים. במקרה שלנו הוא מציין ש-npm יתקין רק גרסאות קטנות מ-1.2.0 או גדולות מ-1.6.8.

יש עוד סימנים מיוחדים וניתן לקרוא עליהם באתר של npm כאן.

הקובץ package-lock.json

כפי שראינו, כל פעם שנריץ npm install יש אפשרות שיותקנו אצלנו גרסאות שונות של ספריות. הדבר הזה יכול לגרום לבעיות. למשל כשיש צוות של מפתחים שעובדים על פרויקט, יש סיכוי טוב שלכל אחד יותקנו גרסאות שונות של הספריות שהפרויקט תלוי בהם.

הקובץ package-lock.json בא לפתור את הבעיה הזו.

בקובץ הזה כתוב את כל המידע שנצרך כדי לדעת איזה גרסאות בדיוק הותקנו בפרוייקט. כל פעם שאני מריץ npm install, אז npm יבדוק אם יש לי קובץ package-lock.json. אם יש, הוא יתקין בדיוק את הגרסאות שכתובות בו. אם אין לי את הקובץ הזה, הוא יחפש את package.json וישתמש בו.

הקובץ package-lock.json נוצר אוטומטית ומתעדכן אוטומטית.

לכן, כדאי מאוד לשמור את הקובץ package-lock.json ב-source control שלנו כחלק מהפרוייקט. ככה כל מפתח שיצטרף לפרוייקט יתקין בדיוק את אותן ספריות ב-node_modules כמו שאנחנו משתמשים, ונעבוד בדיוק על אותו קוד.



מצד שני, ממש לא כדאי למחוק אותו, כי אז נאבד את כל המידע על הגרסאות של הספריות שאנו משתמשים בהן, וכשנעשה npm install ה-npm לא ימצא package-lock.json וישתמש ב-package.json כדי להתקין את הספריות, וכך, ברוב המקרים, חלק מהספריות יותקנו בגרסאות חדשות יותר ממה שהיה לנו עד עכשיו.

ה-package-lock.json החדש שיווצר יכיל את הגרסאות שהותקנו עכשיו, וכל המידע על הגרסאות שהיו עד כה בפרוייקט יאבד. שינוי כזה יכול לגרום לבעיות בפרוייקט. אם מדובר על פרוייקט קטן עם מעט ספריות יש סיכוי שהכל יעבור חלק. אבל בפרוייקטים גדולים שתלויים בהמון ספריות, יש סיכוי גדול ששידרוג של המון ספריות בפעם אחת יגרום לבעיות שידרשו שינויים ותיקונים.

עדכון ידני של package.json ופקודת npm ci

אני ממליץ לא לעדכן את package.json באופן ידני. זה כמובן אפשרי, ואם יש צורך מיוחד נעשה את זה. אבל ככלל עדיף שהוא יתעדכן אוטומטית. אם למשל אני רוצה לעדכן את axios לגרסה 1.6.9 עדיף להשתמש בפקודה:

npm install axios@1.6.9

והיא גם תעדכן אוטומטית את package.json ואת package-lock.json. הדרך הזו טובה יותר מאשר לעדכן ידנית את package.json ולהריץ npm install.

הדבר נכון עוד יותר לגבי עדכון ידני של package-lock.json. גם זה אפשרי, אבל הרבה פעמים זה עלול להביא לטעויות כי הקובץ הזה הרבה יותר מורכב ויש בו כמה חלקים שדורשים עדכון, ולכן עדיף לא לשנות אותו ידנית.

נקודה חשובה שכדאי להכיר - אם יש סתירה בין package.json ל-package-lock.json, אז package.json גובר. למשל אם הגרסה שמוגדרת ב-package-lock.json לא נמצאת בטווח שמוגדר ב-package.json אז תותקן גרסה לפי מה שכתוב ב-package.json והקובץ package-lock.json יעודכן.

לדוגמה, אם ב-package.json כתוב:

"axios": "^1.6.8"

אחרי הרצת npm install מה שיהיה כתוב ב-package-lock.json זה משהו כזה:

"axios": "1.6.8"

לאחר מכן נשנה ידנית את package.json ונכתוב:

"axios": "^1.6.9"

מה שיש ב-package-lock.json כבר לא מתאים לזה, ולכן אם נריץ npm install תותקן גירסה 1.6.9 (במידה והיא קיימת) ו-package-lock.json יעודכן ל:

"axios": "1.6.9"

אגב, אם רוצים להשתמש רק במה שכתוב ב-package-lock.json בלי להתחשב ב-package.json אפשר להשתמש בפקודה:

npm ci

אבל אם יש סתירה בין package.json ל-package-lock.json כמו במקרה שלעיל, נקבל שגיאה.

הפקודה npm ci טובה עבור סביבות אוטומטיות כמו continues integration כשאתה רוצה להיות בטוח שתתקין רק את מה שכתוב ב-package-lock.json.

האם אפשר להשתמש רק ב-package.json עם גרסאות מדוייקות?

נשאלת השאלה, האם אפשר להשתמש רק ב-package.json עם גרסאות מדוייקות (בלי סימונים מיוחדים) למשל בצורה הבאה:

"axios": "1.6.8"

כדי להבטיח שכל מי שיריץ npm install יקבל את אותו עץ ספריות?

התשובה היא: לא. אמנם package.json יכול להבטיח לך שאותם גרסאות מדוייקות שכתבת אכן יותקנו לך, אבל הוא לא יכול להבטיח לך איזה גרסאות יותקנו לספריות שהספריות שלך תלויות בהן.

אם תלך לספריית node_modules ותפתח שם את אחת הספריות שהתקנת, תיראה שגם לה יש קובץ package.json שמפרט את הספריות שהיא תלויה בהן (נהוג לקרוא להן nested dependencies). ככה זה כמעט בכל ספריה שתתקין. את הגרסאות של הספריות האלו package.json לא מציין ולא מבטיח לך מה יותקן.

לעומת זאת, package-lock.json כן שומר את מספרי הגרסאות של ה-nested dependencies ואיתו אתה יכול להיות בטוח שתקבל סביבה זהה אם תריץ npm install כשיש לך package-lock.json.


אם אהבתם, תכתבו משהו למטה...