How to extend default Magento 2 Grunt configuration without touching/editing core files like Gruntfile.js
and dev/tools/grunt/configs/themes.js
?
3 Answers
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.– TischJan 2, 2017 at 11:47
-
1Figured out how to extend the grunt file with additional tasks: magento.stackexchange.com/a/152790/28664– TischJan 2, 2017 at 18:05
-
1Jev when you run
exec:yourthemename
do you getWarning: Required config property "clean.yourthemename" missing. Used --force, continuing.
?? It's odd I am able to clean my theme with theclean:yourthemename
command but I feel theexec
command should do this without error. Feb 8, 2017 at 17:38 -
1I've noticed numerous files inside
dev/tools/grunt/configs
perform arequire('./themes')
, examples areclean.js
andexec.js
inside that configs directory. Which leads me to believe that these files will then be missing our newly added theme fromthemes.yourthemename.js
. Still this setup works I just cannot determine the cause of myRequired config property "clean.yourthemename" missing.
error... Feb 8, 2017 at 17:48
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.
- 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 });