From e68f17c185bdb2f4c39c4f603e659c8449c61600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20T=C3=B6rnqvist?= Date: Mon, 7 Feb 2022 14:05:29 +0100 Subject: [PATCH 1/3] Add support for pseudo class is --- dist/dropcss.cjs.js | 15 ++- dist/dropcss.iife.js | 17 +++- dist/dropcss.iife.min.js | 2 +- src/dropcss.js | 6 +- src/find.js | 5 +- src/sel.js | 6 +- test/src/0-context-free-unary-sel.js | 145 +++++++++++++++++++++++++++ 7 files changed, 180 insertions(+), 16 deletions(-) diff --git a/dist/dropcss.cjs.js b/dist/dropcss.cjs.js index 21dab0f..295ab2d 100644 --- a/dist/dropcss.cjs.js +++ b/dist/dropcss.cjs.js @@ -1,5 +1,5 @@ /** -* Copyright (c) 2021, Leon Sorokin +* Copyright (c) 2022, Leon Sorokin * All rights reserved. (MIT Licensed) * * dropcss.js (DropCSS) @@ -481,6 +481,8 @@ function nth(a, b, pos) { return pos <= b && pos % a === bMod; } +const pseudoClasses = /not|is/; + // assumes stripPseudos(sel); has already been called function parse(sel) { const RE = { @@ -530,7 +532,7 @@ function parse(sel) { if (m[2] == '(') { let subsel = takeUntilMatchedClosing(sel, RE.PSEUDO.lastIndex, '(', ')'); RE.PSEUDO.lastIndex += subsel.length + 1; - m[2] = m[1] == 'not' ? parse(subsel) : subsel; + m[2] = pseudoClasses.test(m[1]) ? parse(subsel) : subsel; } toks.splice( @@ -705,8 +707,13 @@ function find(m, ctx) { switch (name) { case 'not': + debugger res = !find(val, {node: ctx.node, idx: val.length - 1}); break; + case 'is': + debugger + res = find(val, {node: ctx.node, idx: val.length - 1}); + break; case 'first-child': res = tidx == 0; break; @@ -960,7 +967,7 @@ function postProc(out, shouldDrop, log, START) { const ATTRIBUTES = /\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i; -const pseudoAssertable = /:(?:first|last|nth|only|not)\b/; +const pseudoAssertable = /:(?:first|last|nth|only|not|is)\b/; const pseudoNonAssertableParenth = /:(?:lang)\([^)]*\)/g; @@ -1070,7 +1077,7 @@ function dropcss(opts) { if (cleaned in tested) return tested[cleaned]; - +debugger return tested[cleaned] = (some(H.nodes, cleaned) || shouldDrop(s) !== true); } diff --git a/dist/dropcss.iife.js b/dist/dropcss.iife.js index 8cb96a2..5bd9d11 100644 --- a/dist/dropcss.iife.js +++ b/dist/dropcss.iife.js @@ -1,5 +1,5 @@ /** -* Copyright (c) 2021, Leon Sorokin +* Copyright (c) 2022, Leon Sorokin * All rights reserved. (MIT Licensed) * * dropcss.js (DropCSS) @@ -482,6 +482,8 @@ var dropcss = (function () { return pos <= b && pos % a === bMod; } + const pseudoClasses = /not|is/; + // assumes stripPseudos(sel); has already been called function parse(sel) { const RE = { @@ -531,7 +533,7 @@ var dropcss = (function () { if (m[2] == '(') { let subsel = takeUntilMatchedClosing(sel, RE.PSEUDO.lastIndex, '(', ')'); RE.PSEUDO.lastIndex += subsel.length + 1; - m[2] = m[1] == 'not' ? parse(subsel) : subsel; + m[2] = pseudoClasses.test(m[1]) ? parse(subsel) : subsel; } toks.splice( @@ -706,8 +708,13 @@ var dropcss = (function () { switch (name) { case 'not': + debugger res = !find(val, {node: ctx.node, idx: val.length - 1}); break; + case 'is': + debugger + res = find(val, {node: ctx.node, idx: val.length - 1}); + break; case 'first-child': res = tidx == 0; break; @@ -961,7 +968,7 @@ var dropcss = (function () { const ATTRIBUTES = /\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i; - const pseudoAssertable = /:(?:first|last|nth|only|not)\b/; + const pseudoAssertable = /:(?:first|last|nth|only|not|is)\b/; const pseudoNonAssertableParenth = /:(?:lang)\([^)]*\)/g; @@ -1071,7 +1078,7 @@ var dropcss = (function () { if (cleaned in tested) return tested[cleaned]; - + debugger return tested[cleaned] = (some(H.nodes, cleaned) || shouldDrop(s) !== true); } @@ -1091,4 +1098,4 @@ var dropcss = (function () { return dropcss; -}()); +})(); diff --git a/dist/dropcss.iife.min.js b/dist/dropcss.iife.min.js index 7e80177..4b575c8 100644 --- a/dist/dropcss.iife.min.js +++ b/dist/dropcss.iife.min.js @@ -1,2 +1,2 @@ /*! https://github.com/leeoniya/dropcss (v1.0.16) */ -var dropcss=function(){"use strict";function e(e,t,n){throw Error(e+' parser stopped here: "'+t.substring(n,n+100)+'"')}const t=new Set("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")),n=/]*>||]*>[\s\S]*?<\/script>|]*>[\s\S]*?<\/style>|]*>|]*>/gim,s={NAME:/\s*<([\w-]+)\s*/imy,ATTR:/\s*([\w-:]+)(?:="([^"]*)"|='([^']*)'|=([\w-]+))?\s*/imy,TAIL:/\s*(\/?>)\s*/imy,TEXT:/\s*[^<]*/my,CLOSE:/\s*<\/[\w-]+>\s*/imy},r=new Set;function l(e,t,n){return{tagName:t,attributes:n,classList:null!=n&&n.has("class")?new Set(n.get("class").split(/\s+/g)):r,parentNode:e,childNodes:[]}}const a=[];function i(e,t){if(null!=e){let n=e._ofTypes=e._ofTypes||{};if(!(t in n)){let s=0;n[t]=e.childNodes.filter((e=>{if(e.tagName==t)return e._typeIdx=s++,!0}))}return n[t]}return a}const c=/\s*\/\*[\s\S]*?\*\/\s*/gm,o=/\s*[>~+.#]\s*|\[[^\]]+\]|\s+/gm;const d=/:[a-z-]+\([^()]*\)/;function u(e,t,n,s){let r="",l=1;for(;e[t]==n?l++:e[t]==s&&l--,0!=l;)r+=e[t++];return r}function f(t){return function(t){const n={RULE_HEAD:/\s*([^{;]+?)\s*[{;]\s*/my,RULE_TAIL:/\s*([^}]*?)\s*\}/my,AT_TAIL:/\s*\}/my,RULE_FULL:/\s*([^{]*?)\{([^}]+?)\}/my};let s,r=0,l=0,a=[];function i(e){l=e.lastIndex;for(let e in n)n[e].lastIndex=l}function c(){if(r>0&&(s=n.AT_TAIL.exec(t),null!=s))return r--,a.push(2),void i(n.AT_TAIL);if(s=n.RULE_HEAD.exec(t),null!=s){let e=s[1];if(i(n.RULE_HEAD),"@"==e[0])switch(e.match(/@[a-z-]+/)[0]){case"@media":case"@supports":case"@document":r++,a.push(1,e);break;case"@import":case"@charset":case"@namespace":a.push(5,e+";");break;default:r++;let n=u(t,l,"{","}");i({lastIndex:l+n.length}),a.push(1,e,5,n)}else a.push(3,function(e){let t=e.split(/\s*,\s*/gm);return t.push(t.map((e=>function(e){let t=e.length;for(;(e=e.replace(d,"")).length!=t;)t=e.length;return e.replace(/:?:[a-z-]+/gm,"")}(e).trim().replace(o,((e,t)=>(e=e.trim(),0==t?e:"."==e||"#"==e?"`"+e:e.length>1?"`"+e.replace(/['"]/gm,""):"`"))).split(/`+/gm)))),t}(s[1])),s=n.RULE_TAIL.exec(t),a.push(4,s[1]),i(n.RULE_TAIL)}else l=t.length}let f=l;for(;t.length>l;)c(),f===l&&e("css",t,l),f=l;return a}(t=t.replace(c,""))}function h(e){return e.replace(/@[a-z-]+[^{]+\{\s*\}/gm,"")}function p(t){const n={IDENT:/([\w*-]+)/iy,ATTR:/([\w-]+)(?:(.?=)["']?([^\]]*?)["']?)?\]/iy,PSEUDO:/([\w-]+)(\()?/iy,MODE:/\s*[:.#\[]\s*/iy,COMB:/\s*[>~+]\s*|\s+/iy};let s,r=0,l=[],a=-1;function i(e){r=e.lastIndex;for(let e in n)n[e].lastIndex=r}function c(){let e=!1;if(s=n.COMB.exec(t)){e=!0;let t=s[0].trim();""==t&&(t=" "),l.push(t),i(n.COMB),a=l.length-1}else if(s=n.MODE.exec(t)){e=!0;let r=s[0].trim();if(i(n.MODE),":"==r){if(s=n.PSEUDO.exec(t),"("==s[2]){let e=u(t,n.PSEUDO.lastIndex,"(",")");n.PSEUDO.lastIndex+=e.length+1,s[2]="not"==s[1]?p(e):e}l.splice(a+1,0,s[2],s[1],r),i(n.PSEUDO)}else"["==r?(s=n.ATTR.exec(t),l.splice(a+1,0,s[3],s[2],s[1],r),i(n.ATTR)):(s=n.IDENT.exec(t),l.push(s[1],r),i(n.IDENT))}else(s=n.IDENT.exec(t))&&(e=!0,l.push(s[1],"_"),i(n.IDENT));return e}let o=r;for(;t.length>r;)c(),o===r&&e("sel",t,r),o=r;return l}const x=/^([+-]?\d*)?n([+-]\d+)?$/;function g(e,t){return t==e.tagName||"*"==t}function m(e,t,n,s){s=s||"=";let r=e.attributes;if(r.has(t)){let e=r.get(t);switch(s){case"=":return null==n||n==e;case"*=":return-1!=e.indexOf(n);case"^=":return e.startsWith(n);case"$=":return e.endsWith(n);case"~=":return n==e||e.startsWith(n+" ")||e.endsWith(" "+n)||-1!=e.indexOf(" "+n+" ")}}return!1}function y(e,t){return e.classList.has(t)}function b(e,t){let n;if("odd"==t)n=e%2==1;else if("even"==t)n=e%2==0;else if(/^\d+$/.test(t))n=e==+t;else{let s=function(e){let t=x.exec(e);if(null!=t){let e=t[1],n=t[2];return e=null==e||"+"==e?1:"-"==e?-1:+e,n=null==n?0:+n,[e,n]}return[0,0]}(t);n=function(e,t,n){if(0>t&&0>=e)return!1;if(-1===e)return t>=n;if(0===e)return n===t;if(1===e)return 0>t||n>=t;let s=t%e;return 0>s&&(s+=e),e>1?n>=t&&n%e===s:(e*=-1,t>=n&&n%e===s)}(s[0],s[1],e)}return n}function E(e,t){let n,s,r,l,a,c;for(;t.idx>-1;){switch(e[t.idx]){case"_":n=e[--t.idx],c=g(t.node,n),t.idx--;break;case"#":s=e[--t.idx],c=m(t.node,"id",s,"="),t.idx--;break;case".":n=e[--t.idx],c=y(t.node,n),t.idx--;break;case"[":n=e[--t.idx],r=e[--t.idx],s=e[--t.idx],c=m(t.node,n,s,r),t.idx--;break;case":":n=e[--t.idx],s=e[--t.idx];let o=t.node,d=o.tagName;a=o.idx,l=o.parentNode;let u,f=l?l.childNodes.length:1;switch(n){case"not":c=!E(s,{node:t.node,idx:s.length-1});break;case"first-child":c=0==a;break;case"last-child":c=a==f-1;break;case"only-child":c=1==f;break;case"nth-child":c=b(a+1,s);break;case"nth-last-child":c=b(f-a,s);break;case"first-of-type":u=i(l,d),c=0==o._typeIdx;break;case"last-of-type":u=i(l,d),c=o._typeIdx==u.length-1;break;case"only-of-type":u=i(l,d),c=1==u.length;break;case"nth-of-type":u=i(l,d),c=b(o._typeIdx+1,s);break;case"nth-last-of-type":u=i(l,d),c=b(u.length-o._typeIdx,s)}t.idx--;break;case" ":for(a=--t.idx,c=!1;!c&&(l=t.node.parentNode,null!=l);)t.idx=a,t.node=l,c=E(e,t);break;case">":t.idx--,l=t.node.parentNode,null!=l?(t.node=l,c=E(e,t)):c=!1;break;case"+":t.idx--,l=t.node.parentNode,null!=l&&t.node.idx>0?(t.node=l.childNodes[t.node.idx-1],c=E(e,t)):c=!1;break;case"~":if(t.idx--,c=!1,a=t.node.idx,l=t.node.parentNode,null!=l&&a>0)for(let n=0;a>n&&!c;n++)t.node=l.childNodes[n],c=E(e,t)}if(!c)break}return c}function T(e,t){return e.some((e=>E(t,{idx:t.length-1,node:e})))}const k=(e,t)=>T(e,Array.isArray(t)?t:p(t));function w(e,t,n,s){return e.slice(0,t)+s+e.slice(t+n)}function I(e,t,n,s,r){r=r||"";for(let l=t.length-1;l>-1;l--){let a=t[l];n.has(a[2])||!0!==s(r+a[2])||(e=w(e,a[0],a[1],""))}return e}const N=/([{};])\s*(--[\w-]+)\s*:\s*([^;}]+);?\s*/gm,A=/var\(([\w-]+)\)/gm,L=/\s*,\s*/gm;function S(e){return e.trim().replace(/'|"/gm,"").split(L)}const _=/\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i,D=/:(?:first|last|nth|only|not)\b/,O=/:(?:lang)\([^)]*\)/g;function R(e){return e.replace(O,"").replace(/:?:[a-z-]+/gm,(e=>e.startsWith("::")||!D.test(e)?"":e)).replace(/:[a-z-]+\(\)/gm,"")}const U=()=>!0;return function(a){const i=(a=>{let i=function(n){let r,l,a=0,i=[];function c(e){a=e.lastIndex;for(let e in s)s[e].lastIndex=a}function o(){if(r=s.CLOSE.exec(n),null!=r)return c(s.CLOSE),void i.push(3);if(r=s.NAME.exec(n),null!=r){c(s.NAME);let e,a=r[1];for(i.push(1,a);l=s.ATTR.exec(n);)c(s.ATTR),e=e||new Map,e.set(l[1],(l[2]||l[3]||l[4]||"").trim());return e&&i.push(2,e),l=s.TAIL.exec(n),(t.has(a)||"/>"==l[1])&&i.push(3),void c(s.TAIL)}r=s.TEXT.exec(n),null!=r&&c(s.TEXT)}let d=a;for(;n.length>a;)o(),d===a&&e("html",n,a),d=a;return c({lastIndex:0}),i}(a=a.replace(n,""));const c={nodes:[],tag:new Set(["*"]),class:new Set,attr:new Set};return function(e,t){let n,s=l(null,"root",r);for(let a=0;e.length>a;a++)switch(e[a]){case 1:let i=e[++a],c=r;2===e[a+1]&&(a+=2,c=e[a]),n=s.childNodes.length,s.childNodes.push(s=l(s,i,c)),t(s,n);break;case 3:s=s.parentNode}}(i,((e,t)=>function(e,t,n){e.idx=t;let s=e.attributes;n.tag.add(e.tagName),e.classList.forEach((e=>n.class.add(e))),s.has("id")&&n.attr.add("[id="+s.get("id")+"]"),s.has("type")&&n.attr.add("[type="+s.get("type")+"]"),n.nodes.push(e)}(e,t,c))),c})(a.html),c=a.shouldDrop||U,o=a.didRetain||U;let d=f(a.css),p={};for(let e=0;d.length>e;e++){if(3!==d[e])continue;let t=d[e+1],n=t[t.length-1];e++;for(let e=0;n.length>e;e++){let s=n[e];e:for(let n=0;s.length>n;n++){let r,l=s[n],a=!1;if(""!=l){if(l in p)a=p[l];else switch(l[0]){case"#":r=l.substr(1),p[l]=a=i.attr.has("[id="+r+"]");break;case".":r=l.substr(1),p[l]=a=i.class.has(r);break;case"[":if(l.startsWith("[type="))p[l]=a=i.attr.has(l);else{let e=l.match(_);p[l]=a=i.nodes.some((t=>m(t,e[1],e[3],e[2])))}break;default:p[l]=a=i.tag.has(l)}if(!a){!0===c(t[e])?t[e]=null:p[t[e]]=!0;break e}}}}}for(let e=0;d.length>e;e++)3===d[e]&&(e++,d[e]=d[e].filter((e=>{if("string"==typeof e){if(e in p)return p[e];let t=R(e);return""==t||(t in p?p[t]:p[t]=k(i.nodes,t)||!0!==c(e))}return!1})));let x=function(e,t){let n="",s=0;for(let r=0;e.length>r;r++)switch(e[r]){case 3:let l=e[++r];s=l.length,s>0&&(l.forEach(t),n+=l.join());break;case 4:s>0&&(n+="{"+e[++r]+"}");break;case 1:n+=e[++r]+"{";break;case 2:n+="}";break;case 5:n+=e[++r]}return h(n)}(d,o);return x=function(e,t){let n=function(e){let t,n={};for(;A.test(e);){for(;t=N.exec(e);)n[t[2]]=t[3];e=e.replace(A,((e,t)=>A.test(n[t])?e:n[t]))}return e}(e).replace(N,((e,t)=>t));return e=function(e,t,n){let s,r=[],l=/@(?:-\w+-)?keyframes\s+([\w-]+)\s*\{/gm;for(;s=l.exec(e);){let t=u(e,l.lastIndex,"{","}");r.push([s.index,s[0].length+t.length+1,s[1]])}let a=new Set,i=/animation(?:-name)?:([^;!}]+)/gm;for(;s=i.exec(t);)s[1].trim().split(L).forEach((e=>{let t=e.match(/^\S+/)[0];/^-?[\d.]+m?s/.test(t)&&(t=e.match(/\S+$/)[0]),a.add(t)}));return I(e,r,a,n,"@keyframes ")}(e,n,t),e=function(e,t,n){let s,r=/@font-face[^}]+\}+/gm,l=[];for(;s=r.exec(e);)l.push([s.index,s[0].length]);let a,i=/font-family:([^;!}]+)/,c=0;for(;s=r.exec(t);)a=i.exec(s[0]),l[c++].push(S(a[1])[0]);let o=new Set,d=/@font-face[^}]+\}+|font-family:([^;!}]+)/gm;for(;s=d.exec(t);)"@"!==s[0][0]&&S(s[1]).forEach((e=>o.add(e)));let u,f=/font:([^;!}]+)/gm,h=/\s*(?:['"][\w- ]+['"]|[\w-]+)\s*(?:,|$)/gm;for(;s=f.exec(t);){for(u="";a=h.exec(s[1]);)u+=a[0];S(u).forEach((e=>o.add(e)))}return I(e,l,o,n,"@font-face ")}(e,n,t),(e=function(e){let t=e;do{t=(e=t).replace(N,((t,n,s)=>-1!=e.indexOf("var("+s+")")?t:n))}while(t!=e);return t}(e)).replace(/[^{}]+\{\s*\}/gm,"")}(x,c),{css:h(x)}}}(); +var dropcss=function(){"use strict";function e(e,t,n){throw Error(e+' parser stopped here: "'+t.substring(n,n+100)+'"')}const t=new Set("area base br col command embed hr img input keygen link meta param source track wbr".split(" ")),n=/]*>||]*>[\s\S]*?<\/script>|]*>[\s\S]*?<\/style>|]*>|]*>/gim,s={NAME:/\s*<([\w-]+)\s*/imy,ATTR:/\s*([\w-:]+)(?:="([^"]*)"|='([^']*)'|=([\w-]+))?\s*/imy,TAIL:/\s*(\/?>)\s*/imy,TEXT:/\s*[^<]*/my,CLOSE:/\s*<\/[\w-]+>\s*/imy},r=new Set;function l(e,t,n){return{tagName:t,attributes:n,classList:null!=n&&n.has("class")?new Set(n.get("class").split(/\s+/g)):r,parentNode:e,childNodes:[]}}const a=[];function i(e,t){if(null!=e){let n=e._ofTypes=e._ofTypes||{};if(!(t in n)){let s=0;n[t]=e.childNodes.filter((e=>{if(e.tagName==t)return e._typeIdx=s++,!0}))}return n[t]}return a}const c=/\s*\/\*[\s\S]*?\*\/\s*/gm,o=/\s*[>~+.#]\s*|\[[^\]]+\]|\s+/gm;const d=/:[a-z-]+\([^()]*\)/;function u(e,t,n,s){let r="",l=1;for(;e[t]==n?l++:e[t]==s&&l--,0!=l;)r+=e[t++];return r}function f(t){return function(t){const n={RULE_HEAD:/\s*([^{;]+?)\s*[{;]\s*/my,RULE_TAIL:/\s*([^}]*?)\s*\}/my,AT_TAIL:/\s*\}/my,RULE_FULL:/\s*([^{]*?)\{([^}]+?)\}/my};let s,r=0,l=0,a=[];function i(e){l=e.lastIndex;for(let e in n)n[e].lastIndex=l}function c(){if(r>0&&(s=n.AT_TAIL.exec(t),null!=s))return r--,a.push(2),void i(n.AT_TAIL);if(s=n.RULE_HEAD.exec(t),null!=s){let e=s[1];if(i(n.RULE_HEAD),"@"==e[0])switch(e.match(/@[a-z-]+/)[0]){case"@media":case"@supports":case"@document":r++,a.push(1,e);break;case"@import":case"@charset":case"@namespace":a.push(5,e+";");break;default:r++;let n=u(t,l,"{","}");i({lastIndex:l+n.length}),a.push(1,e,5,n)}else a.push(3,function(e){let t=e.split(/\s*,\s*/gm);return t.push(t.map((e=>function(e){let t=e.length;for(;(e=e.replace(d,"")).length!=t;)t=e.length;return e.replace(/:?:[a-z-]+/gm,"")}(e).trim().replace(o,((e,t)=>(e=e.trim(),0==t?e:"."==e||"#"==e?"`"+e:e.length>1?"`"+e.replace(/['"]/gm,""):"`"))).split(/`+/gm)))),t}(s[1])),s=n.RULE_TAIL.exec(t),a.push(4,s[1]),i(n.RULE_TAIL)}else l=t.length}let f=l;for(;t.length>l;)c(),f===l&&e("css",t,l),f=l;return a}(t=t.replace(c,""))}function h(e){return e.replace(/@[a-z-]+[^{]+\{\s*\}/gm,"")}const p=/not|is/;function x(t){const n={IDENT:/([\w*-]+)/iy,ATTR:/([\w-]+)(?:(.?=)["']?([^\]]*?)["']?)?\]/iy,PSEUDO:/([\w-]+)(\()?/iy,MODE:/\s*[:.#\[]\s*/iy,COMB:/\s*[>~+]\s*|\s+/iy};let s,r=0,l=[],a=-1;function i(e){r=e.lastIndex;for(let e in n)n[e].lastIndex=r}function c(){let e=!1;if(s=n.COMB.exec(t)){e=!0;let t=s[0].trim();""==t&&(t=" "),l.push(t),i(n.COMB),a=l.length-1}else if(s=n.MODE.exec(t)){e=!0;let r=s[0].trim();if(i(n.MODE),":"==r){if(s=n.PSEUDO.exec(t),"("==s[2]){let e=u(t,n.PSEUDO.lastIndex,"(",")");n.PSEUDO.lastIndex+=e.length+1,s[2]=p.test(s[1])?x(e):e}l.splice(a+1,0,s[2],s[1],r),i(n.PSEUDO)}else"["==r?(s=n.ATTR.exec(t),l.splice(a+1,0,s[3],s[2],s[1],r),i(n.ATTR)):(s=n.IDENT.exec(t),l.push(s[1],r),i(n.IDENT))}else(s=n.IDENT.exec(t))&&(e=!0,l.push(s[1],"_"),i(n.IDENT));return e}let o=r;for(;t.length>r;)c(),o===r&&e("sel",t,r),o=r;return l}const g=/^([+-]?\d*)?n([+-]\d+)?$/;function m(e,t){return t==e.tagName||"*"==t}function y(e,t,n,s){s=s||"=";let r=e.attributes;if(r.has(t)){let e=r.get(t);switch(s){case"=":return null==n||n==e;case"*=":return-1!=e.indexOf(n);case"^=":return e.startsWith(n);case"$=":return e.endsWith(n);case"~=":return n==e||e.startsWith(n+" ")||e.endsWith(" "+n)||-1!=e.indexOf(" "+n+" ")}}return!1}function b(e,t){return e.classList.has(t)}function E(e,t){let n;if("odd"==t)n=e%2==1;else if("even"==t)n=e%2==0;else if(/^\d+$/.test(t))n=e==+t;else{let s=function(e){let t=g.exec(e);if(null!=t){let e=t[1],n=t[2];return e=null==e||"+"==e?1:"-"==e?-1:+e,n=null==n?0:+n,[e,n]}return[0,0]}(t);n=function(e,t,n){if(0>t&&0>=e)return!1;if(-1===e)return t>=n;if(0===e)return n===t;if(1===e)return 0>t||n>=t;let s=t%e;return 0>s&&(s+=e),e>1?n>=t&&n%e===s:(e*=-1,t>=n&&n%e===s)}(s[0],s[1],e)}return n}function k(e,t){let n,s,r,l,a,c;for(;t.idx>-1;){switch(e[t.idx]){case"_":n=e[--t.idx],c=m(t.node,n),t.idx--;break;case"#":s=e[--t.idx],c=y(t.node,"id",s,"="),t.idx--;break;case".":n=e[--t.idx],c=b(t.node,n),t.idx--;break;case"[":n=e[--t.idx],r=e[--t.idx],s=e[--t.idx],c=y(t.node,n,s,r),t.idx--;break;case":":n=e[--t.idx],s=e[--t.idx];let o=t.node,d=o.tagName;a=o.idx,l=o.parentNode;let u,f=l?l.childNodes.length:1;switch(n){case"not":c=!k(s,{node:t.node,idx:s.length-1});break;case"is":c=k(s,{node:t.node,idx:s.length-1});break;case"first-child":c=0==a;break;case"last-child":c=a==f-1;break;case"only-child":c=1==f;break;case"nth-child":c=E(a+1,s);break;case"nth-last-child":c=E(f-a,s);break;case"first-of-type":u=i(l,d),c=0==o._typeIdx;break;case"last-of-type":u=i(l,d),c=o._typeIdx==u.length-1;break;case"only-of-type":u=i(l,d),c=1==u.length;break;case"nth-of-type":u=i(l,d),c=E(o._typeIdx+1,s);break;case"nth-last-of-type":u=i(l,d),c=E(u.length-o._typeIdx,s)}t.idx--;break;case" ":for(a=--t.idx,c=!1;!c&&(l=t.node.parentNode,null!=l);)t.idx=a,t.node=l,c=k(e,t);break;case">":t.idx--,l=t.node.parentNode,null!=l?(t.node=l,c=k(e,t)):c=!1;break;case"+":t.idx--,l=t.node.parentNode,null!=l&&t.node.idx>0?(t.node=l.childNodes[t.node.idx-1],c=k(e,t)):c=!1;break;case"~":if(t.idx--,c=!1,a=t.node.idx,l=t.node.parentNode,null!=l&&a>0)for(let n=0;a>n&&!c;n++)t.node=l.childNodes[n],c=k(e,t)}if(!c)break}return c}function T(e,t){return e.some((e=>k(t,{idx:t.length-1,node:e})))}const w=(e,t)=>T(e,Array.isArray(t)?t:x(t));function I(e,t,n,s){return e.slice(0,t)+s+e.slice(t+n)}function N(e,t,n,s,r){r=r||"";for(let l=t.length-1;l>-1;l--){let a=t[l];n.has(a[2])||!0!==s(r+a[2])||(e=I(e,a[0],a[1],""))}return e}const A=/([{};])\s*(--[\w-]+)\s*:\s*([^;}]+);?\s*/gm,L=/var\(([\w-]+)\)/gm,S=/\s*,\s*/gm;function _(e){return e.trim().replace(/'|"/gm,"").split(S)}const D=/\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i,O=/:(?:first|last|nth|only|not|is)\b/,R=/:(?:lang)\([^)]*\)/g;function U(e){return e.replace(R,"").replace(/:?:[a-z-]+/gm,(e=>e.startsWith("::")||!O.test(e)?"":e)).replace(/:[a-z-]+\(\)/gm,"")}const M=()=>!0;return function(a){const i=(a=>{let i=function(n){let r,l,a=0,i=[];function c(e){a=e.lastIndex;for(let e in s)s[e].lastIndex=a}function o(){if(r=s.CLOSE.exec(n),null!=r)return c(s.CLOSE),void i.push(3);if(r=s.NAME.exec(n),null!=r){c(s.NAME);let e,a=r[1];for(i.push(1,a);l=s.ATTR.exec(n);)c(s.ATTR),e=e||new Map,e.set(l[1],(l[2]||l[3]||l[4]||"").trim());return e&&i.push(2,e),l=s.TAIL.exec(n),(t.has(a)||"/>"==l[1])&&i.push(3),void c(s.TAIL)}r=s.TEXT.exec(n),null!=r&&c(s.TEXT)}let d=a;for(;n.length>a;)o(),d===a&&e("html",n,a),d=a;return c({lastIndex:0}),i}(a=a.replace(n,""));const c={nodes:[],tag:new Set(["*"]),class:new Set,attr:new Set};return function(e,t){let n,s=l(null,"root",r);for(let a=0;e.length>a;a++)switch(e[a]){case 1:let i=e[++a],c=r;2===e[a+1]&&(a+=2,c=e[a]),n=s.childNodes.length,s.childNodes.push(s=l(s,i,c)),t(s,n);break;case 3:s=s.parentNode}}(i,((e,t)=>function(e,t,n){e.idx=t;let s=e.attributes;n.tag.add(e.tagName),e.classList.forEach((e=>n.class.add(e))),s.has("id")&&n.attr.add("[id="+s.get("id")+"]"),s.has("type")&&n.attr.add("[type="+s.get("type")+"]"),n.nodes.push(e)}(e,t,c))),c})(a.html),c=a.shouldDrop||M,o=a.didRetain||M;let d=f(a.css),p={};for(let e=0;d.length>e;e++){if(3!==d[e])continue;let t=d[e+1],n=t[t.length-1];e++;for(let e=0;n.length>e;e++){let s=n[e];e:for(let n=0;s.length>n;n++){let r,l=s[n],a=!1;if(""!=l){if(l in p)a=p[l];else switch(l[0]){case"#":r=l.substr(1),p[l]=a=i.attr.has("[id="+r+"]");break;case".":r=l.substr(1),p[l]=a=i.class.has(r);break;case"[":if(l.startsWith("[type="))p[l]=a=i.attr.has(l);else{let e=l.match(D);p[l]=a=i.nodes.some((t=>y(t,e[1],e[3],e[2])))}break;default:p[l]=a=i.tag.has(l)}if(!a){!0===c(t[e])?t[e]=null:p[t[e]]=!0;break e}}}}}for(let e=0;d.length>e;e++)3===d[e]&&(e++,d[e]=d[e].filter((e=>{if("string"==typeof e){if(e in p)return p[e];let t=U(e);return""==t||(t in p?p[t]:p[t]=w(i.nodes,t)||!0!==c(e))}return!1})));let x=function(e,t){let n="",s=0;for(let r=0;e.length>r;r++)switch(e[r]){case 3:let l=e[++r];s=l.length,s>0&&(l.forEach(t),n+=l.join());break;case 4:s>0&&(n+="{"+e[++r]+"}");break;case 1:n+=e[++r]+"{";break;case 2:n+="}";break;case 5:n+=e[++r]}return h(n)}(d,o);return x=function(e,t){let n=function(e){let t,n={};for(;L.test(e);){for(;t=A.exec(e);)n[t[2]]=t[3];e=e.replace(L,((e,t)=>L.test(n[t])?e:n[t]))}return e}(e).replace(A,((e,t)=>t));return e=function(e,t,n){let s,r=[],l=/@(?:-\w+-)?keyframes\s+([\w-]+)\s*\{/gm;for(;s=l.exec(e);){let t=u(e,l.lastIndex,"{","}");r.push([s.index,s[0].length+t.length+1,s[1]])}let a=new Set,i=/animation(?:-name)?:([^;!}]+)/gm;for(;s=i.exec(t);)s[1].trim().split(S).forEach((e=>{let t=e.match(/^\S+/)[0];/^-?[\d.]+m?s/.test(t)&&(t=e.match(/\S+$/)[0]),a.add(t)}));return N(e,r,a,n,"@keyframes ")}(e,n,t),e=function(e,t,n){let s,r=/@font-face[^}]+\}+/gm,l=[];for(;s=r.exec(e);)l.push([s.index,s[0].length]);let a,i=/font-family:([^;!}]+)/,c=0;for(;s=r.exec(t);)a=i.exec(s[0]),l[c++].push(_(a[1])[0]);let o=new Set,d=/@font-face[^}]+\}+|font-family:([^;!}]+)/gm;for(;s=d.exec(t);)"@"!==s[0][0]&&_(s[1]).forEach((e=>o.add(e)));let u,f=/font:([^;!}]+)/gm,h=/\s*(?:['"][\w- ]+['"]|[\w-]+)\s*(?:,|$)/gm;for(;s=f.exec(t);){for(u="";a=h.exec(s[1]);)u+=a[0];_(u).forEach((e=>o.add(e)))}return N(e,l,o,n,"@font-face ")}(e,n,t),(e=function(e){let t=e;do{t=(e=t).replace(A,((t,n,s)=>-1!=e.indexOf("var("+s+")")?t:n))}while(t!=e);return t}(e)).replace(/[^{}]+\{\s*\}/gm,"")}(x,c),{css:h(x)}}}(); diff --git a/src/dropcss.js b/src/dropcss.js index 3b1c7e2..c583a09 100644 --- a/src/dropcss.js +++ b/src/dropcss.js @@ -6,7 +6,7 @@ import { LOGGING } from './env'; const ATTRIBUTES = /\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i; -const pseudoAssertable = /:(?:first|last|nth|only|not)\b/; +const pseudoAssertable = /:(?:first|last|nth|only|not|is)\b/; const pseudoNonAssertableParenth = /:(?:lang)\([^)]*\)/g; @@ -128,7 +128,7 @@ export default function dropcss(opts) { if (cleaned in tested) return tested[cleaned]; - +debugger return tested[cleaned] = (some(H.nodes, cleaned) || shouldDrop(s) !== true); } @@ -150,4 +150,4 @@ export default function dropcss(opts) { return { css: stripEmptyAts(out), }; -} \ No newline at end of file +} diff --git a/src/find.js b/src/find.js index 0c85f9e..d96588f 100644 --- a/src/find.js +++ b/src/find.js @@ -99,6 +99,9 @@ function find(m, ctx) { case 'not': res = !find(val, {node: ctx.node, idx: val.length - 1}); break; + case 'is': + res = find(val, {node: ctx.node, idx: val.length - 1}); + break; case 'first-child': res = tidx == 0; break; @@ -200,4 +203,4 @@ function _some(nodes, m) { export const some = (nodes, sel) => { return _some(nodes, Array.isArray(sel) ? sel : parseSel(sel)); -}; \ No newline at end of file +}; diff --git a/src/sel.js b/src/sel.js index 7f446e6..9396fe5 100644 --- a/src/sel.js +++ b/src/sel.js @@ -1,6 +1,8 @@ import { takeUntilMatchedClosing } from './css'; import { parseErr } from './err'; +const pseudoClasses = /not|is/ + // assumes stripPseudos(sel); has already been called export function parse(sel) { const RE = { @@ -50,7 +52,7 @@ export function parse(sel) { if (m[2] == '(') { let subsel = takeUntilMatchedClosing(sel, RE.PSEUDO.lastIndex, '(', ')'); RE.PSEUDO.lastIndex += subsel.length + 1; - m[2] = m[1] == 'not' ? parse(subsel) : subsel; + m[2] = pseudoClasses.test(m[1]) ? parse(subsel) : subsel; } toks.splice( @@ -128,4 +130,4 @@ export function parseNth(expr) { } return [0, 0]; -} \ No newline at end of file +} diff --git a/test/src/0-context-free-unary-sel.js b/test/src/0-context-free-unary-sel.js index f1fd796..27821bc 100644 --- a/test/src/0-context-free-unary-sel.js +++ b/test/src/0-context-free-unary-sel.js @@ -346,5 +346,150 @@ describe('Context-free, unary selector', () => { }); }); + describe(':is()', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is(div) {a:b;}', + }); + assert.equal(out, ':is(div){a:b;}') + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is(span) {a:b;}', + }); + assert.equal(out, '');; + }); + }); + + describe(':is(#id)', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is(#a) {a:b;}', + }); + assert.equal(out, ':is(#a){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is(#b) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':is(.class)', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is(.a) {a:b;}', + }); + assert.equal(out, ':is(.a){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is(.b) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':is([attr])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo]) {a:b;}', + }); + assert.equal(out, ':is([foo]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([bar]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + // todo: test [foo="val"], [foo='val'] + describe(':is([attr=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo=bar]) {a:b;}', + }); + assert.equal(out, ':is([foo=bar]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo=cow]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':is([attr*=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo*=a]) {a:b;}', + }); + assert.equal(out, ':is([foo*=a]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo*=c]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':is([attr^=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo^=b]) {a:b;}', + }); + assert.equal(out, ':is([foo^=b]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo^=c]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':is([attr$=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo$=r]) {a:b;}', + }); + assert.equal(out, ':is([foo$=r]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':is([foo$=z]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + // *-child assertions dont make to test in a unary selector since all root elements will be first/last/only "children" }); From c89ef13c2f129b61cba934a8851e1aee7303465b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20T=C3=B6rnqvist?= Date: Mon, 7 Feb 2022 14:05:59 +0100 Subject: [PATCH 2/3] Add gitignore, npmrc --- .gitignore | 3 +++ .npmrc | 1 + dist/dropcss.cjs.js | 4 +--- dist/dropcss.iife.js | 4 +--- src/dropcss.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 .gitignore create mode 100644 .npmrc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..131da0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.nyc_output +node_modules +package-lock.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/dist/dropcss.cjs.js b/dist/dropcss.cjs.js index 295ab2d..a22639e 100644 --- a/dist/dropcss.cjs.js +++ b/dist/dropcss.cjs.js @@ -707,11 +707,9 @@ function find(m, ctx) { switch (name) { case 'not': - debugger res = !find(val, {node: ctx.node, idx: val.length - 1}); break; case 'is': - debugger res = find(val, {node: ctx.node, idx: val.length - 1}); break; case 'first-child': @@ -1077,7 +1075,7 @@ function dropcss(opts) { if (cleaned in tested) return tested[cleaned]; -debugger + return tested[cleaned] = (some(H.nodes, cleaned) || shouldDrop(s) !== true); } diff --git a/dist/dropcss.iife.js b/dist/dropcss.iife.js index 5bd9d11..3008669 100644 --- a/dist/dropcss.iife.js +++ b/dist/dropcss.iife.js @@ -708,11 +708,9 @@ var dropcss = (function () { switch (name) { case 'not': - debugger res = !find(val, {node: ctx.node, idx: val.length - 1}); break; case 'is': - debugger res = find(val, {node: ctx.node, idx: val.length - 1}); break; case 'first-child': @@ -1078,7 +1076,7 @@ var dropcss = (function () { if (cleaned in tested) return tested[cleaned]; - debugger + return tested[cleaned] = (some(H.nodes, cleaned) || shouldDrop(s) !== true); } diff --git a/src/dropcss.js b/src/dropcss.js index c583a09..69a8dff 100644 --- a/src/dropcss.js +++ b/src/dropcss.js @@ -128,7 +128,7 @@ export default function dropcss(opts) { if (cleaned in tested) return tested[cleaned]; -debugger + return tested[cleaned] = (some(H.nodes, cleaned) || shouldDrop(s) !== true); } From bb83918cb33fb60dca64d1bb8e3373898ac330ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20T=C3=B6rnqvist?= Date: Mon, 7 Feb 2022 14:38:34 +0100 Subject: [PATCH 3/3] Add multi select test for pseudo class is --- test/src/1-context-free-multi-sel.js | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/src/1-context-free-multi-sel.js b/test/src/1-context-free-multi-sel.js index 41f3f44..7102b2c 100644 --- a/test/src/1-context-free-multi-sel.js +++ b/test/src/1-context-free-multi-sel.js @@ -211,6 +211,50 @@ describe('Context-free, multi selector', () => { }); }); + describe(':is(.class)', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: 'div:is(.bar) {a:b;}', + }); + assert.equal(out, 'div:is(.bar){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: 'div:is(.bar) {a:b;}', + }); + assert.equal(out, ''); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '', + css: 'div:is(.foo) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':not(:nth-child(n+3))', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '

', + css: 'p:is(:nth-child(n+3)) {a:b;}', + }); + assert.equal(out, 'p:is(:nth-child(n+3)){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '

', + css: 'p:is(:nth-child(n+5)) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + // TODO: rest that match the non-:not() versions // *-child assertions dont make to test in a unary selector since all root elements will be first/last/only "children"