Sailsjs et Grunt pour automatiser les tâches

Saisljs est un framework basé sur nodejs qui vous permet de créer votre site ou application web. Vous pouvez aussi utiliser sailsjs pour créer votre backend. Dans le blog Sailsjs et les contenus statiques j'ai expliqué comment sailsjs gère les fichiers statiques de manière automatique avec le module Grunt. Dans ce blog, je vais vous expliquer en détail comment sailsjs utilise grunt pour automatiser les tâches.

Qu'est ce que Grunt ou Gruntjs ?

Grunt ou Gruntjs est un module qui vous permet d'automatiser des tâches comme, la copie, les tests, les modifications dans votre projet. Gruntjs est utilisé par plusieurs compagnies comme Jquery, Wordpress, Twitter et Adobe. Pour utiliser Grunt dans un projet, il suffit de l'ajouter à votre projet et de configurer les tâches à automatiser. La communauté autour de Grunt a développé plein de plugins que vous pouvez intégrer rapidement dans votre projet. Il suffit de les configurer. Le module grunt-contrib-concat par exemple perment de faire la concatenation,grunt-contrib-uglifypermet la minification et grunt-contrib-qunit permet de réaliser des tests. Visitez le site officiel de Grunt pour plus de détails.

Grunt dans un projet sailsjs

Sailsjs utilise grunt pour automatiser beaucoup de tâches. Apères avoir créé un projet sailsjs avec la commande sails new myapp, sailsjs crée le fichier de configuration Gruntfile.js. Ce fichier contient le point d'entrée de grunt dans votre projet. Dans ce fichier vous trouverez le bout de code c-dessous.

module.exports = function(grunt) {

  var loadGruntTasks = require('sails-hook-grunt/accessible/load-grunt-tasks');

  // Load Grunt task configurations (from `tasks/config/`) and Grunt
  // task registrations (from `tasks/register/`).
  loadGruntTasks(__dirname, grunt);

};

Ce fichier est éxécuté au démarage de votre projet pour lancer le module Grunt. Il charge les configurations qui sont dans le répertoire tasks/config et les enrégistrements dans le répertoire tasks/register. Le répertoire tasks/config contient la configuration des plugins prédéfinis de grunt et le répertoire tasks/register les tâches à enrégistrer.
Au démarrage, sailsjs exécute le fichier Gruntfile.js qui appelle la fonction loadGruntTasks du module load-grunt-tasks.

Le module load-grunt-tasks de sailsjs

load-grunt-tasks se trouve dans le module sails-hook-grunt dans le répertoire node_modules. Il est installé automatiquement à la création de votre projet sailsjs. C'est le module qui charge le contenu des répertoires tasks/config et tasks/register. Ce module liste l'ensemble des plugins prédéfinis de grunt et les charge dans votre projet. Ensuite il parcourt les deux répertoires tasks/config et tasks/register et charge les configurations et les tâches enrégistrées.

module.exports = function loadGruntTasks (appPath, grunt){


  // Load built-in Grunt plugins.
  // ========================================================
  var BUILT_IN_GRUNT_PLUGINS = [
    'grunt-contrib-clean',
    'grunt-contrib-coffee',
    'grunt-contrib-concat',
    'grunt-contrib-copy',
    'grunt-contrib-cssmin',
    'grunt-contrib-jst',
    'grunt-contrib-less',
    'grunt-hash',
    'grunt-sails-linker',
    'grunt-sync',
    '@sailshq/grunt-contrib-uglify',
    'grunt-contrib-watch',
    'grunt-babel'
  ];

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // > Note:
  // > This is based on implementation of loadNpmTasks as of Grunt v1.0.1:
  // > (https://github.com/gruntjs/grunt/blob/a09f84c8aedfc87cbf357b1ac45b7b70571e954f/lib/grunt/task.js#L368-L402)
  _.each(BUILT_IN_GRUNT_PLUGINS, function (packageName) {

    // First, we get the path to this dependency's base dir.
    // (see http://stackoverflow.com/a/34589830/486547)
    var depBaseDirPath = path.dirname(require.resolve(path.join(packageName, 'package.json')));

    // Then we get the path to its `tasks/` folder.
    var tasksFolderPath = path.join(depBaseDirPath, 'tasks/');

    // And finally, call `grunt.loadTasks()` on that.
    grunt.loadTasks(tasksFolderPath);

  });//</_.each()>
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


  // Load JavaScript files from `tasks/config/**/*` and `tasks/register/**/*`
  // ========================================================
  // Load Grunt configuration modules from the specified
  // relative path. These modules should export a function
  // that, when run, should either load/configure or register
  // a Grunt task.
  //
  // This uses the include-all library in order to require all of
  // the app's grunt configurations and task registrations dynamically.
  var helperTasks = includeAll({
    dirname: path.resolve(appPath, './tasks/config'),
    filter: /(.+)\.js$/,
    excludeDirs: /^\.(git|svn)$/
  }) || {};
  // Same thing for our main tasklists.
  var mainTasks = includeAll({
    dirname: path.resolve(appPath, './tasks/register'),
    filter: /(.+)\.js$/,
    excludeDirs: /^\.(git|svn)$/
  }) || {};


  // Ensure that a default task exists.
  // ========================================================
  if (!mainTasks.default) {
    mainTasks.default = function(grunt) {
      grunt.registerTask('default', []);
    };
  }


  // Run task functions to configure Grunt.
  // ========================================================
  // Invoke the function from each Grunt configuration module
  // with a single argument - the `grunt` object.
  Object.keys(helperTasks).forEach(function (taskName){
    helperTasks[taskName](grunt);
  });
  // Same thing for our main tasklists.
  Object.keys(mainTasks).forEach(function (taskName){
    mainTasks[taskName](grunt);
  });

};

