Related
I'm building a website with Bootstrap using less. I use grunt to automate the tasks.
In my gruntfile.js I have this part:
less: {
compileCore: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
},
src: 'less/bootstrap.less',
dest: 'dist/css/<%= pkg.name %>.css'
},
compileBrandingStyles: {
options: {
strictMath: true,
sourceMap: false,
outputSourceFiles: false
},
src: 'less/branding/**/*.less',
dest: 'dist/css/<%= what do I put here? %>.css'
}
},
In "compileBrandingStyles" I would like to fetch all *.less files in a folder and compile them into seperate css files with their original file names. No concatenation.
In the folder: less/branding I have x number of .less files:
theme-1.less
theme-2.less
theme-3.less
theme-4.less
I would like to output them in the dist/css/ folder like this:
theme-1.css
theme-2.css
theme-3.css
theme-4.css
So how should I write this part to keep their file names?
dest: 'dist/css/<%= what do I put here? %>.css'
Reconfigure your compileBrandingStyles Target to this instead:
// ...
compileBrandingStyles: {
options: {
strictMath: true,
sourceMap: false,
outputSourceFiles: false
},
files: [{
expand: true,
cwd: 'less/branding/',
src: ['**/*.less'],
dest: 'dist/css/',
ext: '.css'
}]
}
See further info on this in the grunt docs.
EDIT
If you don't' want the sub folders included in the destination folder use flatten.
// ...
compileBrandingStyles: {
options: {
strictMath: true,
sourceMap: false,
outputSourceFiles: false
},
files: [{
expand: true,
cwd: 'less/branding/',
src: ['**/*.less'],
dest: 'dist/css/',
ext: '.css',
flatten: true // <-- Remove all path parts from generated dest paths.
}]
}
Grunt doesn't seem to be tracking CSS files.
This is what my Gruntfile.js looks like
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//ec2: 'aws.json',
concat: {
vendorCss: {
src: [
'public/vendor/bootstrap/bootstrap.min.css',
'public/vendor/fontawesome/css/font-awesome.css',
'public/vendor/owlcarousel/owl.carousel.css',
'public/vendor/owlcarousel/owl.theme.css',
'public/vendor/magnific-popup/magnific-popup.css'
],
dest: 'public/stylesheets/build/vendors.css'
},
themeCss: {
src: [
'public/stylesheets/theme.css',
'public/stylesheets/theme-elements.css',
'public/stylesheets/theme-blog.css',
'public/stylesheets/theme-animate.css'
],
dest: 'public/stylesheets/build/theme.css'
},
extensionCss: {
src: [
'public/stylesheets/skins/default.css',
'public/stylesheets/custom.css'
],
dest: 'public/stylesheets/build/theme-extension.css'
},
homeCss: {
src:[
'public/vendor/rs-plugin/css/settings.css',
'public/vendor/circle-flip-slideshow/css/component.css'
],
dest: 'public/stylesheets/build/home-page.css'
},
vendorsJs: {
src: [
'public/vendor/jquery/dist/jquery.js',
'public/vendor/jquery.appear/jquery.appear.js',
'public/vendor/jquery.easing/jquery.easing.js',
'public/vendor/jquery-cookie/jquery-cookie.js',
'public/vendor/bootstrap/bootstrap.min.js',
'public/vendor/common/common.js',
'public/vendor/jquery.validation/jquery.validation.js',
'public/vendor/jquery.stellar/jquery.stellar.js',
'public/vendor/jquery.easy-pie-chart/jquery.easy-pie-chart.js',
'public/vendor/jquery.gmap/jquery.gmap.js',
'public/vendor/twitterjs/twitter.js',
'public/vendor/isotope/jquery.isotope.js',
'public/vendor/owlcarousel/owl.carousel.js',
'public/vendor/jflickrfeed/jflickrfeed.js',
'public/vendor/magnific-popup/jquery.magnific-popup.js',
'public/vendor/vide/jquery.vide.js',
'public/javascripts/app/theme.js'
],
dest: 'public/javascripts/build/vendors.js'
},
homeJs: {
src: [
'public/vendor/rs-plugin/js/jquery.themepunch.tools.min.js',
'public/vendor/rs-plugin/js/jquery.themepunch.revolution.min.js',
'public/vendor/circle-flip-slideshow/js/jquery.flipshow.js',
'public/javascripts/app/views/view.home.js'
],
dest: 'public/javascripts/build/home-page.js'
},
contactJs: {
src: [
'https://www.google.com/recaptcha/api.js',
'public/javascripts/app/views/view.contact.js'
],
dest: 'public/javascripts/build/contact-page.js'
},
themeJs: {
src: [
'public/javascripts/app/custom.js',
'public/javascripts/app/preload.js',
'public/javascripts/app/theme.init.js',
'public/javascripts/app/google-analytics.js',
],
dest: 'public/javascripts/build/theme.js'
},
productionCss: {
src: [
'public/stylesheets/build/vendors.css',
'public/stylesheets/build/theme.css',
'public/stylesheets/build/home-page.css',
'public/stylesheets/build/theme-extension.css'
],
dest: 'public/dist/stylesheets/production.css'
},
productionJs: {
src: [
'public/javascripts/build/vendors.js',
'public/javascripts/build/home-page.js',
'public/javascripts/build/contact-page.js',
'public/javascripts/build/theme.js'
],
dest: 'public/javascripts/build/production.js'
},
productionModernizr: {
src: [
'public/vendor/modernizr/modernizr.js'
],
dest: 'public/javascripts/build/modernizr.js'
}
},
uglify: {
productionJs: {
src: 'public/javascripts/build/production.js',
dest: 'public/dist/javascripts/production.min.js'
},
productionModernizr: {
src: 'public/javascripts/build/modernizr.js',
dest: 'public/dist/javascripts/modernizr.min.js'
}
},
cssmin: {
target: {
files: [{
expand: true,
cwd: 'public/dist/stylesheets',
src: ['*.css', '!*.min.css' ],
dest: 'public/dist/stylesheets',
ext: '.min.css'
}]
}
},
imagemin: {
dynamic: {
files: [{
expand: true,
cwd: 'public/images/',
src: ['**/*.{png,jpg,gif}'],
dest: 'public/dist/images'
}]
}
},
watch: {
scripts: {
files: [
'public/stylesheets/**/*.css',
'public/javascripts/**/*.js'
],
tasks: ['concat', 'uglify'],
options: {
spawn: false
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-css-url-replace');
//grunt.loadNpmTasks('grunt-contrib-ec2');
// Default task(s).
grunt.registerTask('default', ['concat', 'cssmin'
, 'uglify'
//,'imagemin'
]);
};
I've been adding CSS changes to stylesheets/theme.css. Because from what I can tell from the gruntfile, this will convert 'stylesheets/theme.css' into 'public/stylesheets/build/theme.css', which will then be converted into 'public/dist/stylesheets/production.css'.
This was a project I'm taking over from a developer (I'm a designer). Any idea why the CSS changes aren't displaying when I run 'grunt'?
it looks like when you are running grunt all that actually runs is
'concat', 'cssmin', 'uglify'
update the last code block to
grunt.registerTask('default', ['concat', 'cssmin', 'uglify' ,'watch']);
I have set up my grunt to minify my html, but when I run 'grunt htmlmin'
it doesnt send it to my dist folder, here are the settings I used:
htmlmin: {
dist: {
options: {
removeComments: true,
collapseWhitespace: true
},
files: [{
expand: true,
cwd: 'src',
src: '**/*.html',
dest: 'dist/'
}],
},
},
Do I have something set up wrong?
Your configuration seems fine - below is the exact Gruntfile.js that works on my machine. Can you try it on your side?
module.exports = function(grunt) {
grunt.initConfig({
htmlmin: {
dist: {
options: {
removeComments: true,
collapseWhitespace: true
},
files: [{
expand: true,
cwd: 'src',
src: '**/*.html',
dest: 'dist/'
}],
},
},
});
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.registerTask('default', ['htmlmin']);
};
I want to have a gruntfile with 2 tasks: less (compiles all less files) and watch (listens to changes and re-compiles the changed file).
I have the following Gruntfile.js:
module.exports = function(grunt) {
var files = [
{
expand: true,
cwd: 'media/less',
src: ['*.less'],
dest: 'media/css/',
ext: '.css'
},
{
expand: true,
cwd: 'media/less/vendor',
src: ['*.less'],
dest: 'media/css/vendor/',
ext: '.css'
},
{
expand: true,
cwd: 'media/admin/less',
src: ['*.less'],
dest: 'media/admin/css/',
ext: '.css'
}
];
grunt.initConfig({
less: {
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: files
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files
}
},
watch: {
styles: {
files: ['media/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['less:development']);
};
The less task runs correctly without any problems. The watch task however listens to changes, but re-compiles all files when one is changed. I suspect it's something to do with how I set up my less task, because I want my less file list to be dynamic and not to add each file manually.
As per this answer grunt should already support this, but I'm unsure how.
Ended up using the watch event and overriding the files property of the less task. Here's my final code:
module.exports = function(grunt) {
var files = [
{
expand: true,
cwd: 'media/less',
src: ['*.less'],
dest: 'media/css/',
ext: '.css',
extDot: 'last'
},
{
expand: true,
cwd: 'media/less/vendor',
src: ['*.less'],
dest: 'media/css/vendor/',
ext: '.css',
extDot: 'last'
},
{
expand: true,
cwd: 'media/admin/less',
src: ['*.less'],
dest: 'media/admin/css/',
ext: '.css',
extDot: 'last'
}
];
grunt.initConfig({
less: {
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: files
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files
}
},
watch: {
styles: {
files: ['media/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
}
}
});
grunt.event.on('watch', function(action, filepath){
// ignore include files, TODO: have naming convention
// if an include file has been changed, all files will be re-compiled
if(filepath.indexOf('.inc.') > -1)
return true;
// might not be the most efficient way to do this
var srcDir = filepath.split('/');
var filename = srcDir[srcDir.length - 1];
delete srcDir[srcDir.length - 1];
srcDir = srcDir.join('/');
var destDir = srcDir.replace(/less/g, 'css');
grunt.config('less.development.files', [{
src: filename,
dest: destDir,
expand: true,
cwd: srcDir,
ext: '.css',
extDot: 'last'
}]);
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['less:development']);
};
I'm unsure what you mean with the current title of your question Grunt watch less on changed file only. Do you mean that is a problem? That's the expected behavior of a watch task, it will watch the files specified for changes and run the tasks you specified - in this case the LESS compilation.
I did some changes to your file. Some of it was simplified, some was changed keeping flexibility and expandability of the script in mind.
First install underscore as a dependency, by running:
npm install underscore --save-dev
Then do the following changes to your Gruntfile.js:
module.exports = function(grunt) {
var _ = require('underscore');
var files = {
app : {
'<%= path.styles.css %>/styles.css' : '<%= path.styles.less %>/*.less'
},
vendor : {
'<%= path.styles.css %>/styles-vendor.css' : '<%= path.styles.vendor %>/*.less'
},
admin : {
'<%= path.styles.css %>/styles-admin.css' : '<%= path.styles.admin %>/*.less'
}
}
function all() {
'use strict';
var allfiles = {},
i = {};
for (i in files) {
_.extend(allfiles, files[i]);
}
return allfiles;
}
grunt.initConfig({
path : {
media : 'media',
styles : {
css: 'media/css',
less: 'media/less',
admin: 'media/admin/less',
vendor: '<%= path.styles.less %>/vendor'
}
},
less: {
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: (all())
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: (all())
}
},
watch: {
styles: {
files: ['<%= path.media %>/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
// run several tasks as default (handy for complex projects)
grunt.registerTask('dist', [ // run with 'grunt dist'
'less:production'
]);
grunt.registerTask('dev', [ // default, will run with 'grunt' only
'less:development'
]);
grunt.registerTask('default', 'dev');
};
If what you want is to actually compile the sets of files separately (files.app, files.vendor & files.admin), you might need to split the task some more, like so:
less: {
app: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: files.app
},
vendor: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files.vendor
},
admin: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: files.admin
},
development: {
options: {
compress: false,
yuicompress: true,
optimization: 2
},
files: (all())
},
production: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: (all())
}
},
watch: {
all: {
files: ['<%= path.media %>/**/*.less'],
tasks: ['less:development'],
options: {
nospawn: true
}
},
app : {
files: ['<%= path.styles.less %>/*.less'],
tasks: ['less:app'],
options: {
nospawn: true
}
},
vendor : {
files: ['<%= path.styles.vendor %>/*.less'],
tasks: ['less:vendor'],
options: {
nospawn: true
}
},
admin : {
files: ['<%= path.styles.admin %>/*.less'],
tasks: ['less:admin'],
options: {
nospawn: true
}
}
}
Then you could then run either of these:
grunt watch:app
grunt watch:vendor
grunt watch:admin
You can always to choose to run the tasks directly, once:
grunt less:app
grunt less:vendor
grunt less:admin
Hope this helps! Please note that I haven't tested this.
I am using grunt-contrib-uglify plugin in my grunt 0.4.
I have the following task:
uglify: {
dist: {
options: {
sourceMap: 'dist/sm/sm.js'
},
files: grunt.file.expandMapping(['*.js'], 'dist/js', {
cwd: 'dist/js'
})
}
},
As you can see, uglify is configured to compress multiple files, and there is only one source-map specified. (I am not able to figure out a way to specify multiple sourcemap outputs).
Also, uglify is overwriting the soucemap after compressing each js file.
How do I configure this plugin to output the complete source-maps for all my js files?
You can set functtion at sourceMap.
uglify: {
options: {
sourceMap: function(path) { return path.replace(/.js/,".map")}
},
.....
In the V0.4.0 version, sourceMap is Boolean value. Use dynamic build to produce multiple sourceMap with multiple .min.js files.
uglify: {
options: {
sourceMap: true
},
build: {
files: [{
expand: true,
cwd: 'src/',
src: '*.js',
dest: 'build/',
ext: '.min.js',
extDot: 'first'
}]
}
}
options: {
beautify: false,
banner: 'lorem ipsum',
mangle: false,
sourceMap: true,
compress: {
conditionals: true,
booleans: true,
unused: true,
sequences: true,
dead_code: true,
if_return: true,
join_vars: true,
drop_console: true
}
},
min: {
files: [{
expand: true,
cwd: '<%= config.destination.js %>',
src: ['**/*.js', '!**/*.min.js'],
dest: '<%= config.destination.js %>',
ext: '.min.js'
}]
}