postcss-minify-selectors
Advanced tools
Comparing version
@@ -0,1 +1,5 @@ | ||
# 1.4.0 | ||
* Update to postcss-selector-parser to greatly improve parsing logic. | ||
# 1.3.1 | ||
@@ -2,0 +6,0 @@ |
122
index.js
'use strict'; | ||
var uniqs = require('uniqs'); | ||
var minAttributes = require('./lib/transformAttributes'); | ||
var postcss = require('postcss'); | ||
var comma = postcss.list.comma; | ||
var normalize = require('normalize-selector'); | ||
var balanced = require('node-balanced'); | ||
var natural = require('javascript-natural-sort'); | ||
var roq = require('./lib/replaceOutsideQuotes'); | ||
var unquote = require('./lib/unquote'); | ||
var parser = require('postcss-selector-parser'); | ||
function uniq (params, map) { | ||
@@ -20,56 +20,92 @@ var transform = uniqs(comma(params).map(function (selector) { | ||
function optimiseSelector (rule) { | ||
function optimiseAtRule (rule) { | ||
rule.params = normalize(uniq(rule.params)); | ||
} | ||
function getParsed (selectors, callback) { | ||
return parser(callback).process(selectors).result; | ||
} | ||
/** | ||
* Can unquote attribute detection from mothereff.in | ||
* Copyright Mathias Bynens <https://mathiasbynens.be/> | ||
* https://github.com/mathiasbynens/mothereff.in | ||
*/ | ||
var escapes = /\\([0-9A-Fa-f]{1,6})[ \t\n\f\r]?/g; | ||
var range = /[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/; | ||
function canUnquote (value) { | ||
value = unquote(value); | ||
if (value) { | ||
value = value.replace(escapes, 'a').replace(/\\./g, 'a'); | ||
return !(range.test(value) || /^(?:-?\d|--)/.test(value)); | ||
} | ||
return false; | ||
} | ||
function optimise (rule) { | ||
var selector = rule._selector && rule._selector.raw || rule.selector; | ||
// Ensure that the selector list contains no duplicates | ||
selector = uniq(selector, true).map(function (sel) { | ||
return roq(sel, function (range) { | ||
// Remove the star from *qualified* star selectors | ||
// TODO: Improve the parsing here - currently it just skips the selector | ||
// if there is a comment. | ||
range = range.replace(/(?=\S*?)(\s+)(?=\S)/, ' '); | ||
if (!~range.indexOf('/*') && !~range.indexOf('*/')) { | ||
return range.replace(/(\*)([^\+~=>\s])/g, '$2'); | ||
rule.selector = getParsed(selector, function (selectors) { | ||
selectors.nodes.sort(function (a, b) { | ||
return natural(String(a), String(b)); | ||
}); | ||
selectors.eachAttribute(function (selector) { | ||
if (selector.value) { | ||
// Join selectors that are split over new lines | ||
selector.value = selector.value.replace(/\\\n/g, '').trim(); | ||
if (canUnquote(selector.value)) { | ||
selector.value = unquote(selector.value); | ||
} | ||
selector.operator = selector.operator.trim(); | ||
} | ||
return range; | ||
if (selector.raw) { | ||
selector.raw.insensitive = ''; | ||
} | ||
selector.attribute = selector.attribute.trim(); | ||
}); | ||
}).map(minAttributes).join(','); | ||
// Minimise from and 100% in keyframe rules | ||
if (rule.parent.type !== 'root' && ~rule.parent.name.indexOf('keyframes')) { | ||
selector = comma(selector).map(function (value) { | ||
if (value === 'from') { | ||
return '0%'; | ||
var uniques = []; | ||
selectors.eachInside(function (selector) { | ||
// Trim whitespace around the value | ||
selector.spaces.before = selector.spaces.after = ''; | ||
// Minimise from/100% | ||
if (selector.value === 'from') { selector.value = '0%'; } | ||
if (selector.value === '100%') { selector.value = 'to'; } | ||
if (selector.type === 'combinator') { | ||
var value = selector.value.trim(); | ||
selector.value = value.length ? value : ' '; | ||
} | ||
if (value === '100%') { | ||
return 'to'; | ||
if (selector.type === 'selector' && selector.type !== 'pseudo') { | ||
if (!~uniques.indexOf(String(selector))) { | ||
uniques.push(String(selector)); | ||
} else { | ||
selector.removeSelf(); | ||
} | ||
} | ||
return value; | ||
}).join(','); | ||
} | ||
// Trim whitespace around selector combinators | ||
rule.selector = roq(selector, function (range) { | ||
// Trim any useless space inside negation pseudo classes | ||
if (range) { | ||
range = balanced.replacements({ | ||
source: range, | ||
open: ':not(', | ||
close: ')', | ||
replace: function (body, head, tail) { | ||
return head + uniq(body) + tail; | ||
}); | ||
uniques = []; | ||
selectors.eachPseudo(function (pseudo) { | ||
pseudo.eachInside(function (selector) { | ||
if (selector.type === 'selector' && selector.type !== 'pseudo') { | ||
if (!~uniques.indexOf(String(selector))) { | ||
uniques.push(String(selector)); | ||
} else { | ||
selector.removeSelf(); | ||
} | ||
} | ||
}); | ||
return range.replace(/\s*([>+~])\s*/g, '$1'); | ||
} | ||
return range; | ||
}); | ||
selectors.eachUniversal(function (selector) { | ||
var next = selector.next(); | ||
if (next && next.type !== 'combinator') { | ||
selector.removeSelf(); | ||
} | ||
}); | ||
}); | ||
} | ||
function optimiseAtRule (rule) { | ||
rule.params = normalize(uniq(rule.params)); | ||
} | ||
module.exports = postcss.plugin('postcss-minify-selectors', function () { | ||
return function (css) { | ||
css.eachRule(optimiseSelector); | ||
css.eachRule(optimise); | ||
css.eachAtRule(optimiseAtRule); | ||
}; | ||
}); |
{ | ||
"name": "postcss-minify-selectors", | ||
"version": "1.3.1", | ||
"version": "1.4.0", | ||
"description": "Minify selectors with PostCSS.", | ||
@@ -35,7 +35,7 @@ "main": "index.js", | ||
"javascript-natural-sort": "^0.7.1", | ||
"node-balanced": "0.0.14", | ||
"normalize-selector": "0.0.3-a", | ||
"postcss": "^4.1.2", | ||
"postcss": "^4.1.11", | ||
"postcss-selector-parser": "0.0.5", | ||
"uniqs": "^2.0.0" | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
7711
-15.53%7
-22.22%105
-33.96%1
Infinity%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
Updated