Le répertoire tasks/config dans sailsjs

Grunt utilise les 2 répertoires tasks/config et tasks/register. Le répertoire tasks/config configure des tâches prédéfinies. Prenons par exemple la configuration tasks/config/copy.js. C'est ce module qui copie les contenus statiques du répertoire assets vers le répertoire .tmp. Pour plus de détails sur les paramètres de configuration de ce module, je vous invite à consulter le site officiel grunt-contrib-copy. Dans ce répertoire, chaque tâche est configurée pour exécuter un objectif précis dans votre projet. Ce répertoire contient d'autres configurations comme clean.js qui permet d'effaccer des fichiers, concat.js qui permet de faire la concatenation des fichiers.

module.exports = function(grunt) {

  grunt.config.set('copy', {
    dev: {
      files: [{
        expand: true,
        cwd: './assets',
        src: ['**/*.!(coffee|less)'],
        dest: '.tmp/public'
      }]
    },
    build: {
      files: [{
        expand: true,
        cwd: '.tmp/public',
        src: ['**/*'],
        dest: 'www'
      }]
    },
    beforeLinkBuildProd: {
      files: [{
        expand: true,
        cwd: '.tmp/public/hash',
        src: ['**/*'],
        dest: '.tmp/public/dist'
      }]
    },
  });
 

};

Le répertoire tasks/register dans sailsjs

Le répertoire tasks/register contient l'ensemble des tâches à exécuter en fonction des environnements et des commandes. Par défaut, sailsjs exécute la tâche default.js. default.js enrégistre les tâches suivantes: compileAssets, linkAssets et watch. En production, sailsjs exécute prod.js

module.exports = function (grunt) {


  grunt.registerTask('default', [
    // 'polyfill:dev', //« uncomment to ALSO transpile during development (for broader browser compat.)
    'compileAssets',
    // 'babel',        //« uncomment to ALSO transpile during development (for broader browser compat.)
    'linkAssets',
    'watch'
  ]);


};

Pour voir le déroulement de l'exécution de grunt, installez globalement grunt npm install -g grunt-cli puis exécutez la commande grunt dans le répertoire de votre projet. Vous verrez le résultat suivant:

Running "clean:dev" (clean) task
>> 12 paths cleaned.

Running "jst:dev" (jst) task
>> Destination not written because compiled files were empty.

Running "less:dev" (less) task
>> 1 stylesheet created.

Running "copy:dev" (copy) task
Copied 7 files

Running "coffee:dev" (coffee) task
>> 0 files created.

Running "sails-linker:devJs" (sails-linker) task
padding length 4
File "views/layouts/layout.ejs" updated.

Running "sails-linker:devStyles" (sails-linker) task
padding length 4
File "views/layouts/layout.ejs" updated.

Running "sails-linker:clientSideTemplates" (sails-linker) task
padding length 4
File "views/layouts/layout.ejs" updated.

Running "watch" task
Waiting...

La commande grunt exécute tasks/register/default.js qui exécute tasks/register/compileAssets, tasks/register/linkAssets.js et tasks/config/watch.js

/**
 * `tasks/register/compileAssets.js`
 *
 * ---------------------------------------------------------------
 *
 * For more information see:
 *   https://sailsjs.com/anatomy/tasks/register/compile-assets.js
 *
 */
module.exports = function(grunt) {
  grunt.registerTask('compileAssets', [
    'clean:dev',
    'jst:dev',
    'less:dev',
    'copy:dev',
    'coffee:dev'
  ]);
};

et

