Gulp Task: CleanCSS and Paths - css

I've created a task with Gulp that is supposed to:
Join multiple CSS files;
Minify + remove unnecessary CSS;
Fix paths for url() directives and others;
Generate source maps;
My current code for this is:
var gulp = require("gulp"),
concat = require("gulp-concat"),
cleanCSS = require("gulp-clean-css"),
sourcemaps = require("gulp-sourcemaps");
var styleList = [
"Resources/Include/ionicons/css/ionicons.css",
"Resources/base.css",
"Resources/extra.css",
];
gulp.task("deploy-css", function() {
gulp.src(styleList)
.pipe(sourcemaps.init())
.pipe(concat("style.min.css"))
.pipe(cleanCSS({
debug: true,
compatibility: "ie8",
keepSpecialComments : 0,
target: "Resources/",
relativeTo: "Resources/"
})
)
.pipe(sourcemaps.write())
.pipe(gulp.dest("Resources/"))
});
url() path example, taken from file Resources/Include/ionicons/css/ionicons.css:
#font-face { font-family: "Ionicons"; src: url("../fonts/ionicons.eot?v=2.0.0");
This is my current file structure:
./Resources/style.min.css // -> Final processed file
./Resources/base.css
./Resources/extras.css
./Resources/Include/ // -> Original CSS files with URL (installed via Bower)
Test folder: https://dl.dropboxusercontent.com/u/2333896/gulp-path-test.zip (install and then run with gulp deploy-css).
Almost everything works as expected, except for when CSS files include references to images or fonts using the url() option. After running the task (and style.min.css created) those references are broken - no change was made to the paths found on the original CSS files.
Isn't cleanCSS supposed to check where the referenced files are and fix the paths automatically? Aren't the options target and relativeTo used to control that?
How can I fix this? Thank you.

I managed to fix the issue, my main problems were a misplaced concat operation breaking gulp-clean-css rebase feature and wrong target and relativeTo options. Apparently I didn't think much about the previous workflow.
var gulp = require("gulp"),
concat = require("gulp-concat"),
cleanCSS = require("gulp-clean-css"),
sourcemaps = require("gulp-sourcemaps");
var styleList = [
"Resources/Include/ionicons/css/ionicons.css",
"Resources/base.css",
"Resources/extra.css",
"Resources/Include/teste/base.css"
];
gulp.task("deploy-css", function() {
gulp.src(styleList)
.pipe(sourcemaps.init())
.pipe(cleanCSS({
compatibility: "ie8",
keepSpecialComments : 0,
target: "Resources",
relativeTo: ""
})
)
.pipe(concat("style.min.css", {newLine: ""}))
.pipe(sourcemaps.write())
.pipe(gulp.dest("Resources"))
});
This new workflow works as:
Optimize all individual CSS files - including rebasing urls;
Contact individual optimized files into the final file - (note newLine: "" avoids line breaks in the file);
Write the file.

Related

Grunt-complexity on all the files in a directory

I'd like to run Grunt-Complexity on all the files in a directory?
I'd like to get this kind of output.
Is there a way?
My js files are all under a subdirectory called "js".
Here's my gruntfile:
/*global module:false*/
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Task configuration.
complexity: {
generic: {
src: ['grunt.js', 'js/*'],
//exclude: ['doNotTest.js'],
options: {
breakOnErrors: false,
jsLintXML: 'report.xml', // create XML JSLint-like report
checkstyleXML: 'checkstyle.xml', // create checkstyle report
pmdXML: 'pmd.xml', // create pmd report
errorsOnly: false, // show only maintainability errors
cyclomatic: [3, 7, 12], // or optionally a single value, like 3
halstead: [8, 13, 20], // or optionally a single value, like 8
maintainability: 100,
hideComplexFunctions: false, // only display maintainability
broadcast: false // broadcast data over event-bus
}
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-complexity');
// Default task.
grunt.registerTask('default', 'complexity');
};
I'm simply calling this by typing
grunt
from the command line.
then if I type this
grunt complexity js/*
I get
Warning: Task "js/AgencyMediaController.js" not found. Use --force to continue.
Aborted due to warnings.
And AgencyMediaController.js is the first file in my js directory. So it's having a look and listing the files, but then it crashes.
Thanx!
example:
for all js file in JS folder:
src: ['js/**/*.js']
for ass .scss files in scss folder:
src: ['scss/**/*.scss']
I suggest for you create a config for your src folder can be easy in future folder changes in future projects:
sample:
var src;
config.src = src = {
sassMain : 'scss/main.scss',
distFolder : 'public/stylesheets/lovelycss.dist.css',
devFolder : 'public/stylesheets/lovelycss.dev.css',
libFolder : 'lib/**/*.js',
sassFolder : 'scss/**/*.scss',
spriteCssFolder : 'scss/helpers/_sprite.scss',
spriteDestImg : 'public/images/sprite/spritesheet.png',
spriteSrc : 'public/images/min/*.{png,jpg,gif}',
imageminCwd : 'public/images/',
imageminDest : 'public/images/min'
};
//grunt Watch ===============================
config.watch = {
scripts: {
files: ["<%= src.libFolder %>", "<%= src.sassFolder %>"]
,tasks: ["dev", "sass:dist"]
//,tasks: ["dev",'sass:dist']
}
}
I hope that helped you.
It's been quite a long while since I asked this question. I just ran into the same issue again and found the answer so here it is:
In the end it turned out to be that one of the files I was trying to analyse was causing the crash. This particular Javascript environment allows for C-like preprocessor directives and the Javascript file had something like this:
var mySettings = {
//#ifdef FOO_CONSTANT
setting : constants.FOO_SETTING
//#endif
//#ifdef BAR_CONSTANT
setting : constants.BAR_SETTING
//#endif
};
I guess the problem is that if this is read as strictly Javascript, the preprocessor directives are just plane comments, and there's a comma missing between the two properties, so Grunt complexity is unable to read this because of a syntax error. Using --force makes no difference BTW.
The annoying part is that this is all the error shows:
$ grunt --force
Running "complexity:generic" (complexity) task
Warning: undefined: Unexpected token, expected , (17570:1) Used --force, continuing.
Done, but with warnings.
So while it does say expected , (175:1) it doesn't say in which of the several Javascript files in this project the problem was found!
Just adding exclude: ['path/to/MyFileWithPreprocessorDirectives.js'] to Gruntfile.js in order to exclude this file from the analysis gets me around the problem.

Grunt cssmin / CleanCSS source map rebasing

I'm using cssmin with the following "Content" folder structure:
src
|--dir1
| |--style1.css
| |--images
| |--image1.png
|--dir2
|--style2.css
|--images
|--image2.png
dist
|--styles.min.css
|--styles.min.css.map
Where styles.min.css and styles.min.css.map are the result of concatenating/minifying all stylesheets in the"src" folder.
I first had issues where styles.min.css contained URLs for images in the wrong places (i.e. "images/image1.png" instead of "../src/dir1/images/image1.png") but thankfully this grunt configuration fixed that:
cssmin: {
options: {
rebase: true,
sourceMap: true
},
all: {
options: {
keepSpecialComments: 0
},
files: {
'content/dist/styles.min.css': ["content/src/dir1/style1.css", "content/src/dir2/style2.css"]
}
}
}
The new problem: The generated sourcemap ("styles.min.css.map") contains sources like this: ["content/src/dir1/style1.css", "content/src/dir2/style2.css"] instead of ["../src/dir1/style1.css", "../src/dir2/style2.css"]. This means the map is pointing to the incorrect locations, such as:
"content/dist/content/src/dir1/style1.css" and "content/dist/content/src/dir2/style2.css"
What can I do to resolve this?
For reference, I have also tried the csswring, however despite sourcemaps working fine, I found general image/import url rebasing wasn't working properly, so went back to cssmin.
Thanks very much!
Came up with my own solution. I wrote a task which reads the source map JSON, gets the array of sources, rebases them, then writes the file again. This solution seems to work well for me, hopefully this can help someone else too if they're in a similar situation. Just run your cssmin task and then this one:
grunt.registerTask("rebase-css-sourcemap-sources", "Rebases the CSS source map urls", function() {
var filePath = "./content/dist/styles.min.css.map";
if (grunt.file.exists(filePath)) {
var sourceMap = grunt.file.readJSON(filePath);
var sources = sourceMap.sources;
if (sources) {
for (var i = 0; i < sources.length; i++) {
sources[i] = sources[i].replace("content/src", "../src");
}
grunt.file.write(filePath, JSON.stringify(sourceMap));
grunt.log.ok("Rebased CSS source map source urls.");
}
} else {
grunt.log.error("Source map file does not exist: " + filePath);
}
});
While this solution works for me, if anyone knows of an alternative method of solving this problem which ideally just uses cssmin, that would be better.

Ignore empty css files

I'm using gulp to generate css and min.css files from less files. However some less files has no css output (like variables.less that just define variables for import).
Is there a way to skip the empty css stream output for this files?
That's the current code that compile all less files:
var config = {
lessSrc: './Content/**/*.less'
};
gulp.task('css:lessmin', function () {
gulp.src(config.lessSrc, { base: '.' })
.pipe(less())
.pipe(gulp.dest('.'))
.pipe(minifyCss())
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest('.'));
});
I could change the lessSrc to specific only the files I want to compile or to ignore the files I do not want to generate a css and min.css files but I have lots of situations like that, not just in the less files.
A good choice would be to use a filter, filtering the empty results of your globbing set:
var gulp = require('gulp');
var filter = require('gulp-filter');
var less = require('gulp-less');
gulp.task('less', function() {
return gulp.src('*.less')
.pipe(filter(function(file) {
return file.stat && file.contents.length;
}))
.pipe(less())
.pipe(gulp.dest('.'));
});
You pass virtual file objects to your stream, and each one of those contains contents as well as information on the file. The stat object tells you not only that is has been loaded, but also if it's a file. The contents are a buffer. With the length you can check if there's actually any content in there. Note: this will not help you with whitespaces. The file has to be really empty.

