Grunt - run task(saas,cssmin) in loop - gruntjs

I am trying to implement Grunt to automate the process of CSS merging and minification. My Folder Structure:
folder1
-- sass
-- css
folder2
-- sass
-- css
folder3
-- sass
-- css
folder4
-- sass
-- css
Requirement: Individually, the grunt task is running properly on each project when it is triggering on file Save from eclipse. But, what I want is to execute the minification process for all folders by issuing grunt command i.e. when I type "grunt" in cmd, it should loop through the folder to generate minified css for each. Currently, it is generating the css for the last folder in each iteration as initconfig is inside the loop.
Simplified version of Gruntfile.js(carrying only relevant code):
module.exports = function(grunt) {
var tasks = [
'grunt-contrib-cssmin','grunt-sass'
];
tasks.forEach( function ( task ) {
grunt.loadNpmTasks( task );
});
var folder =['folder1', 'folder2', 'folder3', 'folder4'];
var source;
var csspath;
var destination;
for (var i = 0; i < folder.length; i ++ ) {
source=folder[i] + '/sass/*.scss';
csspath=folder[i] + '/css/';
destination=csspath + 'combined-styles.css';
grunt.initConfig({
config: {
destination: destination
},
sass: {
dist: {
files: {
'<%= config.destination %>' : [source]
}
},
},
cssmin: {
target: {
files: [{
expand: true,
cwd: csspath,
src: ['combined-styles.css'],
dest: csspath,
ext: '.min.css'
}]
}
}
});
grunt.task.registerTask(folder[i], ['sass','cssmin']);
}
grunt.task.registerTask('default', folder);
};
Any help would be highly appreciated, Thanks.

I was able to fix the issue. Following are the steps:
Initialized sass and cssmin array
Changed my sass task configuration from
files: {
'<%= config.destination %>' : [source]
}
to
files: [{
expand: true,
cwd: scsspath,
src: [source],
dest: csspath,
ext: '.css'
}]
(similar to cssmin)
Added value to array sass and cssmin for each folder
Assigned the sass and cssmin array to sass and cssmin task respectively using grunt.config.set:
grunt.config.set("sass", sass);
grunt.config.set("cssmin", cssmin);
Register task "sass" and "cssmin"
grunt.task.registerTask('default', ['sass','cssmin']);
Correct Gruntfile.js:
module.exports = function(grunt) {
var tasks = [
'grunt-contrib-cssmin','grunt-sass'
];
tasks.forEach( function ( task ) {
grunt.loadNpmTasks( task );
});
var folder =['folder1', 'folder2', 'folder3', 'folder4'];
var source,csspath,scsspath;
var sass={};
var cssmin={};
for (var i = 0; i < folder.length; i ++ ) {
scsspath=folder[i] + '/sass/';
source='*.scss';
csspath=folder[i] + '/css/';
sass["folder"+i]= {
files: [{
expand: true,
cwd: scsspath,
src: [source],
dest: csspath,
ext: '.css'
}]
};
cssmin["folder"+i]= {
files: [{
expand: true,
cwd: csspath,
src: ['combined-styles.css'],
dest: csspath,
ext: '.min.css'
}]
};
}
grunt.config.set("sass", sass);
grunt.config.set("cssmin", cssmin);
grunt.task.registerTask('default', ['sass','cssmin']);
};

Related

How to use array variable properly in gruntfile.js

