10

How to extend default Magento 2 Grunt configuration without touching/editing core files like Gruntfile.js and dev/tools/grunt/configs/themes.js?

10

Creating new theme for Magento 2 could be a challenge even having experience with Magento before. Decent developers would not change core Magento files, but create "wrappers" instead, so in future installing patches and doing updates you will not have situation when all your changes are overridden or are merged incorrectly.

Extend Gruntfile.js and themes.js files

Let's say you created new theme and as we know from documentation Magento 2 docs you will need to change file dev/tools/grunt/configs/themes.js adding your theme in to the list, so Grunt could compile/symlink/copy css/less files in to pub/static folder.

Step1: Create /dev/tools/grunt/configs/themes.yourthemename.js file which extends default themes.js file as

'use strict';

var defaultThemes   = require('./themes'),
    _               = require('underscore');

var yourTheme = {
    yourthemename: {
        area: 'frontend',
        name: '<vendor>/<yourthemename>',
        locale: 'en_US',
        files: [
            'css/main',
        ],
        dsl: 'less'
    }
};

module.exports = _.extend(defaultThemes, yourTheme);

Step2: Extend Gruntfile.js with file Gruntfile.yourthemename.js

'use strict';

var defaultGruntfile    = require('./Gruntfile'),
    _                   = require('underscore');

var yourthemeGruntfile = {};
    yourthemeGruntfile.themes = require('./dev/tools/grunt/configs/themes.yourthemename');

module.exports = _.extend(defaultGruntfile, yourthemeGruntfile);

Step3: Now you can run Grunt tasks for your theme like:

grunt --gruntfile=Gruntfile.yourthemename.js clean:yourthemename
grunt --gruntfile=Gruntfile.yourthemename.js exec:yourthemename
grunt --gruntfile=Gruntfile.yourthemename.js less:yourthemename
grunt --gruntfile=Gruntfile.yourthemename.js watch:yourthemename
  • Is it possible to register a new task to the Gruntfile using this method? I'm struggling to get access to "grunt" in my extended file. – Tisch Jan 2 '17 at 11:47
  • 1
    Figured out how to extend the grunt file with additional tasks: magento.stackexchange.com/a/152790/28664 – Tisch Jan 2 '17 at 18:05
  • 1
    Jev when you run exec:yourthemename do you get Warning: Required config property "clean.yourthemename" missing. Used --force, continuing.?? It's odd I am able to clean my theme with the clean:yourthemename command but I feel the exec command should do this without error. – Darren Felton Feb 8 '17 at 17:38
  • 1
    I've noticed numerous files inside dev/tools/grunt/configs perform a require('./themes'), examples are clean.js and exec.js inside that configs directory. Which leads me to believe that these files will then be missing our newly added theme from themes.yourthemename.js. Still this setup works I just cannot determine the cause of my Required config property "clean.yourthemename" missing. error... – Darren Felton Feb 8 '17 at 17:48
2

When Jev Mokrousov's solution doesn't fit you, there's two alternatives you could try:

Composer post install command

During the installation of the magento/magento2-base package, the file Gruntfile.js and the folder dev/tools will be copied from the package into your root folder overwriting any existing files, caused by the Magento2 base composer.json mapping (see vendor/magento/magento2-base/composer.json in your project):

{
    "extra": {
        "map": [
            [
                "dev/tools",
                "dev/tools"
            ],
            [
                "Gruntfile.js",
                "Gruntfile.js"
            ]
        ]
    }
}

You could place your versions of Gruntfile.js and dev/tools/grunt/configs/themes.js somewhere (we've put them inside our build directory structure in build/tools/grunt/).

Now there's the possibility to add extra commands or scripts before or after certain Composer events. We could hook into Composer's post-install-cmd event to copy our versions of these files over Magento's core ones. You should add this to your project's composer.json:

{
    "scripts": {
        "post-install-cmd": "cp -vfp build/tools/grunt/Gruntfile.js . && cp -vfp build/tools/grunt/themes.js dev/tools/grunt/configs/"
    }
}

This will show you:

> cp -vfp build/tools/grunt/Gruntfile.js . && cp -vfp build/tools/grunt/themes.js dev/tools/grunt/configs/
‘build/tools/grunt/Gruntfile.js’ -> ‘Gruntfile.js’
‘build/tools/grunt/themes.js’ -> ‘dev/tools/grunt/configs/themes.js’


Theme as a Composer module

Just like the magento/magento2-base package is mapping files into the project's root (as described above) you can also do this yourself.

You can put your theme in a separate Composer package. You'll need to create a separate repository for this. The Magento docs are already describing this process: see "Make your theme a Composer package"

Now add this in your theme's composer.json file:

{
    "extra": {
        "map": [
            [
                "dev/tools/grunt/configs/themes.js",
                "dev/tools/grunt/configs/themes.js"
            ],
            [
                "Gruntfile.js",
                "Gruntfile.js"
            ]
        ]
    }
}

Make sure the first path points to the right location inside your theme package. The paths are relative from the location of the theme's composer.json file.


Warning:
Both solutions are overwriting core files. This can cause patch or upgrade problems. When patching and upgrading you should always check what is going to be changed and apply those changes on your copies of these core files.

2
  • This is a supplementary answer to Jev Mokrousov's answer.
  • Written for Magento 2.1 (I'm told Magento 2.2 will have better built in solutions to this)

As I am sure many people will find themselves extending a parent theme rather than completely building a theme from scratch, here is some slightly different syntax offered for your themes.yourThemeName.js file. Rather than completely defining from scratch the configuration for our theme, we inherit it from the parent, and then append/modify what is new/different.

In this example we're inheriting from blank's configuration, setting our theme name, and adding our theme's additional root file(s). A benefit to this is that you do not have to specifically declare all the files from the parent theme. For people extending a parent theme that receives updates periodically this could be beneficial. (Using blank as my example scenario here probably isn't the most applicable situation, but the concepts applied here is what matters).

'use strict';

// If your theme's parent has it's own "themes.someOtherName.js" file, 
// require that file instead of Magento's native "themes.js" file.
var defaultThemes   = require('./themes'),
    _               = require('underscore');

// Update "blank" to the name of your parent theme's Grunt config.
// Update "<vendor>/<yourThemeName>"
var yourThemeNameConfig = _.extend(defaultThemes.blank, {name:'<vendor>/<yourThemeName>'});

// Change this to add your root files, add more as necessary
yourThemeNameConfig.files.push('css/new-file');

// Change "yourThemeName" to what you want to reference in your Grunt command.
module.exports = _.extend(defaultThemes, { yourThemeName: yourThemeNameConfig });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.