/**
 * `tasks/register/linkAssets.js`
 *
 * ---------------------------------------------------------------
 *
 * For more information see:
 *   https://sailsjs.com/anatomy/tasks/register/link-assets.js
 *
 */
module.exports = function(grunt) {
  grunt.registerTask('linkAssets', [
    'sails-linker:devJs',
    'sails-linker:devStyles',
    'sails-linker:clientSideTemplates'
  ]);
};

et

/**
 * `tasks/config/watch`
 *
 * ---------------------------------------------------------------
 *
 * Run predefined tasks whenever certain files are added, changed or deleted.
 *
 * For more information, see:
 *   https://sailsjs.com/anatomy/tasks/config/watch.js
 *
 */
module.exports = function(grunt) {

  grunt.config.set('watch', {
    assets: {

      // Assets to watch:
      files: [
        'assets/**/*',
        'tasks/pipeline.js',
        '!**/node_modules/**'
      ],

      // When assets are changed:
      tasks: [
        'syncAssets',
        'linkAssets'
      ]
    }
  });

};

Le fichier tasks/config/pipeline.js dans sailsjs

Le fichier tasks/config/pipeline.js configue la gestion des contenus statiques par grunt. Dans le blog Sailsjs et les contenus statiques, j'ai expliqué comment sailsjs injecte automatiqement les fichiers .css et .js dans le layout du projet. Cet injection automatique est configurée dans le fichier pipeline.js. Ce fichier configure et exporte les 3 variables cssFilesToInject, jsFilesToInject et templateFilesToInject. Il definit les fichiers à injecter dans le layout. Grunt inject d'abord les contenus du répertoire assets/dependencies avant ceux des répertoires assets/styles et assets/js. Les contenus des 2 répertoires assets/styles et assets/js sont injectés dans un ordre aléatoire.

var cssFilesToInject = [

  // Bring in `.css` files for themes and style guides (e.g. Bootstrap, Foundation)
  'dependencies/**/*.css',

  // All of the rest of your custom `.css` files will be injected here,
  // in no particular order.  To customize the ordering, add additional
  // items here, _above_ this one.
  'styles/**/*.css'
];
var jsFilesToInject = [

  // Load `sails.io` before everything else.
  'dependencies/sails.io.js',

  // Bring in `.js` files for any other client-side JavaScript dependencies.
  // (e.g. Lodash, Vue.js, jQuery, Bootstrap, Ember, Angular, etc.)
  // > Be sure to list dependencies that depend on each other in the right order!
  'dependencies/**/*.js',

  // All of the rest of your custom client-side js files will be injected here,
  // in no particular order.  To customize the ordering, add additional items
  // here, _above_ this one.
  'js/**/*.js'
];
var templateFilesToInject = [
  'templates/**/*.html'
];

Si vous souhaitez suivre un ordre dans l'injection des contenus des répertoires assets/styles et assets/js vous devez le définir dans le fichier pipeline.js. Créez le fichier zmyfirst.js et myjs.js dans assets/js et mydepend.js dans assets/dependencies puis démarrez votre projet. Vous verrez l'ordre d'injection ci-dessous dans le fichier views/layouts/layout.ejs:

 <!--SCRIPTS-->
    <script src="/dependencies/sails.io.js"></script>
    <script src="/dependencies/mydepend.js"></script>
    <script src="/js/myjs.js"></script>
    <script src="/js/zmyfirst.js"></script>
    <!--SCRIPTS END-->

Si vous modifiez le fichier pipeline.js comme suit:

var jsFilesToInject = [

// Load `sails.io` before everything else.
'dependencies/sails.io.js',

// Bring in `.js` files for any other client-side JavaScript dependencies.
// (e.g. Lodash, Vue.js, jQuery, Bootstrap, Ember, Angular, etc.)
// > Be sure to list dependencies that depend on each other in the right order!
'dependencies/**/*.js',

'js/zmyfirst.js',

// All of the rest of your custom client-side js files will be injected here,
// in no particular order.  To customize the ordering, add additional items
// here, _above_ this one.
'js/**/*.js'
];

L'ordre d'injection va changer comme ci-dessous

    <!--SCRIPTS-->
    <script src="/dependencies/sails.io.js"></script>
    <script src="/dependencies/mydepend.js"></script>
    <script src="/js/zmyfirst.js"></script>
    <script src="/js/myjs.js"></script>
    <!--SCRIPTS END-->

Merci d'avoir lu ce blog sur Sailsjs et Grunt. N'hésitez à le commenter et à le partager sur les réseaux sociaux. Si vous avez besoin de conseils dans la réalisation de vos projets sur sailsjs contacter BEF Technology qui a de l'expertise dans le framework Sailsjs et la technologie Web.