טעינת מודולים חדשים ב-Grunt.js

טעינת מודולים באופן אוטומטי ללא צורך בשימוש חוזר ונשנה ב-loadNpmTasks.

במאמרים הקודמים הסברתי על Grunt.js ואיך משתמשים בו בכל מיני הזדמנויות – כמו למשל הוספה אוטומטית של תחיליות או בקימפול SASS. הסברתי שעל מנת לגרום ל-grunt לבצע משימות, אני צריך להתקין את המודולים הרלוונטיים עם npm install. למשל, אם אני רוצה לבדוק תקינות של קבצי JavaScript ולאכוף coding standards עם מודול jshint, אני צריך להתקין אותו עם npm install grunt-contrib-jshint על מנת שהוא יעבוד. אחרי ההתקנה, אנו צריכים לגרום ל-grunt לטעון את המודול באמצעות שימוש בשורה:

grunt.loadNpmTasks('grunt-contrib-jshint'); 

השורה הזו גורמת ל-Grunt להפעיל את המודול ולאפשר לו להיות זמין במשימות השונות שאני אתן לו. אם אני לא אכניס את המודול לתוך loadNpmTasks, אני אקבל הודעת שגיאה כזו:

Warning: Task "XXXX" not found. Use --force to continue.

Aborted due to warnings.

הכל טוב ויפה, הבעיה היא שבפרויקטים אמיתיים, שהם לא hello world, יש לנו רשימה ענקית של מודולים, ולפעמים לתחזק אותה זה ביג'רס רציני. כמה חבל שאין מודול שיסייע לנו לטעון את כל המודולים ש-Grunt.js צריך בלי שנצטרך לרשום loadNpmTasks לכל אחד מהם…

מה זאת אומרת 'חבל'? מזל שיש! יש לא מעט מודולים שטוענים אוטומטית את הקבצים. אני אכתוב על שני מודולים כאלו שמשתמשים בשיטות שונות. הראשון הוא jit-grunt והשני הוא load-grunt-tasks שמי שהמליץ לי לכתוב עליו הוא אורן שליו, קולגה שלי שגם הוא עובד ב-HP Software (אין קשר למדפסות!).

המודול שבו אני בדרך כלל משתמש jit-grunt כשה-jit זה ראשי תיבות של just in time. הייחוד שלו הוא שהוא טוען את המודולים לא בבת אחת אלא בהתאם למשימות. נחמד ויעיל.

איך משתמשים? קודם כל מתקינים אותו עם:

npm install jit-grunt

אחרי שהתקנו, כל מה שצריך לעשות זה להכניס

require('jit-grunt')(grunt);

במקום המתאים, שזה קצת לפני ה-grunt.initConfig. הנה דוגמה לקובץ.

module.exports = function(grunt) {

require('jit-grunt')(grunt);

grunt.initConfig({
  postcss: {
    options: {

      processors: [
        require('autoprefixer-core')({browsers: ['last 2 versions', 'Android >= 2.3']}), // add vendor prefixes
      ]
    },
    main: {
        expand: true,
        flatten: true,
        src: 'source/*.css',
        dest: 'css/'
    }
  },
	watch: {
	  scripts: {
	    files: ['*.css'],
	    tasks: ['postcss'],
	    options: {
	      spawn: false,
	    },
	  },
	}
});

grunt.registerTask('default', ['postcss']);

};

הדגשתי את ה-require שמופיע מייד בהתחלה. לא צריך לדאוג מעכשיו.

כלומר לא היינו צריכים לדאוג במקרה של hello word. א-מ-מ-ה? יש הבדל בין תיאוריה למציאות. במציאות אנחנו לפעמים משתמשים במודול ולמרות שאנו משתמשים ב-grunt-jit אנו מקבלים שגיאת Warning: Task "XXXX" not found. Use –force to continue. מרגיזה. למה?! בשביל להבין את זה, אנחנו צריכים קצת להבין איך jit עובד. הוא הולך לכל ה-tasks ומנסה לטעון את המודולים לפי שמות ה-tasks:

  1. node_modules/grunt-contrib-task-name
  2. node_modules/grunt-task-name
  3. node_modules/task-name

