Showing posts with label Node. Show all posts
Showing posts with label Node. Show all posts

Monday, March 10, 2014

Amazing Builds with Grunt.js

Continuing with my previous post on my new app Urban Champ, I recently optimized my build process using Grunt.js.

Grunt is an amazing build tool which can be used to do a variety of things. Currently I am using Grunt tasks for concatenating and minifying css files, running the RequireJS build optimizer, preprocessing HTML (to do things like point your built HTML at compiled/minified scripts), and image minification. Grunt is also extremely useful for separating your build targets (such as development and production) and then firing up a server on your localhost. I really like the livereload grunt "watch" task which will automatically reload your server as you change source files in your editor.

My hats off to the Grunt team! I will definitely be keeping a close eye on future Yeoman generators using Grunt. This is an extremely powerful tool for all you web devs out there!

Node grunt packages installed:
{
  "name": "UrbanChamp",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-concat": "^0.3.0",
    "grunt-contrib-uglify": "^0.4.0",
    "grunt-contrib-cssmin": "^0.9.0",
    "grunt-contrib-requirejs": "^0.4.3",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-open": "^0.2.3",
    "grunt-contrib-connect": "^0.7.1",
    "grunt-contrib-watch": "^0.5.3",
    "grunt-contrib-livereload": "^0.1.2",
    "grunt-uncss": "^0.2.0",
    "grunt-processhtml": "^0.3.0",
    "connect-modrewrite": "^0.6.3-pre",
    "grunt-contrib-imagemin": "^0.5.0"
  }
}

Gruntfile.js:
'use strict';
var modRewrite = require('connect-modrewrite');
var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};


module.exports = function (grunt) {
    grunt.initConfig({
      clean: {
        build: {
          src: ['build']
        },
        css: {
          src: ['build/App/css/*.css','!build/App/css/tidy.min.css']
        },
        images: {
          src: ['build/App/images/*']
        }
      },
      watch: {
          livereload: {
              files: [
                  'app/App/*.html',
                  '{.tmp,app}/App/css/{,*/}*.css',
                  '{.tmp,app}/App/js/{,*/}*.js',
                  'app/App/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
              ],
              tasks: ['livereload']
          }
      },  
      connect: {
          options: {
              port: 8888,
              // change this to '0.0.0.0' to access the server from outside
              hostname: 'localhost'
          },
          livereload: {
              options: {
                  middleware: function (connect) {
                      return [
                          modRewrite(
             ['!\\.html|\\.js|\\.svg|\\.css|\\.png|\\.jpg$ /index.html [L]']),
                          lrSnippet,
                          mountFolder(connect, '.tmp'),
                          mountFolder(connect, 'app')
                      ];
                  }
              }
          },
          dist: {
              options: {
                  middleware: function (connect) {
                      return [
                          mountFolder(connect, 'build')
                      ];
                  }
              }
          }
      },          
      open: {
          server: {
              path: 'http://localhost:8888'
          }
      },      
      requirejs: {
        dist: {
         options: {
           appDir: 'app',
            baseUrl: 'App/js', 
            optimize: 'uglify',
            preserveLicenseComments: false,        
            mainConfigFile: 'app/App/js/main.js',
            dir: 'build'
         }
        }
      },
      concat: {
        dist: {
          src: ['app/App/css/*'],
          dest: 'build/App/css/tidy.css'
        }
      },
      cssmin: {
          dist: {
            src:'build/App/css/tidy.css',
            dest: 'build/App/css/tidy.min.css'
          }
      },      
      processhtml: {
        dist: {
          files: {
            'build/index.html': ['app/index.html']
          }
        }
      },
      imagemin:{
        dynamic: {
          files: [{
            expand: true,                  // Enable dynamic expansion
            cwd: 'app/App/images',                   // Src matches are relative to this path
            src: ['**/*.{png,jpg,gif}'],   // Actual patterns to match
            dest: 'build/app/images'                  // Destination path prefix
          }]          
        }

      }            
});


// load plugins
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks('grunt-open');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-processhtml');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-imagemin');


// register at least this one task
grunt.registerTask('build', ['clean:build','requirejs', 'concat','cssmin','processhtml', 'clean:css', 'clean:images', 'imagemin']);
grunt.registerTask('default', ['build']);

grunt.registerTask('server', function (target) {
    if (target === 'dist') {
        return grunt.task.run(['build', 'open:server', 'connect:dist:keepalive']);
    }

grunt.task.run(['open:server', 'connect:livereload:keepalive', 'watch']);
});
};