How to reduce repetition in gruntfile - gruntjs

My grunt file looks like this:
grunt.initConfig({
compress: {
foo: {
options: { archive: 'deploy/foo.zip', mode: 'zip' },
files: [{ expand: true, cwd: 'release/foo/', src: ['**'], dot: true }]
},
bar: {
options: { archive: 'deploy/bar.zip', mode: 'zip' },
files: [{ expand: true, cwd: 'release/bar/', src: ['**'], dot: true }]
},
baz: {
options: { archive: 'deploy/baz.zip', mode: 'zip' },
files: [{ expand: true, cwd: 'release/baz/', src: ['**'], dot: true }]
},...
How do i parameterise this, so i don't need N definitions and I can call (pseudocode)
> grunt compress "foo"

If I understand your problem correctly:
var initData = {
compress: { }
};
var compress = function(n){
initData.compress[n] = {
options: {archive: 'deploy/' + n + '.zip', mode: 'zip'},
files: [{expand: true, cwd: 'release/' + n + '/', src: ['**'], dot: true}]
};
}
compress('foo');
compress('bar');
compress('baz');
grunt.initConfig(initData);

Related

Grunt - Jade files with same start compile into single file

I am using grunt-contrib-jade to compile my jade files. The issue I'm having is that say for example I have the following files:
/views/user.html
/views/user.index.hmtl
/views/user.show.html
These will all be compiled and merged into:
/views/user.html
Why is this happening? I want them to be in separate files like:
/views/user.html
/views/user.index.html
/views/user.show.html
Is there a way to accomplish this?
This is my config:
jade: {
compile: {
options: {
pretty: true,
data: {
debug: false
}
},
files: [{
expand: true,
cwd: '<%= yeoman.client %>',
src: [
'{app,components}/**/*.jade'
],
dest: '.tmp',
ext: '.html'
}]
}
}
You have to use rename instead of ext.
This should works:
jade: {
compile: {
options: {
pretty: true,
data: {
debug: false
}
},
files: [{
expand: true,
cwd: '<%= yeoman.client %>',
src: [
'{app,components}/**/*.jade'
],
dest: '.tmp',
rename : function (dest, src) {
var folder = src.substring(0, src.lastIndexOf('/'));
var filename = src.substring(src.lastIndexOf('/'), src.length);
filename = filename.substring(0, filename.lastIndexOf('.'));
return dest + folder + filename + '.min.js';
}
}]
}
}

grunt htmlmin not sending to dist folder

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']);
};

grunt will not run on my project, which default Task?

I am trying to run grunt in order to get SASS and a few other things to compile my project (that someone else created), however I keep getting the following error:
Warning: Task "default" not found. Use --force to continue.
Aborted due to warnings.
I do not understand which "default" task it is is referring to. The grunt file I have inherited is as follows:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dev: {
src: ['www/_assets/js/vendor-concat/*.js', 'www/_assets/js/*.js', '!www/_assets/js/prototype.js', '!www/_assets/js/scripts.js', '!www/_assets/js/scripts.min.js'],
dest: 'www/_assets/js/scripts.js'
}
},
uglify: {
dist: {
files: {
'www/_assets/js/scripts.min.js': ['www/_assets/js/scripts.js']
}
}
},
jshint: {
files: ['Gruntfile.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
modernizr: {
dist: {
// [REQUIRED] Path to the build you're using for development.
"devFile" : "www/_assets/js/vendor/modernizr-2.7.1.js",
// [REQUIRED] Path to save out the built file.
"outputFile" : "www/_assets/js/vendor/modernizr-custom.js"
}
},
imageoptim: {
dev: {
src: ['www/_assets/img']
}
},
sass: {
dev: {
options: {
includePaths: ['www/_assets/scss'],
outputStyle: 'compressed',
sourceMap: true
},
files: {
'www/_assets/css/main.css' : 'www/_assets/scss/main.scss',
'www/_assets/css/main-ie8.css' : 'www/_assets/scss/main-ie8.scss'
}
}
},
assemble: {
options: {
flatten: false,
expand: true,
assets: 'www/_assets',
layout: 'default.hbs',
layoutdir: 'www/_templates/layouts',
partials: ['www/_templates/partials/*.hbs'],
data: ['www/_templates/data/*.json']
},
dev: {
files: [
{expand: true, cwd: 'www/_templates/pages/', src: '**/*.hbs', dest: 'www/', ext: '.html'}
]
}
},
watch: {
options: {
livereload: true,
spawn: false
},
css: {
files: ['www/_assets/scss/**/*.scss'],
tasks: ['sass']
},
js: {
files: ['www/_assets/js/**/*.js', '!www/_assets/js/scripts.min.js', '!www/_assets/js/scripts.js'],
tasks: ['jshint', 'concat:dev']
},
hbs: {
files: ['www/_templates/{,*/}*.hbs', 'www/_templates/{,*/*/}*.hbs', 'www/_templates/{,*/*/*/}*.hbs'],
tasks: ['assemble']
},
html: {
files: ['www/email.html']
}
},
copyto: {
prototype: {
files: [
{cwd: 'www/', src: ['**/*'], dest: 'prototype/'}
],
options: {
ignore: [
'www/_assets/scss{,/**/*}',
'www/_assets/js{,/**/*}',
'!www/_assets/js/vendor{,/**/*}',
'!www/_assets/js/scripts.min.js',
'!www/_assets/js/prototype.js',
'www/_templates{,/**/*}',
'www/z-backups{,/**/*}',
'www/_assets/css/*.map'
]
}
},
beta: {
files: [
{cwd: 'prototype/_assets/', src: ['**/*'], dest: '_beta-sln/Beta/src/SFA.as.Web.Candidate/Content/_assets/'}
],
options: {
ignore: [
'prototype/_assets/video{,/**/*}',
'prototype/_assets/js/prototype.js'
]
}
},
sprint: {
files: [
{cwd: 'prototype/', src: ['**/*'], dest: 'sprint/'}
]
},
dist: {
files: [
{cwd: 'www/', src: ['**/*'], dest: 'dist/'}
],
options: {
ignore: [
'www/_assets/scss{,/**/*}',
'www/_assets/css/*.map',
'www/_assets/video{,/**/*}',
'www/_assets/js/plugins{,/**/*}',
'www/_assets/js/interactions.js',
'www/_assets/js/scripts.js',
'www/_templates{,/**/*}',
'www/*.html',
'!www/index.html',
'www/z-backups{,/**/*}'
]
}
}
},
clean: {
prototype: {
src: [ 'prototype/' ]
},
sprint: {
src: [ 'sprint/' ]
},
dist: {
src: [ 'dist/' ]
}
},
replace: {
map: {
src: ['www/_assets/css/*.css'],
overwrite: true,
replacements: [{
from: 'sourceMappingURL=main.css.map',
to: 'Map removed'
},{
from: 'sourceMappingURL=main-ie8.css.map',
to: 'Map removed'
}]
},
scripts: {
src: ['www/apprentice/*.html', 'www/test/apprentice/*.html', 'www/trainee/*.html', 'www/employer/*.html', 'www/*.html'],
overwrite: true,
replacements: [{
from: 'scripts.js',
to: 'scripts.min.js'
}]
}
},
prettify: {
options: {
indent: 2,
wrap_line_length: 78,
brace_style: 'expand',
},
// Specify a number to padcomments
dist: {
files: [
{expand: true, cwd: 'dist/', src: ['apprentice/*.html', 'trainee/*.html', 'employer/*.html', '*.html'], dest: 'dist/', ext: '.html'}
]
},
prototype: {
files: [
{expand: true, cwd: 'prototype/', src: ['apprentice/*.html', 'trainee/*.html', 'employer/*.html', '*.html', '!pattern-library.html'], dest: 'prototype/', ext: '.html'}
]
}
},
devUpdate: {
main: {
options: {
updateType: 'report', //just report outdated packages
reportUpdated: false, //don't report already updated packages
semver: true, //use package.json semver rules when updating
packages: { //what packages to check
devDependencies: true, //only devDependencies
dependencies: false
},
packageJson: null //find package.json automatically
}
}
},
browserSync: {
dev: {
bsFiles: {
src : [
'www/_assets/css/*.css',
'www/_assets/js/scripts.js',
'www/**/*.html'
]
},
options: {
ghostMode: {
clicks: true,
scroll: true,
links: true,
forms: true
},
watchTask: true,
server: {
baseDir: "www"
}
}
}
},
connect: {
server: {
options: {
port: 7000,
base: 'www',
livereload: true,
open: true
}
}
},
uncss: {
offline: {
options: {
// csspath : 'www/_assets/css/',
// urls : ['http://localhost:/mypage', '...'], // Deprecated
timeout : 1000,
},
files: {
'www/offline/offline.css': ['www/offline/maintenance.html']
}
}
}
});
[
'assemble',
'grunt-modernizr',
'grunt-contrib-imagemin',
'grunt-imageoptim',
'grunt-contrib-uglify',
'grunt-contrib-jshint',
'grunt-sass',
'grunt-uncss',
'grunt-criticalcss',
'grunt-contrib-concat',
'grunt-text-replace',
'grunt-contrib-watch',
'grunt-copy-to',
'grunt-contrib-clean',
'grunt-contrib-compress',
'grunt-pngmin',
'grunt-browser-sync',
'grunt-dev-update',
'grunt-contrib-connect',
'grunt-prettify'
].forEach(function (task) {
grunt.loadNpmTasks(task);
});
grunt.registerTask('images', ['imageoptim']);
grunt.registerTask('modern', ['modernizr']);
grunt.registerTask('offline', ['uncss:offline']);
grunt.registerTask('dev', ['jshint', 'concat:dev', 'sass', 'assemble', 'connect', 'watch']);
grunt.registerTask('sync', ['jshint', 'concat:dev', 'sass', 'assemble', 'browserSync', 'watch']);
grunt.registerTask('proto', ['uglify:dist', 'replace:map', 'clean:prototype', 'replace:scripts', 'copyto:prototype', 'prettify:prototype']);
grunt.registerTask('sprint', ['clean:sprint', 'copyto:sprint']);
grunt.registerTask('dist', ['uglify:dist', 'replace:map', 'clean:dist', 'replace:scripts', 'copyto:dist', 'prettify:dist']);
grunt.registerTask('beta', ['copyto:beta']);
};
As I have not created this myself, I am reluctant to go ahead and break it but is there anything there which stands out? I have seen similar questions but as the grunt files need to vary it is difficult to find the correct answer for this.
Many thanks!
Since file doesn't have default task you have to either create it or run grunt with one of defined tasks:
> grunt dev
or add default task in your Gruntfile.js
grunt.registerTask('default',['dev']);
please refer to http://gruntjs.com/creating-tasks for more information

Grunt watch less on changed file only

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.

How to specify multiple source-maps in uglify grunt task?

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'
}]
}

Resources