בדיקה סטטית של קוד

פירוט מקיף על static code analysis על ג'אווהסקריפט ואיך עושים אותו
ה-flow המלא של git ו-eslint

תמליל הרצאה שהעברתי במ-meetup של FrontEnd.IL.

בהרצאה הזו אני הולך לדבר על בדיקה סטטית של קוד שידועה בשם lint. מדובר בשלב הבסיסי והראשוני בתהליך שנקרא continious integration. התהליך הזה נועד להקל על שילוב קוד חדש בקוד קיים. חלק אחר מהתהליך הוא בדיקות אוטומטיות מסוגים שונים אך החלק החשוב והמשמעותי ביותר הוא בדיקה סטטית של קוד ועליו כאמור אני הולך לדבר. מה אני מקווה להעביר לכם? ראשית, למי שלא מכיר, מושג כללי על בדיקה סטטית, כלים שונים לבדיקות סטטיות ואיך להטמיע בדיקות סטטיות על מנת להפיק מהן את המיטב ובקלות.

בדיקה סטטית היא בעצם סוג של ניתוח טקסט. כן, ממש כמו רג'קס שאותו אנחנו מכירים ו"אוהבים". כלי הבדיקה הסטטית עובר על כל הקוד שלנו, ג'אווהסקריפט במקרה הזה ובודק אותו לפי מספר פרמטרים שונים שמשתנים בין כלי לכלי. בכלים המשוכללים יותר אנחנו יכולים לערוך מגוון אדיר של בדיקות.

למשל:
אכיפת רווחים וטאבים.
שמות של פונקציות ומשתנים.
אכיפה של כללי ES6.
אכיפה של הגדרת משתנים בתחילת פונקציה או yoda conventions.

הכלי בעצם סורק את כל הקבצים שאנו אומרים לו לסרוק ואומר לנו איזה כללים נשברו ואיזה לא.

נשאלת השאלה, למה צריך בכלל בדיקה סטטית של קוד? למי אכפת מהרווחים, הטאבים וכל הכללים והחוקים האלו? בואו וניתן לקוד להיות חופשי כמו ילדי פרחים היפים בשנות השישים? למה להכניס אותו לתוך סד קשוח? כדי להרוויח:

מניעת שגיאות טריוויאליות..
אכיפה של best standards
אכיפה של coding conventions.
קלות והתמצאות בקוד.

ראשית, כשאני מבצע בדיקה סטטית של קוד אני מונע שגיאות טריוויאליות שיכולות לגרום לשגיאות. באמצעות סטנדרטים אני יכול גם למנוע שגיאות מראש. למשל yoda if statement שבו משפט התנאי הפוך, יכול למנוע השמות בטעות. אכיפת שימוש בהשוואת type יכול לחסוך לא מעט כאב ראש. אכיפת סטנדרטים יכולה להיות כמה זולה מבחינת ביצועים. אם אני משתמש ב-constants למשל, אני משחרר זכרון בסיום השימוש.
מבחינת קריאות קוד, אין שום תחליף לבדיקת קוד סטטית – הקוד הופך לקריא הרבה יותר. במקום קוד שנראה כאילו כמה אנשים כתבו אותו, הוא נראה כמו קוד שאדם אחד כתב אותו. וזה המון. אם אני אוכף ארכיטקטורה מסוימת כמו חלוקה לקונטרולים, טמפלייטים והגדרה של קומפוננטה, אני יכול להיות בטוח שזה יקרה.

כלומר קוד שעובד את בדיקת הקוד הסטטית הוא קוד שעובד לפי הכללים שקבענו. נשאלת השאלה – איזה כלי לבחור? בג׳אווהסקריפט יש 4 כלים עיקריים שהשתמשו בהם שמתוכם אחד הוא אולטרא פופולרי היום. אני אכנס להשוואה קצרה ביניהם, רק כדי שתכירו אותו.

הותיק ביותר הוא jslint. הוא כבר לא בשימוש היום מהסיבה המאוד פשוטה שהוא אוכף סט כללים מאוד קשיח שאי אפשר לשנות. ואם לא בא לי אחד מהכללים שלו? ליצור כלל חדש? אי אפשר. כלומר גמישות אפס.

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

השלישי הוא JCS שיש כאלו שמשתמשים בו. הוא כבר עובד עם קובץ קונפיגורציה אבל הוא בודק רק סגנון ולא דברים יותר מתקדמים וגם הוא איטי. באופן אישי לא יצא לי להשתמש בו. קראתי בתיעוד שלו שהוא לא תומך בבדיקה של הגדרות מתקדמות.

