Code to Browser in < 3 Seconds in Magento 2 (Part 1)

Tom Procter | March 24th 2017

magento_3_seconds

As Magento 2 developers, we have several file types to deal with during our work. These are as follows:

  • XML
  • phtml
  • php
  • CSS
  • js
  • LESS
  • SCSS

When we make changes to these files, we want our local copy to reflect these changes as quickly as possible, so we felt it be our duty to share with the Magento 2 community how we have achieved this through a series of blog posts that will detail how we can do that with Magento 2, without having to spend all our time clearing caches and deploying static content.

For the purpose of this series, the following directory will be used for the Magento 2 root:

~/Code/magento2/public_html

Grunt Commands and the Magento 2 Gruntfile

With Magento 2, they have packaged a fairly decent Grunt configuration. This allows us to do some tasks (http://devdocs.magento.com/guides/v2.0/frontend-dev-guide/css-topics/css_debug.html#grunt_commands) such as remove theme related files, compile CSS and track changes to CSS and LESS files. This will form the foundation of how we eventually get the code to quickly display in the browser once we have edited it.

Extending Magento 2’s Grunt Configuration

We can extend this functionality to get Grunt to watch the list of file types above and also build in some additional packages so that our browsers sync the changes.

The first thing that we need to do is go to the Magento 2 root and rename Gruntfile.js.sample to Gruntfile.js

Then look for the following task:

/**
         * Refresh themes.
         */
        refresh: function () {
            var tasks = [
                'clean',
                'exec:all'
            ];
            _.each(themes, function (theme, name) {
                tasks.push('less:' + name);
            });
            grunt.task.run(tasks);
        },

We are going to use this task as a template to create a new one. Let’s call this task “dev”

/**
         * Custom Development Process.
         */
        dev: function () {
            var tasks = [
                'sass_globbing:theme',
                'sass:theme',
                'concat:css',
                'concat:js',
                'sprite:theme',
                'file-creator:log',
                'browserSync:dev', 
                'watch'
            ];
            grunt.task.run(tasks);
        },

“dev” has some custom tasks in there, and removes the .each functionality that looks through each theme. You’ll see why we do this later.

Adding more Grunt tasks to Magento 2

The first thing you may notice about the task list is the use of tasks that are *not* bundled with Magento 2, such as sass_globbing (the personal favourite of one of our developers!). We use npm to install these packages, like so:

npm install grunt-sass-globbing --save-dev

This will add the package to the package.json file that comes with Magento 2, allowing us to eventually run tasks such as:

grunt sass_globbing

To be able to run those tasks, we need to add the configurations for the additional Grunt packages. (This is where you could deviate slightly from how we have done this, and decide to go with a different file and folder structure; however, for this example we have extended the default Gruntfile and included the additional tasks that way.)

To do this, create the following file along side the default Magento 2 Gruntfile (where custom is the name of your Company name or something similar; we’ve used “pushon”):

Gruntfile.custom.js

This means that we can now run commands such as:

grunt --gruntfile=Gruntfile.custom.js

However, doing so at this stage will not work until we populate the custom file.

Creating a custom Gruntfile

'use strict'; 
 
var defaultGruntfile    = require('./Gruntfile'), 
    _                   = require('underscore'); 
 
var customGruntfile = {}; 
 
customGruntfile.sass_globbing   = require('./vendor/custom/magento-theme-blank/web/grunt/tasks/sass_globbing');  
 
module.exports = _.extend(defaultGruntfile, customGruntfile);

We include the original Grunt file, as well as the underscore, and then use the module.exports feature to extend the original Grunt file to use our custom configuration.

For Grunt modules that are already included in Magento 2 (such as cssmin), we don’t need to add anything extra to dev/tools/grunt/configs, but if we do want to use a module that isn’t included, we need to add a file to extend. This was the case for sass_globbing (amongst others).

The reason for this is that we want to keep hold of the original functionality offered by Magento 2’s Grunt setup (such as the theme regeneration etc.), so we need to put a file in the default location that is in Gruntfile.js:

var _ = require('underscore'),
        path = require('path'),
        themes = require('./dev/tools/grunt/configs/themes'),
        configDir = './dev/tools/grunt/configs',
        tasks = grunt.file.expand('./dev/tools/grunt/tasks/*');

Base on the configDir defined in the Gruntfile.js, we create the following file:

File Name

./dev/tools/grunt/configs/sass_globbing.js

File Contents

'use strict';

var project   = require('./combo');

module.exports = {
  none:{
    files: {}
  }
};

Depending on the task that you want to add a configuration for, this file could like slightly different, but in essence you set up a blank task that won’t be used, and is simply there to extend later. Admittedly, this could be used to add the configuration too; however we found it best to keep all of our custom configurations away in our blank theme that we could deploy easily.

Finally, we can create our file defined in Gruntfile.custom.js, which in this example is “sass_globbing”,

File Name

./vendor/custom/magento-theme-blank/web/grunt/tasks/sass_globbing.js

File Contents

'use strict';

var defaultSassGlobbing  = require('../../../../../../dev/tools/grunt/configs/sass_globbing'),
          _              = require('underscore');

var customSassGlobbing = {
  theme: {
    files: {
      '<%= path.project.sass %>/_00_mixins_importMap.scss': '<%= path.project.sass %>/theme/00_mixins/**/*.scss',
      '<%= path.project.sass %>/_01_styles_importMap.scss': '<%= path.project.sass %>/theme/01_styles/**/*.scss',
    },
    options: {
      useSingleQuotes: false
    }
  }
};

module.exports = _.extend(defaultSassGlobbing, customSassGlobbing);

This file may need to look different, depending on where you have decided to put this file. Where the file goes depends on what you set the relative path to at the top of the file to include the file that has just been added to dev/tools/grunt/configs.

The configuration values that need to go in this file depend on the package that is being configured, but one thing is likely to remain consistent, and that’s the use of the “theme” value. This is so we can run commands such as:

grunt --gruntfile=Gruntfile.custom.js sass_globbing:theme

and skip out the blank file we created that refers the following section of your custom config file:

  theme: {

  }

We repeated this process several times to add the various packages and tasks that we required in our development process (defined further up in this article). Most importantly, we extended the functionality of the watch task that comes with Magento 2. We will look at how we did this in Part 2.