Trying to use a predefined array inside of a grunt file, thought using this.js_paths would work, but doesn't seem to work as I'm getting the error, "Cannot read property IndexOf of undefined" when it comes to trying to uglify the scripts. How can I link the js_paths variable to the files src property properly instead of copying the array into the files. Would like to define it separately at the top. Is this possible?
module.exports = function(grunt) {
// loadNpmTasks from package.json file for all devDependencies that start with grunt-
require("matchdep").filterDev("grunt-*", './package.json').forEach(grunt.loadNpmTasks);
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
js_paths: [
'inc/header1/js/*.js',
'!inc/header1/js/*.min.js',
'inc/header2/js/*.js',
'inc/header2/js/*.js',
'!inc/header2/js/*.min.js',
'js/*.js',
'!js/*.min.js'
],
uglify: {
options: {
mangle: true
},
build: {
files: [{
expand: true,
src: this.js_paths,
rename: function(dst, src) {
return src.replace('.js', '.min.js');
}
}]
}
},
watch: {
scripts: {
files: ['inc/header1/js/*.js', 'inc/header2/js/*.js', 'js/*.js'],
tasks: ['uglify'],
options: {
spawn: false,
}
}
}
});
grunt.registerTask('default', ['uglify', 'watch']);
};
Preferrably would like to use the same array js_paths in the watch files (since it's required there), if that makes sense? Still kinda new to using gruntfile.js
Utilize the Templates syntax. It's described in the docs as following:
Templates
Templates specified using <% %> delimiters will be automatically expanded when tasks read them from the config. Templates are expanded recursively until no more remain.
Essentially, change this.js_paths to '<%= js_paths %>' in your uglify task.
For instance:
// ...
uglify: {
options: {
mangle: true
},
build: {
files: [{
expand: true,
src: '<%= js_paths %>', // <-----
rename: function(dst, src) {
return src.replace('.js', '.min.js');
}
}]
}
},
// ...
Likewise for your watch task too.
For instance:
watch: {
scripts: {
files: '<%= js_paths %>', // <-----
tasks: ['uglify'],
options: {
spawn: false,
}
}
}

Grunt uglify all .js file into one single file

I have installed Grunt, Node and updated npm. I am trying to minify all js files into one single file using "grunt uglify". The above command creating new js files with minified code. I placed all JS files in js and also tried with src folders. Below is my code please help With this, I am new to Grunt:
module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
my_target: {
files: [{
expand: true,
cwd: 'src/js',
src: '**/*.js',
dest: 'dest/.min.js'
}]
}
},
cssmin: {
my_target:{
files : [{
expand: true,
cwd: 'css/',
src: ['*.css', '.min.css'],
//src: '*.css',
dest: 'css/',
ext: '.min.css'
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
}
I know it's been a year since the question was asked, but I'd like to contribute anyway for the sake of googling and finding this thread.
To achieve what you're trying to do you should NOT use expand param. Just set src and dest ones. Like this:
module.exports = function(grunt){
grunt.initConfig({
uglify: {
my_target: {
files: [{
src: "js/src/*.js",
dest: "js/main.min.js"
}]
},
},
});
grunt.loadNpmTasks('grunt-contrib-uglify');
};
Hope this helps.

Extend sails 0.10 grunt set-up

I am intending to add a task to sails's (0.10 rc5) grunt build system.
tasks/config/asset-versioning.js
module.exports = function(grunt) {
grunt.config.set('asset-versioning', {
copy: {
src: '.tmp/public/min/production.js',
dest: '.tmp/public/min/production2.js'
}
});
grunt.task.registerTask('asset-versioning', ['copy']);
grunt.loadNpmTasks('grunt-contrib-copy');
};
tasks/register/prod.js
module.exports = function (grunt) {
grunt.registerTask('prod', [
'compileAssets',
'concat',
'uglify',
'cssmin',
'sails-linker:prodJs',
'sails-linker:prodStyles',
'sails-linker:devTpl',
'asset-versioning' // Added the task config here
]);
};
After running sails lift --prod --verbose I am getting following error:
Grunt :: Warning: Task "asset-versioning" not found.
** Grunt :: An error occurred. **
What am I missing?
EDIT
Apparently I am missing to register my task.
Adding grunt.task.registerTask('asset-versioning'); to the first file. I am not getting any error, but my task is not running! Nothing is happening.
Okay, you're having this all mixed up. Copy is a predefined task, you just need to tweak its configs. Go to tasks/config/copy.js and add your custom configuration (in the grunt.config.set call)
productionfiles: {
src: '.tmp/public/min/production.min.js',
dest: '.tmp/public/min/production2.min.js'
}
Then, in the tasks/register/prod.js, make sure you call copy:productionfiles, and it will do the magic.
Copy.js
module.exports = function(grunt) {
grunt.config.set('copy', {
dev: {
files: [{
expand: true,
cwd: './assets',
src: ['**/*.!(coffee|less)'],
dest: '.tmp/public'
}]
},
build: {
files: [{
expand: true,
cwd: '.tmp/public',
src: ['**/*'],
dest: 'www'
}]
},
productionfiles: {
src: '.tmp/public/min/production.min.js',
dest: '.tmp/public/min/production2.min.js'
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
};
Prod.js
module.exports = function (grunt) {
grunt.registerTask('prod', [
'compileAssets',
'concat',
'uglify',
'cssmin',
'sails-linker:prodJs',
'sails-linker:prodStyles',
'sails-linker:devTpl',
'sails-linker:prodJsJade',
'sails-linker:prodStylesJade',
'sails-linker:devTplJade',
'copy:productionfiles'
]);
};

Change the configuration of the task uglify dynamically

I need to change configuration of my uglify task for only minify file as needed (as explained here for the jshint task : https://github.com/gruntjs/grunt-contrib-watch#compiling-files-as-needed)
The modification works well for the jshint task but not for uglify, i think the problem is the property path...
Any help would be appreciated ;)
Here is my Gruntfile.js :
module.exports = function (grunt) {
grunt.initConfig({
// define source files and their destinations
jshint: {
all: ['dev/**/*.js'],
},
uglify: {
dynamic_mappings: {
// Grunt will search for "**/*.js" under "dev/" when the "minify" task
// runs and build the appropriate src-dest file mappings then, so you
// don't need to update the Gruntfile when files are added or removed.
files: [{
expand: true, // Enable dynamic expansion.
cwd: 'dev/', // Src matches are relative to this path.
src: ['**/*.js'], // Actual pattern(s) to match.
dest: 'build', // Destination path prefix.
ext: '.min.js', // Dest filepaths will have this extension.
},
],
}
}
watch: {
options: { spawn: false },
js: { files: 'dev/**/*.js', tasks: [ 'uglify' ] },
}
});
// load plugins
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
// default task
grunt.registerTask('default', [ 'watch' ]);
grunt.event.on('watch', function(action, filepath) {
grunt.config(['jshint', 'all'], filepath);
grunt.config('uglify.dynamic_mappings.files', [{src: filepath }]);
});
};
The line
grunt.config('uglify.dynamic_mappings.files', [{src: filepath }]);
Is completely replacing the original configuration for uglify.dynamic_mappings.files
Instead try including the other original parameters along with the new src: filepath
How to not minify already minified on each "grunt build" in yeoman
uglify: {
onlyScripts: {
files: [{
dest: '<%= yeoman.dist %>/scripts/scripts.js',
src: ['.tmp/concat/scripts/scripts.js']
}]
}
}
Also, now uglify will not copy your vendor.js from temporary folder, so add "vendorJS" section to "copy" task:
copy:
//...
vendorJS: {
expand: true,
cwd: '.tmp/concat/scripts/',
dest: '<%= yeoman.dist %>/scripts/',
src: 'vendor.js'
}
Then, in "build" task, set target of uglify to 'onlyScripts' and copy vendor.js:
grunt.registerTask('build', [
'jshint',
'clean:dist',
//'wiredep',
// ...
'useminPrepare',
//'concurrent:dist',
'autoprefixer',
'concat',
'ngAnnotate',
'copy:dist',
'cssmin',
'uglify:onlyScripts',
'copy:vendorJS',
// ...
]);
http://eugenioz.blogspot.in/2014/08/how-to-not-minify-already-minified-on.html

How can I rename files with Grunt, based on the respective file's parent folder name?

I have a the following structure:
src/
modules/
module1/
js/
main.js
scss/
main.scss
index.html
module2/
js/
main.js
scss/
main.scss
index.html
I'd like to run a grunt task to copy these out to the following structure:
dev/
js/
module1.js
module2.js
css/
module1.css
module2.css
module1.html
module2.html
Is there a way to do this with an existing grunt plugin? If not, how could I achieve this?
This can be done using the grunt-contrib-copy plugin.
The main thing to note is that you can change the destination programmatically by using a rename function (which takes in the destination and source of each file).
Here is a (somewhat brittle) sample Gruntfile.js that should copy to your desired structure:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
copy: {
main: {
files: [
{
expand: true,
cwd: 'src/modules/',
src: ['**/*.js'],
dest: 'dev/js/',
rename: function(dest, src) {
// use the source directory to create the file
// example with your directory structure
// dest = 'dev/js/'
// src = 'module1/js/main.js'
return dest + src.substring(0, src.indexOf('/')) + '.js';
}
},
{
expand: true,
cwd: 'src/modules/',
src: ['**/*.scss'],
dest: 'dev/css/',
rename: function(dest, src) {
return dest + src.substring(0, src.indexOf('/')) + '.css';
}
},
{
expand: true,
cwd: 'src/modules/',
src: ['**/*.html'],
dest: 'dev/',
rename: function(dest, src) {
return dest + src.substring(0, src.indexOf('/')) + '.html';
}
}
]
}
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
// Default task(s).
grunt.registerTask('default', ['copy']);
};
There's no need to use grunt-contrib-copy just for this any more, you can now take advantage of grunt.file.expandMapping which has options to just change the file extension, or to define a function that returns the output filename.
Here's an example of a files object in a jade task for compiling .jade templates into .html files:
files: [{
expand: true,
src: "**/*.jade",
dest: "<%= distDir %>",
cwd: "<%= assetsDir %>/jade",
rename: function(dest, matchedSrcPath, options) {
// return the destination path and filename:
return (dest + matchedSrcPath).replace('.jade', '.html');
}
}]
It would have been easier to use the ext: '.html' option instead of the rename option in this case, but I'm using rename here so you can see how it works.
More info about the ext and rename (and other) options in the grunt.file docs. Some more examples here and here.
You could simply use the options:
expand : true,
flatten: true
No need for custom rename callbacks.
if u want to rename .coffee files to .js or similar then just adapt it ;)
sudo npm install grunt-contrib-copy
copy: {
rename: {
files: [{
expand: true,
dot: true,
cwd: './app/scripts',
dest: './app/scripts/',
src: [
'**/*.coffee'
],
rename: function(dest, src) {
console.log(dest + src);
return dest + src.replace('.coffee','.js');
}
}]
}
},
Not exactly an answer to your question but i made it here looking for relative dest folders with grunt so... Here is how i solved it
...
base: {
files:
[
{
expand: true,
cwd: 'app/design/frontend/',
src: ['**/Magento_Sales/email-src/*.html'],
dest: '../../Magento_Sales/email/',
rename: function(dest, src, expand) {
src = path.parse(src)
return path.join(expand.cwd, src.dir, dest, src.base);
}
},
],
}
...
This little bit path.join(expand.cwd, src.dir, dest, src.base); just creating the path i need.
expand.cwd = app/design/frontend/
src.dir = <DYNAMIC_FOLDERS>/Magento_Sales/email-src/
dest = ../../Magento_Sales/email/
src.base = <FILE>.html
and all together it = app/design/frontend/<COMPANY>/<MAIN-THEME>/Magento_Sales/email/<FILE>.html
and in my situation it will now compile my html emails in relative destination folders
I wanted to save the file in a min folder created at the root where we found the file so it's more easy to moove the plugin folder into another project :
-- Plugins/
-- bootstrap/
-- bootstrap.js
-- bootstrap.css
-- min/
-- bootstrap.min.js
-- bootstrap.min.css
I post the solution I found thanks to this page maybe it can help others :
uglify: {
options: {
mangle: true
},
dynamic_mappings: {
// Grunt will search for "**/*.js" under "Plugins/" when the "uglify" task
// runs and build the appropriate src-dest file mappings then, so you
// don't need to update the Gruntfile when files are added or removed.
files: [
{
expand: true, // Enable dynamic expansion.
cwd: 'Plugins/', // Src matches are relative to this path.
src: ['**/*.js', '!**/*.min.js'], // Pattern to match (ignore .min files)
dest: '/min/', // Destination path prefix.
ext: '.min.js', // Dest filepaths will have this extension.
extDot: 'last', // Extensions in filenames begin after the first dot
rename: function( dest, matchedPath, options ) {
Pathname = matchedPath.substring( 0, matchedPath.indexOf('/') );
Filename = matchedPath.replace( Pathname, '' );
//Return "Plugins / plugin_folder / min / file_name.min.js"
return options.cwd + Pathname + dest + Filename;
}
},
],
},
}

Resources