Comparing version 4.1.3 to 5.0.0
#!/usr/bin/env node | ||
"use strict";var e=require("commander"),t=require("fs"),s=require("glob"),r=require("path"),o=require("postcss"),i=require("postcss-selector-parser"),n=require("util");function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function c(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var l=c(t),u=a(t),d=a(s),f=a(r),h=c(o),p=a(i),m="4.1.2",g="Remove unused css selectors";function v(e,t){t&&t.forEach(e.add,e)}class y{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?v(this.undetermined,e):e instanceof y?(v(this.undetermined,e.undetermined),v(this.attrNames,e.attrNames),v(this.attrValues,e.attrValues),v(this.classes,e.classes),v(this.ids,e.ids),v(this.tags,e.tags)):(v(this.undetermined,e.undetermined),e.attributes&&(v(this.attrNames,e.attributes.names),v(this.attrValues,e.attributes.values)),v(this.classes,e.classes),v(this.ids,e.ids),v(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue((t=>t.startsWith(e)))}hasAttrSuffix(e){return this.someAttrValue((t=>t.endsWith(e)))}hasAttrSubstr(e){return e.trim().split(" ").every((e=>this.someAttrValue((t=>t.includes(e)))))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const b=["*",":root",":after",":before"],S={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function w(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}class k{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class F{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(this.nodes.has(t)){const s=new k(e),r=this.nodes.get(t)||[];this.nodes.set(t,[...r,s])}else{const s=new k(e);this.nodes.set(t,[s])}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.forEach((t=>{null==e||e.forEach((e=>t.nodes.push(e)))}))}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=this.nodes.get(e);if(t){const e=[...t];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}}removeUnused(){for(const e of this.usedVariables){const t=this.nodes.get(e);if(t)for(const e of t){w(e.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)for(const s of t)s.isUsed||this.isVariablesSafelisted(e)||s.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const A={access:n.promisify(l.access),readFile:n.promisify(l.readFile)};function V(e=[]){return Array.isArray(e)?{...S.safelist,standard:e}:{...S.safelist,...e}}async function j(e="purgecss.config.js"){let t;try{const s=f.default.resolve(process.cwd(),e);t=await function(e){return Promise.resolve().then((function(){return c(require(e))}))}(s)}catch(e){throw new Error(`Error loading the config file ${e.message}`)}return{...S,...t,safelist:V(t.safelist)}}async function x(e,t){return new y(await t(e))}function C(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function U(e){return e.replace(/(^["'])|(["']$)/g,"")}function R(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function E(e,t){return t.hasId(e.value)}function N(e,t){return t.hasTag(e.value)}function P(e){return"atrule"===(null==e?void 0:e.type)}function q(e){return"rule"===(null==e?void 0:e.type)}class G{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new F,this.options=S}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=w(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=U(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find((t=>t.extensions.find((t=>e.endsWith(t)))));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){const s=new y([]);for(const r of e){let e=[];try{await A.access(r,l.constants.F_OK),e.push(r)}catch(t){e=d.default.sync(r,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const r of e){const e=await A.readFile(r,"utf-8"),o=this.getFileExtractor(r,t),i=await x(e,o);s.merge(i)}}return s}async extractSelectorsFromString(e,t){const s=new y([]);for(const{raw:r,extension:o}of e){const e=this.getFileExtractor(`.${o}`,t),i=await x(r,e);s.merge(i)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:U(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&C(s,"next"))return void s.remove();if(e.parent&&P(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!q(e))return;if(function(e){let t=!1;return e.walkComments((e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())})),t}(e))return;let r=!0;const o=[];if(e.selector=p.default((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&o.push(e.toString()),e.remove()))}))})).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const i=e.parent;if(e.selector||e.remove(),function(e){return!!(q(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||P(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(i)&&(null==i||i.remove()),this.options.rejectedCss&&o.length>0){const t=e.clone(),s=null==i?void 0:i.clone().removeAll().append(t);t.selectors=o;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,t){const s=[],r=[];for(const t of e)"string"==typeof t?r.push(...d.default.sync(t,{nodir:!0,ignore:this.options.skippedContentGlobs})):r.push(t);for(const e of r){const r="string"==typeof e?this.options.stdin?e:await A.readFile(e,"utf-8"):e.raw,o=h.parse(r);this.walkThroughCSS(o,t),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const i={css:o.toString(),file:"string"==typeof e?e:e.name};this.options.rejected&&(i.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(i.rejectedCss=h.root({nodes:this.removedNodes}).toString()),s.push(i)}return s}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e))),s=/^::.*/.test(e);return b.includes(e)||s||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await j(e):{...S,...e,safelist:V(e.safelist)};const{content:t,css:s,extractors:r,safelist:o}=this.options;this.options.variables&&(this.variablesStructure.safelist=o.variables||[]);const i=t.filter((e=>"string"==typeof e)),n=t.filter((e=>"object"==typeof e)),a=await this.extractSelectorsFromFiles(i,r),c=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,function(...e){const t=new y([]);return e.forEach(t.merge,t),t}(a,c))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const o of e.nodes){const e=this.getSelectorValue(o);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(b.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(o.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(o.attribute)||R(o,t);break;case"class":r=o,s=t.hasClass(r.value);break;case"id":s=E(o,t);break;case"tag":s=N(o,t);break;default:continue}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk((e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(C(e,"start")?(this.ignore=!0,e.remove()):C(e,"end")&&(this.ignore=!1,e.remove())))))}}async function O(e,t){try{await u.default.promises.writeFile(e,t)}catch(e){console.error(e.message)}}try{!async function(){var t;e.program.description(g).version(m).usage("--css <css...> --content <content...> [options]"),e.program.option("-con, --content <files...>","glob of content files").option("-css, --css <files...>","glob of css files").option("-c, --config <path>","path to the configuration file").option("-o, --output <path>","file path directory to write purged css files to").option("-font, --font-face","option to remove unused font-faces").option("-keyframes, --keyframes","option to remove unused keyframes").option("-v, --variables","option to remove unused variables").option("-rejected, --rejected","option to output rejected selectors").option("-rejected-css, --rejected-css","option to output rejected css").option("-s, --safelist <list...>","list of classes that should not be removed").option("-b, --blocklist <list...>","list of selectors that should be removed").option("-k, --skippedContentGlobs <list...>","list of glob patterns for folders/files that should not be scanned"),e.program.parse(process.argv);const{config:s,css:r,content:o,output:i,fontFace:n,keyframes:a,variables:c,rejected:l,rejectedCss:u,safelist:d,blocklist:f,skippedContentGlobs:h}=e.program.opts();s||o&&r||e.program.help();let p=S;s&&(p=await j(s)),o&&(p.content=o),r&&(p.css=r),n&&(p.fontFace=n),a&&(p.keyframes=a),l&&(p.rejected=l),u&&(p.rejectedCss=u),c&&(p.variables=c),d&&(p.safelist=V(d)),f&&(p.blocklist=f),h&&(p.skippedContentGlobs=h);const v=await(new G).purge(p),y=p.output||i;if(y){if(1===v.length&&y.endsWith(".css"))return void await O(y,v[0].css);for(const e of v){const s=null===(t=null==e?void 0:e.file)||void 0===t?void 0:t.split("/").pop();await O(`${y}/${s}`,e.css)}}else console.log(JSON.stringify(v))}()}catch(e){console.error(e.message),process.exit(1)} | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("commander"),t=require("fs"),s=require("glob"),r=require("path"),o=require("postcss"),i=require("postcss-selector-parser"),n=require("util");function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function c(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var u=c(t),l=c(s),d=c(r),f=c(o),h=a(i),p="4.1.3",m="Remove unused css selectors";function g(e,t){t&&t.forEach(e.add,e)}class v{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?g(this.undetermined,e):e instanceof v?(g(this.undetermined,e.undetermined),g(this.attrNames,e.attrNames),g(this.attrValues,e.attrValues),g(this.classes,e.classes),g(this.ids,e.ids),g(this.tags,e.tags)):(g(this.undetermined,e.undetermined),e.attributes&&(g(this.attrNames,e.attributes.names),g(this.attrValues,e.attributes.values)),g(this.classes,e.classes),g(this.ids,e.ids),g(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue((t=>t.startsWith(e)))}hasAttrSuffix(e){return this.someAttrValue((t=>t.endsWith(e)))}hasAttrSubstr(e){return e.trim().split(" ").every((e=>this.someAttrValue((t=>t.includes(e)))))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const y=["*",":root",":after",":before"],b={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,sourceMap:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function S(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}class w{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class k{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(this.nodes.has(t)){const s=new w(e),r=this.nodes.get(t)||[];this.nodes.set(t,[...r,s])}else{const s=new w(e);this.nodes.set(t,[s])}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.forEach((t=>{null==e||e.forEach((e=>t.nodes.push(e)))}))}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=this.nodes.get(e);if(t){const e=[...t];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}}removeUnused(){for(const e of this.usedVariables){const t=this.nodes.get(e);if(t)for(const e of t){S(e.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)for(const s of t)s.isUsed||this.isVariablesSafelisted(e)||s.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const F={access:n.promisify(u.access),readFile:n.promisify(u.readFile)};function x(e=[]){return Array.isArray(e)?{...b.safelist,standard:e}:{...b.safelist,...e}}async function A(e="purgecss.config.js"){let t;try{const s=d.resolve(process.cwd(),e);t=await function(e){return Promise.resolve().then((function(){return c(require(e))}))}(s)}catch(e){if(e instanceof Error)throw new Error(`Error loading the config file ${e.message}`);throw new Error}return{...b,...t,safelist:x(t.safelist)}}async function V(e,t){return new v(await t(e))}function j(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function C(e){return e.replace(/(^["'])|(["']$)/g,"")}function U(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function E(e,t){return t.hasId(e.value)}function R(e,t){return t.hasTag(e.value)}function N(e){return"atrule"===(null==e?void 0:e.type)}function O(e){return"rule"===(null==e?void 0:e.type)}class P{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new k,this.options=b}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=S(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=C(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find((t=>t.extensions.find((t=>e.endsWith(t)))));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){const s=new v([]);for(const r of e){let e=[];try{await F.access(r,u.constants.F_OK),e.push(r)}catch(t){e=l.sync(r,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const r of e){const e=await F.readFile(r,"utf-8"),o=this.getFileExtractor(r,t),i=await V(e,o);s.merge(i)}}return s}async extractSelectorsFromString(e,t){const s=new v([]);for(const{raw:r,extension:o}of e){const e=this.getFileExtractor(`.${o}`,t),i=await V(r,e);s.merge(i)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:C(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&j(s,"next"))return void s.remove();if(e.parent&&N(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!O(e))return;if(function(e){let t=!1;return e.walkComments((e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())})),t}(e))return;let r=!0;const o=[];if(e.selector=h.default((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&o.push(e.toString()),e.remove()))}))})).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const i=e.parent;if(e.selector||e.remove(),function(e){return!!(O(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||N(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(i)&&(null==i||i.remove()),this.options.rejectedCss&&o.length>0){const t=e.clone(),s=null==i?void 0:i.clone().removeAll().append(t);t.selectors=o;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,t){var s;const r=[],o=[];for(const t of e)"string"==typeof t?o.push(...l.sync(t,{nodir:!0,ignore:this.options.skippedContentGlobs})):o.push(t);for(const e of o){const o="string"==typeof e?this.options.stdin?e:await F.readFile(e,"utf-8"):e.raw,i="string"==typeof e&&!this.options.stdin,n=f.parse(o,{from:i?e:void 0});this.walkThroughCSS(n,t),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const a=n.toResult({map:this.options.sourceMap,to:"object"==typeof this.options.sourceMap?this.options.sourceMap.to:void 0}),c={css:a.toString(),file:"string"==typeof e?e:e.name};this.options.sourceMap&&(c.sourceMap=null===(s=a.map)||void 0===s?void 0:s.toString()),this.options.rejected&&(c.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(c.rejectedCss=f.root({nodes:this.removedNodes}).toString()),r.push(c)}return r}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e))),s=/^::.*/.test(e);return y.includes(e)||s||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await A(e):{...b,...e,safelist:x(e.safelist)};const{content:t,css:s,extractors:r,safelist:o}=this.options;this.options.variables&&(this.variablesStructure.safelist=o.variables||[]);const i=t.filter((e=>"string"==typeof e)),n=t.filter((e=>"object"==typeof e)),a=await this.extractSelectorsFromFiles(i,r),c=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,function(...e){const t=new v([]);return e.forEach(t.merge,t),t}(a,c))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const o of e.nodes){const e=this.getSelectorValue(o);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(y.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(o.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(o.attribute)||U(o,t);break;case"class":r=o,s=t.hasClass(r.value);break;case"id":s=E(o,t);break;case"tag":s=R(o,t);break;default:continue}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk((e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(j(e,"start")?(this.ignore=!0,e.remove()):j(e,"end")&&(this.ignore=!1,e.remove())))))}}async function M(e,t){try{await u.promises.writeFile(e,t)}catch(e){e instanceof Error&&console.error(e.message)}}async function q(e){const t=[];for await(const s of e)t.push(s);return Buffer.concat(t).toString("utf8")}function G(e){return e.description(m).version(p).usage("--css <css...> --content <content...> [options]"),e.option("-con, --content <files...>","glob of content files").option("-css, --css <files...>","glob of css files").option("-c, --config <path>","path to the configuration file").option("-o, --output <path>","file path directory to write purged css files to").option("-font, --font-face","option to remove unused font-faces").option("-keyframes, --keyframes","option to remove unused keyframes").option("-v, --variables","option to remove unused variables").option("-rejected, --rejected","option to output rejected selectors").option("-rejected-css, --rejected-css","option to output rejected css").option("-s, --safelist <list...>","list of classes that should not be removed").option("-b, --blocklist <list...>","list of selectors that should be removed").option("-k, --skippedContentGlobs <list...>","list of glob patterns for folders/files that should not be scanned"),e}async function W(e){const{config:t,css:s,content:r,output:o,fontFace:i,keyframes:n,variables:a,rejected:c,rejectedCss:u,safelist:l,blocklist:d,skippedContentGlobs:f}=e.opts();t||r&&s||e.help();let h=b;return t&&(h=await A(t)),r&&(1===r.length&&"-"===r[0]?h.content=[{raw:await q(process.stdin),extension:""}]:h.content=r),s&&(1===s.length&&"-"===s[0]?h.css=[{raw:await q(process.stdin)}]:h.css=s),i&&(h.fontFace=i),n&&(h.keyframes=n),c&&(h.rejected=c),u&&(h.rejectedCss=u),a&&(h.variables=a),l&&(h.safelist=x(l)),d&&(h.blocklist=d),f&&(h.skippedContentGlobs=f),o&&(h.output=o),h}async function D(e){var t;const s=await W(e),r=await(new P).purge(s);if(s.output){if(1===r.length&&s.output.endsWith(".css"))return void await M(s.output,r[0].css);for(const e of r){const r=null===(t=null==e?void 0:e.file)||void 0===t?void 0:t.split("/").pop();await M(`${s.output}/${r}`,e.css)}}else console.log(JSON.stringify(r))}async function K(){try{const t=G(new e.Command);t.parse(process.argv),D(t)}catch(e){e instanceof Error&&console.error(e.message),process.exit(1)}}exports.getOptions=W,exports.main=K,exports.parseCommandOptions=G,exports.run=D,K(); |
@@ -1,334 +0,509 @@ | ||
import * as postcss from "postcss"; | ||
type PostCSSRoot = postcss.Root; | ||
interface AtRules { | ||
fontFace: Array<{ | ||
name: string; | ||
node: postcss.AtRule; | ||
}>; | ||
keyframes: postcss.AtRule[]; | ||
/** | ||
* Core package of PurgeCSS | ||
* | ||
* Contains the core methods to analyze the files, remove unused CSS. | ||
* | ||
* @packageDocumentation | ||
*/ | ||
import * as postcss from 'postcss'; | ||
/* Excluded from this release type: AtRules */ | ||
/** | ||
* @public | ||
*/ | ||
export declare type ComplexSafelist = { | ||
standard?: StringRegExpArray; | ||
/** | ||
* You can safelist selectors and their children based on a regular | ||
* expression with `safelist.deep` | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* const purgecss = await new PurgeCSS().purge({ | ||
* content: [], | ||
* css: [], | ||
* safelist: { | ||
* deep: [/red$/] | ||
* } | ||
* }) | ||
* ``` | ||
* | ||
* In this example, selectors such as `.bg-red .child-of-bg` will be left | ||
* in the final CSS, even if `child-of-bg` is not found. | ||
* | ||
*/ | ||
deep?: RegExp[]; | ||
greedy?: RegExp[]; | ||
variables?: StringRegExpArray; | ||
keyframes?: StringRegExpArray; | ||
}; | ||
/** | ||
* @public | ||
*/ | ||
export declare const defaultOptions: Options; | ||
/** | ||
* @public | ||
*/ | ||
export declare type ExtractorFunction<T = string> = (content: T) => ExtractorResult; | ||
/** | ||
* @public | ||
*/ | ||
export declare type ExtractorResult = ExtractorResultDetailed | string[]; | ||
/** | ||
* @public | ||
*/ | ||
export declare interface ExtractorResultDetailed { | ||
attributes: { | ||
names: string[]; | ||
values: string[]; | ||
}; | ||
classes: string[]; | ||
ids: string[]; | ||
tags: string[]; | ||
undetermined: string[]; | ||
} | ||
interface RawContent<T = string> { | ||
extension: string; | ||
raw: T; | ||
/** | ||
* @public | ||
*/ | ||
export declare class ExtractorResultSets { | ||
private undetermined; | ||
private attrNames; | ||
private attrValues; | ||
private classes; | ||
private ids; | ||
private tags; | ||
constructor(er: ExtractorResult); | ||
merge(that: ExtractorResult | ExtractorResultSets): this; | ||
hasAttrName(name: string): boolean; | ||
private someAttrValue; | ||
hasAttrPrefix(prefix: string): boolean; | ||
hasAttrSuffix(suffix: string): boolean; | ||
hasAttrSubstr(substr: string): boolean; | ||
hasAttrValue(value: string): boolean; | ||
hasClass(name: string): boolean; | ||
hasId(id: string): boolean; | ||
hasTag(tag: string): boolean; | ||
} | ||
interface RawCSS { | ||
raw: string; | ||
name?: string; | ||
/** | ||
* @public | ||
*/ | ||
export declare interface Extractors { | ||
extensions: string[]; | ||
extractor: ExtractorFunction; | ||
} | ||
interface ExtractorResultDetailed { | ||
attributes: { | ||
names: string[]; | ||
values: string[]; | ||
}; | ||
classes: string[]; | ||
ids: string[]; | ||
tags: string[]; | ||
undetermined: string[]; | ||
/* Excluded from this release type: IgnoreType */ | ||
/** | ||
* Merge two extractor selectors | ||
* | ||
* @param extractorSelectorsA - extractor selectors A | ||
* @param extractorSelectorsB - extractor selectors B | ||
* @returns the merged extractor result sets | ||
* | ||
* @public | ||
*/ | ||
export declare function mergeExtractorSelectors(...extractors: (ExtractorResultDetailed | ExtractorResultSets)[]): ExtractorResultSets; | ||
/** | ||
* Options used by PurgeCSS to remove unused CSS | ||
* Those options are used internally | ||
* @see {@link UserDefinedOptions} for the options defined by the user | ||
* | ||
* @public | ||
*/ | ||
export declare interface Options { | ||
/** | ||
* You can specify content that should be analyzed by PurgeCSS with an | ||
* array of filenames or globs. The files can be HTML, Pug, Blade, etc. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* await new PurgeCSS().purge({ | ||
* content: ['index.html', '*.js', '*.html', '*.vue'], | ||
* css: ['css/app.css'] | ||
* }) | ||
* ``` | ||
* | ||
* @example | ||
* PurgeCSS also works with raw content. To do this, you need to pass an | ||
* object with the `raw` property instead of a filename. To work properly | ||
* with custom extractors you need to pass the `extension` property along | ||
* with the raw content. | ||
* | ||
* ```ts | ||
* await new PurgeCSS().purge({ | ||
* content: [ | ||
* { | ||
* raw: '<html><body><div class="app"></div></body></html>', | ||
* extension: 'html' | ||
* }, | ||
* '*.js', | ||
* '*.html', | ||
* '*.vue' | ||
* ], | ||
* css: [ | ||
* { | ||
* raw: 'body { margin: 0 }' | ||
* }, | ||
* 'css/app.css' | ||
* ] | ||
* }) | ||
* ``` | ||
*/ | ||
content: Array<string | RawContent>; | ||
/** | ||
* Similar to content, you can specify css that should be processed by | ||
* PurgeCSS with an array of filenames or globs | ||
*/ | ||
css: Array<string | RawCSS>; | ||
defaultExtractor: ExtractorFunction; | ||
extractors: Array<Extractors>; | ||
/** | ||
* If there are any unused \@font-face rules in your css, you can remove | ||
* them by setting the `fontFace` option to `true`. | ||
* | ||
* @defaultValue `false` | ||
* | ||
* @example | ||
* ```ts | ||
* await new PurgeCSS().purge({ | ||
* content: ['index.html', '*.js', '*.html', '*.vue'], | ||
* css: ['css/app.css'], | ||
* fontFace: true | ||
* }) | ||
* ``` | ||
*/ | ||
fontFace: boolean; | ||
keyframes: boolean; | ||
output?: string; | ||
rejected: boolean; | ||
rejectedCss: boolean; | ||
/** {@inheritDoc postcss#SourceMapOptions} */ | ||
sourceMap: boolean | (postcss.SourceMapOptions & { | ||
to?: string; | ||
}); | ||
stdin: boolean; | ||
stdout: boolean; | ||
variables: boolean; | ||
/** | ||
* You can indicate which selectors are safe to leave in the final CSS. | ||
* This can be accomplished with the option safelist. | ||
*/ | ||
safelist: Required<ComplexSafelist>; | ||
/** | ||
* Blocklist will block the CSS selectors from appearing in the final | ||
* output CSS. The selectors will be removed even when they are seen | ||
* as used by PurgeCSS. | ||
*/ | ||
blocklist: StringRegExpArray; | ||
/** | ||
* If you provide globs for the content parameter, you can use this option | ||
* to exclude certain files or folders that would otherwise be scanned. | ||
* Pass an array of globs matching items that should be excluded. | ||
* (Note: this option has no effect if content is not globs.) | ||
*/ | ||
skippedContentGlobs: Array<string>; | ||
/** | ||
* Option to add custom CSS attribute selectors like "aria-selected", | ||
* "data-selected", ...etc. | ||
*/ | ||
dynamicAttributes: string[]; | ||
} | ||
type ExtractorResult = ExtractorResultDetailed | string[]; | ||
type ExtractorFunction<T = string> = (content: T) => ExtractorResult; | ||
interface Extractors { | ||
extensions: string[]; | ||
extractor: ExtractorFunction; | ||
/** | ||
* @public | ||
*/ | ||
export declare type PostCSSRoot = postcss.Root; | ||
/** | ||
* Class used to instantiate PurgeCSS and can then be used | ||
* to purge CSS files. | ||
* | ||
* @example | ||
* ```ts | ||
* await new PurgeCSS().purge({ | ||
* content: ['index.html'], | ||
* css: ['css/app.css'] | ||
* }) | ||
* ``` | ||
* | ||
* @public | ||
*/ | ||
export declare class PurgeCSS { | ||
private ignore; | ||
private atRules; | ||
private usedAnimations; | ||
private usedFontFaces; | ||
selectorsRemoved: Set<string>; | ||
removedNodes: postcss.Node[]; | ||
variablesStructure: VariablesStructure; | ||
options: Options; | ||
private collectDeclarationsData; | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param filename - Name of the file | ||
* @param extractors - Array of extractors definition | ||
*/ | ||
private getFileExtractor; | ||
/** | ||
* Extract the selectors present in the files using a PurgeCSS extractor | ||
* | ||
* @param files - Array of files path or glob pattern | ||
* @param extractors - Array of extractors | ||
*/ | ||
extractSelectorsFromFiles(files: string[], extractors: Extractors[]): Promise<ExtractorResultSets>; | ||
/** | ||
* Extract the selectors present in the passed string using a PurgeCSS extractor | ||
* | ||
* @param content - Array of content | ||
* @param extractors - Array of extractors | ||
*/ | ||
extractSelectorsFromString(content: RawContent[], extractors: Extractors[]): Promise<ExtractorResultSets>; | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param node - node of postcss AST | ||
*/ | ||
private evaluateAtRule; | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* | ||
* @param node - node of postcss AST | ||
* @param selectors - selectors used in content files | ||
*/ | ||
private evaluateRule; | ||
/** | ||
* Get the purged version of the css based on the files | ||
* | ||
* @param cssOptions - css options, files or raw strings | ||
* @param selectors - set of extracted css selectors | ||
*/ | ||
getPurgedCSS(cssOptions: Array<string | RawCSS>, selectors: ExtractorResultSets): Promise<ResultPurge[]>; | ||
/** | ||
* Check if the keyframe is safelisted with the option safelist keyframes | ||
* | ||
* @param keyframesName - name of the keyframe animation | ||
*/ | ||
private isKeyframesSafelisted; | ||
/** | ||
* Check if the selector is blocklisted with the option blocklist | ||
* | ||
* @param selector - css selector | ||
*/ | ||
private isSelectorBlocklisted; | ||
/** | ||
* Check if the selector is safelisted with the option safelist standard | ||
* | ||
* @param selector - css selector | ||
*/ | ||
private isSelectorSafelisted; | ||
/** | ||
* Check if the selector is safelisted with the option safelist deep | ||
* | ||
* @param selector - selector | ||
*/ | ||
private isSelectorSafelistedDeep; | ||
/** | ||
* Check if the selector is safelisted with the option safelist greedy | ||
* | ||
* @param selector - selector | ||
*/ | ||
private isSelectorSafelistedGreedy; | ||
/** | ||
* Remove unused CSS | ||
* | ||
* @param userOptions - PurgeCSS options or path to the configuration file | ||
* @returns an array of object containing the filename and the associated CSS | ||
* | ||
* @example Using a configuration file named purgecss.config.js | ||
* ```ts | ||
* const purgeCSSResults = await new PurgeCSS().purge() | ||
* ``` | ||
* | ||
* @example Using a custom path to the configuration file | ||
* ```ts | ||
* const purgeCSSResults = await new PurgeCSS().purge('./purgecss.config.js') | ||
* ``` | ||
* | ||
* @example Using the PurgeCSS options | ||
* ```ts | ||
* const purgeCSSResults = await new PurgeCSS().purge({ | ||
* content: ['index.html', '**\/*.js', '**\/*.html', '**\/*.vue'], | ||
* css: ['css/app.css'] | ||
* }) | ||
* ``` | ||
*/ | ||
purge(userOptions: UserDefinedOptions | string | undefined): Promise<ResultPurge[]>; | ||
/** | ||
* Remove unused CSS variables | ||
*/ | ||
removeUnusedCSSVariables(): void; | ||
/** | ||
* Remove unused font-faces | ||
*/ | ||
removeUnusedFontFaces(): void; | ||
/** | ||
* Remove unused keyframes | ||
*/ | ||
removeUnusedKeyframes(): void; | ||
/** | ||
* Transform a selector node into a string | ||
*/ | ||
private getSelectorValue; | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* | ||
* @param selector - set of css selectors found in the content files or string | ||
* @param selectorsFromExtractor - selectors in the css rule | ||
* | ||
* @returns true if the selector should be kept in the processed CSS | ||
*/ | ||
private shouldKeepSelector; | ||
/** | ||
* Walk through the CSS AST and remove unused CSS | ||
* | ||
* @param root - root node of the postcss AST | ||
* @param selectors - selectors used in content files | ||
*/ | ||
walkThroughCSS(root: PostCSSRoot, selectors: ExtractorResultSets): void; | ||
} | ||
type IgnoreType = "end" | "start" | "next"; | ||
type StringRegExpArray = Array<RegExp | string>; | ||
type ComplexSafelist = { | ||
standard?: StringRegExpArray; | ||
deep?: RegExp[]; | ||
greedy?: RegExp[]; | ||
variables?: StringRegExpArray; | ||
keyframes?: StringRegExpArray; | ||
}; | ||
type UserDefinedSafelist = StringRegExpArray | ComplexSafelist; | ||
interface UserDefinedOptions { | ||
content: Array<string | RawContent>; | ||
css: Array<string | RawCSS>; | ||
defaultExtractor?: ExtractorFunction; | ||
extractors?: Array<Extractors>; | ||
fontFace?: boolean; | ||
keyframes?: boolean; | ||
output?: string; | ||
rejected?: boolean; | ||
rejectedCss?: boolean; | ||
stdin?: boolean; | ||
stdout?: boolean; | ||
variables?: boolean; | ||
safelist?: UserDefinedSafelist; | ||
blocklist?: StringRegExpArray; | ||
skippedContentGlobs?: Array<string>; | ||
dynamicAttributes?: string[]; | ||
/** | ||
* @public | ||
*/ | ||
export declare interface RawContent<T = string> { | ||
extension: string; | ||
raw: T; | ||
} | ||
interface Options { | ||
content: Array<string | RawContent>; | ||
css: Array<string | RawCSS>; | ||
defaultExtractor: ExtractorFunction; | ||
extractors: Array<Extractors>; | ||
fontFace: boolean; | ||
keyframes: boolean; | ||
output?: string; | ||
rejected: boolean; | ||
rejectedCss: boolean; | ||
stdin: boolean; | ||
stdout: boolean; | ||
variables: boolean; | ||
safelist: Required<ComplexSafelist>; | ||
blocklist: StringRegExpArray; | ||
skippedContentGlobs: Array<string>; | ||
dynamicAttributes: string[]; | ||
/** | ||
* @public | ||
*/ | ||
export declare interface RawCSS { | ||
raw: string; | ||
name?: string; | ||
} | ||
interface ResultPurge { | ||
css: string; | ||
rejectedCss?: string; | ||
file?: string; | ||
rejected?: string[]; | ||
/** | ||
* @public | ||
*/ | ||
export declare interface ResultPurge { | ||
css: string; | ||
/** | ||
* sourceMap property will be empty if | ||
* {@link UserDefinedOptions.sourceMap} inline is not set to false, as the | ||
* source map will be contained within the text of ResultPurge.css | ||
*/ | ||
sourceMap?: string; | ||
rejectedCss?: string; | ||
file?: string; | ||
rejected?: string[]; | ||
} | ||
declare class ExtractorResultSets { | ||
private undetermined; | ||
private attrNames; | ||
private attrValues; | ||
private classes; | ||
private ids; | ||
private tags; | ||
constructor(er: ExtractorResult); | ||
merge(that: ExtractorResult | ExtractorResultSets): this; | ||
hasAttrName(name: string): boolean; | ||
private someAttrValue; | ||
hasAttrPrefix(prefix: string): boolean; | ||
hasAttrSuffix(suffix: string): boolean; | ||
hasAttrSubstr(substr: string): boolean; | ||
hasAttrValue(value: string): boolean; | ||
hasClass(name: string): boolean; | ||
hasId(id: string): boolean; | ||
hasTag(tag: string): boolean; | ||
} | ||
declare const defaultOptions: Options; | ||
declare function standardizeSafelist( | ||
userDefinedSafelist?: UserDefinedSafelist | ||
): Required<ComplexSafelist>; | ||
/** | ||
* Load the configuration file from the path | ||
* @param configFile Path of the config file | ||
* | ||
* @param configFile - Path of the config file | ||
* @returns The options from the configuration file | ||
* | ||
* @throws Error | ||
* This exception is thrown if the configuration file was not imported | ||
* | ||
* @public | ||
*/ | ||
declare function setOptions(configFile?: string): Promise<Options>; | ||
export declare function setOptions(configFile?: string): Promise<Options>; | ||
/** | ||
* Merge two extractor selectors | ||
* @param extractorSelectorsA extractor selectors A | ||
* @param extractorSelectorsB extractor selectors B | ||
* Format the user defined safelist into a standardized safelist object | ||
* | ||
* @param userDefinedSafelist - the user defined safelist | ||
* @returns the formatted safelist object that can be used in the PurgeCSS options | ||
* | ||
* @public | ||
*/ | ||
declare function mergeExtractorSelectors( | ||
...extractors: (ExtractorResultDetailed | ExtractorResultSets)[] | ||
): ExtractorResultSets; | ||
declare class PurgeCSS { | ||
private ignore; | ||
private atRules; | ||
private usedAnimations; | ||
private usedFontFaces; | ||
selectorsRemoved: Set<string>; | ||
removedNodes: postcss.Node[]; | ||
private variablesStructure; | ||
options: Options; | ||
private collectDeclarationsData; | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param filename Name of the file | ||
* @param extractors Array of extractors definition | ||
*/ | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param filename Name of the file | ||
* @param extractors Array of extractors definition | ||
*/ | ||
private getFileExtractor; | ||
/** | ||
* Extract the selectors present in the files using a purgecss extractor | ||
* @param files Array of files path or glob pattern | ||
* @param extractors Array of extractors | ||
*/ | ||
/** | ||
* Extract the selectors present in the files using a purgecss extractor | ||
* @param files Array of files path or glob pattern | ||
* @param extractors Array of extractors | ||
*/ | ||
extractSelectorsFromFiles( | ||
files: string[], | ||
extractors: Extractors[] | ||
): Promise<ExtractorResultSets>; | ||
/** | ||
* Extract the selectors present in the passed string using a PurgeCSS extractor | ||
* @param content Array of content | ||
* @param extractors Array of extractors | ||
*/ | ||
/** | ||
* Extract the selectors present in the passed string using a PurgeCSS extractor | ||
* @param content Array of content | ||
* @param extractors Array of extractors | ||
*/ | ||
extractSelectorsFromString( | ||
content: RawContent[], | ||
extractors: Extractors[] | ||
): Promise<ExtractorResultSets>; | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param node node of postcss AST | ||
*/ | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param node node of postcss AST | ||
*/ | ||
private evaluateAtRule; | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* @param node node of postcss AST | ||
* @param selectors selectors used in content files | ||
*/ | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* @param node node of postcss AST | ||
* @param selectors selectors used in content files | ||
*/ | ||
private evaluateRule; | ||
/** | ||
* Get the purged version of the css based on the files | ||
* @param cssOptions css options, files or raw strings | ||
* @param selectors set of extracted css selectors | ||
*/ | ||
/** | ||
* Get the purged version of the css based on the files | ||
* @param cssOptions css options, files or raw strings | ||
* @param selectors set of extracted css selectors | ||
*/ | ||
getPurgedCSS( | ||
cssOptions: Array<string | RawCSS>, | ||
selectors: ExtractorResultSets | ||
): Promise<ResultPurge[]>; | ||
/** | ||
* Check if the keyframe is safelisted with the option safelist keyframes | ||
* @param keyframesName name of the keyframe animation | ||
*/ | ||
/** | ||
* Check if the keyframe is safelisted with the option safelist keyframes | ||
* @param keyframesName name of the keyframe animation | ||
*/ | ||
private isKeyframesSafelisted; | ||
/** | ||
* Check if the selector is blocklisted with the option blocklist | ||
* @param selector css selector | ||
*/ | ||
/** | ||
* Check if the selector is blocklisted with the option blocklist | ||
* @param selector css selector | ||
*/ | ||
private isSelectorBlocklisted; | ||
/** | ||
* Check if the selector is safelisted with the option safelist standard | ||
* @param selector css selector | ||
*/ | ||
/** | ||
* Check if the selector is safelisted with the option safelist standard | ||
* @param selector css selector | ||
*/ | ||
private isSelectorSafelisted; | ||
/** | ||
* Check if the selector is safelisted with the option safelist deep | ||
* @param selector selector | ||
*/ | ||
/** | ||
* Check if the selector is safelisted with the option safelist deep | ||
* @param selector selector | ||
*/ | ||
private isSelectorSafelistedDeep; | ||
/** | ||
* Check if the selector is safelisted with the option safelist greedy | ||
* @param selector selector | ||
*/ | ||
/** | ||
* Check if the selector is safelisted with the option safelist greedy | ||
* @param selector selector | ||
*/ | ||
private isSelectorSafelistedGreedy; | ||
/** | ||
* Remove unused css | ||
* @param userOptions PurgeCSS options | ||
*/ | ||
/** | ||
* Remove unused css | ||
* @param userOptions PurgeCSS options | ||
*/ | ||
purge( | ||
userOptions: UserDefinedOptions | string | undefined | ||
): Promise<ResultPurge[]>; | ||
/** | ||
* Remove unused CSS variables | ||
*/ | ||
/** | ||
* Remove unused CSS variables | ||
*/ | ||
removeUnusedCSSVariables(): void; | ||
/** | ||
* Remove unused font-faces | ||
*/ | ||
/** | ||
* Remove unused font-faces | ||
*/ | ||
removeUnusedFontFaces(): void; | ||
/** | ||
* Remove unused keyframes | ||
*/ | ||
/** | ||
* Remove unused keyframes | ||
*/ | ||
removeUnusedKeyframes(): void; | ||
/** | ||
* Transform a selector node into a string | ||
*/ | ||
/** | ||
* Transform a selector node into a string | ||
*/ | ||
private getSelectorValue; | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* @param selector set of css selectors found in the content files or string | ||
* @param selectorsFromExtractor selectors in the css rule | ||
*/ | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* @param selector set of css selectors found in the content files or string | ||
* @param selectorsFromExtractor selectors in the css rule | ||
*/ | ||
private shouldKeepSelector; | ||
/** | ||
* Walk through the CSS AST and remove unused CSS | ||
* @param root root node of the postcss AST | ||
* @param selectors selectors used in content files | ||
*/ | ||
/** | ||
* Walk through the CSS AST and remove unused CSS | ||
* @param root root node of the postcss AST | ||
* @param selectors selectors used in content files | ||
*/ | ||
walkThroughCSS(root: PostCSSRoot, selectors: ExtractorResultSets): void; | ||
export declare function standardizeSafelist(userDefinedSafelist?: UserDefinedSafelist): Required<ComplexSafelist>; | ||
/** | ||
* @public | ||
*/ | ||
export declare type StringRegExpArray = Array<RegExp | string>; | ||
/** | ||
* Options used by PurgeCSS to remove unused CSS | ||
* | ||
* @public | ||
*/ | ||
export declare interface UserDefinedOptions { | ||
/** {@inheritDoc Options.content} */ | ||
content: Array<string | RawContent>; | ||
/** {@inheritDoc Options.css} */ | ||
css: Array<string | RawCSS>; | ||
/** {@inheritDoc Options.defaultExtractor} */ | ||
defaultExtractor?: ExtractorFunction; | ||
/** {@inheritDoc Options.extractors} */ | ||
extractors?: Array<Extractors>; | ||
/** {@inheritDoc Options.fontFace} */ | ||
fontFace?: boolean; | ||
/** {@inheritDoc Options.keyframes} */ | ||
keyframes?: boolean; | ||
/** {@inheritDoc Options.output} */ | ||
output?: string; | ||
/** {@inheritDoc Options.rejected} */ | ||
rejected?: boolean; | ||
/** {@inheritDoc Options.rejectedCss} */ | ||
rejectedCss?: boolean; | ||
/** {@inheritDoc Options.sourceMap } */ | ||
sourceMap?: boolean | (postcss.SourceMapOptions & { | ||
to?: string; | ||
}); | ||
/** {@inheritDoc Options.stdin} */ | ||
stdin?: boolean; | ||
/** {@inheritDoc Options.stdout} */ | ||
stdout?: boolean; | ||
/** {@inheritDoc Options.variables} */ | ||
variables?: boolean; | ||
/** {@inheritDoc Options.safelist} */ | ||
safelist?: UserDefinedSafelist; | ||
/** {@inheritDoc Options.blocklist} */ | ||
blocklist?: StringRegExpArray; | ||
/** {@inheritDoc Options.skippedContentGlobs} */ | ||
skippedContentGlobs?: Array<string>; | ||
/** {@inheritDoc Options.dynamicAttributes} */ | ||
dynamicAttributes?: string[]; | ||
} | ||
export { | ||
PurgeCSS as default, | ||
PurgeCSS, | ||
defaultOptions, | ||
PostCSSRoot, | ||
AtRules, | ||
RawContent, | ||
RawCSS, | ||
ExtractorResultDetailed, | ||
ExtractorResult, | ||
ExtractorFunction, | ||
Extractors, | ||
IgnoreType, | ||
StringRegExpArray, | ||
ComplexSafelist, | ||
UserDefinedSafelist, | ||
UserDefinedOptions, | ||
Options, | ||
ResultPurge, | ||
standardizeSafelist, | ||
setOptions, | ||
mergeExtractorSelectors, | ||
}; | ||
/** | ||
* @public | ||
*/ | ||
export declare type UserDefinedSafelist = StringRegExpArray | ComplexSafelist; | ||
declare class VariableNode { | ||
nodes: VariableNode[]; | ||
value: postcss.Declaration; | ||
isUsed: boolean; | ||
constructor(declaration: postcss.Declaration); | ||
} | ||
declare class VariablesStructure { | ||
nodes: Map<string, VariableNode[]>; | ||
usedVariables: Set<string>; | ||
safelist: StringRegExpArray; | ||
addVariable(declaration: postcss.Declaration): void; | ||
addVariableUsage(declaration: postcss.Declaration, matchedVariables: RegExpMatchArray[]): void; | ||
addVariableUsageInProperties(matchedVariables: RegExpMatchArray[]): void; | ||
setAsUsed(variableName: string): void; | ||
removeUnused(): void; | ||
isVariablesSafelisted(variable: string): boolean; | ||
} | ||
export { } |
@@ -1,1 +0,1 @@ | ||
import*as e from"fs";import t from"glob";import s from"path";import*as r from"postcss";import i from"postcss-selector-parser";import{promisify as o}from"util";function n(e,t){t&&t.forEach(e.add,e)}class a{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?n(this.undetermined,e):e instanceof a?(n(this.undetermined,e.undetermined),n(this.attrNames,e.attrNames),n(this.attrValues,e.attrValues),n(this.classes,e.classes),n(this.ids,e.ids),n(this.tags,e.tags)):(n(this.undetermined,e.undetermined),e.attributes&&(n(this.attrNames,e.attributes.names),n(this.attrValues,e.attributes.values)),n(this.classes,e.classes),n(this.ids,e.ids),n(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue((t=>t.startsWith(e)))}hasAttrSuffix(e){return this.someAttrValue((t=>t.endsWith(e)))}hasAttrSubstr(e){return e.trim().split(" ").every((e=>this.someAttrValue((t=>t.includes(e)))))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const c=["*",":root",":after",":before"],l={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function u(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}class h{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class d{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(this.nodes.has(t)){const s=new h(e),r=this.nodes.get(t)||[];this.nodes.set(t,[...r,s])}else{const s=new h(e);this.nodes.set(t,[s])}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.forEach((t=>{null==e||e.forEach((e=>t.nodes.push(e)))}))}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=this.nodes.get(e);if(t){const e=[...t];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}}removeUnused(){for(const e of this.usedVariables){const t=this.nodes.get(e);if(t)for(const e of t){u(e.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)for(const s of t)s.isUsed||this.isVariablesSafelisted(e)||s.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const f={access:o(e.access),readFile:o(e.readFile)};function p(e=[]){return Array.isArray(e)?{...l.safelist,standard:e}:{...l.safelist,...e}}async function m(e="purgecss.config.js"){let t;try{const r=s.resolve(process.cwd(),e);t=await import(r)}catch(e){throw new Error(`Error loading the config file ${e.message}`)}return{...l,...t,safelist:p(t.safelist)}}async function g(e,t){return new a(await t(e))}function v(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function y(...e){const t=new a([]);return e.forEach(t.merge,t),t}function S(e){return e.replace(/(^["'])|(["']$)/g,"")}function b(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function w(e,t){return t.hasId(e.value)}function A(e,t){return t.hasTag(e.value)}function F(e){return"atrule"===(null==e?void 0:e.type)}function V(e){return"rule"===(null==e?void 0:e.type)}class k{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new d,this.options=l}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=u(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=S(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find((t=>t.extensions.find((t=>e.endsWith(t)))));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(s,r){const i=new a([]);for(const o of s){let s=[];try{await f.access(o,e.constants.F_OK),s.push(o)}catch(e){s=t.sync(o,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const e of s){const t=await f.readFile(e,"utf-8"),s=this.getFileExtractor(e,r),o=await g(t,s);i.merge(o)}}return i}async extractSelectorsFromString(e,t){const s=new a([]);for(const{raw:r,extension:i}of e){const e=this.getFileExtractor(`.${i}`,t),o=await g(r,e);s.merge(o)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:S(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&v(s,"next"))return void s.remove();if(e.parent&&F(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!V(e))return;if(function(e){let t=!1;return e.walkComments((e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())})),t}(e))return;let r=!0;const o=[];if(e.selector=i((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&o.push(e.toString()),e.remove()))}))})).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const n=e.parent;if(e.selector||e.remove(),function(e){return!!(V(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||F(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(n)&&(null==n||n.remove()),this.options.rejectedCss&&o.length>0){const t=e.clone(),s=null==n?void 0:n.clone().removeAll().append(t);t.selectors=o;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,s){const i=[],o=[];for(const s of e)"string"==typeof s?o.push(...t.sync(s,{nodir:!0,ignore:this.options.skippedContentGlobs})):o.push(s);for(const e of o){const t="string"==typeof e?this.options.stdin?e:await f.readFile(e,"utf-8"):e.raw,o=r.parse(t);this.walkThroughCSS(o,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const n={css:o.toString(),file:"string"==typeof e?e:e.name};this.options.rejected&&(n.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(n.rejectedCss=r.root({nodes:this.removedNodes}).toString()),i.push(n)}return i}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e))),s=/^::.*/.test(e);return c.includes(e)||s||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await m(e):{...l,...e,safelist:p(e.safelist)};const{content:t,css:s,extractors:r,safelist:i}=this.options;this.options.variables&&(this.variablesStructure.safelist=i.variables||[]);const o=t.filter((e=>"string"==typeof e)),n=t.filter((e=>"object"==typeof e)),a=await this.extractSelectorsFromFiles(o,r),c=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,y(a,c))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const i of e.nodes){const e=this.getSelectorValue(i);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(c.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(i.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(i.attribute)||b(i,t);break;case"class":r=i,s=t.hasClass(r.value);break;case"id":s=w(i,t);break;case"tag":s=A(i,t);break;default:continue}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk((e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(v(e,"start")?(this.ignore=!0,e.remove()):v(e,"end")&&(this.ignore=!1,e.remove())))))}}export{k as PurgeCSS,k as default,l as defaultOptions,y as mergeExtractorSelectors,m as setOptions,p as standardizeSafelist}; | ||
import*as e from"fs";import*as t from"glob";import*as s from"path";import*as r from"postcss";import o from"postcss-selector-parser";import{promisify as i}from"util";function a(e,t){t&&t.forEach(e.add,e)}class ExtractorResultSets{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?a(this.undetermined,e):e instanceof ExtractorResultSets?(a(this.undetermined,e.undetermined),a(this.attrNames,e.attrNames),a(this.attrValues,e.attrValues),a(this.classes,e.classes),a(this.ids,e.ids),a(this.tags,e.tags)):(a(this.undetermined,e.undetermined),e.attributes&&(a(this.attrNames,e.attributes.names),a(this.attrValues,e.attributes.values)),a(this.classes,e.classes),a(this.ids,e.ids),a(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue((t=>t.startsWith(e)))}hasAttrSuffix(e){return this.someAttrValue((t=>t.endsWith(e)))}hasAttrSubstr(e){return e.trim().split(" ").every((e=>this.someAttrValue((t=>t.includes(e)))))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const n=["*",":root",":after",":before"],c={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,sourceMap:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function l(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}class VariableNode{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class VariablesStructure{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(this.nodes.has(t)){const s=new VariableNode(e),r=this.nodes.get(t)||[];this.nodes.set(t,[...r,s])}else{const s=new VariableNode(e);this.nodes.set(t,[s])}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.forEach((t=>{null==e||e.forEach((e=>t.nodes.push(e)))}))}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=this.nodes.get(e);if(t){const e=[...t];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}}removeUnused(){for(const e of this.usedVariables){const t=this.nodes.get(e);if(t)for(const e of t){l(e.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)for(const s of t)s.isUsed||this.isVariablesSafelisted(e)||s.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const u={access:i(e.access),readFile:i(e.readFile)};function d(e=[]){return Array.isArray(e)?{...c.safelist,standard:e}:{...c.safelist,...e}}async function h(e="purgecss.config.js"){let t;try{const r=s.resolve(process.cwd(),e);t=await import(r)}catch(e){if(e instanceof Error)throw new Error(`Error loading the config file ${e.message}`);throw new Error}return{...c,...t,safelist:d(t.safelist)}}async function f(e,t){return new ExtractorResultSets(await t(e))}function p(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function m(...e){const t=new ExtractorResultSets([]);return e.forEach(t.merge,t),t}function g(e){return e.replace(/(^["'])|(["']$)/g,"")}function v(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function S(e,t){return t.hasId(e.value)}function y(e,t){return t.hasTag(e.value)}function b(e){return"atrule"===(null==e?void 0:e.type)}function w(e){return"rule"===(null==e?void 0:e.type)}class PurgeCSS{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new VariablesStructure,this.options=c}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=l(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=g(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find((t=>t.extensions.find((t=>e.endsWith(t)))));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(s,r){const o=new ExtractorResultSets([]);for(const i of s){let s=[];try{await u.access(i,e.constants.F_OK),s.push(i)}catch(e){s=t.sync(i,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const e of s){const t=await u.readFile(e,"utf-8"),s=this.getFileExtractor(e,r),i=await f(t,s);o.merge(i)}}return o}async extractSelectorsFromString(e,t){const s=new ExtractorResultSets([]);for(const{raw:r,extension:o}of e){const e=this.getFileExtractor(`.${o}`,t),i=await f(r,e);s.merge(i)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:g(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&p(s,"next"))return void s.remove();if(e.parent&&b(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!w(e))return;if(function(e){let t=!1;return e.walkComments((e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())})),t}(e))return;let r=!0;const i=[];if(e.selector=o((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&i.push(e.toString()),e.remove()))}))})).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const a=e.parent;if(e.selector||e.remove(),function(e){return!!(w(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||b(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(a)&&(null==a||a.remove()),this.options.rejectedCss&&i.length>0){const t=e.clone(),s=null==a?void 0:a.clone().removeAll().append(t);t.selectors=i;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,s){var o;const i=[],a=[];for(const s of e)"string"==typeof s?a.push(...t.sync(s,{nodir:!0,ignore:this.options.skippedContentGlobs})):a.push(s);for(const e of a){const t="string"==typeof e?this.options.stdin?e:await u.readFile(e,"utf-8"):e.raw,a="string"==typeof e&&!this.options.stdin,n=r.parse(t,{from:a?e:void 0});this.walkThroughCSS(n,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const c=n.toResult({map:this.options.sourceMap,to:"object"==typeof this.options.sourceMap?this.options.sourceMap.to:void 0}),l={css:c.toString(),file:"string"==typeof e?e:e.name};this.options.sourceMap&&(l.sourceMap=null===(o=c.map)||void 0===o?void 0:o.toString()),this.options.rejected&&(l.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(l.rejectedCss=r.root({nodes:this.removedNodes}).toString()),i.push(l)}return i}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e))),s=/^::.*/.test(e);return n.includes(e)||s||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await h(e):{...c,...e,safelist:d(e.safelist)};const{content:t,css:s,extractors:r,safelist:o}=this.options;this.options.variables&&(this.variablesStructure.safelist=o.variables||[]);const i=t.filter((e=>"string"==typeof e)),a=t.filter((e=>"object"==typeof e)),n=await this.extractSelectorsFromFiles(i,r),l=await this.extractSelectorsFromString(a,r);return this.getPurgedCSS(s,m(n,l))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const o of e.nodes){const e=this.getSelectorValue(o);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(n.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(o.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(o.attribute)||v(o,t);break;case"class":r=o,s=t.hasClass(r.value);break;case"id":s=S(o,t);break;case"tag":s=y(o,t);break;default:continue}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk((e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(p(e,"start")?(this.ignore=!0,e.remove()):p(e,"end")&&(this.ignore=!1,e.remove())))))}}export{ExtractorResultSets,PurgeCSS,c as defaultOptions,m as mergeExtractorSelectors,h as setOptions,d as standardizeSafelist}; |
@@ -1,1 +0,1 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("fs"),t=require("glob"),s=require("path"),r=require("postcss"),i=require("postcss-selector-parser"),n=require("util");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var c=a(e),u=o(t),l=o(s),d=a(r),h=o(i);function f(e,t){t&&t.forEach(e.add,e)}class p{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?f(this.undetermined,e):e instanceof p?(f(this.undetermined,e.undetermined),f(this.attrNames,e.attrNames),f(this.attrValues,e.attrValues),f(this.classes,e.classes),f(this.ids,e.ids),f(this.tags,e.tags)):(f(this.undetermined,e.undetermined),e.attributes&&(f(this.attrNames,e.attributes.names),f(this.attrValues,e.attributes.values)),f(this.classes,e.classes),f(this.ids,e.ids),f(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue((t=>t.startsWith(e)))}hasAttrSuffix(e){return this.someAttrValue((t=>t.endsWith(e)))}hasAttrSubstr(e){return e.trim().split(" ").every((e=>this.someAttrValue((t=>t.includes(e)))))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const m=["*",":root",":after",":before"],g={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function v(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}class y{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class S{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(this.nodes.has(t)){const s=new y(e),r=this.nodes.get(t)||[];this.nodes.set(t,[...r,s])}else{const s=new y(e);this.nodes.set(t,[s])}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.forEach((t=>{null==e||e.forEach((e=>t.nodes.push(e)))}))}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=this.nodes.get(e);if(t){const e=[...t];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}}removeUnused(){for(const e of this.usedVariables){const t=this.nodes.get(e);if(t)for(const e of t){v(e.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)for(const s of t)s.isUsed||this.isVariablesSafelisted(e)||s.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const b={access:n.promisify(c.access),readFile:n.promisify(c.readFile)};function w(e=[]){return Array.isArray(e)?{...g.safelist,standard:e}:{...g.safelist,...e}}async function x(e="purgecss.config.js"){let t;try{const s=l.default.resolve(process.cwd(),e);t=await function(e){return Promise.resolve().then((function(){return a(require(e))}))}(s)}catch(e){throw new Error(`Error loading the config file ${e.message}`)}return{...g,...t,safelist:w(t.safelist)}}async function A(e,t){return new p(await t(e))}function F(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function V(...e){const t=new p([]);return e.forEach(t.merge,t),t}function k(e){return e.replace(/(^["'])|(["']$)/g,"")}function j(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function C(e,t){return t.hasId(e.value)}function U(e,t){return t.hasTag(e.value)}function E(e){return"atrule"===(null==e?void 0:e.type)}function R(e){return"rule"===(null==e?void 0:e.type)}class P{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new S,this.options=g}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=v(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=k(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find((t=>t.extensions.find((t=>e.endsWith(t)))));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){const s=new p([]);for(const r of e){let e=[];try{await b.access(r,c.constants.F_OK),e.push(r)}catch(t){e=u.default.sync(r,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const r of e){const e=await b.readFile(r,"utf-8"),i=this.getFileExtractor(r,t),n=await A(e,i);s.merge(n)}}return s}async extractSelectorsFromString(e,t){const s=new p([]);for(const{raw:r,extension:i}of e){const e=this.getFileExtractor(`.${i}`,t),n=await A(r,e);s.merge(n)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:k(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&F(s,"next"))return void s.remove();if(e.parent&&E(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!R(e))return;if(function(e){let t=!1;return e.walkComments((e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())})),t}(e))return;let r=!0;const i=[];if(e.selector=h.default((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&i.push(e.toString()),e.remove()))}))})).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const n=e.parent;if(e.selector||e.remove(),function(e){return!!(R(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||E(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(n)&&(null==n||n.remove()),this.options.rejectedCss&&i.length>0){const t=e.clone(),s=null==n?void 0:n.clone().removeAll().append(t);t.selectors=i;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,t){const s=[],r=[];for(const t of e)"string"==typeof t?r.push(...u.default.sync(t,{nodir:!0,ignore:this.options.skippedContentGlobs})):r.push(t);for(const e of r){const r="string"==typeof e?this.options.stdin?e:await b.readFile(e,"utf-8"):e.raw,i=d.parse(r);this.walkThroughCSS(i,t),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const n={css:i.toString(),file:"string"==typeof e?e:e.name};this.options.rejected&&(n.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(n.rejectedCss=d.root({nodes:this.removedNodes}).toString()),s.push(n)}return s}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e))),s=/^::.*/.test(e);return m.includes(e)||s||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await x(e):{...g,...e,safelist:w(e.safelist)};const{content:t,css:s,extractors:r,safelist:i}=this.options;this.options.variables&&(this.variablesStructure.safelist=i.variables||[]);const n=t.filter((e=>"string"==typeof e)),o=t.filter((e=>"object"==typeof e)),a=await this.extractSelectorsFromFiles(n,r),c=await this.extractSelectorsFromString(o,r);return this.getPurgedCSS(s,V(a,c))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const i of e.nodes){const e=this.getSelectorValue(i);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(m.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(i.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(i.attribute)||j(i,t);break;case"class":r=i,s=t.hasClass(r.value);break;case"id":s=C(i,t);break;case"tag":s=U(i,t);break;default:continue}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk((e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(F(e,"start")?(this.ignore=!0,e.remove()):F(e,"end")&&(this.ignore=!1,e.remove())))))}}exports.PurgeCSS=P,exports.default=P,exports.defaultOptions=g,exports.mergeExtractorSelectors=V,exports.setOptions=x,exports.standardizeSafelist=w; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("fs"),t=require("glob"),s=require("path"),r=require("postcss"),i=require("postcss-selector-parser"),o=require("util");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var c=a(e),u=a(t),l=a(s),d=a(r),h=n(i);function f(e,t){t&&t.forEach(e.add,e)}class ExtractorResultSets{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?f(this.undetermined,e):e instanceof ExtractorResultSets?(f(this.undetermined,e.undetermined),f(this.attrNames,e.attrNames),f(this.attrValues,e.attrValues),f(this.classes,e.classes),f(this.ids,e.ids),f(this.tags,e.tags)):(f(this.undetermined,e.undetermined),e.attributes&&(f(this.attrNames,e.attributes.names),f(this.attrValues,e.attributes.values)),f(this.classes,e.classes),f(this.ids,e.ids),f(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue((t=>t.startsWith(e)))}hasAttrSuffix(e){return this.someAttrValue((t=>t.endsWith(e)))}hasAttrSubstr(e){return e.trim().split(" ").every((e=>this.someAttrValue((t=>t.includes(e)))))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const p=["*",":root",":after",":before"],m={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,sourceMap:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function g(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}class VariableNode{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class VariablesStructure{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(this.nodes.has(t)){const s=new VariableNode(e),r=this.nodes.get(t)||[];this.nodes.set(t,[...r,s])}else{const s=new VariableNode(e);this.nodes.set(t,[s])}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.forEach((t=>{null==e||e.forEach((e=>t.nodes.push(e)))}))}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=this.nodes.get(e);if(t){const e=[...t];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}}removeUnused(){for(const e of this.usedVariables){const t=this.nodes.get(e);if(t)for(const e of t){g(e.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)for(const s of t)s.isUsed||this.isVariablesSafelisted(e)||s.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const v={access:o.promisify(c.access),readFile:o.promisify(c.readFile)};function S(e=[]){return Array.isArray(e)?{...m.safelist,standard:e}:{...m.safelist,...e}}async function y(e="purgecss.config.js"){let t;try{const s=l.resolve(process.cwd(),e);t=await function(e){return Promise.resolve().then((function(){return a(require(e))}))}(s)}catch(e){if(e instanceof Error)throw new Error(`Error loading the config file ${e.message}`);throw new Error}return{...m,...t,safelist:S(t.safelist)}}async function b(e,t){return new ExtractorResultSets(await t(e))}function w(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function x(...e){const t=new ExtractorResultSets([]);return e.forEach(t.merge,t),t}function V(e){return e.replace(/(^["'])|(["']$)/g,"")}function A(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function F(e,t){return t.hasId(e.value)}function k(e,t){return t.hasTag(e.value)}function E(e){return"atrule"===(null==e?void 0:e.type)}function R(e){return"rule"===(null==e?void 0:e.type)}exports.ExtractorResultSets=ExtractorResultSets,exports.PurgeCSS=class PurgeCSS{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new VariablesStructure,this.options=m}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=g(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=V(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find((t=>t.extensions.find((t=>e.endsWith(t)))));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){const s=new ExtractorResultSets([]);for(const r of e){let e=[];try{await v.access(r,c.constants.F_OK),e.push(r)}catch(t){e=u.sync(r,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const r of e){const e=await v.readFile(r,"utf-8"),i=this.getFileExtractor(r,t),o=await b(e,i);s.merge(o)}}return s}async extractSelectorsFromString(e,t){const s=new ExtractorResultSets([]);for(const{raw:r,extension:i}of e){const e=this.getFileExtractor(`.${i}`,t),o=await b(r,e);s.merge(o)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:V(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&w(s,"next"))return void s.remove();if(e.parent&&E(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!R(e))return;if(function(e){let t=!1;return e.walkComments((e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())})),t}(e))return;let r=!0;const i=[];if(e.selector=h.default((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&i.push(e.toString()),e.remove()))}))})).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const o=e.parent;if(e.selector||e.remove(),function(e){return!!(R(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||E(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(o)&&(null==o||o.remove()),this.options.rejectedCss&&i.length>0){const t=e.clone(),s=null==o?void 0:o.clone().removeAll().append(t);t.selectors=i;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,t){var s;const r=[],i=[];for(const t of e)"string"==typeof t?i.push(...u.sync(t,{nodir:!0,ignore:this.options.skippedContentGlobs})):i.push(t);for(const e of i){const i="string"==typeof e?this.options.stdin?e:await v.readFile(e,"utf-8"):e.raw,o="string"==typeof e&&!this.options.stdin,n=d.parse(i,{from:o?e:void 0});this.walkThroughCSS(n,t),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const a=n.toResult({map:this.options.sourceMap,to:"object"==typeof this.options.sourceMap?this.options.sourceMap.to:void 0}),c={css:a.toString(),file:"string"==typeof e?e:e.name};this.options.sourceMap&&(c.sourceMap=null===(s=a.map)||void 0===s?void 0:s.toString()),this.options.rejected&&(c.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(c.rejectedCss=d.root({nodes:this.removedNodes}).toString()),r.push(c)}return r}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e))),s=/^::.*/.test(e);return p.includes(e)||s||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await y(e):{...m,...e,safelist:S(e.safelist)};const{content:t,css:s,extractors:r,safelist:i}=this.options;this.options.variables&&(this.variablesStructure.safelist=i.variables||[]);const o=t.filter((e=>"string"==typeof e)),n=t.filter((e=>"object"==typeof e)),a=await this.extractSelectorsFromFiles(o,r),c=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,x(a,c))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const i of e.nodes){const e=this.getSelectorValue(i);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(p.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(i.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(i.attribute)||A(i,t);break;case"class":r=i,s=t.hasClass(r.value);break;case"id":s=F(i,t);break;case"tag":s=k(i,t);break;default:continue}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk((e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(w(e,"start")?(this.ignore=!0,e.remove()):w(e,"end")&&(this.ignore=!1,e.remove())))))}},exports.defaultOptions=m,exports.mergeExtractorSelectors=x,exports.setOptions=y,exports.standardizeSafelist=S; |
{ | ||
"name": "purgecss", | ||
"version": "4.1.3", | ||
"version": "5.0.0", | ||
"description": "Remove unused css selectors", | ||
@@ -39,12 +39,13 @@ "author": "Ffloriel", | ||
"scripts": { | ||
"test": "echo \"Error: run tests from root\" && exit 1" | ||
"build": "ts-node build.ts", | ||
"test": "jest" | ||
}, | ||
"dependencies": { | ||
"commander": "^8.0.0", | ||
"glob": "^7.1.7", | ||
"postcss": "^8.3.5", | ||
"postcss-selector-parser": "^6.0.6" | ||
"commander": "^9.0.0", | ||
"glob": "^8.0.3", | ||
"postcss": "^8.4.4", | ||
"postcss-selector-parser": "^6.0.7" | ||
}, | ||
"devDependencies": { | ||
"@types/glob": "^7.1.1" | ||
"@types/glob": "^7.2.0" | ||
}, | ||
@@ -57,4 +58,3 @@ "bugs": { | ||
"registry": "https://registry.npmjs.org/" | ||
}, | ||
"gitHead": "37e5053a446880d12fd2f55abfc362c3dac9c49c" | ||
} | ||
} |
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
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
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
54904
6
624
1
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedcommander@9.5.0(transitive)
+ Addedglob@8.1.0(transitive)
+ Addedminimatch@5.1.6(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedcommander@8.3.0(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedglob@7.2.3(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
Updatedcommander@^9.0.0
Updatedglob@^8.0.3
Updatedpostcss@^8.4.4