Socket
Socket
Sign inDemoInstall

colorguard

Package Overview
Dependencies
24
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.3.0 to 1.0.0

lib/formatter.js

213

lib/colorguard.js

@@ -1,15 +0,9 @@

var walk = require('rework-walk');
var rework = require('rework');
var assign = require('object-assign');
var colorDiff = require('color-diff');
var formatter = require('./formatter');
var postcss = require('postcss');
var pipetteur = require('pipetteur');
var colorDiff = require('color-diff');
var SourceMapConsumer = require('source-map').SourceMapConsumer;
var colors = {};
var sourceMapConsumer;
var reporter = require('postcss-reporter');
function getWhitelistHashKey(pair) {
pair = pair.sort();
return pair[0] + '-' + pair[1];
}
function convertToLab(clr) {
function convertToLab (clr) {
clr = clr.rgb();

@@ -31,45 +25,28 @@

// we need line position instead
function getColumnPositionRelativeToLine(position, which) {
return position.declaration.position[which].column + position.column + position.declaration.property.length;
function getColumnPositionRelativeToLine (position) {
var decl = position.declaration;
return decl.source.start.column + position.column + decl.prop.length;
}
function renderOriginalPosition (position) {
return position.source + ':' + position.line + ':' + position.column;
function getWhitelistHashKey (pair) {
pair = pair.sort();
return pair[0] + '-' + pair[1];
}
function renderConflictLine (data) {
return '_match_ (_hex_) [line: _lines_]'
.replace('_match_', data.lines[0].match)
.replace('_hex_', data.lines[0].color.hex())
.replace('_lines_', data.lines.map(function (info, index) {
var result = info.declaration.position.start.line + ':' + getColumnPositionRelativeToLine(info, 'start');
if (data.originalLines && data.originalLines.length) {
result += ' (' + renderOriginalPosition(data.originalLines[index]) + ')';
}
return result;
}).join(', '));
function getDiff (a, b) {
return Math.min(colorDiff.diff(convertToLab(a), convertToLab(b)), 100);
}
function getOriginalPosition (info) {
return sourceMapConsumer && sourceMapConsumer.originalPositionFor({
line: info.declaration.position.start.line,
column: getColumnPositionRelativeToLine(info, 'start')
});
}
var colorguard = postcss.plugin('css-colorguard', function (opts) {
opts = assign({
ignore: [],
threshold: 3
}, opts);
exports.inspect = function (css, options, map) {
options = options || {};
colors = {};
if (map) {
sourceMapConsumer = new SourceMapConsumer(map);
}
var threshold = typeof options.threshold !== 'undefined' ? options.threshold : 3;
options.ignore = options.ignore || [];
var whitelistHash = {};
if (options.whitelist) {
options.whitelist.forEach(function (pair) {
if (opts.whitelist) {
opts.whitelist.forEach(function (pair) {
if (!Array.isArray(pair)) {
throw new Error('The whitelist option takes an array of array pairs. You probably sent an array of strings.');
throw new Error('The whitelist option takes an array of array pairs. ' +
'You probably sent an array of strings.');
}

@@ -80,117 +57,51 @@ whitelistHash[getWhitelistHashKey(pair)] = true;

// In this section, we more or less ruin the actual css, but not for our purposes. The following
// changes are necessary for the parser to not barf at us. We'll need to undo this if we ever
// wanted to use the rework output. For now, it's fine as long as we keep the line numbers the
// same.
return function (css, result) {
var colors = {};
// https://github.com/SlexAxton/css-colorguard/issues/2
css.walkDecls(function (decl) {
var matches = pipetteur(decl.value);
matches.forEach(function (match) {
// FIXME: This discards alpha channel
var name = match.color.hex();
// Just bail if we want to ignore the color
if (opts.ignore.indexOf(name) > -1) {
return;
}
css = css.replace(/url\((.*?#.*?)\)/ig, function (match, content) {
// Capture the content in order to replace with a similar length string
// This allows us to keep the column numer correct
return match.replace(content, new Array(content.length + 1).join('_'));
});
match.declaration = decl;
// Run rework over it so we can parse out all the colors
// rework(css).use(findColors()).toString();
var workingTree = rework(css).use(function (style) {
walk(style, function (rule) {
if (rule.declarations) {
rule.declarations.forEach(function (declaration) {
if (declaration.type === 'declaration') {
var matches = pipetteur(declaration.value);
if (!(name in colors)) {
colors[name] = colors[name] || [];
colors[name].push(match);
}
if (matches.length) {
matches.forEach(function (match) {
// FIXME: This discards alpha channel
var name = match.color.hex();
Object.keys(colors).forEach(function (color) {
var cached = colors[color];
if (cached[0] === match || cached[0].match === match.match) {
return;
}
var diffAmount = getDiff(cached[0].color, match.color);
match.declaration = declaration;
colors[name] = colors[name] || [];
colors[name].push(match);
});
}
var whitelisted = getWhitelistHashKey([color, name]);
if (diffAmount < opts.threshold && !whitelistHash[whitelisted]) {
decl.warn(result, match.match + ' collides with ' +
cached[0].match +
' (' +
cached[0].declaration.source.start.line +
':' +
getColumnPositionRelativeToLine(cached[0]) +
')');
}
});
}
});
});
});
var colorNames = Object.keys(colors);
var colorLen = colorNames.length;
var c1;
var c2;
var diffAmount;
var infoBlock;
// Mock the output structure
var output = {
collisions: [],
info: [],
stats: {
counts: {}
}
};
});
// Set the total number of different colors
output.stats.total = colorLen;
colorguard.process = function (css, opts) {
opts = opts || {};
var processor = postcss([ colorguard(opts), reporter({formatter: formatter}) ]);
return processor.process(css, opts);
};
// Generate the stats for the colors
colorNames.forEach(function (colorName) {
// Counts of colors
output.stats.counts[colorName] = colors[colorName].length;
});
// Loop over the object but avoid duplicates and collisions
for (var i = 0; i < colorLen; i += 1) {
// Just bail if we want to ignore this color altogether
if (options.ignore.indexOf(colorNames[i]) >= 0) {
continue;
}
for (var j = i + 1; j < colorLen; j += 1) {
if (options.ignore.indexOf(colorNames[j]) >= 0) {
continue;
}
// Convert each to rgb format
c1 = colors[colorNames[i]];
c2 = colors[colorNames[j]];
// Avoid greater than 100 values
diffAmount = Math.min(colorDiff.diff(convertToLab(c1[0].color), convertToLab(c2[0].color)), 100);
// All distances go into the info block
infoBlock = {
colors: [{
rgb: colorNames[i],
lines: c1,
originalLines: sourceMapConsumer && c1.map(getOriginalPosition) || []
}, {
rgb: colorNames[j],
lines: c2,
originalLines: sourceMapConsumer && c2.map(getOriginalPosition) || []
}],
distance: diffAmount
};
// push it onto the block
output.info.push(infoBlock);
// If the amount is less than the threshold, and if the two colors aren't whitelisted, then
// we have a collision
if (diffAmount < threshold && !whitelistHash[getWhitelistHashKey([colorNames[i], colorNames[j]])]) {
infoBlock.message = '_1_ is too close (_diffAmount_) to _2_'
.replace('_1_', renderConflictLine(infoBlock.colors[0]))
.replace('_2_', renderConflictLine(infoBlock.colors[1]))
.replace('_diffAmount_', diffAmount);
output.collisions.push(infoBlock);
}
}
}
return output;
};
module.exports = colorguard;
{
"name": "colorguard",
"version": "0.3.0",
"version": "1.0.0",
"description": "Keep a watchful eye on your css colors",

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

"scripts": {
"test": "mocha --require should --reporter spec"
"test": "tape test/*.js | tap-spec"
},

@@ -26,3 +26,4 @@ "files": [

"csslint",
"rework"
"postcss",
"postcss-plugin"
],

@@ -36,14 +37,17 @@ "author": "Alex Sexton <alexsexton@gmail.com>",

"dependencies": {
"chalk": "^1.1.1",
"color-diff": "^0.1.3",
"log-symbols": "^1.0.2",
"object-assign": "^4.0.1",
"pipetteur": "^1.0.1",
"rework": "^1.0.0",
"rework-walk": "^1.0.0",
"source-map": "^0.2.0",
"source-map-resolve": "^0.3.1",
"plur": "^2.0.0",
"postcss": "^5.0.4",
"postcss-reporter": "^1.2.1",
"text-table": "^0.2.0",
"yargs": "^1.2.6"
},
"devDependencies": {
"mocha": "^1.20.1",
"should": "^4.0.4"
"tap-spec": "^4.1.0",
"tape": "^4.2.0"
}
}

@@ -10,78 +10,63 @@ [![Build Status](https://travis-ci.org/SlexAxton/css-colorguard.svg?branch=master)](https://travis-ci.org/SlexAxton/css-colorguard)

## Usage
## How it works
Generally, you'll want to do this after you've run things through your css preprocessor, so variables
and other preprocessor specific things are out of the way.
Colorguard uses the [CIEDE2000](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000) algorithm to determine
the similarity of each of the colors in your CSS file. This algorithm is quite complex, but is used
in the broadcasting community as the best approximation of human ability to discern differences in
color. RGB on the other hand, is pretty bad at representing differences in color purely based on the
numerical difference of the hex values.
### Command Line
Luckily, [someone else already implemented CIEDE2000](https://github.com/markusn/color-diff), so I
didn't have to. Tight. Cause this thing is mathy as hell.
```bash
$ npm install -g colorguard
```
![http://f.cl.ly/items/061h1y0x0G2X2e2t1q1f/Screen%20Shot%202014-07-03%20at%205.55.17%20PM.png](http://f.cl.ly/items/061h1y0x0G2X2e2t1q1f/Screen%20Shot%202014-07-03%20at%205.55.17%20PM.png)
### Alpha Transparency
```bash
# Just regular
colorguard --file style.css
Currently, alpha transparency is just stripped from the colors. So `rgb(0, 0, 0)` exactly matches
`rgba(0,0,0,0.5)`. This is usually fine unless someone is alphatransparency-happy and uses it for
darkening and lightening colors too often. It could probably be its own check in the future that
there aren't too many different alpha transparencies of the same color. This is not currently a
thing though.
# pipe a file
cat file.css | colorguard
## API
# Threshold is available via command line
colorguard --file style.css --threshold 3
### `colorguard.process(css, [options]).then(function(result) {})`
# The other options are too hard to type, so just pass it a json object
# with `ignore` or `whitelist` properties (overrides `--threshold option`)
colorguard --file style.css --options colorguard.json
#### options
# Change the output type to full json (includes stats)
colorguard --file style.css --format json
```
##### ignore
Example output
Type: `array`
```bash
$ colorguard --file test/fixtures/simple.css
Collision: #020202, #000000
- #020202 [line: 2] is too close (0.3146196209793196) to #000000 [line: 2, 3, 7, 12, 13, 16, 17]
Collision: #020202, #010101
- #020202 [line: 2] is too close (0.1574963682909058) to #010101 [line: 20]
Collision: #000000, #010101
- #000000 [line: 2, 3, 7, 12, 13, 16, 17] is too close (0.15712369811016996) to #010101 [line: 20]
```
Specify hex codes of colors that you would like to ignore completely.
Use with caution.
```bash
$ cat test/fixtures/simple.css | colorguard --format json
{"collisions":[{"colors":[{"rgb":"#020202","lines":[2]},{"rgb":"#000000","lines":[2,3,7,12,13,16,17]}],"distance":0.3146196209793196,"message":"#020202 [line: 2] is too close (0.3146196209793196) to #000000 [line: 2, 3, 7, 12, 13, 16, 17]"},{"colors":[{"rgb":"#020202","lines":[2]},{"rgb":"#010101","lines":[20]}],"distance":0.1574963682909058,"message":"#020202 [line: 2] is too close (0.1574963682909058) to #010101 [line: 20]"},{"colors":[{"rgb":"#000000","lines":[2,3,7,12,13,16,17]},{"rgb":"#010101","lines":[20]}],"distance":0.15712369811016996,"message":"#000000 [line: 2, 3, 7, 12, 13, 16, 17] is too close (0.15712369811016996) to #010101 [line: 20]"}],"info":[{"colors":[{"rgb":"#020202","lines":[2]},{"rgb":"#000000","lines":[2,3,7,12,13,16,17]}],"distance":0.3146196209793196,"message":"#020202 [line: 2] is too close (0.3146196209793196) to #000000 [line: 2, 3, 7, 12, 13, 16, 17]"},{"colors":[{"rgb":"#020202","lines":[2]},{"rgb":"#663399","lines":[9]}],"distance":34.12252478659537},{"colors":[{"rgb":"#020202","lines":[2]},{"rgb":"#010101","lines":[20]}],"distance":0.1574963682909058,"message":"#020202 [line: 2] is too close (0.1574963682909058) to #010101 [line: 20]"},{"colors":[{"rgb":"#020202","lines":[2]},{"rgb":"#FFFFFF","lines":[21]}],"distance":99.42663222854084},{"colors":[{"rgb":"#000000","lines":[2,3,7,12,13,16,17]},{"rgb":"#663399","lines":[9]}],"distance":34.321183445222175},{"colors":[{"rgb":"#000000","lines":[2,3,7,12,13,16,17]},{"rgb":"#010101","lines":[20]}],"distance":0.15712369811016996,"message":"#000000 [line: 2, 3, 7, 12, 13, 16, 17] is too close (0.15712369811016996) to #010101 [line: 20]"},{"colors":[{"rgb":"#000000","lines":[2,3,7,12,13,16,17]},{"rgb":"#FFFFFF","lines":[21]}],"distance":100},{"colors":[{"rgb":"#663399","lines":[9]},{"rgb":"#010101","lines":[20]}],"distance":34.22102591917981},{"colors":[{"rgb":"#663399","lines":[9]},{"rgb":"#FFFFFF","lines":[21]}],"distance":60.25283160954553},{"colors":[{"rgb":"#010101","lines":[20]},{"rgb":"#FFFFFF","lines":[21]}],"distance":99.7195446868893}],"stats":{"counts":{"#020202":1,"#000000":7,"#663399":1,"#010101":1,"#FFFFFF":1},"total":5}}
```
##### threshold
Type: `number`
Default: `3`
### Programmatic
`0` through `100`. Lower values are more precise; the default is `3` but that's
mostly personal opinion.
```bash
$ npm install --save-dev colorguard
```
##### whitelist
```javascript
var colorguard = require('colorguard');
var fs = require('fs');
Type: `array`
var css = fs.readFileSync('./file.css', 'utf8');
Pass an array of color pairs to ignore:
var output = colorguard.inspect(css, {
// 0 through 100. Lower is more similar. Anything below 3 warns you.
// 3 is the default threshold, but that's mostly personal opinion
threshold: 3,
```js
[['#000000', '#010101']]
```
// This color is just ignored entirely (use with caution)
ignore: ["#030303"],
### `postcss([ colorguard(opts) ])`
// These color combinations are ignored (usually use this)
whitelist: [["#000000", "#010101"]]
});
```
CSS Colorguard can be consumed as a PostCSS plugin. See the
[documentation](https://github.com/postcss/postcss#usage) for examples for
your environment.
### Build Time
CSS Colorguard can also be used in conjunction with other javascript build systems, such as:
CSS Colorguard can be used in conjunction with other javascript build systems, such as:

@@ -92,62 +77,27 @@ * [gulp-colorguard](https://github.com/pgilad/gulp-colorguard)

### CLI
## The Output
CSS Colorguard also ships with a CLI app. To see the available options, just run:
You'll get warnings back (as an object via js or if the format is set to `json`), as well as some
additional color stats. Those are just for fun or whatever.
```json
{
"collisions": [
{
"colors": [
{
"rgb": "#010101",
"lines": [23, 45, 234]
},
{
"rgb": "#020202",
"lines": [29]
}
],
"distance": 0.1574963682909058,
"message": "#010101 [line: 23, 45, 234] is too close (0.1574963682909058) to #020202 [line: 29]."
}
],
"stats": {
"counts": {
"#010101": 3,
"#020202": 1,
"#030303": 0
},
"total": 3
}
}
```bash
$ colorguard --help
```
## How it works
## Install
Colorguard uses the [CIEDE2000](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000) algorithm to determine
the similarity of each of the colors in your CSS file. This algorithm is quite complex, but is used
in the broadcasting community as the best approximation of human ability to discern differences in
color. RGB on the other hand, is pretty bad at representing differences in color purely based on the
numerical difference of the hex values.
With npm, to get the command do:
Luckily, [someone else already implemented CIEDE2000](https://github.com/markusn/color-diff), so I
didn't have to. Tight. Cause this thing is mathy as hell.
```bash
npm install -g colorguard
```
![http://f.cl.ly/items/061h1y0x0G2X2e2t1q1f/Screen%20Shot%202014-07-03%20at%205.55.17%20PM.png](http://f.cl.ly/items/061h1y0x0G2X2e2t1q1f/Screen%20Shot%202014-07-03%20at%205.55.17%20PM.png)
To get the library & PostCSS plugin, do:
### Alpha Transparency
```bash
npm install colorguard
```
Currently, alpha transparency is just stripped from the colors. So `rgb(0, 0, 0)` exactly matches
`rgba(0,0,0,0.5)`. This is usually fine unless someone is alphatransparency-happy and uses it for
darkening and lightening colors too often. It could probably be it's own check in the future that
there aren't too many different alpha transparencies of the same color. This is not currently a
thing though.
## Thanks
* [Stripe](https://stripe.com/) - They let me build this at work
* [reworkcss](https://github.com/reworkcss) - Makes this work
* [@markusn](https://github.com/markusn) - Best CIEDE2000 implementation ever

@@ -154,0 +104,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc