Comparing version 1.1.0 to 1.2.0
# Changelog | ||
# v1.2.0 - 2019-04-05 | ||
* fix issue https://github.com/FullHuman/purgecss/issues/148, so the default extract is used for any file type that is not specified | ||
* Merged https://github.com/FullHuman/purgecss/pull/167, fixing issue #166 | ||
* Merged https://github.com/FullHuman/purgecss/pull/176, fixing invalid json output by the CLI | ||
* Update dependencies | ||
* Update dev dependencies, including babel 7 | ||
# v1.1.0 - 2018-08-21 | ||
@@ -4,0 +12,0 @@ |
@@ -6,2 +6,73 @@ import fs from 'fs'; | ||
function _typeof(obj) { | ||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { | ||
_typeof = function (obj) { | ||
return typeof obj; | ||
}; | ||
} else { | ||
_typeof = function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
} | ||
return _typeof(obj); | ||
} | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function _toConsumableArray(arr) { | ||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); | ||
} | ||
function _arrayWithoutHoles(arr) { | ||
if (Array.isArray(arr)) { | ||
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} | ||
} | ||
function _iterableToArray(iter) { | ||
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); | ||
} | ||
function _nonIterableSpread() { | ||
throw new TypeError("Invalid attempt to spread non-iterable instance"); | ||
} | ||
// Copyright Joyent, Inc. and other Node contributors. | ||
@@ -27,3 +98,2 @@ // | ||
// USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
// resolves . and .. elements in a path array with directory names there | ||
@@ -36,4 +106,6 @@ // must be no slashes, empty elements, or device names (c:\) in the array | ||
var up = 0; | ||
for (var i = parts.length - 1; i >= 0; i--) { | ||
var last = parts[i]; | ||
if (last === '.') { | ||
@@ -48,5 +120,5 @@ parts.splice(i, 1); | ||
} | ||
} | ||
} // if the path is allowed to go above the root, restore leading ..s | ||
// if the path is allowed to go above the root, restore leading ..s | ||
if (allowAboveRoot) { | ||
@@ -59,13 +131,14 @@ for (; up--; up) { | ||
return parts; | ||
} | ||
} // Split a filename into [root, dir, basename, ext], unix version | ||
// 'root' is just a slash, or nothing. | ||
// Split a filename into [root, dir, basename, ext], unix version | ||
// 'root' is just a slash, or nothing. | ||
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; | ||
var splitPath = function splitPath(filename) { | ||
var splitPath = function (filename) { | ||
return splitPathRe.exec(filename).slice(1); | ||
}; | ||
}; // path.resolve([from ...], to) | ||
// posix version | ||
// path.resolve([from ...], to) | ||
// posix version | ||
function resolve() { | ||
@@ -76,5 +149,4 @@ var resolvedPath = '', | ||
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { | ||
var path = i >= 0 ? arguments[i] : '/'; | ||
var path = i >= 0 ? arguments[i] : '/'; // Skip empty and invalid entries | ||
// Skip empty and invalid entries | ||
if (typeof path !== 'string') { | ||
@@ -88,21 +160,18 @@ throw new TypeError('Arguments to path.resolve must be strings'); | ||
resolvedAbsolute = path.charAt(0) === '/'; | ||
} | ||
// At this point the path should be resolved to a full absolute path, but | ||
} // At this point the path should be resolved to a full absolute path, but | ||
// handle relative paths to be safe (might happen when process.cwd() fails) | ||
// Normalize the path | ||
// Normalize the path | ||
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function (p) { | ||
return !!p; | ||
}), !resolvedAbsolute).join('/'); | ||
return (resolvedAbsolute ? '/' : '') + resolvedPath || '.'; | ||
} | ||
// path.normalize(path) | ||
// posix version | ||
function normalize(path) { | ||
var isPathAbsolute = isAbsolute(path), | ||
trailingSlash = substr(path, -1) === '/'; | ||
trailingSlash = substr(path, -1) === '/'; // Normalize the path | ||
// Normalize the path | ||
path = normalizeArray(filter(path.split('/'), function (p) { | ||
@@ -115,2 +184,3 @@ return !!p; | ||
} | ||
if (path && trailingSlash) { | ||
@@ -122,8 +192,7 @@ path += '/'; | ||
} | ||
// posix version | ||
function isAbsolute(path) { | ||
return path.charAt(0) === '/'; | ||
} | ||
} // posix version | ||
// posix version | ||
function join() { | ||
@@ -135,8 +204,8 @@ var paths = Array.prototype.slice.call(arguments, 0); | ||
} | ||
return p; | ||
}).join('/')); | ||
} | ||
} // path.relative(from, to) | ||
// posix version | ||
// path.relative(from, to) | ||
// posix version | ||
function relative(from, to) { | ||
@@ -148,2 +217,3 @@ from = resolve(from).substr(1); | ||
var start = 0; | ||
for (; start < arr.length; start++) { | ||
@@ -154,2 +224,3 @@ if (arr[start] !== '') break; | ||
var end = arr.length - 1; | ||
for (; end >= 0; end--) { | ||
@@ -165,5 +236,5 @@ if (arr[end] !== '') break; | ||
var toParts = trim(to.split('/')); | ||
var length = Math.min(fromParts.length, toParts.length); | ||
var samePartsLength = length; | ||
for (var i = 0; i < length; i++) { | ||
@@ -177,2 +248,3 @@ if (fromParts[i] !== toParts[i]) { | ||
var outputParts = []; | ||
for (var i = samePartsLength; i < fromParts.length; i++) { | ||
@@ -183,9 +255,6 @@ outputParts.push('..'); | ||
outputParts = outputParts.concat(toParts.slice(samePartsLength)); | ||
return outputParts.join('/'); | ||
} | ||
var sep = '/'; | ||
var delimiter = ':'; | ||
function dirname(path) { | ||
@@ -208,12 +277,11 @@ var result = splitPath(path), | ||
} | ||
function basename(path, ext) { | ||
var f = splitPath(path)[2]; // TODO: make this comparison case-insensitive on windows? | ||
function basename(path, ext) { | ||
var f = splitPath(path)[2]; | ||
// TODO: make this comparison case-insensitive on windows? | ||
if (ext && f.substr(-1 * ext.length) === ext) { | ||
f = f.substr(0, f.length - ext.length); | ||
} | ||
return f; | ||
} | ||
function extname(path) { | ||
@@ -234,12 +302,15 @@ return splitPath(path)[3]; | ||
}; | ||
function filter(xs, f) { | ||
if (xs.filter) return xs.filter(f); | ||
var res = []; | ||
for (var i = 0; i < xs.length; i++) { | ||
if (f(xs[i], i, xs)) res.push(xs[i]); | ||
} | ||
return res; | ||
} | ||
} // String.prototype.substr - negative index don't work in IE8 | ||
// String.prototype.substr - negative index don't work in IE8 | ||
var substr = 'ab'.substr(-1) === 'b' ? function (str, start, len) { | ||
@@ -253,13 +324,12 @@ return str.substr(start, len); | ||
// | ||
var defaultOptions = { | ||
css: [], | ||
content: [], | ||
extractors: [], | ||
whitelist: [], | ||
output: undefined, | ||
stdout: false, | ||
keyframes: false, | ||
fontFace: false, | ||
rejected: false | ||
css: [], | ||
content: [], | ||
extractors: [], | ||
whitelist: [], | ||
output: undefined, | ||
stdout: false, | ||
keyframes: false, | ||
fontFace: false, | ||
rejected: false | ||
}; | ||
@@ -270,5 +340,4 @@ | ||
var IGNORE_ANNOTATION_END = 'purgecss end ignore'; | ||
var CONFIG_FILENAME = 'purgecss.config.js'; | ||
var CONFIG_FILENAME = 'purgecss.config.js'; // Error Message | ||
// Error Message | ||
var ERROR_CONFIG_FILE_LOADING = 'Error loading the config file'; | ||
@@ -278,10 +347,7 @@ var ERROR_MISSING_CONTENT = 'No content provided.'; | ||
var ERROR_EXTRACTER_FAILED = 'The extractor has failed to extract the selectors.'; | ||
var ERROR_OPTIONS_TYPE = 'Error Type Options: expected an object'; | ||
var ERROR_OUTPUT_TYPE = 'Error Type option output: expected a string'; | ||
var ERROR_EXTRACTERS_TYPE = 'Error Type option extractors: expected an array'; | ||
var ERROR_WHITELIST_TYPE = 'Error Type option whitelist: expected an array'; | ||
var ERROR_WHITELIST_PATTERNS_TYPE = 'Error Type option whitelistPatterns: expected an array'; | ||
var ERROR_STDOUT_TYPE = 'Error Type option stdout: expected a boolean'; | ||
@@ -293,778 +359,721 @@ | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
return typeof obj; | ||
} : function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
var DefaultExtractor = | ||
/*#__PURE__*/ | ||
function () { | ||
function DefaultExtractor() { | ||
_classCallCheck(this, DefaultExtractor); | ||
} | ||
}; | ||
var createClass = function () { | ||
function defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
_createClass(DefaultExtractor, null, [{ | ||
key: "extract", | ||
value: function extract(content) { | ||
return content.match(/[A-Za-z0-9_-]+/g) || []; | ||
} | ||
} | ||
}]); | ||
return function (Constructor, protoProps, staticProps) { | ||
if (protoProps) defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
}; | ||
return DefaultExtractor; | ||
}(); | ||
var toConsumableArray = function (arr) { | ||
if (Array.isArray(arr)) { | ||
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
var Purgecss = | ||
/*#__PURE__*/ | ||
function () { | ||
function Purgecss(options) { | ||
_classCallCheck(this, Purgecss); | ||
return arr2; | ||
} else { | ||
return Array.from(arr); | ||
_defineProperty(this, "atRules", { | ||
keyframes: [], | ||
fontFace: [] | ||
}); | ||
_defineProperty(this, "usedAnimations", new Set()); | ||
_defineProperty(this, "usedFontFaces", new Set()); | ||
_defineProperty(this, "selectorsRemoved", new Set()); | ||
_defineProperty(this, "ignore", false); | ||
if (typeof options === 'string' || typeof options === 'undefined') options = this.loadConfigFile(options); | ||
this.checkOptions(options); | ||
this.options = Object.assign(defaultOptions, options); | ||
} | ||
}; | ||
/** | ||
* Load the configuration file from the path | ||
* @param {string} configFile Path of the config file | ||
*/ | ||
var DefaultExtractor = function () { | ||
function DefaultExtractor() { | ||
classCallCheck(this, DefaultExtractor); | ||
} | ||
createClass(DefaultExtractor, null, [{ | ||
key: "extract", | ||
value: function extract(content) { | ||
return content.match(/[A-Za-z0-9_-]+/g) || []; | ||
} | ||
}]); | ||
return DefaultExtractor; | ||
}(); | ||
_createClass(Purgecss, [{ | ||
key: "loadConfigFile", | ||
value: function loadConfigFile(configFile) { | ||
var pathConfig = typeof configFile === 'undefined' ? CONFIG_FILENAME : configFile; | ||
var options; | ||
// | ||
try { | ||
var t = path.resolve(process.cwd(), pathConfig); | ||
options = require(t); | ||
} catch (e) { | ||
throw new Error(ERROR_CONFIG_FILE_LOADING + e.message); | ||
} | ||
var Purgecss = function () { | ||
function Purgecss(options) { | ||
classCallCheck(this, Purgecss); | ||
this.atRules = { | ||
keyframes: [], | ||
fontFace: [] | ||
}; | ||
this.usedAnimations = new Set(); | ||
this.usedFontFaces = new Set(); | ||
this.selectorsRemoved = new Set(); | ||
this.ignore = false; | ||
return options; | ||
} | ||
/** | ||
* Verify that the purgecss options provided are valid | ||
* @param {object} options Purgecss options | ||
*/ | ||
if (typeof options === 'string' || typeof options === 'undefined') options = this.loadConfigFile(options); | ||
this.checkOptions(options); | ||
this.options = Object.assign(defaultOptions, options); | ||
}, { | ||
key: "checkOptions", | ||
value: function checkOptions(options) { | ||
if (_typeof(options) !== 'object') throw new TypeError(ERROR_OPTIONS_TYPE); | ||
if (!options.content || !options.content.length) throw new Error(ERROR_MISSING_CONTENT); | ||
if (!options.css || !options.css.length) throw new Error(ERROR_MISSING_CSS); | ||
if (options.output && typeof options.output !== 'string') throw new TypeError(ERROR_OUTPUT_TYPE); | ||
if (options.extractors && !Array.isArray(options.extractors)) throw new TypeError(ERROR_EXTRACTERS_TYPE); | ||
if (options.whitelist && !Array.isArray(options.whitelist)) throw new TypeError(ERROR_WHITELIST_TYPE); | ||
if (options.stdout && typeof options.stdout !== 'boolean') throw new TypeError(ERROR_STDOUT_TYPE); | ||
if (options.whitelistPatterns && !Array.isArray(options.whitelistPatterns)) throw new TypeError(ERROR_WHITELIST_PATTERNS_TYPE); | ||
} | ||
/** | ||
* Main function that purge the css file | ||
*/ | ||
}, { | ||
key: "purge", | ||
value: function purge() { | ||
// Get selectors from content files | ||
var _this$options = this.options, | ||
content = _this$options.content, | ||
extractors = _this$options.extractors, | ||
css = _this$options.css; | ||
var fileFormatContents = content.filter(function (o) { | ||
return typeof o === 'string'; | ||
}); | ||
var rawFormatContents = content.filter(function (o) { | ||
return _typeof(o) === 'object'; | ||
}); | ||
var cssFileSelectors = this.extractFileSelector(fileFormatContents, extractors); | ||
var cssRawSelectors = this.extractRawSelector(rawFormatContents, extractors); // Get css selectors and remove unused ones | ||
return this.getCssContents(css, new Set([].concat(_toConsumableArray(cssFileSelectors), _toConsumableArray(cssRawSelectors)))); | ||
} | ||
/** | ||
* Load the configuration file from the path | ||
* @param {string} configFile Path of the config file | ||
* Get the content of the css files, or return the raw content | ||
* @param {array} cssOptions Array of css options, files and raw | ||
* @param {Set} cssSelectors Set of all extracted css selectors | ||
*/ | ||
}, { | ||
key: "getCssContents", | ||
value: function getCssContents(cssOptions, cssSelectors) { | ||
var sources = []; // resolve any globs and flatten again | ||
createClass(Purgecss, [{ | ||
key: 'loadConfigFile', | ||
value: function loadConfigFile(configFile) { | ||
var pathConfig = typeof configFile === 'undefined' ? CONFIG_FILENAME : configFile; | ||
var options = void 0; | ||
try { | ||
var t = path.resolve(process.cwd(), pathConfig); | ||
options = require(t); | ||
} catch (e) { | ||
throw new Error(ERROR_CONFIG_FILE_LOADING + e.message); | ||
} | ||
return options; | ||
} | ||
cssOptions = cssOptions.map(function (option) { | ||
return typeof option === 'string' ? glob.sync(option) : option; | ||
}); | ||
cssOptions = [].concat.apply([], cssOptions); | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
/** | ||
* Verify that the purgecss options provided are valid | ||
* @param {object} options Purgecss options | ||
*/ | ||
try { | ||
for (var _iterator = cssOptions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var option = _step.value; | ||
var file = null; | ||
var rejected = null; | ||
var cssContent = ''; | ||
}, { | ||
key: 'checkOptions', | ||
value: function checkOptions(options) { | ||
if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') throw new TypeError(ERROR_OPTIONS_TYPE); | ||
if (!options.content || !options.content.length) throw new Error(ERROR_MISSING_CONTENT); | ||
if (!options.css || !options.css.length) throw new Error(ERROR_MISSING_CSS); | ||
if (options.output && typeof options.output !== 'string') throw new TypeError(ERROR_OUTPUT_TYPE); | ||
if (options.extractors && !Array.isArray(options.extractors)) throw new TypeError(ERROR_EXTRACTERS_TYPE); | ||
if (options.whitelist && !Array.isArray(options.whitelist)) throw new TypeError(ERROR_WHITELIST_TYPE); | ||
if (options.stdout && typeof options.stdout !== 'boolean') throw new TypeError(ERROR_STDOUT_TYPE); | ||
if (options.whitelistPatterns && !Array.isArray(options.whitelistPatterns)) throw new TypeError(ERROR_WHITELIST_PATTERNS_TYPE); | ||
} | ||
if (typeof option === 'string') { | ||
file = option; | ||
cssContent = this.options.stdin ? file : fs.readFileSync(file, 'utf8'); | ||
} else { | ||
cssContent = option.raw; | ||
} | ||
/** | ||
* Main function that purge the css file | ||
*/ | ||
this.root = postcss.parse(cssContent); // purge selectors | ||
}, { | ||
key: 'purge', | ||
value: function purge() { | ||
// Get selectors from content files | ||
var _options = this.options, | ||
content = _options.content, | ||
extractors = _options.extractors, | ||
css = _options.css; | ||
this.getSelectorsCss(cssSelectors); // purge keyframes | ||
if (this.options.keyframes) this.removeUnusedKeyframes(); // purge font face | ||
var fileFormatContents = content.filter(function (o) { | ||
return typeof o === 'string'; | ||
}); | ||
var rawFormatContents = content.filter(function (o) { | ||
return (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === 'object'; | ||
}); | ||
if (this.options.fontFace) this.removeUnusedFontFaces(); | ||
var purgeResult = { | ||
file: file, | ||
css: this.root.toString(), | ||
rejected: rejected | ||
}; | ||
var cssFileSelectors = this.extractFileSelector(fileFormatContents, extractors); | ||
var cssRawSelectors = this.extractRawSelector(rawFormatContents, extractors); | ||
if (this.options.rejected) { | ||
rejected = Array.from(this.selectorsRemoved); | ||
this.selectorsRemoved.clear(); | ||
} | ||
// Get css selectors and remove unused ones | ||
return this.getCssContents(css, new Set([].concat(toConsumableArray(cssFileSelectors), toConsumableArray(cssRawSelectors)))); | ||
purgeResult.rejected = rejected; | ||
sources.push(purgeResult); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
/** | ||
* Get the content of the css files, or return the raw content | ||
* @param {array} cssOptions Array of css options, files and raw | ||
* @param {Set} cssSelectors Set of all extracted css selectors | ||
*/ | ||
return sources; | ||
} | ||
/** | ||
* Remove Keyframes that were never used | ||
*/ | ||
}, { | ||
key: 'getCssContents', | ||
value: function getCssContents(cssOptions, cssSelectors) { | ||
var sources = []; | ||
}, { | ||
key: "removeUnusedKeyframes", | ||
value: function removeUnusedKeyframes() { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
// resolve any globs and flatten again | ||
cssOptions = cssOptions.map(function (option) { | ||
return typeof option === 'string' ? glob.sync(option) : option; | ||
}); | ||
cssOptions = [].concat.apply([], cssOptions); | ||
try { | ||
for (var _iterator2 = this.atRules.keyframes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var node = _step2.value; | ||
var nodeName = node.params; | ||
var used = this.usedAnimations.has(nodeName); | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Remove Font-Faces that were never used | ||
*/ | ||
try { | ||
for (var _iterator = cssOptions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var option = _step.value; | ||
}, { | ||
key: "removeUnusedFontFaces", | ||
value: function removeUnusedFontFaces() { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
var file = null; | ||
var rejected = null; | ||
var cssContent = ''; | ||
if (typeof option === 'string') { | ||
file = option; | ||
cssContent = this.options.stdin ? file : fs.readFileSync(file, 'utf8'); | ||
} else { | ||
cssContent = option.raw; | ||
} | ||
try { | ||
for (var _iterator3 = this.atRules.fontFace[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var _step3$value = _step3.value, | ||
node = _step3$value.node, | ||
name = _step3$value.name; | ||
var used = this.usedFontFaces.has(name); | ||
this.root = postcss.parse(cssContent); | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Extract the selectors present in the passed string using a purgecss extractor | ||
* @param {array} content Array of content | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
// purge selectors | ||
this.getSelectorsCss(cssSelectors); | ||
}, { | ||
key: "extractRawSelector", | ||
value: function extractRawSelector(content, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
// purge keyframes | ||
if (this.options.keyframes) this.removeUnusedKeyframes(); | ||
try { | ||
for (var _iterator4 = content[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var _step4$value = _step4.value, | ||
raw = _step4$value.raw, | ||
extension = _step4$value.extension; | ||
var extractor = this.getFileExtractor(".".concat(extension), extractors); | ||
selectors = new Set([].concat(_toConsumableArray(selectors), _toConsumableArray(this.extractSelectors(raw, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return != null) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
// purge font face | ||
if (this.options.fontFace) this.removeUnusedFontFaces(); | ||
return selectors; | ||
} | ||
/** | ||
* Extract the selectors present in the files using a purgecss extractor | ||
* @param {array} files Array of files path or glob pattern | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
var purgeResult = { | ||
file: file, | ||
css: this.root.toString(), | ||
rejected: rejected | ||
}; | ||
}, { | ||
key: "extractFileSelector", | ||
value: function extractFileSelector(files, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
if (this.options.rejected) { | ||
rejected = Array.from(this.selectorsRemoved); | ||
this.selectorsRemoved.clear(); | ||
} | ||
try { | ||
for (var _iterator5 = files[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var globfile = _step5.value; | ||
var filesnames = []; | ||
purgeResult.rejected = rejected; | ||
if (fs.existsSync(globfile)) { | ||
filesnames.push(globfile); | ||
} else { | ||
filesnames = glob.sync(globfile); | ||
} | ||
sources.push(purgeResult); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator6 = filesnames[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var file = _step6.value; | ||
var content = fs.readFileSync(file, 'utf8'); | ||
var extractor = this.getFileExtractor(file, extractors); | ||
selectors = new Set([].concat(_toConsumableArray(selectors), _toConsumableArray(this.extractSelectors(content, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return != null) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
return sources; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return != null) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
} | ||
} | ||
/** | ||
* Remove Keyframes that were never used | ||
*/ | ||
return selectors; | ||
} | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param {string} filename Name of the file | ||
* @param {array} extractors Array of extractors definition objects | ||
*/ | ||
}, { | ||
key: 'removeUnusedKeyframes', | ||
value: function removeUnusedKeyframes() { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
}, { | ||
key: "getFileExtractor", | ||
value: function getFileExtractor(filename) { | ||
var extractors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
if (!extractors.length) return DefaultExtractor; | ||
var extractorObj = extractors.find(function (extractor) { | ||
return extractor.extensions.find(function (ext) { | ||
return filename.endsWith(ext); | ||
}); | ||
}) || DefaultExtractor; | ||
return extractorObj.extractor; | ||
} | ||
/** | ||
* Use the extract function of the extractor to get the list of selectors | ||
* @param {string} content Content (e.g: html file) | ||
* @param {object} extractor Purgecss extractor use to extract the selector | ||
*/ | ||
try { | ||
for (var _iterator2 = this.atRules.keyframes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var node = _step2.value; | ||
}, { | ||
key: "extractSelectors", | ||
value: function extractSelectors(content, extractor) { | ||
var selectors = new Set(); | ||
var arraySelector = typeof extractor.extract === 'undefined' ? extractor(content) : extractor.extract(content); | ||
var nodeName = node.params; | ||
var used = this.usedAnimations.has(nodeName); | ||
if (arraySelector === null) { | ||
throw new Error(ERROR_EXTRACTER_FAILED); | ||
} | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
arraySelector.forEach(function (selector) { | ||
selectors.add(selector); | ||
}); // Remove empty string | ||
/** | ||
* Remove Font-Faces that were never used | ||
*/ | ||
selectors.delete(''); | ||
return selectors; | ||
} | ||
/** | ||
* Use postcss to walk through the css ast and remove unused css | ||
* @param {*} selectors selectors used in content files | ||
*/ | ||
}, { | ||
key: 'removeUnusedFontFaces', | ||
value: function removeUnusedFontFaces() { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
}, { | ||
key: "getSelectorsCss", | ||
value: function getSelectorsCss(selectors) { | ||
var _this = this; | ||
try { | ||
for (var _iterator3 = this.atRules.fontFace[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var _ref = _step3.value; | ||
var node = _ref.node; | ||
var name = _ref.name; | ||
this.root.walk(function (node) { | ||
if (node.type === 'rule') { | ||
return _this.evaluateRule(node, selectors); | ||
} | ||
var used = this.usedFontFaces.has(name); | ||
if (node.type === 'atrule') { | ||
return _this.evaluateAtRule(node); | ||
} | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
if (node.type === 'comment') { | ||
if (_this.isIgnoreAnnotation(node, 'start')) _this.ignore = true;else if (_this.isIgnoreAnnotation(node, 'end')) _this.ignore = false; | ||
} | ||
}); | ||
} | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* @param {AST} node postcss ast node | ||
* @param {Set} selectors selectors used in content files | ||
*/ | ||
/** | ||
* Extract the selectors present in the passed string using a purgecss extractor | ||
* @param {array} content Array of content | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
}, { | ||
key: "evaluateRule", | ||
value: function evaluateRule(node, selectors) { | ||
var _this2 = this; | ||
}, { | ||
key: 'extractRawSelector', | ||
value: function extractRawSelector(content, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
var annotation = node.prev(); | ||
if (this.isIgnoreAnnotation(annotation, 'next') || this.ignore === true) return; | ||
var keepSelector = true; | ||
node.selector = selectorParser(function (selectorsParsed) { | ||
selectorsParsed.walk(function (selector) { | ||
var selectorsInRule = []; | ||
try { | ||
for (var _iterator4 = content[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var _ref2 = _step4.value; | ||
var raw = _ref2.raw; | ||
var extension = _ref2.extension; | ||
var extractor = this.getFileExtractor('.' + extension, extractors); | ||
selectors = new Set([].concat(toConsumableArray(selectors), toConsumableArray(this.extractSelectors(raw, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
if (selector.type === 'selector') { | ||
// if inside :not pseudo class, ignore | ||
if (selector.parent && selector.parent.value === ':not' && selector.parent.type === 'pseudo') { | ||
return; | ||
} | ||
return selectors; | ||
} | ||
var _iteratorNormalCompletion7 = true; | ||
var _didIteratorError7 = false; | ||
var _iteratorError7 = undefined; | ||
/** | ||
* Extract the selectors present in the files using a purgecss extractor | ||
* @param {array} files Array of files path or glob pattern | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
}, { | ||
key: 'extractFileSelector', | ||
value: function extractFileSelector(files, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
try { | ||
for (var _iterator5 = files[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var globfile = _step5.value; | ||
for (var _iterator7 = selector.nodes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { | ||
var _step7$value = _step7.value, | ||
type = _step7$value.type, | ||
value = _step7$value.value; | ||
var filesnames = []; | ||
if (fs.existsSync(globfile)) { | ||
filesnames.push(globfile); | ||
} else { | ||
filesnames = glob.sync(globfile); | ||
} | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator6 = filesnames[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var file = _step6.value; | ||
var content = fs.readFileSync(file, 'utf8'); | ||
var extractor = this.getFileExtractor(file, extractors); | ||
selectors = new Set([].concat(toConsumableArray(selectors), toConsumableArray(this.extractSelectors(content, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
} | ||
if (SELECTOR_STANDARD_TYPES.includes(type) && typeof value !== 'undefined' && /^\d/g.test(value) === false) { | ||
selectorsInRule.push(value); | ||
} else if (type === 'tag' && !/[+]|n|-|(even)|(odd)|^from$|^to$|^\d/.test(value)) { | ||
// test if we do not have a pseudo class parameter (e.g. 2n in :nth-child(2n)) | ||
selectorsInRule.push(value); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
_didIteratorError7 = true; | ||
_iteratorError7 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
try { | ||
if (!_iteratorNormalCompletion7 && _iterator7.return != null) { | ||
_iterator7.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError7) { | ||
throw _iteratorError7; | ||
} | ||
} | ||
} | ||
return selectors; | ||
} | ||
keepSelector = _this2.shouldKeepSelector(selectors, selectorsInRule); | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param {string} filename Name of the file | ||
* @param {array} extractors Array of extractors definition objects | ||
*/ | ||
}, { | ||
key: 'getFileExtractor', | ||
value: function getFileExtractor(filename) { | ||
var extractors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
if (!extractors.length) return DefaultExtractor; | ||
var extractorObj = extractors.find(function (extractor) { | ||
return extractor.extensions.find(function (ext) { | ||
return filename.endsWith(ext); | ||
}); | ||
}); | ||
return extractorObj.extractor; | ||
} | ||
/** | ||
* Use the extract function of the extractor to get the list of selectors | ||
* @param {string} content Content (e.g: html file) | ||
* @param {object} extractor Purgecss extractor use to extract the selector | ||
*/ | ||
}, { | ||
key: 'extractSelectors', | ||
value: function extractSelectors(content, extractor) { | ||
var selectors = new Set(); | ||
var arraySelector = extractor.extract(content); | ||
if (arraySelector === null) { | ||
throw new Error(ERROR_EXTRACTER_FAILED); | ||
if (!keepSelector) { | ||
if (_this2.options.rejected) _this2.selectorsRemoved.add(selector.toString()); | ||
selector.remove(); | ||
} | ||
arraySelector.forEach(function (selector) { | ||
selectors.add(selector); | ||
}); | ||
// Remove empty string | ||
selectors.delete(''); | ||
return selectors; | ||
} | ||
} | ||
}); | ||
}).processSync(node.selector); // loop declarations | ||
/** | ||
* Use postcss to walk through the css ast and remove unused css | ||
* @param {*} selectors selectors used in content files | ||
*/ | ||
if (keepSelector) { | ||
var _iteratorNormalCompletion8 = true; | ||
var _didIteratorError8 = false; | ||
var _iteratorError8 = undefined; | ||
}, { | ||
key: 'getSelectorsCss', | ||
value: function getSelectorsCss(selectors) { | ||
var _this = this; | ||
try { | ||
for (var _iterator8 = node.nodes[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { | ||
var _step8$value = _step8.value, | ||
prop = _step8$value.prop, | ||
value = _step8$value.value; | ||
this.root.walk(function (node) { | ||
if (node.type === 'rule') { | ||
return _this.evaluateRule(node, selectors); | ||
} | ||
if (node.type === 'atrule') { | ||
return _this.evaluateAtRule(node); | ||
} | ||
if (node.type === 'comment') { | ||
if (_this.isIgnoreAnnotation(node, 'start')) _this.ignore = true;else if (_this.isIgnoreAnnotation(node, 'end')) _this.ignore = false; | ||
} | ||
}); | ||
} | ||
if (this.options.keyframes) { | ||
if (prop === 'animation' || prop === 'animation-name') { | ||
var _iteratorNormalCompletion9 = true; | ||
var _didIteratorError9 = false; | ||
var _iteratorError9 = undefined; | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* @param {AST} node postcss ast node | ||
* @param {Set} selectors selectors used in content files | ||
*/ | ||
}, { | ||
key: 'evaluateRule', | ||
value: function evaluateRule(node, selectors) { | ||
var _this2 = this; | ||
var annotation = node.prev(); | ||
if (this.isIgnoreAnnotation(annotation, 'next') || this.ignore === true) return; | ||
var keepSelector = true; | ||
node.selector = selectorParser(function (selectorsParsed) { | ||
selectorsParsed.walk(function (selector) { | ||
var selectorsInRule = []; | ||
if (selector.type === 'selector') { | ||
// if inside :not pseudo class, ignore | ||
if (selector.parent && selector.parent.value === ':not' && selector.parent.type === 'pseudo') { | ||
return; | ||
} | ||
var _iteratorNormalCompletion7 = true; | ||
var _didIteratorError7 = false; | ||
var _iteratorError7 = undefined; | ||
try { | ||
for (var _iterator7 = selector.nodes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { | ||
var _ref3 = _step7.value; | ||
var type = _ref3.type; | ||
var value = _ref3.value; | ||
if (SELECTOR_STANDARD_TYPES.includes(type) && typeof value !== 'undefined' && /^\d/g.test(value) === false) { | ||
selectorsInRule.push(value); | ||
} else if (type === 'tag' && !/[+]|n|-|(even)|(odd)|^from$|^to$|^\d/.test(value)) { | ||
// test if we do not have a pseudo class parameter (e.g. 2n in :nth-child(2n)) | ||
selectorsInRule.push(value); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError7 = true; | ||
_iteratorError7 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion7 && _iterator7.return) { | ||
_iterator7.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError7) { | ||
throw _iteratorError7; | ||
} | ||
} | ||
} | ||
keepSelector = _this2.shouldKeepSelector(selectors, selectorsInRule); | ||
if (!keepSelector) { | ||
if (_this2.options.rejected) _this2.selectorsRemoved.add(selector.toString()); | ||
selector.remove(); | ||
} | ||
} | ||
}); | ||
}).processSync(node.selector); | ||
// loop declarations | ||
if (keepSelector) { | ||
var _iteratorNormalCompletion8 = true; | ||
var _didIteratorError8 = false; | ||
var _iteratorError8 = undefined; | ||
try { | ||
for (var _iterator8 = node.nodes[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { | ||
var _ref4 = _step8.value; | ||
var prop = _ref4.prop; | ||
var value = _ref4.value; | ||
if (this.options.keyframes) { | ||
if (prop === 'animation' || prop === 'animation-name') { | ||
var _iteratorNormalCompletion9 = true; | ||
var _didIteratorError9 = false; | ||
var _iteratorError9 = undefined; | ||
try { | ||
for (var _iterator9 = value.split(' ')[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { | ||
var word = _step9.value; | ||
this.usedAnimations.add(word); | ||
} | ||
} catch (err) { | ||
_didIteratorError9 = true; | ||
_iteratorError9 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion9 && _iterator9.return) { | ||
_iterator9.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError9) { | ||
throw _iteratorError9; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (this.options.fontFace) { | ||
if (prop === 'font-family') { | ||
this.usedFontFaces.add(value); | ||
} | ||
} | ||
} | ||
for (var _iterator9 = value.split(/[\s,]+/)[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { | ||
var word = _step9.value; | ||
this.usedAnimations.add(word); | ||
} | ||
} catch (err) { | ||
_didIteratorError8 = true; | ||
_iteratorError8 = err; | ||
_didIteratorError9 = true; | ||
_iteratorError9 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion8 && _iterator8.return) { | ||
_iterator8.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError8) { | ||
throw _iteratorError8; | ||
} | ||
try { | ||
if (!_iteratorNormalCompletion9 && _iterator9.return != null) { | ||
_iterator9.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError9) { | ||
throw _iteratorError9; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
var parent = node.parent; | ||
// Remove empty rules | ||
if (!node.selector) node.remove(); | ||
if (this.isRuleEmpty(parent)) parent.remove(); | ||
if (this.options.fontFace) { | ||
if (prop === 'font-family') { | ||
this.usedFontFaces.add(value); | ||
} | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError8 = true; | ||
_iteratorError8 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion8 && _iterator8.return != null) { | ||
_iterator8.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError8) { | ||
throw _iteratorError8; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param {AST} node postcss ast node | ||
*/ | ||
var parent = node.parent; // Remove empty rules | ||
}, { | ||
key: 'evaluateAtRule', | ||
value: function evaluateAtRule(node) { | ||
if (this.options.keyframes && node.name.endsWith('keyframes')) { | ||
this.atRules.keyframes.push(node); | ||
return; | ||
} | ||
if (!node.selector) node.remove(); | ||
if (this.isRuleEmpty(parent)) parent.remove(); | ||
} | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param {AST} node postcss ast node | ||
*/ | ||
if (this.options.fontFace && node.name === 'font-face') { | ||
var _iteratorNormalCompletion10 = true; | ||
var _didIteratorError10 = false; | ||
var _iteratorError10 = undefined; | ||
}, { | ||
key: "evaluateAtRule", | ||
value: function evaluateAtRule(node) { | ||
if (this.options.keyframes && node.name.endsWith('keyframes')) { | ||
this.atRules.keyframes.push(node); | ||
return; | ||
} | ||
try { | ||
for (var _iterator10 = node.nodes[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { | ||
var _ref5 = _step10.value; | ||
var prop = _ref5.prop; | ||
var value = _ref5.value; | ||
if (this.options.fontFace && node.name === 'font-face') { | ||
var _iteratorNormalCompletion10 = true; | ||
var _didIteratorError10 = false; | ||
var _iteratorError10 = undefined; | ||
if (prop === 'font-family') { | ||
this.atRules.fontFace.push({ | ||
name: value, | ||
node: node | ||
}); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError10 = true; | ||
_iteratorError10 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion10 && _iterator10.return) { | ||
_iterator10.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError10) { | ||
throw _iteratorError10; | ||
} | ||
} | ||
} | ||
try { | ||
for (var _iterator10 = node.nodes[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { | ||
var _step10$value = _step10.value, | ||
prop = _step10$value.prop, | ||
value = _step10$value.value; | ||
return; | ||
if (prop === 'font-family') { | ||
this.atRules.fontFace.push({ | ||
name: value, | ||
node: node | ||
}); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError10 = true; | ||
_iteratorError10 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion10 && _iterator10.return != null) { | ||
_iterator10.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError10) { | ||
throw _iteratorError10; | ||
} | ||
} | ||
} | ||
/** | ||
* Check if the node is a css comment to ignore the selector rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
* @param {string} type Type of css comment (next, start, end) | ||
*/ | ||
return; | ||
} | ||
} | ||
/** | ||
* Check if the node is a css comment to ignore the selector rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
* @param {string} type Type of css comment (next, start, end) | ||
*/ | ||
}, { | ||
key: 'isIgnoreAnnotation', | ||
value: function isIgnoreAnnotation(node, type) { | ||
if (node && node.type === 'comment') { | ||
switch (type) { | ||
case 'next': | ||
return node.text.includes(IGNORE_ANNOTATION_NEXT); | ||
case 'start': | ||
return node.text.includes(IGNORE_ANNOTATION_START); | ||
case 'end': | ||
return node.text.includes(IGNORE_ANNOTATION_END); | ||
} | ||
} | ||
return false; | ||
} | ||
}, { | ||
key: "isIgnoreAnnotation", | ||
value: function isIgnoreAnnotation(node, type) { | ||
if (node && node.type === 'comment') { | ||
switch (type) { | ||
case 'next': | ||
return node.text.includes(IGNORE_ANNOTATION_NEXT); | ||
/** | ||
* Check if the node correspond to an empty css rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
*/ | ||
case 'start': | ||
return node.text.includes(IGNORE_ANNOTATION_START); | ||
}, { | ||
key: 'isRuleEmpty', | ||
value: function isRuleEmpty(node) { | ||
if (node.type === 'decl' && !node.value || node.type === 'rule' && !node.selector || node.nodes && !node.nodes.length || node.type === 'atrule' && (!node.nodes && !node.params || !node.params && !node.nodes.length)) { | ||
return true; | ||
} | ||
return false; | ||
case 'end': | ||
return node.text.includes(IGNORE_ANNOTATION_END); | ||
} | ||
} | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* @param {Set} selectorsInContent Set of css selectors found in the content files | ||
* @param {Array} selectorsInRule Array of selectors | ||
*/ | ||
return false; | ||
} | ||
/** | ||
* Check if the node correspond to an empty css rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
*/ | ||
}, { | ||
key: 'shouldKeepSelector', | ||
value: function shouldKeepSelector(selectorsInContent, selectorsInRule) { | ||
var _iteratorNormalCompletion11 = true; | ||
var _didIteratorError11 = false; | ||
var _iteratorError11 = undefined; | ||
}, { | ||
key: "isRuleEmpty", | ||
value: function isRuleEmpty(node) { | ||
if (node.type === 'decl' && !node.value || node.type === 'rule' && !node.selector || node.nodes && !node.nodes.length || node.type === 'atrule' && (!node.nodes && !node.params || !node.params && !node.nodes.length)) { | ||
return true; | ||
} | ||
try { | ||
for (var _iterator11 = selectorsInRule[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { | ||
var selector = _step11.value; | ||
return false; | ||
} | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* @param {Set} selectorsInContent Set of css selectors found in the content files | ||
* @param {Array} selectorsInRule Array of selectors | ||
*/ | ||
// pseudo class | ||
var unescapedSelector = selector.replace(/\\/g, ''); | ||
}, { | ||
key: "shouldKeepSelector", | ||
value: function shouldKeepSelector(selectorsInContent, selectorsInRule) { | ||
var _iteratorNormalCompletion11 = true; | ||
var _didIteratorError11 = false; | ||
var _iteratorError11 = undefined; | ||
if (unescapedSelector.startsWith(':')) { | ||
continue; | ||
} | ||
try { | ||
for (var _iterator11 = selectorsInRule[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { | ||
var selector = _step11.value; | ||
// pseudo class | ||
var unescapedSelector = selector.replace(/\\/g, ''); | ||
// If the selector is whitelisted with children keep, simply | ||
// returns true to keep all children selectors | ||
if (this.isSelectorWhitelistedChildren(unescapedSelector)) { | ||
return true; | ||
} | ||
if (unescapedSelector.startsWith(':')) { | ||
continue; | ||
} // If the selector is whitelisted with children keep, simply | ||
// returns true to keep all children selectors | ||
if (!(selectorsInContent.has(unescapedSelector) || CSS_WHITELIST.includes(unescapedSelector) || this.isSelectorWhitelisted(unescapedSelector))) { | ||
return false; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError11 = true; | ||
_iteratorError11 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion11 && _iterator11.return) { | ||
_iterator11.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError11) { | ||
throw _iteratorError11; | ||
} | ||
} | ||
} | ||
if (this.isSelectorWhitelistedChildren(unescapedSelector)) { | ||
return true; | ||
} | ||
if (!(selectorsInContent.has(unescapedSelector) || CSS_WHITELIST.includes(unescapedSelector) || this.isSelectorWhitelisted(unescapedSelector))) { | ||
return false; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError11 = true; | ||
_iteratorError11 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion11 && _iterator11.return != null) { | ||
_iterator11.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError11) { | ||
throw _iteratorError11; | ||
} | ||
} | ||
} | ||
/** | ||
* Check if the selector is whitelisted by the options whitelist or whitelistPatterns | ||
* @param {string} selector css selector | ||
*/ | ||
return true; | ||
} | ||
/** | ||
* Check if the selector is whitelisted by the options whitelist or whitelistPatterns | ||
* @param {string} selector css selector | ||
*/ | ||
}, { | ||
key: 'isSelectorWhitelisted', | ||
value: function isSelectorWhitelisted(selector) { | ||
return !!(CSS_WHITELIST.includes(selector) || this.options.whitelist && this.options.whitelist.some(function (v) { | ||
return v === selector; | ||
}) || this.options.whitelistPatterns && this.options.whitelistPatterns.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
}, { | ||
key: "isSelectorWhitelisted", | ||
value: function isSelectorWhitelisted(selector) { | ||
return !!(CSS_WHITELIST.includes(selector) || this.options.whitelist && this.options.whitelist.some(function (v) { | ||
return v === selector; | ||
}) || this.options.whitelistPatterns && this.options.whitelistPatterns.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
/** | ||
* Check if the selector is whitelisted by the whitelistPatternsChildren | ||
* options element | ||
* | ||
* @param {string} selector | ||
*/ | ||
/** | ||
* Check if the selector is whitelisted by the whitelistPatternsChildren | ||
* options element | ||
* | ||
* @param {string} selector | ||
*/ | ||
}, { | ||
key: "isSelectorWhitelistedChildren", | ||
value: function isSelectorWhitelistedChildren(selector) { | ||
return !!(this.options.whitelistPatternsChildren && this.options.whitelistPatternsChildren.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
}]); | ||
}, { | ||
key: 'isSelectorWhitelistedChildren', | ||
value: function isSelectorWhitelistedChildren(selector) { | ||
return !!(this.options.whitelistPatternsChildren && this.options.whitelistPatternsChildren.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
}]); | ||
return Purgecss; | ||
return Purgecss; | ||
}(); | ||
export default Purgecss; |
1437
lib/purgecss.js
@@ -10,2 +10,73 @@ 'use strict'; | ||
function _typeof(obj) { | ||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { | ||
_typeof = function (obj) { | ||
return typeof obj; | ||
}; | ||
} else { | ||
_typeof = function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
} | ||
return _typeof(obj); | ||
} | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function _toConsumableArray(arr) { | ||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); | ||
} | ||
function _arrayWithoutHoles(arr) { | ||
if (Array.isArray(arr)) { | ||
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} | ||
} | ||
function _iterableToArray(iter) { | ||
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); | ||
} | ||
function _nonIterableSpread() { | ||
throw new TypeError("Invalid attempt to spread non-iterable instance"); | ||
} | ||
// Copyright Joyent, Inc. and other Node contributors. | ||
@@ -31,3 +102,2 @@ // | ||
// USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
// resolves . and .. elements in a path array with directory names there | ||
@@ -40,4 +110,6 @@ // must be no slashes, empty elements, or device names (c:\) in the array | ||
var up = 0; | ||
for (var i = parts.length - 1; i >= 0; i--) { | ||
var last = parts[i]; | ||
if (last === '.') { | ||
@@ -52,5 +124,5 @@ parts.splice(i, 1); | ||
} | ||
} | ||
} // if the path is allowed to go above the root, restore leading ..s | ||
// if the path is allowed to go above the root, restore leading ..s | ||
if (allowAboveRoot) { | ||
@@ -63,13 +135,14 @@ for (; up--; up) { | ||
return parts; | ||
} | ||
} // Split a filename into [root, dir, basename, ext], unix version | ||
// 'root' is just a slash, or nothing. | ||
// Split a filename into [root, dir, basename, ext], unix version | ||
// 'root' is just a slash, or nothing. | ||
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; | ||
var splitPath = function splitPath(filename) { | ||
var splitPath = function (filename) { | ||
return splitPathRe.exec(filename).slice(1); | ||
}; | ||
}; // path.resolve([from ...], to) | ||
// posix version | ||
// path.resolve([from ...], to) | ||
// posix version | ||
function resolve() { | ||
@@ -80,5 +153,4 @@ var resolvedPath = '', | ||
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { | ||
var path = i >= 0 ? arguments[i] : '/'; | ||
var path = i >= 0 ? arguments[i] : '/'; // Skip empty and invalid entries | ||
// Skip empty and invalid entries | ||
if (typeof path !== 'string') { | ||
@@ -92,21 +164,18 @@ throw new TypeError('Arguments to path.resolve must be strings'); | ||
resolvedAbsolute = path.charAt(0) === '/'; | ||
} | ||
// At this point the path should be resolved to a full absolute path, but | ||
} // At this point the path should be resolved to a full absolute path, but | ||
// handle relative paths to be safe (might happen when process.cwd() fails) | ||
// Normalize the path | ||
// Normalize the path | ||
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function (p) { | ||
return !!p; | ||
}), !resolvedAbsolute).join('/'); | ||
return (resolvedAbsolute ? '/' : '') + resolvedPath || '.'; | ||
} | ||
// path.normalize(path) | ||
// posix version | ||
function normalize(path) { | ||
var isPathAbsolute = isAbsolute(path), | ||
trailingSlash = substr(path, -1) === '/'; | ||
trailingSlash = substr(path, -1) === '/'; // Normalize the path | ||
// Normalize the path | ||
path = normalizeArray(filter(path.split('/'), function (p) { | ||
@@ -119,2 +188,3 @@ return !!p; | ||
} | ||
if (path && trailingSlash) { | ||
@@ -126,8 +196,7 @@ path += '/'; | ||
} | ||
// posix version | ||
function isAbsolute(path) { | ||
return path.charAt(0) === '/'; | ||
} | ||
} // posix version | ||
// posix version | ||
function join() { | ||
@@ -139,8 +208,8 @@ var paths = Array.prototype.slice.call(arguments, 0); | ||
} | ||
return p; | ||
}).join('/')); | ||
} | ||
} // path.relative(from, to) | ||
// posix version | ||
// path.relative(from, to) | ||
// posix version | ||
function relative(from, to) { | ||
@@ -152,2 +221,3 @@ from = resolve(from).substr(1); | ||
var start = 0; | ||
for (; start < arr.length; start++) { | ||
@@ -158,2 +228,3 @@ if (arr[start] !== '') break; | ||
var end = arr.length - 1; | ||
for (; end >= 0; end--) { | ||
@@ -169,5 +240,5 @@ if (arr[end] !== '') break; | ||
var toParts = trim(to.split('/')); | ||
var length = Math.min(fromParts.length, toParts.length); | ||
var samePartsLength = length; | ||
for (var i = 0; i < length; i++) { | ||
@@ -181,2 +252,3 @@ if (fromParts[i] !== toParts[i]) { | ||
var outputParts = []; | ||
for (var i = samePartsLength; i < fromParts.length; i++) { | ||
@@ -187,9 +259,6 @@ outputParts.push('..'); | ||
outputParts = outputParts.concat(toParts.slice(samePartsLength)); | ||
return outputParts.join('/'); | ||
} | ||
var sep = '/'; | ||
var delimiter = ':'; | ||
function dirname(path) { | ||
@@ -212,12 +281,11 @@ var result = splitPath(path), | ||
} | ||
function basename(path, ext) { | ||
var f = splitPath(path)[2]; // TODO: make this comparison case-insensitive on windows? | ||
function basename(path, ext) { | ||
var f = splitPath(path)[2]; | ||
// TODO: make this comparison case-insensitive on windows? | ||
if (ext && f.substr(-1 * ext.length) === ext) { | ||
f = f.substr(0, f.length - ext.length); | ||
} | ||
return f; | ||
} | ||
function extname(path) { | ||
@@ -238,12 +306,15 @@ return splitPath(path)[3]; | ||
}; | ||
function filter(xs, f) { | ||
if (xs.filter) return xs.filter(f); | ||
var res = []; | ||
for (var i = 0; i < xs.length; i++) { | ||
if (f(xs[i], i, xs)) res.push(xs[i]); | ||
} | ||
return res; | ||
} | ||
} // String.prototype.substr - negative index don't work in IE8 | ||
// String.prototype.substr - negative index don't work in IE8 | ||
var substr = 'ab'.substr(-1) === 'b' ? function (str, start, len) { | ||
@@ -257,13 +328,12 @@ return str.substr(start, len); | ||
// | ||
var defaultOptions = { | ||
css: [], | ||
content: [], | ||
extractors: [], | ||
whitelist: [], | ||
output: undefined, | ||
stdout: false, | ||
keyframes: false, | ||
fontFace: false, | ||
rejected: false | ||
css: [], | ||
content: [], | ||
extractors: [], | ||
whitelist: [], | ||
output: undefined, | ||
stdout: false, | ||
keyframes: false, | ||
fontFace: false, | ||
rejected: false | ||
}; | ||
@@ -274,5 +344,4 @@ | ||
var IGNORE_ANNOTATION_END = 'purgecss end ignore'; | ||
var CONFIG_FILENAME = 'purgecss.config.js'; | ||
var CONFIG_FILENAME = 'purgecss.config.js'; // Error Message | ||
// Error Message | ||
var ERROR_CONFIG_FILE_LOADING = 'Error loading the config file'; | ||
@@ -282,10 +351,7 @@ var ERROR_MISSING_CONTENT = 'No content provided.'; | ||
var ERROR_EXTRACTER_FAILED = 'The extractor has failed to extract the selectors.'; | ||
var ERROR_OPTIONS_TYPE = 'Error Type Options: expected an object'; | ||
var ERROR_OUTPUT_TYPE = 'Error Type option output: expected a string'; | ||
var ERROR_EXTRACTERS_TYPE = 'Error Type option extractors: expected an array'; | ||
var ERROR_WHITELIST_TYPE = 'Error Type option whitelist: expected an array'; | ||
var ERROR_WHITELIST_PATTERNS_TYPE = 'Error Type option whitelistPatterns: expected an array'; | ||
var ERROR_STDOUT_TYPE = 'Error Type option stdout: expected a boolean'; | ||
@@ -297,778 +363,721 @@ | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
return typeof obj; | ||
} : function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
var classCallCheck = function (instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
var DefaultExtractor = | ||
/*#__PURE__*/ | ||
function () { | ||
function DefaultExtractor() { | ||
_classCallCheck(this, DefaultExtractor); | ||
} | ||
}; | ||
var createClass = function () { | ||
function defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
_createClass(DefaultExtractor, null, [{ | ||
key: "extract", | ||
value: function extract(content) { | ||
return content.match(/[A-Za-z0-9_-]+/g) || []; | ||
} | ||
} | ||
}]); | ||
return function (Constructor, protoProps, staticProps) { | ||
if (protoProps) defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
}; | ||
return DefaultExtractor; | ||
}(); | ||
var toConsumableArray = function (arr) { | ||
if (Array.isArray(arr)) { | ||
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; | ||
var Purgecss = | ||
/*#__PURE__*/ | ||
function () { | ||
function Purgecss(options) { | ||
_classCallCheck(this, Purgecss); | ||
return arr2; | ||
} else { | ||
return Array.from(arr); | ||
_defineProperty(this, "atRules", { | ||
keyframes: [], | ||
fontFace: [] | ||
}); | ||
_defineProperty(this, "usedAnimations", new Set()); | ||
_defineProperty(this, "usedFontFaces", new Set()); | ||
_defineProperty(this, "selectorsRemoved", new Set()); | ||
_defineProperty(this, "ignore", false); | ||
if (typeof options === 'string' || typeof options === 'undefined') options = this.loadConfigFile(options); | ||
this.checkOptions(options); | ||
this.options = Object.assign(defaultOptions, options); | ||
} | ||
}; | ||
/** | ||
* Load the configuration file from the path | ||
* @param {string} configFile Path of the config file | ||
*/ | ||
var DefaultExtractor = function () { | ||
function DefaultExtractor() { | ||
classCallCheck(this, DefaultExtractor); | ||
} | ||
createClass(DefaultExtractor, null, [{ | ||
key: "extract", | ||
value: function extract(content) { | ||
return content.match(/[A-Za-z0-9_-]+/g) || []; | ||
} | ||
}]); | ||
return DefaultExtractor; | ||
}(); | ||
_createClass(Purgecss, [{ | ||
key: "loadConfigFile", | ||
value: function loadConfigFile(configFile) { | ||
var pathConfig = typeof configFile === 'undefined' ? CONFIG_FILENAME : configFile; | ||
var options; | ||
// | ||
try { | ||
var t = path.resolve(process.cwd(), pathConfig); | ||
options = require(t); | ||
} catch (e) { | ||
throw new Error(ERROR_CONFIG_FILE_LOADING + e.message); | ||
} | ||
var Purgecss = function () { | ||
function Purgecss(options) { | ||
classCallCheck(this, Purgecss); | ||
this.atRules = { | ||
keyframes: [], | ||
fontFace: [] | ||
}; | ||
this.usedAnimations = new Set(); | ||
this.usedFontFaces = new Set(); | ||
this.selectorsRemoved = new Set(); | ||
this.ignore = false; | ||
return options; | ||
} | ||
/** | ||
* Verify that the purgecss options provided are valid | ||
* @param {object} options Purgecss options | ||
*/ | ||
if (typeof options === 'string' || typeof options === 'undefined') options = this.loadConfigFile(options); | ||
this.checkOptions(options); | ||
this.options = Object.assign(defaultOptions, options); | ||
}, { | ||
key: "checkOptions", | ||
value: function checkOptions(options) { | ||
if (_typeof(options) !== 'object') throw new TypeError(ERROR_OPTIONS_TYPE); | ||
if (!options.content || !options.content.length) throw new Error(ERROR_MISSING_CONTENT); | ||
if (!options.css || !options.css.length) throw new Error(ERROR_MISSING_CSS); | ||
if (options.output && typeof options.output !== 'string') throw new TypeError(ERROR_OUTPUT_TYPE); | ||
if (options.extractors && !Array.isArray(options.extractors)) throw new TypeError(ERROR_EXTRACTERS_TYPE); | ||
if (options.whitelist && !Array.isArray(options.whitelist)) throw new TypeError(ERROR_WHITELIST_TYPE); | ||
if (options.stdout && typeof options.stdout !== 'boolean') throw new TypeError(ERROR_STDOUT_TYPE); | ||
if (options.whitelistPatterns && !Array.isArray(options.whitelistPatterns)) throw new TypeError(ERROR_WHITELIST_PATTERNS_TYPE); | ||
} | ||
/** | ||
* Main function that purge the css file | ||
*/ | ||
}, { | ||
key: "purge", | ||
value: function purge() { | ||
// Get selectors from content files | ||
var _this$options = this.options, | ||
content = _this$options.content, | ||
extractors = _this$options.extractors, | ||
css = _this$options.css; | ||
var fileFormatContents = content.filter(function (o) { | ||
return typeof o === 'string'; | ||
}); | ||
var rawFormatContents = content.filter(function (o) { | ||
return _typeof(o) === 'object'; | ||
}); | ||
var cssFileSelectors = this.extractFileSelector(fileFormatContents, extractors); | ||
var cssRawSelectors = this.extractRawSelector(rawFormatContents, extractors); // Get css selectors and remove unused ones | ||
return this.getCssContents(css, new Set([].concat(_toConsumableArray(cssFileSelectors), _toConsumableArray(cssRawSelectors)))); | ||
} | ||
/** | ||
* Load the configuration file from the path | ||
* @param {string} configFile Path of the config file | ||
* Get the content of the css files, or return the raw content | ||
* @param {array} cssOptions Array of css options, files and raw | ||
* @param {Set} cssSelectors Set of all extracted css selectors | ||
*/ | ||
}, { | ||
key: "getCssContents", | ||
value: function getCssContents(cssOptions, cssSelectors) { | ||
var sources = []; // resolve any globs and flatten again | ||
createClass(Purgecss, [{ | ||
key: 'loadConfigFile', | ||
value: function loadConfigFile(configFile) { | ||
var pathConfig = typeof configFile === 'undefined' ? CONFIG_FILENAME : configFile; | ||
var options = void 0; | ||
try { | ||
var t = path.resolve(process.cwd(), pathConfig); | ||
options = require(t); | ||
} catch (e) { | ||
throw new Error(ERROR_CONFIG_FILE_LOADING + e.message); | ||
} | ||
return options; | ||
} | ||
cssOptions = cssOptions.map(function (option) { | ||
return typeof option === 'string' ? glob.sync(option) : option; | ||
}); | ||
cssOptions = [].concat.apply([], cssOptions); | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
/** | ||
* Verify that the purgecss options provided are valid | ||
* @param {object} options Purgecss options | ||
*/ | ||
try { | ||
for (var _iterator = cssOptions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var option = _step.value; | ||
var file = null; | ||
var rejected = null; | ||
var cssContent = ''; | ||
}, { | ||
key: 'checkOptions', | ||
value: function checkOptions(options) { | ||
if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') throw new TypeError(ERROR_OPTIONS_TYPE); | ||
if (!options.content || !options.content.length) throw new Error(ERROR_MISSING_CONTENT); | ||
if (!options.css || !options.css.length) throw new Error(ERROR_MISSING_CSS); | ||
if (options.output && typeof options.output !== 'string') throw new TypeError(ERROR_OUTPUT_TYPE); | ||
if (options.extractors && !Array.isArray(options.extractors)) throw new TypeError(ERROR_EXTRACTERS_TYPE); | ||
if (options.whitelist && !Array.isArray(options.whitelist)) throw new TypeError(ERROR_WHITELIST_TYPE); | ||
if (options.stdout && typeof options.stdout !== 'boolean') throw new TypeError(ERROR_STDOUT_TYPE); | ||
if (options.whitelistPatterns && !Array.isArray(options.whitelistPatterns)) throw new TypeError(ERROR_WHITELIST_PATTERNS_TYPE); | ||
} | ||
if (typeof option === 'string') { | ||
file = option; | ||
cssContent = this.options.stdin ? file : fs.readFileSync(file, 'utf8'); | ||
} else { | ||
cssContent = option.raw; | ||
} | ||
/** | ||
* Main function that purge the css file | ||
*/ | ||
this.root = postcss.parse(cssContent); // purge selectors | ||
}, { | ||
key: 'purge', | ||
value: function purge() { | ||
// Get selectors from content files | ||
var _options = this.options, | ||
content = _options.content, | ||
extractors = _options.extractors, | ||
css = _options.css; | ||
this.getSelectorsCss(cssSelectors); // purge keyframes | ||
if (this.options.keyframes) this.removeUnusedKeyframes(); // purge font face | ||
var fileFormatContents = content.filter(function (o) { | ||
return typeof o === 'string'; | ||
}); | ||
var rawFormatContents = content.filter(function (o) { | ||
return (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === 'object'; | ||
}); | ||
if (this.options.fontFace) this.removeUnusedFontFaces(); | ||
var purgeResult = { | ||
file: file, | ||
css: this.root.toString(), | ||
rejected: rejected | ||
}; | ||
var cssFileSelectors = this.extractFileSelector(fileFormatContents, extractors); | ||
var cssRawSelectors = this.extractRawSelector(rawFormatContents, extractors); | ||
if (this.options.rejected) { | ||
rejected = Array.from(this.selectorsRemoved); | ||
this.selectorsRemoved.clear(); | ||
} | ||
// Get css selectors and remove unused ones | ||
return this.getCssContents(css, new Set([].concat(toConsumableArray(cssFileSelectors), toConsumableArray(cssRawSelectors)))); | ||
purgeResult.rejected = rejected; | ||
sources.push(purgeResult); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
/** | ||
* Get the content of the css files, or return the raw content | ||
* @param {array} cssOptions Array of css options, files and raw | ||
* @param {Set} cssSelectors Set of all extracted css selectors | ||
*/ | ||
return sources; | ||
} | ||
/** | ||
* Remove Keyframes that were never used | ||
*/ | ||
}, { | ||
key: 'getCssContents', | ||
value: function getCssContents(cssOptions, cssSelectors) { | ||
var sources = []; | ||
}, { | ||
key: "removeUnusedKeyframes", | ||
value: function removeUnusedKeyframes() { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
// resolve any globs and flatten again | ||
cssOptions = cssOptions.map(function (option) { | ||
return typeof option === 'string' ? glob.sync(option) : option; | ||
}); | ||
cssOptions = [].concat.apply([], cssOptions); | ||
try { | ||
for (var _iterator2 = this.atRules.keyframes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var node = _step2.value; | ||
var nodeName = node.params; | ||
var used = this.usedAnimations.has(nodeName); | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Remove Font-Faces that were never used | ||
*/ | ||
try { | ||
for (var _iterator = cssOptions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var option = _step.value; | ||
}, { | ||
key: "removeUnusedFontFaces", | ||
value: function removeUnusedFontFaces() { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
var file = null; | ||
var rejected = null; | ||
var cssContent = ''; | ||
if (typeof option === 'string') { | ||
file = option; | ||
cssContent = this.options.stdin ? file : fs.readFileSync(file, 'utf8'); | ||
} else { | ||
cssContent = option.raw; | ||
} | ||
try { | ||
for (var _iterator3 = this.atRules.fontFace[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var _step3$value = _step3.value, | ||
node = _step3$value.node, | ||
name = _step3$value.name; | ||
var used = this.usedFontFaces.has(name); | ||
this.root = postcss.parse(cssContent); | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Extract the selectors present in the passed string using a purgecss extractor | ||
* @param {array} content Array of content | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
// purge selectors | ||
this.getSelectorsCss(cssSelectors); | ||
}, { | ||
key: "extractRawSelector", | ||
value: function extractRawSelector(content, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
// purge keyframes | ||
if (this.options.keyframes) this.removeUnusedKeyframes(); | ||
try { | ||
for (var _iterator4 = content[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var _step4$value = _step4.value, | ||
raw = _step4$value.raw, | ||
extension = _step4$value.extension; | ||
var extractor = this.getFileExtractor(".".concat(extension), extractors); | ||
selectors = new Set([].concat(_toConsumableArray(selectors), _toConsumableArray(this.extractSelectors(raw, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return != null) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
// purge font face | ||
if (this.options.fontFace) this.removeUnusedFontFaces(); | ||
return selectors; | ||
} | ||
/** | ||
* Extract the selectors present in the files using a purgecss extractor | ||
* @param {array} files Array of files path or glob pattern | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
var purgeResult = { | ||
file: file, | ||
css: this.root.toString(), | ||
rejected: rejected | ||
}; | ||
}, { | ||
key: "extractFileSelector", | ||
value: function extractFileSelector(files, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
if (this.options.rejected) { | ||
rejected = Array.from(this.selectorsRemoved); | ||
this.selectorsRemoved.clear(); | ||
} | ||
try { | ||
for (var _iterator5 = files[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var globfile = _step5.value; | ||
var filesnames = []; | ||
purgeResult.rejected = rejected; | ||
if (fs.existsSync(globfile)) { | ||
filesnames.push(globfile); | ||
} else { | ||
filesnames = glob.sync(globfile); | ||
} | ||
sources.push(purgeResult); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator6 = filesnames[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var file = _step6.value; | ||
var content = fs.readFileSync(file, 'utf8'); | ||
var extractor = this.getFileExtractor(file, extractors); | ||
selectors = new Set([].concat(_toConsumableArray(selectors), _toConsumableArray(this.extractSelectors(content, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return != null) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
return sources; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return != null) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
} | ||
} | ||
/** | ||
* Remove Keyframes that were never used | ||
*/ | ||
return selectors; | ||
} | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param {string} filename Name of the file | ||
* @param {array} extractors Array of extractors definition objects | ||
*/ | ||
}, { | ||
key: 'removeUnusedKeyframes', | ||
value: function removeUnusedKeyframes() { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
}, { | ||
key: "getFileExtractor", | ||
value: function getFileExtractor(filename) { | ||
var extractors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
if (!extractors.length) return DefaultExtractor; | ||
var extractorObj = extractors.find(function (extractor) { | ||
return extractor.extensions.find(function (ext) { | ||
return filename.endsWith(ext); | ||
}); | ||
}) || DefaultExtractor; | ||
return extractorObj.extractor; | ||
} | ||
/** | ||
* Use the extract function of the extractor to get the list of selectors | ||
* @param {string} content Content (e.g: html file) | ||
* @param {object} extractor Purgecss extractor use to extract the selector | ||
*/ | ||
try { | ||
for (var _iterator2 = this.atRules.keyframes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var node = _step2.value; | ||
}, { | ||
key: "extractSelectors", | ||
value: function extractSelectors(content, extractor) { | ||
var selectors = new Set(); | ||
var arraySelector = typeof extractor.extract === 'undefined' ? extractor(content) : extractor.extract(content); | ||
var nodeName = node.params; | ||
var used = this.usedAnimations.has(nodeName); | ||
if (arraySelector === null) { | ||
throw new Error(ERROR_EXTRACTER_FAILED); | ||
} | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
arraySelector.forEach(function (selector) { | ||
selectors.add(selector); | ||
}); // Remove empty string | ||
/** | ||
* Remove Font-Faces that were never used | ||
*/ | ||
selectors.delete(''); | ||
return selectors; | ||
} | ||
/** | ||
* Use postcss to walk through the css ast and remove unused css | ||
* @param {*} selectors selectors used in content files | ||
*/ | ||
}, { | ||
key: 'removeUnusedFontFaces', | ||
value: function removeUnusedFontFaces() { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
}, { | ||
key: "getSelectorsCss", | ||
value: function getSelectorsCss(selectors) { | ||
var _this = this; | ||
try { | ||
for (var _iterator3 = this.atRules.fontFace[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var _ref = _step3.value; | ||
var node = _ref.node; | ||
var name = _ref.name; | ||
this.root.walk(function (node) { | ||
if (node.type === 'rule') { | ||
return _this.evaluateRule(node, selectors); | ||
} | ||
var used = this.usedFontFaces.has(name); | ||
if (node.type === 'atrule') { | ||
return _this.evaluateAtRule(node); | ||
} | ||
if (!used) { | ||
node.remove(); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
if (node.type === 'comment') { | ||
if (_this.isIgnoreAnnotation(node, 'start')) _this.ignore = true;else if (_this.isIgnoreAnnotation(node, 'end')) _this.ignore = false; | ||
} | ||
}); | ||
} | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* @param {AST} node postcss ast node | ||
* @param {Set} selectors selectors used in content files | ||
*/ | ||
/** | ||
* Extract the selectors present in the passed string using a purgecss extractor | ||
* @param {array} content Array of content | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
}, { | ||
key: "evaluateRule", | ||
value: function evaluateRule(node, selectors) { | ||
var _this2 = this; | ||
}, { | ||
key: 'extractRawSelector', | ||
value: function extractRawSelector(content, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
var annotation = node.prev(); | ||
if (this.isIgnoreAnnotation(annotation, 'next') || this.ignore === true) return; | ||
var keepSelector = true; | ||
node.selector = selectorParser(function (selectorsParsed) { | ||
selectorsParsed.walk(function (selector) { | ||
var selectorsInRule = []; | ||
try { | ||
for (var _iterator4 = content[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var _ref2 = _step4.value; | ||
var raw = _ref2.raw; | ||
var extension = _ref2.extension; | ||
var extractor = this.getFileExtractor('.' + extension, extractors); | ||
selectors = new Set([].concat(toConsumableArray(selectors), toConsumableArray(this.extractSelectors(raw, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
if (selector.type === 'selector') { | ||
// if inside :not pseudo class, ignore | ||
if (selector.parent && selector.parent.value === ':not' && selector.parent.type === 'pseudo') { | ||
return; | ||
} | ||
return selectors; | ||
} | ||
var _iteratorNormalCompletion7 = true; | ||
var _didIteratorError7 = false; | ||
var _iteratorError7 = undefined; | ||
/** | ||
* Extract the selectors present in the files using a purgecss extractor | ||
* @param {array} files Array of files path or glob pattern | ||
* @param {array} extractors Array of extractors | ||
*/ | ||
}, { | ||
key: 'extractFileSelector', | ||
value: function extractFileSelector(files, extractors) { | ||
var selectors = new Set(); | ||
var _iteratorNormalCompletion5 = true; | ||
var _didIteratorError5 = false; | ||
var _iteratorError5 = undefined; | ||
try { | ||
for (var _iterator5 = files[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
var globfile = _step5.value; | ||
for (var _iterator7 = selector.nodes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { | ||
var _step7$value = _step7.value, | ||
type = _step7$value.type, | ||
value = _step7$value.value; | ||
var filesnames = []; | ||
if (fs.existsSync(globfile)) { | ||
filesnames.push(globfile); | ||
} else { | ||
filesnames = glob.sync(globfile); | ||
} | ||
var _iteratorNormalCompletion6 = true; | ||
var _didIteratorError6 = false; | ||
var _iteratorError6 = undefined; | ||
try { | ||
for (var _iterator6 = filesnames[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { | ||
var file = _step6.value; | ||
var content = fs.readFileSync(file, 'utf8'); | ||
var extractor = this.getFileExtractor(file, extractors); | ||
selectors = new Set([].concat(toConsumableArray(selectors), toConsumableArray(this.extractSelectors(content, extractor)))); | ||
} | ||
} catch (err) { | ||
_didIteratorError6 = true; | ||
_iteratorError6 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion6 && _iterator6.return) { | ||
_iterator6.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError6) { | ||
throw _iteratorError6; | ||
} | ||
} | ||
} | ||
if (SELECTOR_STANDARD_TYPES.includes(type) && typeof value !== 'undefined' && /^\d/g.test(value) === false) { | ||
selectorsInRule.push(value); | ||
} else if (type === 'tag' && !/[+]|n|-|(even)|(odd)|^from$|^to$|^\d/.test(value)) { | ||
// test if we do not have a pseudo class parameter (e.g. 2n in :nth-child(2n)) | ||
selectorsInRule.push(value); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError5 = true; | ||
_iteratorError5 = err; | ||
_didIteratorError7 = true; | ||
_iteratorError7 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion5 && _iterator5.return) { | ||
_iterator5.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError5) { | ||
throw _iteratorError5; | ||
} | ||
try { | ||
if (!_iteratorNormalCompletion7 && _iterator7.return != null) { | ||
_iterator7.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError7) { | ||
throw _iteratorError7; | ||
} | ||
} | ||
} | ||
return selectors; | ||
} | ||
keepSelector = _this2.shouldKeepSelector(selectors, selectorsInRule); | ||
/** | ||
* Get the extractor corresponding to the extension file | ||
* @param {string} filename Name of the file | ||
* @param {array} extractors Array of extractors definition objects | ||
*/ | ||
}, { | ||
key: 'getFileExtractor', | ||
value: function getFileExtractor(filename) { | ||
var extractors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
if (!extractors.length) return DefaultExtractor; | ||
var extractorObj = extractors.find(function (extractor) { | ||
return extractor.extensions.find(function (ext) { | ||
return filename.endsWith(ext); | ||
}); | ||
}); | ||
return extractorObj.extractor; | ||
} | ||
/** | ||
* Use the extract function of the extractor to get the list of selectors | ||
* @param {string} content Content (e.g: html file) | ||
* @param {object} extractor Purgecss extractor use to extract the selector | ||
*/ | ||
}, { | ||
key: 'extractSelectors', | ||
value: function extractSelectors(content, extractor) { | ||
var selectors = new Set(); | ||
var arraySelector = extractor.extract(content); | ||
if (arraySelector === null) { | ||
throw new Error(ERROR_EXTRACTER_FAILED); | ||
if (!keepSelector) { | ||
if (_this2.options.rejected) _this2.selectorsRemoved.add(selector.toString()); | ||
selector.remove(); | ||
} | ||
arraySelector.forEach(function (selector) { | ||
selectors.add(selector); | ||
}); | ||
// Remove empty string | ||
selectors.delete(''); | ||
return selectors; | ||
} | ||
} | ||
}); | ||
}).processSync(node.selector); // loop declarations | ||
/** | ||
* Use postcss to walk through the css ast and remove unused css | ||
* @param {*} selectors selectors used in content files | ||
*/ | ||
if (keepSelector) { | ||
var _iteratorNormalCompletion8 = true; | ||
var _didIteratorError8 = false; | ||
var _iteratorError8 = undefined; | ||
}, { | ||
key: 'getSelectorsCss', | ||
value: function getSelectorsCss(selectors) { | ||
var _this = this; | ||
try { | ||
for (var _iterator8 = node.nodes[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { | ||
var _step8$value = _step8.value, | ||
prop = _step8$value.prop, | ||
value = _step8$value.value; | ||
this.root.walk(function (node) { | ||
if (node.type === 'rule') { | ||
return _this.evaluateRule(node, selectors); | ||
} | ||
if (node.type === 'atrule') { | ||
return _this.evaluateAtRule(node); | ||
} | ||
if (node.type === 'comment') { | ||
if (_this.isIgnoreAnnotation(node, 'start')) _this.ignore = true;else if (_this.isIgnoreAnnotation(node, 'end')) _this.ignore = false; | ||
} | ||
}); | ||
} | ||
if (this.options.keyframes) { | ||
if (prop === 'animation' || prop === 'animation-name') { | ||
var _iteratorNormalCompletion9 = true; | ||
var _didIteratorError9 = false; | ||
var _iteratorError9 = undefined; | ||
/** | ||
* Evaluate css selector and decide if it should be removed or not | ||
* @param {AST} node postcss ast node | ||
* @param {Set} selectors selectors used in content files | ||
*/ | ||
}, { | ||
key: 'evaluateRule', | ||
value: function evaluateRule(node, selectors) { | ||
var _this2 = this; | ||
var annotation = node.prev(); | ||
if (this.isIgnoreAnnotation(annotation, 'next') || this.ignore === true) return; | ||
var keepSelector = true; | ||
node.selector = selectorParser(function (selectorsParsed) { | ||
selectorsParsed.walk(function (selector) { | ||
var selectorsInRule = []; | ||
if (selector.type === 'selector') { | ||
// if inside :not pseudo class, ignore | ||
if (selector.parent && selector.parent.value === ':not' && selector.parent.type === 'pseudo') { | ||
return; | ||
} | ||
var _iteratorNormalCompletion7 = true; | ||
var _didIteratorError7 = false; | ||
var _iteratorError7 = undefined; | ||
try { | ||
for (var _iterator7 = selector.nodes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { | ||
var _ref3 = _step7.value; | ||
var type = _ref3.type; | ||
var value = _ref3.value; | ||
if (SELECTOR_STANDARD_TYPES.includes(type) && typeof value !== 'undefined' && /^\d/g.test(value) === false) { | ||
selectorsInRule.push(value); | ||
} else if (type === 'tag' && !/[+]|n|-|(even)|(odd)|^from$|^to$|^\d/.test(value)) { | ||
// test if we do not have a pseudo class parameter (e.g. 2n in :nth-child(2n)) | ||
selectorsInRule.push(value); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError7 = true; | ||
_iteratorError7 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion7 && _iterator7.return) { | ||
_iterator7.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError7) { | ||
throw _iteratorError7; | ||
} | ||
} | ||
} | ||
keepSelector = _this2.shouldKeepSelector(selectors, selectorsInRule); | ||
if (!keepSelector) { | ||
if (_this2.options.rejected) _this2.selectorsRemoved.add(selector.toString()); | ||
selector.remove(); | ||
} | ||
} | ||
}); | ||
}).processSync(node.selector); | ||
// loop declarations | ||
if (keepSelector) { | ||
var _iteratorNormalCompletion8 = true; | ||
var _didIteratorError8 = false; | ||
var _iteratorError8 = undefined; | ||
try { | ||
for (var _iterator8 = node.nodes[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { | ||
var _ref4 = _step8.value; | ||
var prop = _ref4.prop; | ||
var value = _ref4.value; | ||
if (this.options.keyframes) { | ||
if (prop === 'animation' || prop === 'animation-name') { | ||
var _iteratorNormalCompletion9 = true; | ||
var _didIteratorError9 = false; | ||
var _iteratorError9 = undefined; | ||
try { | ||
for (var _iterator9 = value.split(' ')[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { | ||
var word = _step9.value; | ||
this.usedAnimations.add(word); | ||
} | ||
} catch (err) { | ||
_didIteratorError9 = true; | ||
_iteratorError9 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion9 && _iterator9.return) { | ||
_iterator9.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError9) { | ||
throw _iteratorError9; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (this.options.fontFace) { | ||
if (prop === 'font-family') { | ||
this.usedFontFaces.add(value); | ||
} | ||
} | ||
} | ||
for (var _iterator9 = value.split(/[\s,]+/)[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { | ||
var word = _step9.value; | ||
this.usedAnimations.add(word); | ||
} | ||
} catch (err) { | ||
_didIteratorError8 = true; | ||
_iteratorError8 = err; | ||
_didIteratorError9 = true; | ||
_iteratorError9 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion8 && _iterator8.return) { | ||
_iterator8.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError8) { | ||
throw _iteratorError8; | ||
} | ||
try { | ||
if (!_iteratorNormalCompletion9 && _iterator9.return != null) { | ||
_iterator9.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError9) { | ||
throw _iteratorError9; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
var parent = node.parent; | ||
// Remove empty rules | ||
if (!node.selector) node.remove(); | ||
if (this.isRuleEmpty(parent)) parent.remove(); | ||
if (this.options.fontFace) { | ||
if (prop === 'font-family') { | ||
this.usedFontFaces.add(value); | ||
} | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError8 = true; | ||
_iteratorError8 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion8 && _iterator8.return != null) { | ||
_iterator8.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError8) { | ||
throw _iteratorError8; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param {AST} node postcss ast node | ||
*/ | ||
var parent = node.parent; // Remove empty rules | ||
}, { | ||
key: 'evaluateAtRule', | ||
value: function evaluateAtRule(node) { | ||
if (this.options.keyframes && node.name.endsWith('keyframes')) { | ||
this.atRules.keyframes.push(node); | ||
return; | ||
} | ||
if (!node.selector) node.remove(); | ||
if (this.isRuleEmpty(parent)) parent.remove(); | ||
} | ||
/** | ||
* Evaluate at-rule and register it for future reference | ||
* @param {AST} node postcss ast node | ||
*/ | ||
if (this.options.fontFace && node.name === 'font-face') { | ||
var _iteratorNormalCompletion10 = true; | ||
var _didIteratorError10 = false; | ||
var _iteratorError10 = undefined; | ||
}, { | ||
key: "evaluateAtRule", | ||
value: function evaluateAtRule(node) { | ||
if (this.options.keyframes && node.name.endsWith('keyframes')) { | ||
this.atRules.keyframes.push(node); | ||
return; | ||
} | ||
try { | ||
for (var _iterator10 = node.nodes[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { | ||
var _ref5 = _step10.value; | ||
var prop = _ref5.prop; | ||
var value = _ref5.value; | ||
if (this.options.fontFace && node.name === 'font-face') { | ||
var _iteratorNormalCompletion10 = true; | ||
var _didIteratorError10 = false; | ||
var _iteratorError10 = undefined; | ||
if (prop === 'font-family') { | ||
this.atRules.fontFace.push({ | ||
name: value, | ||
node: node | ||
}); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError10 = true; | ||
_iteratorError10 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion10 && _iterator10.return) { | ||
_iterator10.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError10) { | ||
throw _iteratorError10; | ||
} | ||
} | ||
} | ||
try { | ||
for (var _iterator10 = node.nodes[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { | ||
var _step10$value = _step10.value, | ||
prop = _step10$value.prop, | ||
value = _step10$value.value; | ||
return; | ||
if (prop === 'font-family') { | ||
this.atRules.fontFace.push({ | ||
name: value, | ||
node: node | ||
}); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError10 = true; | ||
_iteratorError10 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion10 && _iterator10.return != null) { | ||
_iterator10.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError10) { | ||
throw _iteratorError10; | ||
} | ||
} | ||
} | ||
/** | ||
* Check if the node is a css comment to ignore the selector rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
* @param {string} type Type of css comment (next, start, end) | ||
*/ | ||
return; | ||
} | ||
} | ||
/** | ||
* Check if the node is a css comment to ignore the selector rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
* @param {string} type Type of css comment (next, start, end) | ||
*/ | ||
}, { | ||
key: 'isIgnoreAnnotation', | ||
value: function isIgnoreAnnotation(node, type) { | ||
if (node && node.type === 'comment') { | ||
switch (type) { | ||
case 'next': | ||
return node.text.includes(IGNORE_ANNOTATION_NEXT); | ||
case 'start': | ||
return node.text.includes(IGNORE_ANNOTATION_START); | ||
case 'end': | ||
return node.text.includes(IGNORE_ANNOTATION_END); | ||
} | ||
} | ||
return false; | ||
} | ||
}, { | ||
key: "isIgnoreAnnotation", | ||
value: function isIgnoreAnnotation(node, type) { | ||
if (node && node.type === 'comment') { | ||
switch (type) { | ||
case 'next': | ||
return node.text.includes(IGNORE_ANNOTATION_NEXT); | ||
/** | ||
* Check if the node correspond to an empty css rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
*/ | ||
case 'start': | ||
return node.text.includes(IGNORE_ANNOTATION_START); | ||
}, { | ||
key: 'isRuleEmpty', | ||
value: function isRuleEmpty(node) { | ||
if (node.type === 'decl' && !node.value || node.type === 'rule' && !node.selector || node.nodes && !node.nodes.length || node.type === 'atrule' && (!node.nodes && !node.params || !node.params && !node.nodes.length)) { | ||
return true; | ||
} | ||
return false; | ||
case 'end': | ||
return node.text.includes(IGNORE_ANNOTATION_END); | ||
} | ||
} | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* @param {Set} selectorsInContent Set of css selectors found in the content files | ||
* @param {Array} selectorsInRule Array of selectors | ||
*/ | ||
return false; | ||
} | ||
/** | ||
* Check if the node correspond to an empty css rule | ||
* @param {object} node Node of postcss abstract syntax tree | ||
*/ | ||
}, { | ||
key: 'shouldKeepSelector', | ||
value: function shouldKeepSelector(selectorsInContent, selectorsInRule) { | ||
var _iteratorNormalCompletion11 = true; | ||
var _didIteratorError11 = false; | ||
var _iteratorError11 = undefined; | ||
}, { | ||
key: "isRuleEmpty", | ||
value: function isRuleEmpty(node) { | ||
if (node.type === 'decl' && !node.value || node.type === 'rule' && !node.selector || node.nodes && !node.nodes.length || node.type === 'atrule' && (!node.nodes && !node.params || !node.params && !node.nodes.length)) { | ||
return true; | ||
} | ||
try { | ||
for (var _iterator11 = selectorsInRule[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { | ||
var selector = _step11.value; | ||
return false; | ||
} | ||
/** | ||
* Determine if the selector should be kept, based on the selectors found in the files | ||
* @param {Set} selectorsInContent Set of css selectors found in the content files | ||
* @param {Array} selectorsInRule Array of selectors | ||
*/ | ||
// pseudo class | ||
var unescapedSelector = selector.replace(/\\/g, ''); | ||
}, { | ||
key: "shouldKeepSelector", | ||
value: function shouldKeepSelector(selectorsInContent, selectorsInRule) { | ||
var _iteratorNormalCompletion11 = true; | ||
var _didIteratorError11 = false; | ||
var _iteratorError11 = undefined; | ||
if (unescapedSelector.startsWith(':')) { | ||
continue; | ||
} | ||
try { | ||
for (var _iterator11 = selectorsInRule[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { | ||
var selector = _step11.value; | ||
// pseudo class | ||
var unescapedSelector = selector.replace(/\\/g, ''); | ||
// If the selector is whitelisted with children keep, simply | ||
// returns true to keep all children selectors | ||
if (this.isSelectorWhitelistedChildren(unescapedSelector)) { | ||
return true; | ||
} | ||
if (unescapedSelector.startsWith(':')) { | ||
continue; | ||
} // If the selector is whitelisted with children keep, simply | ||
// returns true to keep all children selectors | ||
if (!(selectorsInContent.has(unescapedSelector) || CSS_WHITELIST.includes(unescapedSelector) || this.isSelectorWhitelisted(unescapedSelector))) { | ||
return false; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError11 = true; | ||
_iteratorError11 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion11 && _iterator11.return) { | ||
_iterator11.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError11) { | ||
throw _iteratorError11; | ||
} | ||
} | ||
} | ||
if (this.isSelectorWhitelistedChildren(unescapedSelector)) { | ||
return true; | ||
} | ||
if (!(selectorsInContent.has(unescapedSelector) || CSS_WHITELIST.includes(unescapedSelector) || this.isSelectorWhitelisted(unescapedSelector))) { | ||
return false; | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError11 = true; | ||
_iteratorError11 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion11 && _iterator11.return != null) { | ||
_iterator11.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError11) { | ||
throw _iteratorError11; | ||
} | ||
} | ||
} | ||
/** | ||
* Check if the selector is whitelisted by the options whitelist or whitelistPatterns | ||
* @param {string} selector css selector | ||
*/ | ||
return true; | ||
} | ||
/** | ||
* Check if the selector is whitelisted by the options whitelist or whitelistPatterns | ||
* @param {string} selector css selector | ||
*/ | ||
}, { | ||
key: 'isSelectorWhitelisted', | ||
value: function isSelectorWhitelisted(selector) { | ||
return !!(CSS_WHITELIST.includes(selector) || this.options.whitelist && this.options.whitelist.some(function (v) { | ||
return v === selector; | ||
}) || this.options.whitelistPatterns && this.options.whitelistPatterns.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
}, { | ||
key: "isSelectorWhitelisted", | ||
value: function isSelectorWhitelisted(selector) { | ||
return !!(CSS_WHITELIST.includes(selector) || this.options.whitelist && this.options.whitelist.some(function (v) { | ||
return v === selector; | ||
}) || this.options.whitelistPatterns && this.options.whitelistPatterns.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
/** | ||
* Check if the selector is whitelisted by the whitelistPatternsChildren | ||
* options element | ||
* | ||
* @param {string} selector | ||
*/ | ||
/** | ||
* Check if the selector is whitelisted by the whitelistPatternsChildren | ||
* options element | ||
* | ||
* @param {string} selector | ||
*/ | ||
}, { | ||
key: "isSelectorWhitelistedChildren", | ||
value: function isSelectorWhitelistedChildren(selector) { | ||
return !!(this.options.whitelistPatternsChildren && this.options.whitelistPatternsChildren.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
}]); | ||
}, { | ||
key: 'isSelectorWhitelistedChildren', | ||
value: function isSelectorWhitelistedChildren(selector) { | ||
return !!(this.options.whitelistPatternsChildren && this.options.whitelistPatternsChildren.some(function (v) { | ||
return v.test(selector); | ||
})); | ||
} | ||
}]); | ||
return Purgecss; | ||
return Purgecss; | ||
}(); | ||
module.exports = Purgecss; |
{ | ||
"name": "purgecss", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Remove unused css selectors.", | ||
@@ -48,25 +48,27 @@ "main": "./lib/purgecss.js", | ||
"dependencies": { | ||
"glob": "^7.1.2", | ||
"postcss": "^7.0.0", | ||
"postcss-selector-parser": "^5.0.0-rc.3", | ||
"yargs": "^12.0.1" | ||
"glob": "^7.1.3", | ||
"postcss": "^7.0.14", | ||
"postcss-selector-parser": "^6.0.0", | ||
"yargs": "^13.2.2" | ||
}, | ||
"devDependencies": { | ||
"babel-eslint": "^8.2.5", | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-plugin-transform-class-properties": "^6.24.1", | ||
"babel-preset-env": "^1.7.0", | ||
"babel-preset-flow": "^6.23.0", | ||
"codacy-coverage": "^3.0.0", | ||
"eslint": "^5.4.0", | ||
"eslint-plugin-flowtype": "^2.50.0", | ||
"flow-bin": "^0.79.0", | ||
"jest": "^23.5.0", | ||
"prettier": "^1.14.0", | ||
"regenerator-runtime": "^0.12.0", | ||
"rollup": "^0.64.0", | ||
"rollup-plugin-babel": "^3.0.7", | ||
"@babel/core": "^7.0.0", | ||
"@babel/plugin-proposal-class-properties": "^7.0.0", | ||
"@babel/preset-env": "^7.0.0", | ||
"@babel/preset-flow": "^7.0.0", | ||
"babel-core": "^7.0.0-bridge.0", | ||
"babel-eslint": "^10.0.0", | ||
"babel-jest": "^24.7.1", | ||
"codacy-coverage": "^3.4.0", | ||
"eslint": "^5.16.0", | ||
"eslint-plugin-flowtype": "^3.5.0", | ||
"flow-bin": "^0.96.0", | ||
"jest": "^24.7.0", | ||
"prettier": "^1.16.4", | ||
"regenerator-runtime": "^0.13.0", | ||
"rollup": "^1.9.0", | ||
"rollup-plugin-babel": "^4.3.1", | ||
"rollup-plugin-flow": "^1.1.1", | ||
"rollup-plugin-node-builtins": "^2.1.2", | ||
"rollup-plugin-node-resolve": "^3.3.0", | ||
"rollup-plugin-node-resolve": "^4.1.0", | ||
"rollup-watch": "^4.3.1" | ||
@@ -73,0 +75,0 @@ }, |
@@ -65,3 +65,3 @@ # Purgecss | ||
}) | ||
const result = purgecss.purge() | ||
const result = purgeCss.purge() | ||
``` | ||
@@ -84,3 +84,3 @@ | ||
}) | ||
const result = purgecss.purge() | ||
const result = purgeCss.purge() | ||
``` | ||
@@ -87,0 +87,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1910
82374
20
+ Addedansi-regex@4.1.1(transitive)
+ Addedansi-styles@3.2.1(transitive)
+ Addedcliui@5.0.0(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedcssesc@3.0.0(transitive)
+ Addedemoji-regex@7.0.3(transitive)
+ Addedget-caller-file@2.0.5(transitive)
+ Addedpostcss-selector-parser@6.1.2(transitive)
+ Addedrequire-main-filename@2.0.0(transitive)
+ Addedstring-width@3.1.0(transitive)
+ Addedstrip-ansi@5.2.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwrap-ansi@5.1.0(transitive)
+ Addedyargs@13.3.2(transitive)
+ Addedyargs-parser@13.1.2(transitive)
- Removedansi-regex@2.1.13.0.1(transitive)
- Removedcliui@4.1.0(transitive)
- Removedcode-point-at@1.1.0(transitive)
- Removedcross-spawn@6.0.6(transitive)
- Removedcssesc@2.0.0(transitive)
- Removedend-of-stream@1.4.4(transitive)
- Removedexeca@1.0.0(transitive)
- Removedget-caller-file@1.0.3(transitive)
- Removedget-stream@4.1.0(transitive)
- Removedindexes-of@1.0.1(transitive)
- Removedinvert-kv@2.0.0(transitive)
- Removedis-fullwidth-code-point@1.0.0(transitive)
- Removedis-stream@1.1.0(transitive)
- Removedisexe@2.0.0(transitive)
- Removedlcid@2.0.0(transitive)
- Removedmap-age-cleaner@0.1.3(transitive)
- Removedmem@4.3.0(transitive)
- Removedmimic-fn@2.1.0(transitive)
- Removednice-try@1.0.5(transitive)
- Removednpm-run-path@2.0.2(transitive)
- Removednumber-is-nan@1.0.1(transitive)
- Removedos-locale@3.1.0(transitive)
- Removedp-defer@1.0.0(transitive)
- Removedp-finally@1.0.0(transitive)
- Removedp-is-promise@2.1.0(transitive)
- Removedpath-key@2.0.1(transitive)
- Removedpostcss-selector-parser@5.0.0(transitive)
- Removedpump@3.0.2(transitive)
- Removedrequire-main-filename@1.0.1(transitive)
- Removedsemver@5.7.2(transitive)
- Removedshebang-command@1.2.0(transitive)
- Removedshebang-regex@1.0.0(transitive)
- Removedsignal-exit@3.0.7(transitive)
- Removedstring-width@1.0.22.1.1(transitive)
- Removedstrip-ansi@3.0.14.0.0(transitive)
- Removedstrip-eof@1.0.0(transitive)
- Removeduniq@1.0.1(transitive)
- Removedwhich@1.3.1(transitive)
- Removedwrap-ansi@2.1.0(transitive)
- Removedyargs@12.0.5(transitive)
- Removedyargs-parser@11.1.1(transitive)
Updatedglob@^7.1.3
Updatedpostcss@^7.0.14
Updatedyargs@^13.2.2