Gulp - TypeError: Cannot assign to read only property 'cwd' of bower / bootstrap.css

I'm just getting to grips with Gulp.js and this is the only error I can't seem to get around.
I have install bootstrap via bower, and I'm trying to minify the bootstrap css.
Here's my gulp task;
gulp.task('minifycss', function() {
gulp.src('www/css/animate.css', 'www/bower_components/bootstrap/dist/css/bootstrap.css')
.pipe(minifycss({compatibility: 'ie8'}))
.pipe(gulp.dest('www-deploy/css/'));
});
However when I run my main gulp task. It states that the file is "read only"
Karls-MacBook-Pro:cmp-2015 karltaylor$ gulp
[13:48:50] Using gulpfile ~/Documents/web/cmp-2015/gulpfile.js
[13:48:50] Starting 'sassMin'...
[13:48:50] Finished 'sassMin' after 12 ms
[13:48:50] Starting 'minifycss'...
[13:48:50] 'minifycss' errored after 508 μs
[13:48:50] TypeError: Cannot assign to read only property 'cwd' of www/bower_components/bootstrap/dist/css/bootstrap.css
at Object.gs.createStream (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/vinyl-fs/node_modules/glob-stream/index.js:19:46)
at Object.gs.create (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/vinyl-fs/node_modules/glob-stream/index.js:68:42)
at Gulp.src (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/vinyl-fs/lib/src/index.js:33:23)
at Gulp.<anonymous> (/Users/karltaylor/Documents/web/cmp-2015/gulpfile.js:35:7)
at module.exports (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:34:7)
at Gulp.Orchestrator._runTask (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/index.js:273:3)
at Gulp.Orchestrator._runStep (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/index.js:214:10)
at /Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/index.js:279:18
at finish (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:21:8)
at module.exports (/Users/karltaylor/Documents/web/cmp-2015/node_modules/gulp/node_modules/orchestrator/lib/runTask.js:60:3)
Things I've tried:
Reinstalling bootstrap
Moving the actual bootstrap.css file to my css file, however it does exactly the same thing.
EDIT: SOLVED = To use multiple sources you have to put them in an array.
Source: https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
Like #topleft mentioned, it was error in the syntax from using multiple files in my gulp.src. To use multiple sources you need to put them in an array.
Example:
var gulp = require('gulp');
var concat = require('gulp-concat');
gulp.task('default', function() {
return gulp.src(['foo/*', 'bar/*'])
.pipe(concat('result.txt'))
.pipe(gulp.dest('build'));
});
I had this issue when I tried to output 2 css files and I didn't use array [].
Instead of:
gulp.src('www/css/animate.css', 'www/bower_components/bootstrap/dist/css/bootstrap.css')
.pipe(minifycss({compatibility: 'ie8'}))
.pipe(gulp.dest('www-deploy/css/'));
});
Use this:
gulp.src(['www/css/animate.css', 'www/bower_components/bootstrap/dist/css/bootstrap.css'])
.pipe(minifycss({compatibility: 'ie8'}))
.pipe(gulp.dest('www-deploy/css/'));
});
Explanation:
gulp.src(['./file1.scss', './file2.scss']);
alternatively you could also try with
return gulp.src('www/**/*.css')
it should add every .css in your directories if thats the wanted effect

How do I process multiple files as templates with yeoman generator?

I'm working on a custom generator that I originally wrote with grunt-init. One difference I'm noticing is grunt-init automatically processes all the files in root as templates but with yeoman generator you have to do this using .template(). I'm familiar with how to process any individual file with .template() but is it possible to process an entire directory?
This issue is an annoying one. I came across this when I used yeoman for the first time. I think the below code snippet can help you.
this.directory('scripts', 'scripts');// script is folder name
Looking at the Yeoman generator code, there doesn't seem to be a built in way to do this. The way I solved this was to copy some of the code from the built in generator code and modify it for my needs. I determine if a file is a template based on the _ prefix convention since I want to rename the files to exclude that prefix, but you could just treat every file as a template and it would work fine. This will copy all of the files in the directory, so what I also did is to exclude the .DS_STORE files that you find by default on OSX, but since that is a specific case I didn't include that here.
require('path');
MyGenerator.prototype._processDirectory = function(source, destination) {
var root = this.isPathAbsolute(source) ? source : path.join(this.sourceRoot(), source);
var files = this.expandFiles('**', { dot: true, cwd: root });
for (var i = 0; i < files.length; i++) {
var f = files[i];
var src = path.join(root, f);
if(path.basename(f).indexOf('_') == 0){
var dest = path.join(destination, path.dirname(f), path.basename(f).replace(/^_/, ''));
this.template(src, dest);
}
else{
var dest = path.join(destination, f);
this.copy(src, dest);
}
}
};
Yeoman uses mem-fs-editor, which has support for glob patterns. However the documentation is not very clear, and you may miss that point. Here is the documentation of copyTpl, that says it accepts the same options as copy. So since copy has suppport for glob patterns, copyTpl too.
At any point on your yeoman generator you can do:
const from = 'myFolder/**.js'
const to = 'project/'
this
.fs
.copyTpl(
this.templatePath(from),
this.destinationPath(to),
this.props, {interpolate: /<%=([\s\S]+?)%>/g}
);
},
Not that, if you are using a glob pattern the destination path should be a folder.

Resources