I'm setting up a Grunt script that needs to copy and reorganise directories of images from A to B. Simple enough.
Directory structure:
components
componentA
js
img
imgfolderA
imgfolderB
css
componentB
js
img
imgfolderA
Each img directory could contain other directories and directories within those directories to help organise the images.
I want to use Grunt to take all those images and put them under one directory (assets/img):
assets
img
dirA
imgfolderA
imgfolderB
dirB
imgfolderA
Any ideas on how could I do this in grunt without specifying each component directory (it needs to be fully automated)?
know it's a bit late but this should do the job, use 'grunt-contrib-copy' like so
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
copy: {
production: {
files: [{
expand: true,
cwd: "componentA/img/imgfolderA/",
src: ["*.*", "**/*.*"],
dest: "./assets/img/dirA/",
},
{
expand: true,
cwd: "componentB/img/imgfolderB/",
src: ["*.*", "**/*.*"],
dest: "./assets/img/dirB/",
},
]
}
}
});
// Production Build Tools
grunt.loadNpmTasks('grunt-contrib-copy');
// Default Production Build task(s).
grunt.registerTask('default', ['copy']);
};
ps magic is in the files objects, there not very well documented, but the documentation is here, after one or two reads it makes sense honest!
grunt-contrib-copy setup: https://github.com/gruntjs/grunt-contrib-copy (the readme at the bottom)
files object setup: http://gruntjs.com/configuring-tasks#globbing-patterns
task setup: http://gruntjs.com/configuring-tasks
This is rather simple using grunt.file.expand.
Just pass matching glob patterns (e.g. **/img/**), and then recurse on the returned matching file values to copy.
Related
The Context
I'm new to Grunt and am trying to learn a bit by modifying the meanjs boilerplate to support stylus, I would like to keep my precompiled css assets organized in modular buckets, as recommended by the current meanjs defaults.
The Question
I have the following file structure:
- app
- config
- public
- modules
- foo
- assets
- stylesheets
...
- css
...
...
How can I use Grunt to take Stylus .styl files in the public/modules/*/assets/stylesheets directory, and have them compile to the public/modules/*/css directory?
Naive Attempt:
Below is an example attempt, which didn't get very far.
stylus: {
compile: {
files: [{
dest: '../../css',
src: 'public/modules/*/assets/stylesheets/*.styl',
ext: '.css',
expand: true
}]
}
}
This results in: File ../../css/public/modules/foo/assets/stylesheets/baz.css created.
If I leave "dest" empty, it does properly compile but the output is in the assets/stylesheets folder (as expected). I'm sure there is a clean way to do this, but I don't know yet.
setting the src, dest, cwd, as well as using the hidden rename options of grunt should get stylus files in your desired format.
example:
stylus: {
compile: {
options: {
compress: true
},
files: [{
cwd: 'public/modules',
dest: 'public/modules',
src: ['*/assets/stylesheets/*.styl'],
expand: true,
rename: function(dest, src) {
var path = require('path');
var module = src.split(path.sep).slice(0,1)[0];
return path.join(dest, module + '/css/' + module + '.css');
}
}]
}
},
grunt tricks - customize file output rename
I am using grunt-contrib-less for compiling less files.
I have grunt locally installed at root of project folder.
The css files are located at qa1/avinash/html5/phase1/css/ path from root of project folder.
So this is the path i am specifying for cwd (current working directory), src and dest parameters of the grunt-less task. there are no issues in compilation of css and source map.
The only issue i face is that the source map is generated in the same folder of gruntfile. but my generated css is at the dest path i specified. since the css and source map are at different locations i have to manually edit the less path references in source map and bring it to the generated css directory. or use sourceMapURL to specify the source map location ../../../../../style.css.map(backwards). Both ways are not convenient.
So can anyone help me how to specify the source map output destination path like we specify for destination path for generated css something like
sourceMapDest: 'qa1/avinash/html5/phase1/css/'
--
currently used Gruntfile.js:
module.exports = function(grunt) {
grunt.initConfig({
less: {
options: {
sourceMap:true,
sourceMapFilename: "style.css.map",
sourceMapURL: '../../../../../style.css.map'
},
src: {
// no need for files, the config below should work
expand: true,
cwd: "qa1/avinash/html5/phase1/css/",
src: "style.less",
dest: "qa1/avinash/html5/phase1/css/",
ext: ".css"
}
},
watch: {
js: {
files: ['qa1/avinash/html5/phase1/css/'],
tasks: ['default'],
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['less']);
};
The sourceMapFilename option may include a path part as well.
I.e. just change it to:
sourceMapFilename: "qa1/avinash/html5/phase1/css/style.css.map"
I'm new with Grunt and I wasn't able to find what I'm looking for.
I have this folder's structure configuration :
app/
public/
assets/
... some javascript/css libs like jQuery, Bootstrap, etc
css/
js/
img/
What I'd like to do is compress all the js files in public/assets/ into one assets.js file that would be in js/assets.js, and do the same for all the css files into assets.css in css/assets.css.
Moreover, I'd like those two assets.js/css file to be compressed.
A link to a solution or some start of a solution is all I need.
Thank you!
Firstly you need to concatenate your files and then run them through a minifier. Grunt has plenty of plugins that will do these things but some of the more popular ones are grunt-contrib-concat, grunt-contrib-uglify and grunt-contrib-cssmin.
These tasks have plenty of options available to taylor them to your needs but this should help you get started.
As sample configuration for the concat task would be something like:
grunt.initConfig({
concat: {
options: {
separator: ';',
},
js: {
src: ['public/assets/a.js', 'public/assets/b.js', 'public/assets/c.js'],
dest: 'public/js/assets.js',
},
js: {
src: ['public/assets/a.css', 'public/assets/b.css', 'public/assets/c.css'],
dest: 'public/css/assets.css',
},
},
});
Then for your minify js task:
uglify: {
js: {
files: {
'public/assets/js/assets.min.js': 'public/assets/js/assets.js'
}
}
}
And finally, css minify task:
cssmin: {
files: {
'public/assets/css/assets.min.css' : 'public/assets/css/assets.css'
}
}
I keep seeing the recommendation for making JS files ready for production to be concat then uglify.
For example here, in on of Yeoman's grunt tasks.
By default the flow is: concat -> uglifyjs.
Considering UglifyJS can do both concatenation and minification, why would you ever need both at the same time?
Thanks.
Running a basic test to see if there is a performance difference between executing concat and then uglify vs. just uglify.
package.json
{
"name": "grunt-concat-vs-uglify",
"version": "0.0.1",
"description": "A basic test to see if we can ditch concat and use only uglify for JS files.",
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-uglify": "^0.6.0",
"load-grunt-tasks": "^1.0.0",
"time-grunt": "^1.0.0"
}
}
Gruntfile.js
module.exports = function (grunt) {
// Display the elapsed execution time of grunt tasks
require('time-grunt')(grunt);
// Load all grunt-* packages from package.json
require('load-grunt-tasks')(grunt);
grunt.initConfig({
paths: {
src: {
js: 'src/**/*.js'
},
dest: {
js: 'dist/main.js',
jsMin: 'dist/main.min.js'
}
},
concat: {
js: {
options: {
separator: ';'
},
src: '<%= paths.src.js %>',
dest: '<%= paths.dest.js %>'
}
},
uglify: {
options: {
compress: true,
mangle: true,
sourceMap: true
},
target: {
src: '<%= paths.src.js %>',
dest: '<%= paths.dest.jsMin %>'
}
}
});
grunt.registerTask('default', 'concat vs. uglify', function (concat) {
// grunt default:true
if (concat) {
// Update the uglify dest to be the result of concat
var dest = grunt.config('concat.js.dest');
grunt.config('uglify.target.src', dest);
grunt.task.run('concat');
}
// grunt default
grunt.task.run('uglify');
});
};
In src, I've put a bunch of JS files, including the uncompressed source of jQuery, copied several times, spread around into subfolders. Much more than what a normal site/app usually has.
Turns out the time it takes to concat and compress all of these files is essentially the same in both scenarios.
Except when using the sourceMap: true option on concat as well (see below).
On my computer:
grunt default : 6.2s (just uglify)
grunt default:true : 6s (concat and uglify)
It's worth noting that the resulting main.min.js is the same in both cases.
Also, uglify automatically takes care of using the proper separator when combining the files.
The only case where it does matter is when adding sourceMap: true to the concat options.
This creates a main.js.map file next to main.js, and results in:
grunt default : 6.2s (just uglify)
grunt default:true : 13s (concat and uglify)
But if the production site loads only the min version, this option is useless.
I did found a major disadvantage with using concat before uglify.
When an error occurs in one of the JS files, the sourcemap will link to the concatenated main.js file and not the original file. Whereas when uglify does the whole work, it will link to the original file.
Update:
We can add 2 more options to uglify that will link the uglify sourcemap to concat sourcemap, thus handling the "disadvantage" I mentioned above.
uglify: {
options: {
compress: true,
mangle: true,
sourceMap: true,
sourceMapIncludeSources: true,
sourceMapIn: '<%= paths.dest.js %>.map',
},
target: {
src: '<%= paths.src.js %>',
dest: '<%= paths.dest.jsMin %>'
}
}
But it seems highly unnecessary.
Conclusion
I think it's safe to conclude that we can ditch concat for JS files if we're using uglify, and use it for other purposes, when needed.
In the example you mention, which I'm quoting below, the files are first concatenated with concat and then uglified/minified by uglify:
{
concat: {
'.tmp/concat/js/app.js': [
'app/js/app.js',
'app/js/controllers/thing-controller.js',
'app/js/models/thing-model.js',
'app/js/views/thing-view.js'
]
},
uglifyjs: {
'dist/js/app.js': ['.tmp/concat/js/app.js']
}
}
The same could be achieved with:
{
uglifyjs: {
'dist/js/app.js': [
'app/js/app.js',
'app/js/controllers/thing-controller.js',
'app/js/models/thing-model.js',
'app/js/views/thing-view.js'
]
}
}
Typically, the task clean would then run after tasks that write to a temporary folder (in this example concat) and delete whatever content is in that folder. Some people also like to run clean before tasks like compass, to delete things like randomly named image sprites (which are newly generated every time the task runs). This would keep wheels turning even for the most paranoid.
This is all a matter of preference and workflow, as is with when to run jshint. Some people like to run it before the compilation, others prefer to run it on compiled files.
Complex projects with an incredible amount of JavaScript files - or with a increasingly broad number of peers & contributors, might choose to concatenate files outside uglify just to keep things more readable and maintainable. I think this was the reasoning behind Yeoman's choice of transformation flow.
uglify can be notoriously slow depending of the project's configuration, so there might be some small gain in concatenating it with concat first - but that would have to be confirmed.
concat also supports separators, which uglify doesn't as far as README.md files are concerned.
concat: {
options: {
separator: ';',
}
}
I'm using Grunt 0.4.1 and Grunt-contrib-copy version 0.4.1. I'm trying to copy images from a src directory to a distribution directory, but it's copying the full src directory instead of only the contents of the images directory. In other words, this is creating dist/images/src/images/..., whereas I want the result to just be dist/images/...
Here's my copy task:
copy: {
images: {
files: {
'dist/images/': 'src/images/**/*'
}
}
}
And it's being run as follows:
// Default task.
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('default', ['less', 'concat', 'uglify', 'cssmin', 'copy']);
How do I get it so that only the subdirectories of src/images get copied into dist/images?
These files are already compressed and only need to be copied to the destination during build.
What actually fixed my problem was designating the root directory structure from which I was copying with the "cwd" directive and then the src exactly like above. Just came back to post this and saw the same solution basically that I had come up with. Here is what worked for me:
copy: {
images: {
files: [
{
expand: true,
cwd: 'src/images/',
src: ['**/*.{png,jpg,svg}'],
dest:'dist/images/'
}
]
}
}
The "cwd" property allows you to define the root of the source from which you are copying. This will not be included in the path of the copied files. So in my case, by defined "cwd" as "src/images", I'm then able to define the source as "**/*.{png,jpg,svg}" This finds all the subdirectories in the source images directory and only copies the files with the extensions I've provided. This gives me the result I want: dist/images/... with all the subdirectories as expected.
Without using "cwd", the grunt-contrib-copy literally copies the entire source tree structure into the destination. The "cwd" property allows you to cherry pick subdirectories and files from one file structure and copy them into another.
You'll want to specify the flatten option, which removes the directory tree.
copy: {
images: {
expand: true,
cwd: 'src/images/',
src: ['**/*'],
dest: 'dist/images/',
filter: 'isFile',
flatten: true
}
}