Photo of Christoph Rumpel

How to Laravel series: Two ways of implementing Grunt to Laravel

In this third part of my Laravel series we are going to take a look at implementing some Grunt tasks in your workflow. The web has become more complicated and so has our work. We have to learn new stuff every day and to adapt us and our workflows to the new needs. Tools like Grunt are perfect to help us on this rocky road. First we will setup Grunt from scratch and then we will take a look at a Laravel package doing this for us.

No project without Grunt

Grunt is a so called task runner. It automates things you are likely to do in every project. In my case this would be minifying / concatenating CSS and JS, compiling Sass, optimizing images and using LiveReload. These are just some simple, but important examples. There is so much more you can do with Grunt, but I will keep it simple. Of course, the tasks you will want to automate will also depend on you and your project’s need. If you think there is no need for Grunt or you are not yet familiar with some Grunt basics, I would recommend this great article by Chris Coyer to you. If he can’t convince you, probably even Chuck Norris would have difficulties.

Install Laravel, the lightning way

What this article will focus on is how to use Grunt in your Laravel projects. If you haven’t used Grunt, you probably would appreciate some help. The best way to get yourself familiar with the setup is to do it yourself. We will implement some Grunt tasks to a Laravel project, but most of the following steps will be the same in every other project environment. First we are going to install Laravel and wer are using the new Laravel installer, which I wrote about in my last article. It is so much faster compared to the installation through Composer. The command is “larvavel new” followed by the directory name.

// Install fresh Laravel
larvavel new laravel-grunt

Assets structure

Next step is to set up our assets structure. We will create some folders and files for this example. The assets folder  contains, as the name already indicates, all of our assets. Our Sass files from the sass folder will be compiled into our CSS folder. We import two Sass partial files into the main styles.scss. This is a good way to organise you styles.

// Import Sass partials
@import "ui/_normalize.scss";
@import "ui/_base.scss";

All of our images will be stored in the images-orig folder, before they get optimised and copied to the main images folder. We will be working with one main JavaScript file which can be found in the js folder. This is how the final structure will look like:

assets/
    css
    images
    images-orig/
        image.jpg
    js/
        main.js
    sass/
        _styles.scss
        ui/
            _base.scss
            _normalize.scss

Basic Grunt setup

Now that the structure is ready, we will start with the Grunt setup. I once again recommend to checkout Grunt basics, because I am not going into detail on the installation. First we create a package.json file in our assets folder. This is where we provide some project infos and tell Node which dependencies we will need. We start with the Grunt module.

{
    "name": "Laravel-Grunt",
    "version": "0.1.0",
    "devDependencies": {
        "grunt": "~0.4.1"
    }
}

After we have saved this file, we can run "npm install" to install the dependencies through the Node Package Manager. (NPM) This will create a new folder with the installed modules. (node_modules)

// Install NMP dependecies
npm install

Then we have to install the Grunt command line interface. It is just a one-liner too.

// Install NMP dependecies
npm install

If you get some errors, you will probably have to run this command as root. (sudo)
The second file we have to create is the Gruntfile.js. This is where we keep all of our task’s settings. It will also be placed in the assets folder's root direcory.

// Basic Grunt configuration
module.exports = function(grunt) {

    // 1. All configuration goes here
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        // Task settings here

    });

    // 3. Where we tell Grunt we plan to use this plug-in.
    grunt.loadNpmTasks('');

    // 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
    grunt.registerTask('default', ['']);

};

So, now we got Grunt installed. Only the tasks we want to automate are missing. In our case:

  • Sass: Compiling and compressing our CSS
  • Uglify: Compressing our JavaScript
  • Imagemin: Optimising our images
  • Watch: Doing all the things above automatically

Grunt task Sass

As I already mentioned above, Grunt is a task runner. We need to load the tasks we will use in our project through the NPM. The steps for including a new task are always the same:

  • Install task through NPM
  • Set task settings in Gruntfile.js
  • Load new task in Gruntfile.js

Let’s start with the Sass task. All tasks provide information about installation and  usage on their site. We install the Sass task with the following command.

// Install Grunt task Sass
npm install grunt-contrib-sass --save-dev

This will install the module to our node_modules folder and it will write the new dependency to the package.json file as well.

// New dependency added
{
    "name": "Laravel-Grunt",
    "version": "0.1.0",
    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-sass": "~0.6.0",
    }
}

In order to use it we have to configure this task in our Gruntfile.js. What we are doing here is setting up a "sass" task which will compile our styles.scss file to a new file in our CSS folder. Additionally we are defining that this file will be compressed.

// Settings for the Sass task
sass: {
    dist: {
        options: {
            style: 'compressed'
        },
        files: {
            'css/styles.css': 'sass/styles.scss'
        }
    }
}

Let’s take a look at the whole Gruntfile.js now.

