purgecss-whitelister
Advanced tools
Comparing version 1.2.0 to 2.0.0
156
index.js
@@ -1,109 +0,91 @@ | ||
const listSelectors = require('list-css-selectors'); | ||
const sanitizeArgs = require('list-css-selectors/sanitizeArgs'); | ||
const flattenArray = require('list-css-selectors/flattenArray'); | ||
const cssWhat = require('css-what'); | ||
const attrs = ['equals', 'start', 'end', 'element', 'hyphen', 'any', 'not']; | ||
const { parse } = require('scss-parser') // https://github.com/salesforce-ux/scss-parser | ||
const { readFileSync } = require('fs') | ||
const globAll = require('glob-all') | ||
const shouldParse = ['rule', 'selector', 'block'] | ||
const shouldKeep = ['id', 'class', 'attribute'] | ||
const exts = ['css', 'sass', 'scss', 'less'] | ||
/* | ||
This function parses css file(s) for their selectors, | ||
and massages those selectors into plain text words | ||
so that Purgecss can successfully whitelist them. | ||
function makeWhitelist(filenames) { | ||
filenames = sanitizeArgs(filenames) | ||
if (!filenames.length) return [] | ||
Examples | ||
-------- | ||
.some-class => some-class | ||
#some-id => some-id | ||
[an-attribute] => an-attribute | ||
[data-test='hello'] => data-test, hello | ||
// Create a deep array, each level containing a list of selectors. | ||
const deepArray = filenames.reduce((acc, filename) => { | ||
// Do nothing for non-style files. | ||
const ext = filename.split('.').pop() | ||
if (!exts.includes(ext)) return acc | ||
Arguments: | ||
* `filenames` - An array of strings representing file names | ||
* `list` - For testing purposes only, not officially part | ||
of the API. You can manually pass a list of selectors | ||
and avoid having `makeWhitelist` read file data. | ||
*/ | ||
function makeWhitelist(filenames, list) { | ||
filenames = !list && sanitizeArgs(filenames); | ||
if (!filenames.length && !list) return []; | ||
const fileContents = readFileSync(filename, 'utf-8') | ||
const parsedData = parse(fileContents).value | ||
const selectors = parseStyleAST(parsedData) | ||
return acc.concat(selectors) | ||
}, []) | ||
const selectorErrors = []; | ||
const selectors = list || listSelectors(filenames); | ||
const whitelist = selectors.map(selector => { | ||
let what = []; | ||
// Flatten the array. | ||
const flattenedArray = flattenArray(deepArray) | ||
try { | ||
what = cssWhat(selector); | ||
} catch(e) { | ||
selectorErrors.push({ selector, e }); | ||
} | ||
// Return an array of unique selectors in alphabetical order. | ||
return [...new Set(flattenedArray)].sort() | ||
} | ||
return what.map(arr => extractNames(arr)); | ||
}); | ||
function sanitizeArgs(arr) { | ||
if (!Array.isArray(arr)) arr = [arr] | ||
arr = arr.filter(Boolean) | ||
if (selectorErrors.length) { | ||
console.log(`\n\nErrors with the following selectors (${selectorErrors.length}):`); | ||
console.log('----------------------------------------'); | ||
// Avoids errors if an empty array, no arguments, or falsey things are passed. | ||
if (!arr.length) { | ||
console.log('\n\nNo items for processing. Moving right along...\n\n') | ||
return [] | ||
} | ||
selectorErrors.forEach(({ selector, e }) => { | ||
console.log('Selector:', selector); | ||
console.log('Associated error:'); | ||
console.log(e); | ||
console.log(''); | ||
console.log('*** *** *** *** ***'); | ||
console.log(''); | ||
}); | ||
// Each thing in the array must be a string. | ||
if (arr.some(s => typeof s !== 'string')) throw `Oops! Something passed wasn't a string.` | ||
// Ensure absolute paths for filenames, especially if globs were passed. | ||
arr = globAll.sync(arr, { absolute: true }) | ||
// If, at the end of it all, we have nothing, leave empty-handed. | ||
if (!arr.length) { | ||
console.log('\n\nNo matching files found.\n\n') | ||
return [] | ||
} | ||
const flatWhitelist = flattenArray(whitelist); | ||
return Array.from(new Set(flatWhitelist)); // Remove duplicates. | ||
return arr | ||
} | ||
/* | ||
Iterates through an array from the results of `cssWhat` | ||
and returns names without selector characters such as [., #, >], etc. | ||
function parseStyleAST(arr) { | ||
return arr.reduce((acc, { type, value }) => { | ||
https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes | ||
List of psuedo selectors that can contain other selectors: | ||
* host | ||
* host-context | ||
* not | ||
*/ | ||
function extractNames(arr) { | ||
const newArray = arr.map(({ type, name, value, action, data }) => { | ||
if (type === 'attribute') { | ||
// #id | ||
if (name === 'id') return value; | ||
// Trigger recursion for types that need it. | ||
if (shouldParse.includes(type)) { | ||
return acc.concat(parseStyleAST(value)) | ||
// .class | ||
if (name === 'class') return value; | ||
// Iterate through a type's values to extract selectors. | ||
} else if (shouldKeep.includes(type)) { | ||
return value | ||
.reduce((acc, { type, value }) => { | ||
return (type === 'identifier' && !!value) ? acc.concat(value) : acc | ||
}, acc) | ||
// [attr] | ||
if (action === 'exists') return name; | ||
// Concatenate a type's value if no iteration is needed. | ||
} else if (type === 'identifier' && !!value) { | ||
return acc.concat(value) | ||
/* | ||
Example Action | ||
----------------------- | ||
[attr=val] | 'equals' | ||
[attr^=val] | 'start' | ||
[attr$=val] | 'end' | ||
[attr~=val] | 'element' | ||
[attr|=val] | 'hyphen' | ||
[attr*=val] | 'any' | ||
[attr!=val] | 'not' | ||
*/ | ||
if (attrs.includes(action)) return [name, value]; | ||
// No matches - acc is unchanged. | ||
// This allows us to skip filtering out falsy's later. | ||
} else { | ||
return acc | ||
} | ||
}, []) | ||
} | ||
// tag | ||
if (type === 'tag') return name; | ||
function flattenArray(arr) { | ||
if (!Array.isArray(arr)) return arr | ||
// Pseudo stuffs - recursion! | ||
// Type might be 'pseudo' or 'pseudo-element'. | ||
if (type.includes('pseudo') && Array.isArray(data)) return data.map(arr => extractNames(arr)); | ||
}); | ||
return flattenArray(newArray).filter(Boolean); | ||
return arr.reduce((acc, thing) => { | ||
return Array.isArray(thing) ? acc.concat(flattenArray(thing)) : acc.concat(thing) | ||
}, []) | ||
} | ||
module.exports = makeWhitelist; | ||
module.exports = makeWhitelist |
{ | ||
"name": "purgecss-whitelister", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "A utility for creating whitelists of CSS selectors for use with Purgecss.", | ||
@@ -16,5 +16,5 @@ "main": "index.js", | ||
"dependencies": { | ||
"css-what": "^2.1.0", | ||
"list-css-selectors": "^1.3.0" | ||
"glob-all": "^3.1.0", | ||
"scss-parser": "^1.0.0" | ||
} | ||
} |
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
6587
71
1
+ Addedglob-all@^3.1.0
+ Addedscss-parser@^1.0.0
+ Addedansi-regex@5.0.1(transitive)
+ Addedansi-styles@4.3.0(transitive)
+ Addedcamelcase@5.3.1(transitive)
+ Addedcliui@6.0.0(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addeddecamelize@1.2.0(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedfind-up@4.1.0(transitive)
+ Addedget-caller-file@2.0.5(transitive)
+ Addedglob-all@3.3.1(transitive)
+ Addedinvariant@2.2.4(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedlocate-path@5.0.0(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedloose-envify@1.4.0(transitive)
+ Addedp-limit@2.3.0(transitive)
+ Addedp-locate@4.1.0(transitive)
+ Addedp-try@2.2.0(transitive)
+ Addedpath-exists@4.0.0(transitive)
+ Addedrequire-directory@2.1.1(transitive)
+ Addedrequire-main-filename@2.0.0(transitive)
+ Addedscss-parser@1.0.6(transitive)
+ Addedset-blocking@2.0.0(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedwhich-module@2.0.1(transitive)
+ Addedwrap-ansi@6.2.0(transitive)
+ Addedy18n@4.0.3(transitive)
+ Addedyargs@15.4.1(transitive)
+ Addedyargs-parser@18.1.3(transitive)
- Removedcss-what@^2.1.0
- Removedlist-css-selectors@^1.3.0
- Removedansi-styles@3.2.1(transitive)
- Removedchalk@2.4.2(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedcss-what@2.1.3(transitive)
- Removedescape-string-regexp@1.0.5(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedlist-css-selectors@1.3.0(transitive)
- Removedpostcss@6.0.23(transitive)
- Removedsource-map@0.6.1(transitive)
- Removedsupports-color@5.5.0(transitive)