Comparing version 1.0.2 to 1.0.3
@@ -7,3 +7,3 @@ /** | ||
* An exceptionally fast, thorough and tiny unused-CSS cleaner | ||
* https://github.com/leeoniya/dropcss (v1.0.2) | ||
* https://github.com/leeoniya/dropcss (v1.0.3) | ||
*/ | ||
@@ -192,3 +192,3 @@ | ||
var COMMENTS = /\s*\/\*[\s\S]*?\*\/\s*/gm; | ||
var COMBINATORS = /\s*[>~+]\s*|\s+/g; | ||
var COMBINATORS = /\s*[>~+.#]\s*|\[[^\]]+\]|\s+/gm; | ||
@@ -202,2 +202,4 @@ var START_AT = 1; | ||
// mission: "#a > b.c~g[a='a z'] y>.foo.bar" -> ["#a", "b", ".c", "g", "[a=a z]", "y", ".foo", ".bar"] | ||
// selsStr e.g. "table > a, foo.bar" | ||
@@ -208,10 +210,18 @@ function quickSels(selsStr) { | ||
var sep = '`'; | ||
// -> ["table > a", "foo.bar", [["table", "a"], ["foo", ".bar"]]] | ||
selsArr.push(selsArr.map(function (sel) { | ||
return stripAllPseudos(sel).trim() | ||
// for quick checks we can actually split input[type=month] into "input [type=month]" and | ||
// .foo.bar#moo into ".foo .bar #moo". this way each can be quick-checked without context | ||
.replace(/(\.|#|\[)/gm, ' $1').replace(/\]/gm, '] ').trim() | ||
.split(COMBINATORS); | ||
})); | ||
selsArr.push(selsArr.map(function (sel) { return stripAllPseudos(sel) | ||
.trim() | ||
.replace(COMBINATORS, function (m, i) { | ||
m = m.trim(); | ||
return ( | ||
i == 0 ? m : | ||
m == '.' || m == '#' ? sep + m : | ||
m.length <= 1 ? sep : | ||
sep + m.replace(/['"]/gm, '') | ||
); | ||
}) | ||
.split(/`+/gm); } | ||
)); | ||
@@ -265,4 +275,2 @@ return selsArr; | ||
function next() { | ||
@@ -269,0 +277,0 @@ if (inAt > 0) { |
@@ -7,3 +7,3 @@ /** | ||
* An exceptionally fast, thorough and tiny unused-CSS cleaner | ||
* https://github.com/leeoniya/dropcss (v1.0.2) | ||
* https://github.com/leeoniya/dropcss (v1.0.3) | ||
*/ | ||
@@ -196,3 +196,3 @@ | ||
var COMMENTS = /\s*\/\*[\s\S]*?\*\/\s*/gm; | ||
var COMBINATORS = /\s*[>~+]\s*|\s+/g; | ||
var COMBINATORS = /\s*[>~+.#]\s*|\[[^\]]+\]|\s+/gm; | ||
@@ -206,2 +206,4 @@ var START_AT = 1; | ||
// mission: "#a > b.c~g[a='a z'] y>.foo.bar" -> ["#a", "b", ".c", "g", "[a=a z]", "y", ".foo", ".bar"] | ||
// selsStr e.g. "table > a, foo.bar" | ||
@@ -212,10 +214,18 @@ function quickSels(selsStr) { | ||
var sep = '`'; | ||
// -> ["table > a", "foo.bar", [["table", "a"], ["foo", ".bar"]]] | ||
selsArr.push(selsArr.map(function (sel) { | ||
return stripAllPseudos(sel).trim() | ||
// for quick checks we can actually split input[type=month] into "input [type=month]" and | ||
// .foo.bar#moo into ".foo .bar #moo". this way each can be quick-checked without context | ||
.replace(/(\.|#|\[)/gm, ' $1').replace(/\]/gm, '] ').trim() | ||
.split(COMBINATORS); | ||
})); | ||
selsArr.push(selsArr.map(function (sel) { return stripAllPseudos(sel) | ||
.trim() | ||
.replace(COMBINATORS, function (m, i) { | ||
m = m.trim(); | ||
return ( | ||
i == 0 ? m : | ||
m == '.' || m == '#' ? sep + m : | ||
m.length <= 1 ? sep : | ||
sep + m.replace(/['"]/gm, '') | ||
); | ||
}) | ||
.split(/`+/gm); } | ||
)); | ||
@@ -269,4 +279,2 @@ return selsArr; | ||
function next() { | ||
@@ -273,0 +281,0 @@ if (inAt > 0) { |
@@ -1,2 +0,2 @@ | ||
/*! https://github.com/leeoniya/dropcss (v1.0.2) */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).dropcss=t()}(this,function(){"use strict";var e=1,t=2,r=3,n=new Set("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")),a=/<!doctype[^>]*>|<!--[\s\S]*?-->|<script[^>]*>[\s\S]*?<\/script>|<style[^>]*>[\s\S]*?<\/style>|<link[^>]*>|<meta[^>]*>/gim,s=/([\w-]+)(?:="([^"]*)"|='([^']*)'|=(\S+))?/gm,i={TAG_HEAD:/\s*<([a-z0-9_-]+)(?:\s+([^>]*))?>\s*/imy,TEXT:/\s*[^<]*/my,TAG_CLOSE:/\s*<\/[a-z0-9_-]+>\s*/imy};var c=new Set;function o(e,t,r){return{tagName:t,attributes:r,classList:null!=r&&r.has("class")?new Set(r.get("class").split(/\s+/g)):c,parentNode:e,childNodes:[]}}function u(e,t){if(null!=e){var r=e._ofTypes=e._ofTypes||{};if(!(t in r)){var n=0;r[t]=e.childNodes.filter(function(e){if(e.tagName==t)return e._typeIdx=n++,!0})}return r[t]}return null}var l=function(u){var l=function(a){var c,o=0,u=[];function l(e){for(var t in o=e.lastIndex,i)i[t].lastIndex=o}function d(){if(null!=(c=i.TAG_CLOSE.exec(a)))return l(i.TAG_CLOSE),void u.push(r);if(null==(c=i.TAG_HEAD.exec(a)))null!=(c=i.TEXT.exec(a))&&l(i.TEXT);else{l(i.TAG_HEAD);var o=c[1];u.push(e,o);var d=c[2];if(null!=d){for(var f,h=new Map;f=s.exec(d);)h.set(f[1],(f[2]||f[3]||f[4]||"").trim());u.push(t,h)}n.has(o)&&u.push(r)}}for(;o<a.length;)d();return l({lastIndex:0}),u}(u=u.replace(a,"")),d={nodes:[],tag:new Set(["*"]),class:new Set,attr:new Set};!function(n,a){for(var s,i=o(null,"root",c),u=0;u<n.length;u++)switch(n[u]){case e:var l=n[++u],d=c;n[u+1]===t&&(d=n[u+=2]),s=i.childNodes.length,i.childNodes.push(i=o(i,l,d)),a(i,s);break;case r:i=i.parentNode}}(l,function(e,t){return function(e,t,r){e.idx=t;var n=e.attributes;r.tag.add(e.tagName),e.classList.forEach(function(e){return r.class.add(e)}),n.has("id")&&r.attr.add("[id="+n.get("id")+"]"),n.has("type")&&r.attr.add("[type="+n.get("type")+"]"),r.nodes.push(e)}(e,t,d)});return d},d=/\s*\/\*[\s\S]*?\*\/\s*/gm,f=/\s*[>~+]\s*|\s+/g,h=1,p=2,m=3,v=4,x=5;function g(e,t){for(var r="",n=1;"{"==e[t]?n++:"}"==e[t]&&n--,0!=n;)r+=e[t++];return r}function y(e){var t,r={RULE_HEAD:/\s*([^{;]+?)\s*[{;]\s*/my,RULE_TAIL:/\s*([^}]*?)\s*\}/my,AT_TAIL:/\s*\}/my,RULE_FULL:/\s*([^{]*?)\{([^}]+?)\}/my},n=0,a=0,s=[];function i(e){for(var t in a=e.lastIndex,r)r[t].lastIndex=a}function c(){if(n>0&&null!=(t=r.AT_TAIL.exec(e)))return n--,s.push(p),void i(r.AT_TAIL);if(null!=(t=r.RULE_HEAD.exec(e))){var c=t[1];if(i(r.RULE_HEAD),"@"==c[0])switch(c.match(/@[a-z-]+/)[0]){case"@media":case"@supports":case"@document":n++,s.push(h,c);break;case"@import":case"@charset":case"@namespace":s.push(x,c+";");break;default:n++;var o=g(e,a);i({lastIndex:a+o.length}),s.push(h,c,x,o)}else s.push(m,(u=t[1],(l=u.split(/\s*,\s*/gm)).push(l.map(function(e){return function(e){return e.replace(/:?:[a-z-]+(?:\([^()]+\))?/gm,"")}(e).trim().replace(/(\.|#|\[)/gm," $1").replace(/\]/gm,"] ").trim().split(f)})),l)),t=r.RULE_TAIL.exec(e),s.push(v,t[1]),i(r.RULE_TAIL)}else a=e.length;var u,l}for(;a<e.length;)c();return s}function b(e){return e.replace(/@[a-z-]+[^{]+\{\s*\}/gm,"")}var k=/^([+-]?\d*)?n([+-]\d+)?$/;function T(e,t){return t==e.tagName||"*"==t}function E(e,t,r,n){n=n||"=";var a=e.attributes;if(a.has(t)){var s=a.get(t);switch(n){case"=":return null==r||r==s;case"*=":return-1!=s.indexOf(r);case"^=":return s.startsWith(r);case"$=":return s.endsWith(r)}}return!1}function _(e,t){return e.classList.has(t)}function A(e,t){var r;if("odd"==t)r=e%2==1;else if("even"==t)r=e%2==0;else if(/^\d+$/.test(t))r=e==+t;else{var n=function(e){var t=k.exec(e);if(null!=t){var r=t[1],n=t[2];return[r=null==r||"+"==r?1:"-"==r?-1:+r,n=null==n?0:+n]}return[0,0]}(t);r=function(e,t,r){if(t<0&&e<=0)return!1;if(-1===e)return r<=t;if(0===e)return r===t;if(1===e)return t<0||r>=t;var n=t%e;return n<0&&(n+=e),e>1?r>=t&&r%e===n:(e*=-1,r<=t&&r%e===n)}(n[0],n[1],e)}return r}function w(e,t){return e.some(function(e){return function e(t,r){for(var n,a,s,i,c,o;r.idx>-1;){switch(t[r.idx]){case"_":n=t[--r.idx],o=T(r.node,n),r.idx--;break;case"#":a=t[--r.idx],o=E(r.node,"id",a,"="),r.idx--;break;case".":n=t[--r.idx],o=_(r.node,n),r.idx--;break;case"[":n=t[--r.idx],s=t[--r.idx],a=t[--r.idx],o=E(r.node,n,a,s),r.idx--;break;case":":n=t[--r.idx],a=t[--r.idx];var l=r.node,d=l.tagName;c=l.idx;var f=(i=l.parentNode)?i.childNodes.length:1,h=void 0;switch(n){case"not":o=!e(a,{node:r.node,idx:a.length-1});break;case"first-child":o=0==c;break;case"last-child":o=c==f-1;break;case"only-child":o=1==f;break;case"nth-child":o=A(c+1,a);break;case"nth-last-child":o=A(f-c,a);break;case"first-of-type":h=u(i,d),o=0==l._typeIdx;break;case"last-of-type":h=u(i,d),o=l._typeIdx==h.length-1;break;case"only-of-type":o=1==(h=u(i,d)).length;break;case"nth-of-type":h=u(i,d),o=A(l._typeIdx+1,a);break;case"nth-last-of-type":o=A((h=u(i,d)).length-l._typeIdx,a)}r.idx--;break;case" ":for(c=--r.idx,o=!1;!o&&null!=(i=r.node.parentNode);)r.idx=c,r.node=i,o=e(t,r);break;case">":r.idx--,null!=(i=r.node.parentNode)?(r.node=i,o=e(t,r)):o=!1;break;case"+":r.idx--,null!=(i=r.node.parentNode)&&r.node.idx>0?(r.node=i.childNodes[r.node.idx-1],o=e(t,r)):o=!1;break;case"~":if(r.idx--,o=!1,c=r.node.idx,null!=(i=r.node.parentNode)&&c>0)for(var p=0;p<c&&!o;p++)r.node=i.childNodes[p],o=e(t,r)}if(!o)break}return o}(t,{idx:t.length-1,node:e})})}var I=function(e,t){return w(e,Array.isArray(t)?t:function e(t){var r,n={IDENT:/([\w*-]+)/iy,ATTR:/([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/iy,PSEUDO:/([\w-]+)(?:\(([^)]*)\))?/iy,MODE:/\s*[:.#\[]\s*/iy,COMB:/\s*[>~+]\s*|\s+/iy},a=0,s=[],i=-1;function c(e){for(var t in a=e.lastIndex,n)n[t].lastIndex=a}function o(){var o=!1;if(r=n.COMB.exec(t)){o=!0;var u=r[0].trim();""==u&&(u=" "),s.push(u),c(n.COMB),i=a}else if(r=n.MODE.exec(t)){o=!0;var l=r[0].trim();c(n.MODE),":"==l?(r=n.PSEUDO.exec(t),s.splice(i+1,0,null!=r[2]&&"not"==r[1]?e(r[2]):r[2],r[1],l),c(n.PSEUDO)):"["==l?(r=n.ATTR.exec(t),s.splice(i+1,0,r[3],r[2],r[1],l),c(n.ATTR)):(r=n.IDENT.exec(t),s.push(r[1],l),c(n.IDENT))}else(r=n.IDENT.exec(t))&&(o=!0,s.push(r[1],"_"),c(n.IDENT));return o}for(;a<t.length;)o();return s}(t))},N=/\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i,S=/:(?:first|last|nth|only|not)\b/;function L(e,t,r,n,a){a=a||"";for(var s=t.length-1;s>-1;s--){var i=t[s];r.has(i[2])||!0!==n(a+i[2])||(c=e,o=i[0],u=i[1],l="",e=c.slice(0,o)+l+c.slice(o+u))}var c,o,u,l;return e}var D=function(e){return!0};return function(e){for(var t=l(e.html,!e.keepText),r=e.shouldDrop||D,n=y(e.css.replace(d,"")),a={},s=0;s<n.length;s++)if(n[s]===m){var i=n[s+1],c=i[i.length-1];s++;for(var o=0;o<c.length;o++){var u=c[o];e:for(var f=0;f<u.length;f++){var k=u[f],T=!1,_=void 0;if(""!=k){if(k in a)T=a[k];else switch(k[0]){case"#":_=k.substr(1),a[k]=T=t.attr.has("[id="+_+"]");break;case".":_=k.substr(1),a[k]=T=t.class.has(_);break;case"[":if(k.startsWith("[type="))a[k]=T=t.attr.has(k);else{var A=k.match(N);a[k]=T=t.nodes.some(function(e){return E(e,A[1],A[3],A[2])})}break;default:a[k]=T=t.tag.has(k)}if(!T){!0===r(i[o])?i[o]=null:a[i[o]]=!0;break e}}}}}for(var w=0;w<n.length;w++)n[w]===m&&(n[++w].length,n[w]=n[w].filter(function(e){if("string"==typeof e){if(e in a)return a[e];var n=(s=e).replace(/:?:[a-z-]+/gm,function(e){return s.startsWith("::")||!S.test(e)?"":e}).replace(/:[a-z-]+\(\)/gm,"");return""==n||(n in a?a[n]:a[n]=I(t.nodes,n)||!0!==r(e))}var s;return!1}));var O=function(e){for(var t="",r=0,n=0;n<e.length;n++)switch(e[n]){case m:var a=e[++n];(r=a.length)>0&&(t+=a.join());break;case v:r>0&&(t+="{"+e[++n]+"}");break;case h:t+=e[++n]+"{";break;case p:t+="}";break;case x:t+=e[++n]}return b(t)}(n);return{css:b(O=function(e,t){for(var r,n=[],a=new Set,s=/@font-face[\s\S]+?font-family:\s*(['"\w-]+)[^}]+\}/gm;r=s.exec(e);){var i=r[1].replace(/['"]/gm,"");n.push([r.index,r[0].length,i])}for(var c=/font-family:([^;!}]+)/gm;r=c.exec(e);)n.some(function(e){return r.index>e[0]&&r.index<e[0]+e[1]})||r[1].trim().split(",").forEach(function(e){a.add(e.trim().replace(/['"]/gm,""))});return L(e,n,a,t,"@font-face ")}(O=function(e,t){for(var r,n=[],a=new Set,s=/@(?:-\w+-)?keyframes\s+([\w-]+)\s*\{/gm;r=s.exec(e);){var i=g(e,s.lastIndex);n.push([r.index,r[0].length+i.length+1,r[1]])}for(var c=/animation(?:-name)?:([^;!}]+)/gm;r=c.exec(e);)r[1].trim().split(",").forEach(function(e){var t=(e=e.trim()).match(/^\S+/)[0];/^-?[\d.]+m?s/.test(t)&&(t=e.match(/\S+$/)[0]),a.add(t)});return L(e,n,a,t,"@keyframes ")}(O,r),r))}}}); | ||
/*! https://github.com/leeoniya/dropcss (v1.0.3) */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).dropcss=t()}(this,function(){"use strict";var e=1,t=2,r=3,n=new Set("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")),a=/<!doctype[^>]*>|<!--[\s\S]*?-->|<script[^>]*>[\s\S]*?<\/script>|<style[^>]*>[\s\S]*?<\/style>|<link[^>]*>|<meta[^>]*>/gim,s=/([\w-]+)(?:="([^"]*)"|='([^']*)'|=(\S+))?/gm,i={TAG_HEAD:/\s*<([a-z0-9_-]+)(?:\s+([^>]*))?>\s*/imy,TEXT:/\s*[^<]*/my,TAG_CLOSE:/\s*<\/[a-z0-9_-]+>\s*/imy};var c=new Set;function o(e,t,r){return{tagName:t,attributes:r,classList:null!=r&&r.has("class")?new Set(r.get("class").split(/\s+/g)):c,parentNode:e,childNodes:[]}}function u(e,t){if(null!=e){var r=e._ofTypes=e._ofTypes||{};if(!(t in r)){var n=0;r[t]=e.childNodes.filter(function(e){if(e.tagName==t)return e._typeIdx=n++,!0})}return r[t]}return null}var l=function(u){var l=function(a){var c,o=0,u=[];function l(e){for(var t in o=e.lastIndex,i)i[t].lastIndex=o}function d(){if(null!=(c=i.TAG_CLOSE.exec(a)))return l(i.TAG_CLOSE),void u.push(r);if(null==(c=i.TAG_HEAD.exec(a)))null!=(c=i.TEXT.exec(a))&&l(i.TEXT);else{l(i.TAG_HEAD);var o=c[1];u.push(e,o);var d=c[2];if(null!=d){for(var f,h=new Map;f=s.exec(d);)h.set(f[1],(f[2]||f[3]||f[4]||"").trim());u.push(t,h)}n.has(o)&&u.push(r)}}for(;o<a.length;)d();return l({lastIndex:0}),u}(u=u.replace(a,"")),d={nodes:[],tag:new Set(["*"]),class:new Set,attr:new Set};!function(n,a){for(var s,i=o(null,"root",c),u=0;u<n.length;u++)switch(n[u]){case e:var l=n[++u],d=c;n[u+1]===t&&(d=n[u+=2]),s=i.childNodes.length,i.childNodes.push(i=o(i,l,d)),a(i,s);break;case r:i=i.parentNode}}(l,function(e,t){return function(e,t,r){e.idx=t;var n=e.attributes;r.tag.add(e.tagName),e.classList.forEach(function(e){return r.class.add(e)}),n.has("id")&&r.attr.add("[id="+n.get("id")+"]"),n.has("type")&&r.attr.add("[type="+n.get("type")+"]"),r.nodes.push(e)}(e,t,d)});return d},d=/\s*\/\*[\s\S]*?\*\/\s*/gm,f=/\s*[>~+.#]\s*|\[[^\]]+\]|\s+/gm,h=1,p=2,m=3,v=4,x=5;function g(e,t){for(var r="",n=1;"{"==e[t]?n++:"}"==e[t]&&n--,0!=n;)r+=e[t++];return r}function y(e){var t,r={RULE_HEAD:/\s*([^{;]+?)\s*[{;]\s*/my,RULE_TAIL:/\s*([^}]*?)\s*\}/my,AT_TAIL:/\s*\}/my,RULE_FULL:/\s*([^{]*?)\{([^}]+?)\}/my},n=0,a=0,s=[];function i(e){for(var t in a=e.lastIndex,r)r[t].lastIndex=a}function c(){if(n>0&&null!=(t=r.AT_TAIL.exec(e)))return n--,s.push(p),void i(r.AT_TAIL);if(null!=(t=r.RULE_HEAD.exec(e))){var c=t[1];if(i(r.RULE_HEAD),"@"==c[0])switch(c.match(/@[a-z-]+/)[0]){case"@media":case"@supports":case"@document":n++,s.push(h,c);break;case"@import":case"@charset":case"@namespace":s.push(x,c+";");break;default:n++;var o=g(e,a);i({lastIndex:a+o.length}),s.push(h,c,x,o)}else s.push(m,(u=t[1],(l=u.split(/\s*,\s*/gm)).push(l.map(function(e){return function(e){return e.replace(/:?:[a-z-]+(?:\([^()]+\))?/gm,"")}(e).trim().replace(f,function(e,t){return e=e.trim(),0==t?e:"."==e||"#"==e?"`"+e:e.length<=1?"`":"`"+e.replace(/['"]/gm,"")}).split(/`+/gm)})),l)),t=r.RULE_TAIL.exec(e),s.push(v,t[1]),i(r.RULE_TAIL)}else a=e.length;var u,l}for(;a<e.length;)c();return s}function b(e){return e.replace(/@[a-z-]+[^{]+\{\s*\}/gm,"")}var k=/^([+-]?\d*)?n([+-]\d+)?$/;function T(e,t){return t==e.tagName||"*"==t}function E(e,t,r,n){n=n||"=";var a=e.attributes;if(a.has(t)){var s=a.get(t);switch(n){case"=":return null==r||r==s;case"*=":return-1!=s.indexOf(r);case"^=":return s.startsWith(r);case"$=":return s.endsWith(r)}}return!1}function _(e,t){return e.classList.has(t)}function A(e,t){var r;if("odd"==t)r=e%2==1;else if("even"==t)r=e%2==0;else if(/^\d+$/.test(t))r=e==+t;else{var n=function(e){var t=k.exec(e);if(null!=t){var r=t[1],n=t[2];return[r=null==r||"+"==r?1:"-"==r?-1:+r,n=null==n?0:+n]}return[0,0]}(t);r=function(e,t,r){if(t<0&&e<=0)return!1;if(-1===e)return r<=t;if(0===e)return r===t;if(1===e)return t<0||r>=t;var n=t%e;return n<0&&(n+=e),e>1?r>=t&&r%e===n:(e*=-1,r<=t&&r%e===n)}(n[0],n[1],e)}return r}function w(e,t){return e.some(function(e){return function e(t,r){for(var n,a,s,i,c,o;r.idx>-1;){switch(t[r.idx]){case"_":n=t[--r.idx],o=T(r.node,n),r.idx--;break;case"#":a=t[--r.idx],o=E(r.node,"id",a,"="),r.idx--;break;case".":n=t[--r.idx],o=_(r.node,n),r.idx--;break;case"[":n=t[--r.idx],s=t[--r.idx],a=t[--r.idx],o=E(r.node,n,a,s),r.idx--;break;case":":n=t[--r.idx],a=t[--r.idx];var l=r.node,d=l.tagName;c=l.idx;var f=(i=l.parentNode)?i.childNodes.length:1,h=void 0;switch(n){case"not":o=!e(a,{node:r.node,idx:a.length-1});break;case"first-child":o=0==c;break;case"last-child":o=c==f-1;break;case"only-child":o=1==f;break;case"nth-child":o=A(c+1,a);break;case"nth-last-child":o=A(f-c,a);break;case"first-of-type":h=u(i,d),o=0==l._typeIdx;break;case"last-of-type":h=u(i,d),o=l._typeIdx==h.length-1;break;case"only-of-type":o=1==(h=u(i,d)).length;break;case"nth-of-type":h=u(i,d),o=A(l._typeIdx+1,a);break;case"nth-last-of-type":o=A((h=u(i,d)).length-l._typeIdx,a)}r.idx--;break;case" ":for(c=--r.idx,o=!1;!o&&null!=(i=r.node.parentNode);)r.idx=c,r.node=i,o=e(t,r);break;case">":r.idx--,null!=(i=r.node.parentNode)?(r.node=i,o=e(t,r)):o=!1;break;case"+":r.idx--,null!=(i=r.node.parentNode)&&r.node.idx>0?(r.node=i.childNodes[r.node.idx-1],o=e(t,r)):o=!1;break;case"~":if(r.idx--,o=!1,c=r.node.idx,null!=(i=r.node.parentNode)&&c>0)for(var p=0;p<c&&!o;p++)r.node=i.childNodes[p],o=e(t,r)}if(!o)break}return o}(t,{idx:t.length-1,node:e})})}var I=function(e,t){return w(e,Array.isArray(t)?t:function e(t){var r,n={IDENT:/([\w*-]+)/iy,ATTR:/([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/iy,PSEUDO:/([\w-]+)(?:\(([^)]*)\))?/iy,MODE:/\s*[:.#\[]\s*/iy,COMB:/\s*[>~+]\s*|\s+/iy},a=0,s=[],i=-1;function c(e){for(var t in a=e.lastIndex,n)n[t].lastIndex=a}function o(){var o=!1;if(r=n.COMB.exec(t)){o=!0;var u=r[0].trim();""==u&&(u=" "),s.push(u),c(n.COMB),i=a}else if(r=n.MODE.exec(t)){o=!0;var l=r[0].trim();c(n.MODE),":"==l?(r=n.PSEUDO.exec(t),s.splice(i+1,0,null!=r[2]&&"not"==r[1]?e(r[2]):r[2],r[1],l),c(n.PSEUDO)):"["==l?(r=n.ATTR.exec(t),s.splice(i+1,0,r[3],r[2],r[1],l),c(n.ATTR)):(r=n.IDENT.exec(t),s.push(r[1],l),c(n.IDENT))}else(r=n.IDENT.exec(t))&&(o=!0,s.push(r[1],"_"),c(n.IDENT));return o}for(;a<t.length;)o();return s}(t))},N=/\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i,S=/:(?:first|last|nth|only|not)\b/;function L(e,t,r,n,a){a=a||"";for(var s=t.length-1;s>-1;s--){var i=t[s];r.has(i[2])||!0!==n(a+i[2])||(c=e,o=i[0],u=i[1],l="",e=c.slice(0,o)+l+c.slice(o+u))}var c,o,u,l;return e}var D=function(e){return!0};return function(e){for(var t=l(e.html,!e.keepText),r=e.shouldDrop||D,n=y(e.css.replace(d,"")),a={},s=0;s<n.length;s++)if(n[s]===m){var i=n[s+1],c=i[i.length-1];s++;for(var o=0;o<c.length;o++){var u=c[o];e:for(var f=0;f<u.length;f++){var k=u[f],T=!1,_=void 0;if(""!=k){if(k in a)T=a[k];else switch(k[0]){case"#":_=k.substr(1),a[k]=T=t.attr.has("[id="+_+"]");break;case".":_=k.substr(1),a[k]=T=t.class.has(_);break;case"[":if(k.startsWith("[type="))a[k]=T=t.attr.has(k);else{var A=k.match(N);a[k]=T=t.nodes.some(function(e){return E(e,A[1],A[3],A[2])})}break;default:a[k]=T=t.tag.has(k)}if(!T){!0===r(i[o])?i[o]=null:a[i[o]]=!0;break e}}}}}for(var w=0;w<n.length;w++)n[w]===m&&(n[++w].length,n[w]=n[w].filter(function(e){if("string"==typeof e){if(e in a)return a[e];var n=(s=e).replace(/:?:[a-z-]+/gm,function(e){return s.startsWith("::")||!S.test(e)?"":e}).replace(/:[a-z-]+\(\)/gm,"");return""==n||(n in a?a[n]:a[n]=I(t.nodes,n)||!0!==r(e))}var s;return!1}));var O=function(e){for(var t="",r=0,n=0;n<e.length;n++)switch(e[n]){case m:var a=e[++n];(r=a.length)>0&&(t+=a.join());break;case v:r>0&&(t+="{"+e[++n]+"}");break;case h:t+=e[++n]+"{";break;case p:t+="}";break;case x:t+=e[++n]}return b(t)}(n);return{css:b(O=function(e,t){for(var r,n=[],a=new Set,s=/@font-face[\s\S]+?font-family:\s*(['"\w-]+)[^}]+\}/gm;r=s.exec(e);){var i=r[1].replace(/['"]/gm,"");n.push([r.index,r[0].length,i])}for(var c=/font-family:([^;!}]+)/gm;r=c.exec(e);)n.some(function(e){return r.index>e[0]&&r.index<e[0]+e[1]})||r[1].trim().split(",").forEach(function(e){a.add(e.trim().replace(/['"]/gm,""))});return L(e,n,a,t,"@font-face ")}(O=function(e,t){for(var r,n=[],a=new Set,s=/@(?:-\w+-)?keyframes\s+([\w-]+)\s*\{/gm;r=s.exec(e);){var i=g(e,s.lastIndex);n.push([r.index,r[0].length+i.length+1,r[1]])}for(var c=/animation(?:-name)?:([^;!}]+)/gm;r=c.exec(e);)r[1].trim().split(",").forEach(function(e){var t=(e=e.trim()).match(/^\S+/)[0];/^-?[\d.]+m?s/.test(t)&&(t=e.match(/\S+$/)[0]),a.add(t)});return L(e,n,a,t,"@keyframes ")}(O,r),r))}}}); |
{ | ||
"name": "dropcss", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "An exceptionally fast, thorough and tiny unused-CSS cleaner", | ||
@@ -28,3 +28,3 @@ "main": "./dist/dropcss.cjs.js", | ||
"devDependencies": { | ||
"mocha": "^6.1.2", | ||
"mocha": "^6.1.3", | ||
"nyc": "^13.3.0", | ||
@@ -31,0 +31,0 @@ "rollup": "^1.10.0", |
@@ -8,7 +8,7 @@ ## ๐ DropCSS | ||
DropCSS is an exceptionally fast, thorough and tiny ([~8 KB min](https://github.com/leeoniya/dropcss/tree/master/dist/dropcss.min.js)) unused-CSS cleaner; it takes your HTML and CSS as input and returns only the used CSS as output. Its custom HTML and CSS parsers are highly optimized for the 99% use case and thus avoid the overhead of handling malformed markup or stylesheets, so you must provide well-formed input. There is minimal handling for complex escaping rules, so there will always exist cases of valid input that cannot be processed by DropCSS; for these infrequent cases, please [start a discussion](https://github.com/leeoniya/dropcss/issues), use a previous, larger and slower [0.3.x version](https://github.com/leeoniya/dropcss/releases) that uses heavier but more compliant parsers, or use an alternative CSS cleaner. | ||
DropCSS is an exceptionally fast, thorough and tiny ([~8 KB min](https://github.com/leeoniya/dropcss/tree/master/dist/dropcss.min.js)) unused-CSS cleaner; it takes your HTML and CSS as input and returns only the used CSS as output. Its custom HTML and CSS parsers are highly optimized for the 99% use case and thus avoid the overhead of handling malformed markup or stylesheets, so you must provide well-formed input. There is minimal handling for complex escaping rules, so there will always exist cases of valid input that cannot be processed by DropCSS; for these infrequent cases, please [start a discussion](https://github.com/leeoniya/dropcss/issues). | ||
As a bonus, DropCSS will also remove unused `@keyframes` and `@font-face` blocks - an out-of-scope, purely intra-CSS optimization. Speaking of which, it's a good idea to run your CSS through a structural optimizer like [clean-css](https://github.com/jakubpawlowicz/clean-css), [csso](https://github.com/css/csso), [cssnano](https://github.com/cssnano/cssnano) or [crass](https://github.com/mattbasta/crass) to re-group selectors, merge redundant rules, etc. It probably makes sense to do this after DropCSS, which can leave redundant blocks, e.g. `.foo, .bar { color: red; }; .bar { width: 50%; }` -> `.bar { color: red; }; .bar { width: 50%; }` if `.foo` is absent from your markup. | ||
A bit more on this project's backstory & discussions in [/r/javascript](https://old.reddit.com/r/javascript/comments/b3mcu8/dropcss_010_a_minimal_and_thorough_unused_css/) and on [Hacker News](https://news.ycombinator.com/item?id=19469080). | ||
More on this project's backstory & discussions: v0.1.0 alpha: [/r/javascript](https://old.reddit.com/r/javascript/comments/b3mcu8/dropcss_010_a_minimal_and_thorough_unused_css/), [Hacker News](https://news.ycombinator.com/item?id=19469080) and v1.0.0 release: [/r/javascript](https://old.reddit.com/r/javascript/comments/bb7im2/dropcss_v100_an_exceptionally_fast_thorough_and/). | ||
@@ -136,4 +136,4 @@ --- | ||
<td> | ||
58.4 KB<br> | ||
6 Files, 2 Folders | ||
58.4 KB<br> | ||
6 Files, 2 Folders | ||
</td> | ||
@@ -194,3 +194,3 @@ <td>6.58 KB</td> | ||
[Here's a 30 line script](/demos/puppeteer/index.js) which does exactly that: | ||
[Here's a 35 line script](/demos/puppeteer/index.js) which does exactly that: | ||
@@ -216,2 +216,4 @@ ```js | ||
fetch(href).then(r => r.text()).then(css => { | ||
let start = +new Date(); | ||
let clean = dropcss({ | ||
@@ -222,3 +224,7 @@ css, | ||
console.log({stylesheet: href, cleanCss: clean.css}); | ||
console.log({ | ||
stylesheet: href, | ||
cleanCss: clean.css, | ||
elapsed: +new Date() - start, | ||
}); | ||
}) | ||
@@ -232,6 +238,59 @@ )); | ||
--- | ||
### TODO | ||
### Special / Escaped Sequences | ||
- Moar tests. DropCSS is currently developed against gigantic blobs of diverse, real-world CSS and HTML. These inputs & outputs are also used for perf testing and regression detection. While not all output was verified by hand (this would be infeasible for giganitic mis-matched HTML/CSS inputs), it was loosely verified against what other cleaners remove and what they leave behind. Writing tests is additonally challenging because the way selectors are drop-tested is optimized to fast-path many cases; a complex-looking test like `.foo > ul + p:not([foo*=bar]):hover` will actually short circuit early if `.foo`, `ul` or `p` are missing from the dom, and will never continue to structural/context or negation assertions. Tests must be carefully written to ensure they hit all the desired paths; it's easy to waste a lot of time writing useless tests that add no value. Unfortunately, even 100% cumulative code coverage of the test suite would only serve as a starting point. Good tests would be a diverse set of real-world inputs and manually verified outputs. | ||
DropCSS is stupid and will choke on unusual selectors, like the ones used by the popular [Tailwind CSS](https://github.com/tailwindcss/tailwindcss) framework: | ||
`class` attributes can look like this: | ||
```html | ||
<div class="px-6 pt-6 overflow-y-auto text-base lg:text-sm lg:py-12 lg:pl-6 lg:pr-8 sticky?lg:h-(screen-16)"></div> | ||
<div class="px-2 -mx-2 py-1 transition-fast relative block hover:translate-r-2px hover:text-gray-900 text-gray-600 font-medium"></div> | ||
``` | ||
...and the CSS looks like this: | ||
```css | ||
.sticky\?lg\:h-\(screen-16\){...} | ||
.lg\:text-sm{...} | ||
.lg\:focus\:text-green-700:focus{...} | ||
``` | ||
Ouch. | ||
The solution is to temporarily replace the escaped characters in the HTML and CSS with some unique strings which match `/[\w-]/`. This allows DropCSS's tokenizer to consider the classname as one contiguous thing. After processing, we simply reverse the operation. | ||
```js | ||
// remap | ||
let css2 = css | ||
.replace(/\\\:/gm, '__0') | ||
.replace(/\\\//gm, '__1') | ||
.replace(/\\\?/gm, '__2') | ||
.replace(/\\\(/gm, '__3') | ||
.replace(/\\\)/gm, '__4'); | ||
let html2 = html.replace(/class=["'][^"']*["']/gm, m => | ||
m | ||
.replace(/\:/gm, '__0') | ||
.replace(/\//gm, '__1') | ||
.replace(/\?/gm, '__2') | ||
.replace(/\(/gm, '__3') | ||
.replace(/\)/gm, '__4') | ||
); | ||
let res = dropcss({ | ||
css: css2, | ||
html: html2, | ||
}); | ||
// undo | ||
res.css = res.css | ||
.replace(/__0/gm, '\\:') | ||
.replace(/__1/gm, '\\/') | ||
.replace(/__2/gm, '\\?') | ||
.replace(/__3/gm, '\\(') | ||
.replace(/__4/gm, '\\)'); | ||
``` | ||
This performant work-around allows DropCSS to process Tailwind without issues \o/ and is easily adaptable to support other "interesting" cases. One thing to keep in mind is that `shouldDrop()` will be called with selectors containing the temp replacements rather than original selectors, so make sure to account for this if `shouldDrop()` is used to test against some whitelist. | ||
--- | ||
@@ -238,0 +297,0 @@ ### Caveats |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
61855
1591
300