// Whole Gruntfile.js so far
module.exports = function(grunt) {

    // 1. All configuration goes here
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        // Configuration for the Sass task
        sass: {
            dist: {
                options: {
                    style: 'compressed'
                },
                files: {
                     'css/styles.css': 'sass/styles.scss'
                }
            }
         }

    });

    // 3. Where we tell Grunt we plan to use this plug-in.
    grunt.loadNpmTasks('grunt-contrib-sass');

    // 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
    grunt.registerTask('default', [’sass']);

};

No we can run Grunt with the "grunt" command and our new compressed CSS file will be created. Grunt Sass task For the next tasks I will only explain the settings, because the installation will be very similar.

Grunt task Uglify

Installation We are using Uglify to minfy our JavaScript. We could define a banner for the new file, but we're  leaving this setting empty for now. In "target_1" we define the settings for our main.js file. The name doesn’t matter. It just signals that there could be more files. The source is our main.js file and we want that file minified in the same folder with a new extension “min.js”. That’s it.

// Grunt task Uglify
uglify: {
    options: {
        banner: ''
    },
    target_1: {

        // Source file
        src: ['js/main.js'],

        // Minified new file
        dest: 'js/main.min.js'

    }
}

Again it is necessary to load this task and to put it in the array of the default tasks. These will be executed when we run the grunt command.

// Load Uglify task
grunt.loadNpmTasks('grunt-contrib-uglify’);

...

// Add Uglify task to default tasks array
grunt.registerTask('default', [’sass, uglify']);

When we run Grunt now, both tasks (sass and uglify) will be executed. Grunt Sass and Uglify

Grunt task Imagmin

Installation More than 60% of the Internet’s transfer size belongs to images. Therefor it is crucial to think about optimising them. "Front-end performance part 02: Images"  is an article I wrote about this topic. In order to find a way to automate image optimisation, we can use Grunt as the tool of our choice. We have a folder with our original images and Grunt will optimise them for us. The new, smaller images will be automatically placed in the main image folder afterwards. Again we need to configure this task, load it and define it in the default tasks array. Instead of targeting one file, like we did for the main.js, we are now targeting all images with a specific extensions.

// Grunt task Imagemin
...
imagemin: {
    dynamic: {
        files: [{
            expand: true,
            cwd: 'images-orig/',
            src: ['**/*.{png,jpg,gif}'],
            dest: 'images/'
        }]
    }
},

...
grunt.loadNpmTasks('grunt-contrib-imagemin’);
...
grunt.registerTask('default', [’sass, uglify’, ‘imagemin']);
...

Grunt Sass, Uglify and Imagemin No we can run all these three tasks with only one command. This is really great, but running this command every time we have changed something isn’t really a good example of automation. This is why we are installing another and last task called watch.

Grunt task watch

Installation If you have worked with Compass, then you are probably already familiar with the watch command. Once activated this task will look for changes and then automatically trigger the right tasks. This is what really makes Grunt powerful! Ok let’s take a look at the settings. For every task we define two things. First where to look for changes and second what task to run if something has changed. If there is a modification in one of the .scss files, we want to run the sass task, if there is a change in the main.js file, we want to trigger the uglify task and so on.

// Grunt task watch
...
watch: {
    sass: {
        files: ['sass/**/*.scss'],
        tasks: ['sass'],
    },
    uglify: {
        files: ['js/main.js'],
        tasks: ['uglify']
    },
    imagemin:{
        files: ['images-orig/*.{png,jpg,gif}'],
        tasks: ['imagemin']
    },
    livereload: {
        options: {
            livereload: true
        },
        files: [
            '../../app/views/*.php', 'css/*.css'
        ]
    },

},
...

Now we need to modify the default task of our Grunt setup to run only the watch task. All other tasks will be triggered through this one.

// Change default Grunt task to watch
grunt.registerTask('default', ['watch']);

But there is one extra feature within the watch task. It includes LiveReload! We are looking for changes in our Laravel view files and in our CSS files. If something changes there, we want to reload the browser. You will have to install the LiveReload browser extension to use this feature, but I can totally recommend doing this. I wouldn't want to work without LiveReload. Here is what our final Gruntfile.js looks like.

// Final Gruntfile
module.exports = function(grunt) {

    // 1. All configuration goes here
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        // Compile and compress Sass
        sass: {
            dist: {
                options: {
                    style: 'compressed'
                },
                files: {
                    'css/styles.css': 'sass/styles.scss'
                }
            }
        },

        // Minify JS
        uglify: {
            options: {
                banner: ''
            },
            target: {
                // Source file
                src: ['js/main.js'],

                // Minified new file
                dest: 'js/main.min.js'

            }
        },

        // Optimize images
        imagemin: {
            dynamic: {
                files: [{
                    expand: true,
                    cwd: 'images-orig/',
                    src: ['**/*.{png,jpg,gif}'],
                    dest: 'images/'
                }]
            }
        },

        // Watch files and trigger tasks
        watch: {
            sass: {
                files: ['sass/**/*.scss'],
                tasks: ['sass'],
            },
            uglify: {
                files: ['js/main.js'],
                tasks: ['uglify']
            },
            imagemin:{
                files: ['images-orig/*.{png,jpg,gif}'],
                tasks: ['imagemin']
            },
            livereload: {
                options: {
                    livereload: true
                },
                files: [
                    '../../app/views/*.php', 'css/*.css'
                ]
            },

        },

    });

    // 3. Where we tell Grunt we plan to use this plug-in.
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-imagemin');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
    grunt.registerTask('default', ['watch']);

};

Grunt package for Laravel

Now that we know how to setup Grunt in our Laravel projects from scratch, we are prepared to take a look at at the "Laravel 4 + Grunt Asset Workflow Package - BETA” by Jason Morton. This package can help us with the process of setting up Grunt in Laravel.
It already includes the Grunt tasks for compiling Sass, LESS, Stylus, minifying, concatenating, watching and LiveReload.
As you have seen, there is quite a lot of configuration needed to setup Grunt at the beginning. But before we can take a look at the pros and cons here, let’s test this package right away. We will start a brand new project with the Laravel installer. Install Laravel Afterwards we need to add the Grunt package to our project. Adding a package to Laravel has the same steps in most cases:

  • Add the package to the composer.json file
  • Update composer to load the package
  • Add the service provider
So let’s do this together. First we add the package name to our project’s composer.json file.
// Add Laravel Grunt package to Composer settings file
...
"require": {
    "laravel/framework": "4.1.*",
    "jason-morton-nz/laravel-grunt": "dev-master"
},
"autoload": {
    "classmap": [
...

Then we need to update our Composer packages. This will download the new package.

// Update Composer
composer update —dev

And last but not least we set the new service provider in our app/config/app.php file, so that it automatically will be loaded on the request to our application. Now that the package is ready to use, we will start with creating a new config file. This package comes with a simple command for this task.

// Create package config file
php artisan grunt:config

This new file can be found here: app/config/packages/json-morton-nz/laravel-grunt/config.php Jason did a good job in documenting this file, so that it is easy to see through. We will try to create an asset setup similar to our first one. Most of the default settings will remain untouched, but there are a few changes we need to do. First we change the css_files array. These are the CSS files that will be concatenated and minified. Since we are using Sass we will produce only on CSS file named sass.css.

'css_files' => array(
    'assets/css/sass.css',
),

The js_files array will be changed as well, because we will work with one main JavaScript file in this example.

'js_files' => array(
    'assets/js/main.js',
),

Finally we set a different main Sass file where we will import all our other Sass partials like before.

'sass_file' => 'assets/sass/main.scss’,

That’s it with the settings. Let’s sum it up. We are saying that we will have a  assets folder in our project’s root called assets. There we will have a sass.css file in a CSS folder that we want to be minified. Additionally there is a js folder , where we want a main.js file minified too. Those minified files will be put in the public/assets folder. These are the files we will import to our templates. And the last thing we defined is a main.scss file that will be compiled to the CSS folder. From there it gets minified, as I mentioned before. Once we’re ready with our settings, the package can now create the Grunt tasks with the appropriate configurations.

// Create Grunt settings from our configurations
php artisan grunt:setup

This will create all of the tasks for us. You will get asked if you want to use a pre-processor and if yes which one. Make sure to say yes and sass. Ok let’s catch a breath here. It sounds a litte bit complicated, but I promise it isn’t. Once you have tested it yourself, it will get much clearer. Now that the settings are set, we can go on. Of course we need to create the files we defined in the package’s settings. You can copy the files we used in the first part of this article.
Just make sure the final structure looks like this:

// Assets structure
assets/
    css
    js/
        main.js
    sass/
        main.scss
        ui/
            _base.scss
            _normalize.scss

The CSS folder is empty at first, because the sass.css file will be created automatically by the package from the Sass file once we run it.
Great! Let’s run the tasks with the following command. We did the same when we ran the default Grunt tasks.

// Run Grunt tasks
php artisan grunt:build

If everything worked out fine, you now will  see the new files script.min.js and style.min.css in the public/assets folder. As I already mentioned the watch task and LiveReload comes with this package too.

// Watch tasks
php artisan grunt:watch

Now all changes will run the appropriate tasks and the browser will reload automatically if you have the LiveReload browser extension running. This package is really great and totally helpful when you are starting to work with Grunt and Laravel. Especially setting everything up is much faster than doing it yourself. On the other site I am missing some features here. What about adding Tasks? A workflow like this needs to grow and adapt to your needs and this is not possible with this plugin yet.
But since this package is just a beta version, I am sure new features will be added soon.

Conclusion

If you made it from the top of this article to this end here, then I am sure you have learned a lot today. That’s awesome! We have covered a lot about Grunt and how to use it in a Laravel project today. We got our hands dirty and did all the setup by ourselves in the first part. But we also took a look at a great Laravel package which helps us with all the configurations and NPM stuff in the second part. Please let me know about your Grunt experiences and your favourite tasks.

Resources

Let's stay in touch

Sign up for my newsletter and I will let you know about more content and new projects of mine once a month.