From 5e9155dec1937577f71a1dd8a07d949118190c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20T=C3=B6rnqvist?= Date: Mon, 7 Feb 2022 15:14:58 +0100 Subject: [PATCH] Add support for pseudo class has --- dist/dropcss.cjs.js | 9 +- dist/dropcss.iife.js | 9 +- dist/dropcss.iife.min.js | 2 +- src/dropcss.js | 2 +- src/find.js | 5 + src/sel.js | 2 +- test/src/2-context-aware-unary-sel.js | 147 +++++++++++++++++++++++++- 7 files changed, 168 insertions(+), 8 deletions(-) diff --git a/dist/dropcss.cjs.js b/dist/dropcss.cjs.js index a22639e..b0807a7 100644 --- a/dist/dropcss.cjs.js +++ b/dist/dropcss.cjs.js @@ -481,7 +481,7 @@ function nth(a, b, pos) { return pos <= b && pos % a === bMod; } -const pseudoClasses = /not|is/; +const pseudoClasses = /not|is|has/; // assumes stripPseudos(sel); has already been called function parse(sel) { @@ -712,6 +712,11 @@ function find(m, ctx) { case 'is': res = find(val, {node: ctx.node, idx: val.length - 1}); break; + case 'has': + res = ctx.node.childNodes.some( + (node) => find(val, { node, idx: val.length - 1}) + ); + break; case 'first-child': res = tidx == 0; break; @@ -965,7 +970,7 @@ function postProc(out, shouldDrop, log, START) { const ATTRIBUTES = /\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i; -const pseudoAssertable = /:(?:first|last|nth|only|not|is)\b/; +const pseudoAssertable = /:(?:first|last|nth|only|not|is|has)\b/; const pseudoNonAssertableParenth = /:(?:lang)\([^)]*\)/g; diff --git a/dist/dropcss.iife.js b/dist/dropcss.iife.js index 3008669..8dce567 100644 --- a/dist/dropcss.iife.js +++ b/dist/dropcss.iife.js @@ -482,7 +482,7 @@ var dropcss = (function () { return pos <= b && pos % a === bMod; } - const pseudoClasses = /not|is/; + const pseudoClasses = /not|is|has/; // assumes stripPseudos(sel); has already been called function parse(sel) { @@ -713,6 +713,11 @@ var dropcss = (function () { case 'is': res = find(val, {node: ctx.node, idx: val.length - 1}); break; + case 'has': + res = ctx.node.childNodes.some( + (node) => find(val, { node, idx: val.length - 1}) + ); + break; case 'first-child': res = tidx == 0; break; @@ -966,7 +971,7 @@ var dropcss = (function () { const ATTRIBUTES = /\[([\w-]+)(?:(.?=)"?([^\]]*?)"?)?\]/i; - const pseudoAssertable = /:(?:first|last|nth|only|not|is)\b/; + const pseudoAssertable = /:(?:first|last|nth|only|not|is|has)\b/; const pseudoNonAssertableParenth = /:(?:lang)\([^)]*\)/g; diff --git a/dist/dropcss.iife.min.js b/dist/dropcss.iife.min.js index 4b575c8..cb779bc 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,"")}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)}}}(); +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|has/;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 k(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 E(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=!E(s,{node:t.node,idx:s.length-1});break;case"is":c=E(s,{node:t.node,idx:s.length-1});break;case"has":c=t.node.childNodes.some((e=>E(s,{node:e,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=k(a+1,s);break;case"nth-last-child":c=k(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=k(o._typeIdx+1,s);break;case"nth-last-of-type":u=i(l,d),c=k(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 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|has)\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 69a8dff..a20728c 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|is)\b/; +const pseudoAssertable = /:(?:first|last|nth|only|not|is|has)\b/; const pseudoNonAssertableParenth = /:(?:lang)\([^)]*\)/g; diff --git a/src/find.js b/src/find.js index d96588f..6002473 100644 --- a/src/find.js +++ b/src/find.js @@ -102,6 +102,11 @@ function find(m, ctx) { case 'is': res = find(val, {node: ctx.node, idx: val.length - 1}); break; + case 'has': + res = ctx.node.childNodes.some( + (node) => find(val, { node, idx: val.length - 1}) + ); + break; case 'first-child': res = tidx == 0; break; diff --git a/src/sel.js b/src/sel.js index 9396fe5..0fb6863 100644 --- a/src/sel.js +++ b/src/sel.js @@ -1,7 +1,7 @@ import { takeUntilMatchedClosing } from './css'; import { parseErr } from './err'; -const pseudoClasses = /not|is/ +const pseudoClasses = /not|is|has/ // assumes stripPseudos(sel); has already been called export function parse(sel) { diff --git a/test/src/2-context-aware-unary-sel.js b/test/src/2-context-aware-unary-sel.js index 93fb046..f1903e3 100644 --- a/test/src/2-context-aware-unary-sel.js +++ b/test/src/2-context-aware-unary-sel.js @@ -195,4 +195,149 @@ describe('Context-aware, unary selector', () => { assert.equal(out, ''); }); }); -}); \ No newline at end of file + + describe(':has()', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has(span) {a:b;}', + }); + assert.equal(out, ':has(span){a:b;}') + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has(span) {a:b;}', + }); + assert.equal(out, '');; + }); + }); + + describe(':has(#id)', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has(#a) {a:b;}', + }); + assert.equal(out, ':has(#a){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has(#a) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':has(.class)', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has(.a) {a:b;}', + }); + assert.equal(out, ':has(.a){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has(.a) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':has([attr])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo]) {a:b;}', + }); + assert.equal(out, ':has([foo]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + // todo: test [foo="val"], [foo='val'] + describe(':has([attr=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo=bar]) {a:b;}', + }); + assert.equal(out, ':has([foo=bar]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo=bar]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':has([attr*=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo*=a]) {a:b;}', + }); + assert.equal(out, ':has([foo*=a]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo*=c]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':has([attr^=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo^=b]) {a:b;}', + }); + assert.equal(out, ':has([foo^=b]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo^=c]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); + + describe(':has([attr$=value])', () => { + it('should retain present', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo$=r]) {a:b;}', + }); + assert.equal(out, ':has([foo$=r]){a:b;}'); + }); + + it('should drop absent', function() { + let {css: out} = dropcss({ + html: '
', + css: ':has([foo$=z]) {a:b;}', + }); + assert.equal(out, ''); + }); + }); +});