Experience with GruntJS

    image

    Hey. We finally finished working on one interactive book, and now I really want to talk about one of the most interesting tools that we used - about GruntJS.

    A little bit about the project


    Actually, we did an interactive book of one popular Russian writer. The book is written in JS, ECT-JS and LESS templates. Grunt is engaged in assembly, concatenation, minification and deployment, the book works on iPad under Phonegap.

    Technically, we made a prototype - we actively studied and applied various technologies. With something, it turned out cool, with something not very. Be that as it may, the book works, and you can even download it from the App Store.

    I think that is enough. Now you can go to GruntJS ...



    Grunt Brain



    - I have two variables, how can I add them?
    - I installed the plugin “jquery.math”, very convenient!


    I'll tell you a little exaggerated, but instructive story. The main graphics format in our project was PNG24 with transparency. The project took approximately 500MB. We decided to optimize it. Of course, with the help of the plugin for GruntJS ...

    There was no sane result. 500MB did not fit into any framework. Hardly compressed to 450Mb. As a result, after several days of searching, they dropped 250MB. Like this:

    find . -name "*.png" | xargs pngquant -f -v --ext .png --quality 0-90
    


    GruntJS is awesome, but addictive. Of course, this story is a little exaggerated, but I really saw how people do such things. Reminds plugins to jQuery. Still, sometimes it’s easier to get by with a one-liner on the bash, or connect console-proven utilities that are time-tested.

    In general, you can execute bash scripts and commands through grunt. There is a grunt-shell for this .

    grunt.initConfig({
      shell: {
        compressPNG: {
          options: {
            stdout: true
          },
          command: 'find . -name "*.png" | xargs pngquant -f -v --ext .png --quality 0-90'
        }
      }
    });
    grunt.loadNpmTasks('grunt-shell');
    grunt.registerTask('compress', ['shell: compressPNG']);
    


    Config for Gruntfile.js



    In our project, Gruntfile.js contained about 200 lines of code, where the logic and parameters were mixed. We decided to make a separate config for the Gruntfile. In my project for this purpose, I created a config.json file in which I store:

    • Source directory
    • Directory for deployment
    • Variables for the template engine (for example, the name of the book)
    • Ignore list (files that do not fall into the deployment)
    • LESS file list and destination file
    • JS file list and target file
    • Parameters for deploying to the server for testing


    There are many benefits to this. Firstly, adding files becomes much easier, and secondly, small changes to the config can be made by a person completely unfamiliar with Grunt. Well, in the third, the logic does not mix with the parameters.

    The config looks like this:

    {
      "src": "_src",
      "dst": "www",
      "port": 8000,
      "variables": { "bookname": "Чапаев и Пустота"},
      "ignore": [ "**/*.ect", "**/*.md", "**/*.less"],
      "less": {
        "www/app/assets/css/app.min.css": ["_src/app/assets/less/main.less"],
        "www/content/assets/css/content.min.css": ["_src/content/assets/less/main.less","_src/content/widgets/**/widget.less"]
        }
      },
      "js": {
        "www/app/assets/js/app.min.js": [
          "_src/app/assets/js/utilities.js",
          "_src/app/assets/js/modules/*.js",
          "_src/app/assets/js/setups/*.js",
          "_src/content/widgets/**/*.js"
          "_src/app/assets/js/init.js",
        ]
      }
    }
    


    Gruntfile example



    module.exports = function (grunt) {
      //  Подключаем config.json
      var config = grunt.file.readJSON('config.json') || grunt.fatal('config.json not found');
      // Разбираемся с паттернами в игнор-листе
      config.ignore = getIgnorePatterns(config);
      var tasks = {
        clean: {
          dst: path.join(config.dst, '**/*'),
          ignore: config.ignore
        },
        livereload: {…},
        regarde: {…},
        copy: {…},
        ect: {…},
        less: {…},
        uglify: {…},
        rsync: {…}
      };
      grunt.initConfig(tasks);
      grunt.registerTask('lvrld', ['livereload-start', 'connect', 'regarde']);
      grunt.registerTask('main', ['clean:dst', 'copy:main', 'clean:ignore','uglify:main', 'ect', 'less:browser']);
      grunt.registerTask('browser', ['main', 'lvrld']);
      grunt.registerTask('phonegap', ['clean:dst', 'copy:main', 'clean:ignore', 'uglify:main', 'less:phonegap', 'ect']);
      grunt.registerTask('deploy', ['main', 'rsync:deploy']);
      grunt.registerTask('default', ['browser']);
      require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
    };
    


    Convenient debugging on devices



    Debugging code on a pair of iPads and MacBooks is very inconvenient. You have to constantly refresh the page with your hands. This is terrible.

    Grunt can help here. If you run grunt-contrib-watch, configure livereload, and connect all devices by IP, then when you change the code, all gadgets will reload the page themselves. This is amazing! Seriously, if you constantly reload the page even on three devices, then in an hour I want to smash everything into chips.

    In general, there is a problem. There are relatively many files in our project. About a thousand 2. So, if you cover everything with different livereloads (with different tasks, of course), then nodejs starts to fall. We have to refuse full coverage. We went under the knife pictures.

    Writing a plugin



    In general, I chose the template engine quite simply - I took the first one, with layout and partials. The first one turned out to be ECTJS. Now I would take jade, but then I took this one. Not finding a suitable plugin for the template engine, I decided to write my own.

    In general, writing a plugin for gruntjs is quite simple. Almost everything that may be needed is written in the instructions . In general, the algorithm is as follows:

    1. grunt-init gruntplugincreate the project structure
    2. We write the code, connect the modules ...
    3. When necessary, we peek into the GruntJS API




    In general, that's all I wanted to share.

    Also popular now: