Socket
Socket
Sign inDemoInstall

@mapbox/postcss-html-filter

Package Overview
Dependencies
Maintainers
219
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mapbox/postcss-html-filter - npm Package Compare versions

Comparing version 1.0.0 to 1.0.1

test/__snapshots__/test.js.snap

7

CHANGELOG.md
# Changelog
## 1.0.1
- Strip all pseudo-elements and -classes from selectors before checking them.
The resultant increase in CSS size should be minor (e.g. you might keep that `.foo:nth-child(8)` selector that isn't really used); but the potential for bugs in this library much, much lower.
**This should not be a breaking change.**
It fixes some possible bugs; and the only downside is it could possibly add some weight to your inlined CSS, if your CSS uses a *lot* of pseudo selectors.
## 1.0.0

@@ -4,0 +11,0 @@

133

index.js

@@ -8,101 +8,56 @@ 'use strict';

const postcssDiscardUnused = require('postcss-discard-unused');
const selectorParser = require('postcss-selector-parser');
// These are the CSS 1 and 2 pseudo-elements that can be prefixed with
// just one colon.
const earlyPseudoElements = ['first-line', 'first-letter', 'before', 'after'];
// These are CSS pseudo classes listed at
// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
// that are not supported by Cheerio (via css-select), as noted in
// https://github.com/fb55/css-select#supported-selectors
// These should be kept only if the selector applies without them.
const pseudoClassBlacklist = [
'active',
'any',
'default',
'dir',
'first',
'focus',
'hover',
'indeterminate',
'in-range',
'invalid',
'lang',
'left',
'out-of-range',
'read-only',
'read-write',
'right',
'scope',
'target',
'valid',
'visited',
'fullscreen'
];
// Always keep selectors that include these pseudo-classes.
const pseudoClassesToAlwaysKeep = ['root'];
const removalRegExpFragments = [
// Remove all vendor-prefixed pseudo selectors.
'::?-[a-zA-Z-]+\\b',
// Remove all pseudo-elements. Single colon works for early pseudo-elements.
// Besides that, we can remove everything prefixed with two colons.
'::[a-zA-Z-]+\\b',
// Remove single-colon pseudo-elements and banished pseudo-classes.
`:(${earlyPseudoElements.concat(pseudoClassBlacklist).join('|')})\\b`
];
const pseudoSelectorRemovalRegExp = new RegExp(
`(${removalRegExpFragments.join('|')})`,
'g'
);
const alwaysKeepRegExp = new RegExp(
`:(${pseudoClassesToAlwaysKeep.join('|')})\b`
);
const plugin = postcss.plugin('postcss-html-filter', options => {
const $ = cheerio.load(options.html);
const query = cheerio.load(options.html);
return (root, result) => {
root.walkRules(rule => {
let cleanedSelectors = [];
const transformSelector = selector => {
selector.walkPseudos(pseudo => {
// Keep all :root selectors.
if (pseudo.value === 'root') {
return;
}
pseudo.remove();
});
};
// Skip keyframe selectors.
if (/keyframes/.test(_.get(rule, 'parent.name', ''))) return;
const transformRule = rule => {
let cleanedSelectors = [];
rule.selectors.forEach(selector => {
if (alwaysKeepRegExp.test(selector)) return;
// Keep all keyframe selectors.
if (/keyframes/.test(_.get(rule, 'parent.name', ''))) return;
// Pseudo-selectors are red herrings in Cheerio queries.
const pseudoSafeSelector = selector.replace(
pseudoSelectorRemovalRegExp,
''
);
rule.selectors.forEach(selector => {
const pseudolessSelector = selectorParser(transformSelector).processSync(
selector
);
if (!pseudoSafeSelector) {
cleanedSelectors.push(selector);
return;
}
let matchingElements;
try {
matchingElements = $(pseudoSafeSelector);
} catch (cheerioError) {
throw new Error(
`Cheerio failed to interpret the following selector:\n ${selector}`
);
}
if (matchingElements.length !== 0) {
cleanedSelectors.push(selector);
}
});
// Remove the rule if it has no applicable selectors
if (cleanedSelectors.length === 0) {
rule.remove();
} else {
rule.selector = cleanedSelectors.join(', ');
if (!pseudolessSelector) {
cleanedSelectors.push(selector);
return;
}
let matchingElements;
try {
matchingElements = query(pseudolessSelector);
} catch (cheerioError) {
throw new Error(
`Cheerio failed to interpret the following selector:\n ${selector}`
);
}
if (matchingElements.length !== 0) {
cleanedSelectors.push(selector);
}
});
// Remove the rule if it no longer has applicable selectors.
if (cleanedSelectors.length === 0) {
rule.remove();
} else {
rule.selector = cleanedSelectors.join(', ');
}
};
return (root, result) => {
root.walkRules(transformRule);
// Discard any at-rules we've emptied of rules.

@@ -109,0 +64,0 @@ postcssDiscardEmpty()(root, result);

{
"name": "@mapbox/postcss-html-filter",
"version": "1.0.0",
"version": "1.0.1",
"description": "Filter CSS through HTML, removing selectors that do not apply to the HTML",

@@ -8,3 +8,3 @@ "main": "index.js",

"precommit": "lint-staged",
"format": "prettier --single-quote --write '{,lib/**/,test/**/}*.js'",
"format": "prettier --write '**/*.js'",
"lint": "eslint .",

@@ -34,15 +34,16 @@ "test-jest": "jest",

"dependencies": {
"cheerio": "^1.0.0-rc.1",
"lodash": "^4.17.4",
"cheerio": "^1.0.0-rc.2",
"lodash": "^4.17.5",
"postcss-discard-empty": "^2.1.0",
"postcss-discard-unused": "^2.2.3"
"postcss-discard-unused": "^2.2.3",
"postcss-selector-parser": "^3.1.1"
},
"devDependencies": {
"eslint": "^4.0.0",
"eslint-plugin-node": "^5.0.0",
"husky": "^0.14.1",
"jest": "^20.0.4",
"lint-staged": "^4.0.0",
"postcss": "^6.0.0",
"prettier": "^1.4.4"
"eslint": "^4.19.1",
"eslint-plugin-node": "^6.0.1",
"husky": "^0.14.3",
"jest": "^22.4.3",
"lint-staged": "^7.0.0",
"postcss": "^6.0.21",
"prettier": "^1.11.1"
},

@@ -52,2 +53,5 @@ "peerDependencies": {

},
"prettier": {
"singleQuote": true
},
"jest": {

@@ -69,3 +73,3 @@ "coverageReporters": [

"eslint",
"prettier --single-quote --write",
"prettier --write",
"git add"

@@ -72,0 +76,0 @@ ]

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc