How to stop Jekyll from overriding Grunt's minified HTML files? - gruntjs

I'm using Jekyll to build a simple static website and Grunt to manage assets.
I'm trying to get grunt-contrib-htmlmin to minify my Jekyll templates. When I run jekyll build it takes my template files and places them in the _site directory. htmlmin then takes these html files and minifies them.
But once I run jekyll serve, the html files are overwritten with the unminified ones.
Is there a solution, or a better way of going about minifying them? I saw that Yeoman manages to minify html without any issues on Jekyll, but I can't figure out exactly how they do it.
My htmlmin settings in Gulpfile:
htmlmin: {
dist: {
options: {
removeComments: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
minifyJS: true,
minifyCSS: true
},
files: {
'_site/index.html': '_site/index.html',
'_site/it/index.html': '_site/it/index.html',
'_site/fr/index.html': '_site/fr/index.html'
}
}
},

Related

imagemin not minifying SVGs

I'm using imagemin with Grunt and it works as expected with PNGs/JPGs, but not with SVGs.
When I add an SVG into my project, Grunt says:
>> File "../public/microsite/src/img/Sketch.svg" added.
Running "imagemin:dynamic" (imagemin) task
Minified 0 images (saved 0 B)
Is this simply because it can't optimise them any further, or because it's not trying to at all? Even if an SVG can't be optimised, which I doubt is the case, I'd still like it imagemin to place it in the dist folder.
This is my config:
imagemin: {
dynamic: {
options: {
optimizationLevel: 3,
svgoPlugins: [{ removeViewBox: false }]
},
files: [{
expand: true,
cwd: '../public/microsite/src/img/',
src: ['*.{png, jpg, svg}'],
dest: '../public/microsite/build/img/'
}]
}
}
I figured out the spaces in the src object were invalid. I removed them, which allowed all image files to be minified. src: ['*.*'] also works.

Keeping LESS sourceMaps for minified css with cssmin

My LESS files are compiled with grunt-contrib-less and corresponding grunt task with the following config:
module.exports = {
options: {
sourceMap: true,
sourceMapFilename: 'Content/styles/e-life.css.map'
},
compile: {
files: {
'Content/styles/e-life.css' : 'Content/styles/common.less'
}
}
}
Then I procced with cssmin for output css file. I get it minified, but I want to bind source maps from the previous step for the minified css.
module.exports = {
options: {
sourceMap: 'Content/styles/e-life.css.map'
},
all: {
files: {
'Content/styles/e-life.css': ['Content/styles/e-life.css']
}
}
}
The task fails if I mention source map path in options.sourceMap. I see the following in css-clean docs:
sourceMap - exposes source map under sourceMap property, e.g. new CleanCSS().minify(source).sourceMap (default is false) If input styles are a product of CSS preprocessor (Less, Sass) an input source map can be passed as a string.
But i can not understand how to pass this string to the task. Is it even possible? How can I do this?
grunt-contrib-cssmin does NOT let you chain sourcemaps.
Its sourceMap option is true/false only, and will generate a map from the minified css to the original css, not to the original Less, sorry.
Considering that source mapping is useful mainly for debugging, I would suggest:
do not use cssmin in your development environment, that way you get mapping from css to your Less files if needed.
use cssmin without mapping for production.
You could also avoid the Grunt cssmin task and use the Less compression with compress option.
module.exports = {
options: {
compress: true,
sourceMap: true,
sourceMapFilename: 'Content/styles/e-life.css.map'
},
compile: {
files: {
'Content/styles/e-life.css' : 'Content/styles/common.less'
}
}
}
https://github.com/gruntjs/grunt-contrib-less#compress

Why is it recommended to use concat then uglify when the latter can do both?

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: ';',
}
}

Expand not working in grunt-contrib-less task for dynamic selection

I've been playing with Grunt and one of the things I wanted to use it for was to compile my LESS files, but for some reason the expand: true (I commented everything out from the bottom up and it stopped throwing the err after commenting out expand) in the less:mini task is causing this error: Warning: Object true has no method 'indexOf' Use --force to continue. Does anyone know why this is happening? I can dynamically build the file object in grunt-contrib-copy no problem, and expand is required for the other options to work.
less: { // Set up to detect files dynamically versus statically
mini: {
options: {
cleancss: true, // minify
report: 'min' // minification results
},
files: {
expand: true, // set to true to enable options following options:
cwd: "dev/less/", // all sources relative to this path
src: "*.less", // source folder patterns to match, relative to cwd
dest: "dev/css/", // destination folder path prefix
ext: ".css", // replace any existing extension with this value in dest folder
flatten: true // flatten folder structure to single level
}
}
}
Thanks
files is intended to be used as an array or src-dest mappings. Grunt is interpreting the above properties within less.mini.files as src-dest mappings. See: http://gruntjs.com/configuring-tasks#files-object-format
You don't need to nest properties within files if you're not using that format. Modify your config to:
less: { // Set up to detect files dynamically versus statically
mini: {
options: {
cleancss: true, // minify
report: 'min' // minification results
},
expand: true, // set to true to enable options following options:
cwd: "dev/less/", // all sources relative to this path
src: "*.less", // source folder patterns to match, relative to cwd
dest: "dev/css/", // destination folder path prefix
ext: ".css", // replace any existing extension with this value in dest folder
flatten: true // flatten folder structure to single level
}
}

Grunt recursive copy

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.

Resources