Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
build(): add script to remove duplicate CSS properties
Browse files Browse the repository at this point in the history
Adds a custom script to remove duplicated CSS properties during builds, because cssnano only does it if the declarations are exactly the same. Note that this will skip prefixed properties and will print a warning with the offending selectors.

Closes #9320
  • Loading branch information
crisbeto authored and ThomasBurleson committed Aug 17, 2016
1 parent 7874074 commit c447c12
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
9 changes: 7 additions & 2 deletions gulp/tasks/build-scss.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ exports.task = function() {
filename = args['filename'] || 'angular-material',
baseFiles = config.scssBaseFiles,
layoutDest= dest + 'layouts/',
scssPipe = undefined;
scssPipe = null;

gutil.log("Building css files...");

Expand All @@ -39,13 +39,14 @@ exports.task = function() {
.pipe(concat('angular-material.scss'))
.pipe(gulp.dest(dest)) // raw uncompiled SCSS
.pipe(sass())
.pipe(util.dedupeCss())
.pipe(util.autoprefix())
.pipe(insert.prepend(config.banner))
.pipe(gulp.dest(dest)) // unminified
.pipe(gulpif(!IS_DEV, minifyCss()))
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
.pipe(rename({extname: '.min.css'}))
.pipe(gulp.dest(dest)) // minified

);

streams.push(
Expand All @@ -68,10 +69,12 @@ exports.task = function() {
.pipe(insert.prepend(config.banner))
.pipe(gulp.dest(layoutDest)) // raw uncompiled SCSS
.pipe(sass())
.pipe(util.dedupeCss())
.pipe(util.autoprefix())
.pipe(rename({ extname : '.css'}))
.pipe(gulp.dest(layoutDest))
.pipe(gulpif(!IS_DEV, minifyCss()))
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
.pipe(rename({extname: '.min.css'}))
.pipe(gulp.dest(layoutDest))
);
Expand All @@ -88,11 +91,13 @@ exports.task = function() {
.pipe(sassUtils.hoistScssVariables())
.pipe(gulp.dest(layoutDest)) // raw uncompiled SCSS
.pipe(sass())
.pipe(util.dedupeCss())
.pipe(util.autoprefix())
.pipe(rename({ extname : '.css'}))
.pipe(insert.prepend(config.banner))
.pipe(gulp.dest(layoutDest))
.pipe(gulpif(!IS_DEV, minifyCss()))
.pipe(gulpif(!IS_DEV, util.dedupeCss()))
.pipe(rename({extname: '.min.css'}))
.pipe(gulp.dest(layoutDest))
);
Expand Down
50 changes: 50 additions & 0 deletions gulp/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ var ngAnnotate = require('gulp-ng-annotate');
var insert = require('gulp-insert');
var gulpif = require('gulp-if');
var nano = require('gulp-cssnano');
var postcss = require('postcss');
var _ = require('lodash');
var constants = require('./const');
var VERSION = constants.VERSION;
var BUILD_MODE = constants.BUILD_MODE;
Expand All @@ -33,6 +35,7 @@ exports.filterNonCodeFiles = filterNonCodeFiles;
exports.readModuleArg = readModuleArg;
exports.themeBuildStream = themeBuildStream;
exports.minifyCss = minifyCss;
exports.dedupeCss = dedupeCss;
exports.args = args;

/**
Expand Down Expand Up @@ -181,6 +184,7 @@ function buildModule(module, opts) {
// In some cases there are multiple theme SCSS files, which should be concatenated together.
.pipe(gulpif, /default-theme.scss/, concat(name + '-default-theme.scss'))
.pipe(sass)
.pipe(dedupeCss)
.pipe(autoprefix)
(); // Invoke the returning lazypipe function to create our new pipe.
}
Expand Down Expand Up @@ -221,5 +225,51 @@ function themeBuildStream() {
.pipe(concat('default-theme.scss'))
.pipe(utils.hoistScssVariables())
.pipe(sass())
.pipe(dedupeCss())
.pipe(utils.cssToNgConstant('material.core', '$MD_THEME_CSS'));
}

// Removes duplicated CSS properties.
function dedupeCss() {
var prefixRegex = /-(webkit|moz|ms|o)-.+/;

return insert.transform(function(contents) {
// Parse the CSS into an AST.
var parsed = postcss.parse(contents);

// Walk through all the rules, skipping comments, media queries etc.
parsed.walk(function(rule) {
// Skip over any comments, media queries and rules that have less than 2 properties.
if (rule.type !== 'rule' || !rule.nodes || rule.nodes.length < 2) return;

// Walk all of the properties within a rule.
rule.walk(function(prop) {
// Check if there's a similar property that comes after the current one.
var hasDuplicate = validateProp(prop) && _.find(rule.nodes, function(otherProp) {
return prop !== otherProp && prop.prop === otherProp.prop && validateProp(otherProp);
});

// Remove the declaration if it's duplicated.
if (hasDuplicate) {
prop.remove();

gutil.log(gutil.colors.yellow(
'Removed duplicate property: "' +
prop.prop + ': ' + prop.value + '" from "' + rule.selector + '"...'
));
}
});
});

// Turn the AST back into CSS.
return parsed.toResult().css;
});

// Checks if a property is a style declaration and that it
// doesn't contain any vendor prefixes.
function validateProp(prop) {
return prop && prop.type === 'decl' && ![prop.prop, prop.value].some(function(value) {
return value.indexOf('-') > -1 && prefixRegex.test(value);
});
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"minimist": "^1.1.0",
"mkdirp": "^0.5.0",
"phantomjs-prebuilt": "^2.1.7",
"postcss": "^5.1.2",
"prompt-sync": "^1.0.0",
"q": "^1.0.1",
"stream-series": "^0.1.1",
Expand Down

0 comments on commit c447c12

Please sign in to comment.