האחרון והיותר פופולרי הוא ESLINT שהפך דה פקטו לסטנדרד בתחום בדיקת הקוד הסטטית. יש בו את הטוב בכל העולמות:
1. יש לו חוקים בילט אין.
2. אפשר לשנות את כל החוקים ולהחליט מה אזהרה ומה לא.
3.. תומך בהרחבות ותוספים.

כלומר ESLINT מאוד גמיש, נוח וקל לתפעול. לא פלא שהיום כל פרויקט ג׳אווהסקריפטי שהוא מגיע איתו.

התקנה והרצה של eslint
התקנה והרצה של eslint

זה קל, ראשית עלי להתקין את eslint כמו כל מודול של node בפרויקט שלי. השלב הבא הוא להגדיר קובץ חוקים. אם אני רוצה להגדיר קובץ חוקים בסיסי, אני יכול להשתמש בפקודת init שמחוללת קובץ הגדרות פשוט ובסיסי. אני יכול להרחיב את ההגדרות האלו כרצוניֿ עם עשרות חוקים בילט אין שמשתנים לפי סביבה. יש חוקים שרלוונטיים ל-node וחוקים שרלוונטים לדפדפן.
אחרי שהגדרנו את קובץ ההגדרות, כל מה שעלינו לעשות זה להריץ את הכלי, שהוא כלי מבוסס node, באופן לוקלי דרך שורת הפקודה.

למעט החוקים שכתובים כבילט אין, יש לנו המון הרחבות שבודקות חוקים מתוחכמים יותר שאין בגרסה של eslint. למשל הרחבה שכוללת חוקים רבים הרלוונטיים לאנגולר – כמו אכיפה של הפרדה של קבצי קונטרולר. או הרחבה הכוללת חוקים הרלוונטיים לריאקט או ל-vue. כמעט לכל פריימוורק שמכבד את עצמו יש הרחבות שניתן להתקין בקלות רבה שיכולים לבדוק חוקים שרלוונטיים לפריימוורק שלכם.

הרצת eslint עם הרחבה
הרצת eslint עם הרחבה

החוכמה הגדולה בeslint היא לאו דווקא לשנות את החוקים הקיימים אלא ללכת על סט חוקים שהגדולים משתמשים בהם. יש חברות גדולות שמפתחות לא מעט ג׳אווהסקריפט וסביר להניח שלמדו במאמץ רב מה החוקים שחשוב לאכוף אותם. חלק מהחברות האלו כמו גוגל, וויקס ו-airbnb למשל משחררות את החוקים שלהם ממש כחבילה שאפשר להוריד אותה ולהשתמש בה. השימוש הוא פשוט: ההתקנה היא התקנה כמו כל מודול node ורק צריך לציין extends מתאים.

דיאגרמה של התהליך הפשוט מנת להקל על המפתחים כמעט לכל IDE היום יש תוסף שמתממשק לקובץ החוקים של eslint ומציג על גבי ה-IDE את השגיאות כדי שנוכל לתקן. אבל איך נוכל לאכוף את זה? בקלות, באמצעות הבילד שלנו. כיוון שהכל מבוסס node ורץ דרך שורת הפקודה מאוד קל לגרום ל-Travis|CI או ג׳נקינס להריץ את זה לפני ה-commit. שורה אחת, זה כל מה שצריך.

מה שמפתחים ממש אוהבים זה את מוד ה-autofix, מה זה? זה חוק שאפשר לתקן באופן אוטומטי תקלות שקל לתקן. כמו למשל נקודה פסיק שחסרים בסוף שורה. חוק שאומר שכל קובץ צריך להסתיים בשורה ריקה או שבירות שורה של CRLF. איך עושים את זה? עם הוספה של fix–.

הרצת eslint עם תיקון
הרצת eslint עם תיקון

היופי האמיתי של ה-fix מגיע אם אנו משלבים אותו בהליך הפיתוח עצמו. אני אסביר איך אנחנו עושים את זה ב AOL. ראשית, אני מאמין שאתם מכירים את ההוקים של גיט שמאפשרים לנו לקשור פעולות שונות לפני כל פעולת גיט. אחד הדברים שאני אוהב לעשות הוא בדיקת eslint לפני push. אני נעזר ב-hook שנקרא pre-push ורץ לפני שאני עושה push. השלב הראשוני הוא הרצת הבדיקה של eslint, אם היא נכשלת מקומית ה-push לא עובר. ככה אני חוסך המון זמן שעלול להתבזבז אם הבילד יפול על איזו שגיאה איזוטרית. אני גם מוסיף ל-hooks שלי גם הרצת בדיקה אוטומטית לפני ה-push.

שקופית דיאגרמת הליך

אם אתם עובדים עם גיט נכון, אפשר לקחת את זה שלב אחד קדימה. העבודה הנכונה עם גיט היא יצירת branch, הכנסת שינויים מהירה ואז דחיפה חזרה ולא צ׳רי פיקינג שבו אני בוחר את הקבצים הספציפיים שנכנסים לתוך ה-commit שלי. אם אתם עובדים במוד הזה, שהוא המוד הנכון לעבודה עם גיט, אפשר לעשות את הדבר הבא – לפני כל push אני מריץ בדיקת lint עם autofix פלאג. אחרי זה אני בודק, באמצעות porcelain אם יש לי שינויים, כלומר אם ה-autofix עשה שינויים ותיקן לי דברים. במידה וכן, אני יוצר מהקבצים ששונו קומיט עם הערה ומוסיף את השינויים. כך אני בעצם יכול להיות בטוח שאם תהיה לי נפילה ב-eslint זה בגלל דברים חשובים ולא דברים איזוטריים שהמכונה יכולה לתקן אותם לבד.

ה-flow המלא של git ו-eslint

את פקודת ה-shell הזו אני מריץ כחלק מ-npm.

איך מתחילים בקוד קיים שאין בו eslint?

נשאלת השאלה איך אני מוסיף בדיקת קוד סטטית לקוד קיים? אם יש לי קוד בייס מאוד גדול שאין בו את בדיקת קוד סטטית, אני לא יכול להשבית את עבודות הפיתוח לחלוטין עד שנתקן את כל הקבצים. אפילו עם autofix. יש כמה גישות, אני אציג את הגישה שאנחנו משתמשים בה ואחר כך נדבר על prettier.
הגישה שלי כוללת כמה צעדים:
1. סקריפט שרץ על כל הקבצים ומוסיף הודעת ignore לכל קובץ.
2. הכנסת ה-eslint ל-build. הכל יעבוד כי כל הקבצים ב-ignore
3. מדיניות של ׳נגעת-נסעת׳. שינית קובץ קיים? תשקיע עוד כמה דקות בתיקון הקובץ הבעייתי.

/* eslint-disable angular/log,no-console,angular/json-functions,no-loop-func */
const fs = require('fs');
const glob = require('glob');
const pathToFiles = './src/**/*.less';
const disableString = '/* stylelint-disable */';

const files = glob.sync(pathToFiles, { cwd: './', realpath: true });

for (const file of files) {
  const content = fs.readFileSync(file, 'utf8');
  if (content.startsWith(disableString) === false) {
    fs.writeFile(file, `${disableString} \n ${content}`, 'utf-8', (err) => {
      if (err) {
        throw err;
      } else {
        console.log(`Add disable string to ${file}`);
      }
    });
  } else {
    console.log(`disable string already existing ${file}`);
  }
}

Prettier

השלב הבא הוא לנטוש את eslint לחלוטין וללכת על prettier.

מה זה prettier? זו ספריה שכוללת גישה חדשה לקוד שדומה לגישה שהצעתי לעיל. pretiify יוצאת מנקודת הנחה שהכל ניתן לתיקון. היא לוקחת את כל הקוד שאנחנו כותבים וממירה אותו מייד לפורמט שלה. אין לי את הזמן להכנס לוודו שבאמצעותו היא עושה את זה, אבל זה בדיוק ככה. יש לנו pre commit hook שרץ לפני כל commit ומבצע את התיקונים שניה לפני שהקוד נכנס. פשוט שבפשוטים.

דוגמה פשוטה של prettier
דוגמה פשוטה של prettier

מה הבעיה עם הגישה של prettier? שיש דברים רבים ש-prettier לא יכול לבדוק. זו הסיבה שחלק ממליצים לעבוד עם prettier יחד עם eslint. מה אני חושב? שבינתיים אני אשתמש ב-autofix שלי. ארחיב בנושא במאמר או בהרצאה אחרת, כיוון שלומר ש-prettier זה כמו autofix זה עוול ל-prettier.

פוסטים נוספים שכדאי לקרוא

רספברי פיי

מה זה AIoT? ואיך אפשר להתחיל?

פוסט עם המון קישורים, מידע, סרטונים ופרק בפודקאסט שיפתח לכם שער לעולם ה-AIoT המרתק.

גלילה לראש העמוד