אם מדובר -tasks עם שמות סטנדרטיים כמו למשל postcss כמו בדוגמה, אין בעיה. למשימה קוראים postcss ולמודול קוראים grunt-postcss (מציית לכלל השני). הבעיה מתחילה אם אנו בוחרים ל-task שלנו שאין שום קשר בינו לבין המודול שאיתו הוא עובד. למשל task בשם ahlaBahlaPostcss. מן הסתם, אין לנו מודול בשם grunt-contrib-ahlaBahlaPostcss או grunt-ahlaBahlaPostcss ואפילו לא ahlaBahlaPostcss לבד.

module.exports = function(grunt) {

require('jit-grunt')(grunt);

grunt.initConfig({
  ahlaBahlaPostcss: {
    options: {

      processors: [
        require('autoprefixer-core')({browsers: ['last 2 versions', 'Android >= 2.3']}), // add vendor prefixes
      ]
    },
    main: {
        expand: true,
        flatten: true,
        src: 'source/*.css',
        dest: 'css/'
    }
  },
	watch: {
	  scripts: {
	    files: ['*.css'],
	    tasks: ['postcss'],
	    options: {
	      spawn: false,
	    },
	  },
	}
});

grunt.registerTask('default', ['ahlaBahlaPostcss']);

};

אם אני אריץ את זה, אני אקבל שגיאה כזו:

$ grunt  jit-grunt: Plugin for the "ahlaBahlaPostcss" task not found. If you have installed the plugin already, please setting the static mapping. See https://github.com/shootaroo/jit-grunt#static-mappings  Warning: Task "ahlaBahlaPostcss" failed. Use --force to continue.  Aborted due to warnings.
$ grunt
jit-grunt: Plugin for the "ahlaBahlaPostcss" task not found.
If you have installed the plugin already, please setting the static mapping.
See https://github.com/shootaroo/jit-grunt#static-mappings
Warning: Task "ahlaBahlaPostcss" failed. Use –force to continue.
Aborted due to warnings.

דרך טובה להימנע מזה היא לקרוא לשמות של ה-tasks לפי המודולים שלהם. לא תמיד זה אפשרי ובדיוק בשביל זה אפשר להכניס static mapping, שזה בשפת הקודש היכולת שלי לקשר בין המשימה למודול שאותה היא מפעילה. במקרה שלנו:

require('jit-grunt')(grunt, {
  ahlaBahlaPostcss: 'grunt-postcss'
});

המודול השני הוא load-grunt-tasks. אני כותב עליו יחסית בצמצום כי השתמשתי בו רק פעם אחת בפרויקט. הוא נהדר אם אתם לא צריכים את הפונקציונליות של -Just in time (ולא כולם צריכים).
המודול הזה עובד באופן שונה. הוא לא מסתכל על ה-tasks בכלל, אלא הולך ישר ל package.json ומבצע טעינה של כל המודולים שמופיעים שם ישירות ל-grunt. ואז כל מה שצריך לעשות זה לדאוג שיהיה לכם package.json שהוא קובץ שמכיל את כל המודולים שנמצאים בתיקית node_modules. למי שלא מכיר node, זה יכול להיות קצת מרתיע, אבל קל מאוד ליצור קובץ כזה באמצעות npm init.

כל מה שנצטרך לעשות זה לשמור על ה-package.json מעודכן, להתקין את המודול באמצעות npm install load-grunt-tasks ולהכניס את השורה הזו:

require('load-grunt-tasks')(grunt);

זה הכל! המודול ידאג שכל המודולים שנמצאים ב-package.json שלכם יהיו זמינים עבור ה-grunt. נהדר וחוסך כאב ראש. כיוון שהוא לא מסתכל על ה-tasks בכלל והם לא מעניינים אותו, אז גם לא נצטרך static mapping אלא רק תחזוק של ה-package.json שאת זה גם ככה עושים.

נשאלת השאלה במה להשתמש? ב-jit או ב-load-grunt-tasks? התשובה תלויה בפרויקט שלכם ובצרכים שלו. אם הפרויקט שלכם עמוס במודולים של Node.js שלא צריכים להיטען ביחד עם ה-grunt, אז אני הייתי ממליץ על jit. אם יש לכם פרויקט קטן יחסית, שימוש ב-load-grunt-tasks (או דומיו) הוא אידיאלי.

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

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