Skip to content

Instantly share code, notes, and snippets.

@mcasimir
Last active September 5, 2022 15:56
Show Gist options
  • Save mcasimir/9484b74819228deecc5cd330c108647c to your computer and use it in GitHub Desktop.
Save mcasimir/9484b74819228deecc5cd330c108647c to your computer and use it in GitHub Desktop.
Lg colors
#!/usr/bin/env node
const { Command } = require('commander');
const fs = require('fs');
const colorConvert = require('color-convert');
const colorDiff = require('color-diff');
const isBinaryFileSync = require("isbinaryfile").isBinaryFileSync;
const chalk = require('chalk');
const NAMED_COLOR_RE = /[^@\-.A-z0-9](aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgrey|lightgreen|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)/ig;
const HEX_COLOR_RE = /#[0-9a-fA-F]{3,6}\b/g;
const HEX_COLOR_ALPHA_RE = /#[0-9a-fA-F]{8,8}\b/g;
const RGB_COLOR_RE = /[^.A-z0-9]rgba?\(/ig;
const rgbObject = ([R, G, B]) => ({R, G, B});
const toRgb = (hex) => rgbObject(colorConvert.hex.rgb(hex));
const toHex = ({R, G, B}) => '#' + colorConvert.rgb.hex([R, G, B]).toLowerCase();
const palette = [
'#FFFFFF',
'#001E2B',
'#112733',
'#1C2D38',
'#3D4F58',
'#5C6C75',
'#889397',
'#C1C7C6',
'#E8EDEB',
'#F9FBFA',
'#023430',
'#00684A',
'#00A35C',
'#00ED64',
'#71F6BA',
'#C0FAE6',
'#E3FCF7',
'#2D0B59',
'#5E0C9E',
'#B45AF2',
'#F1D4FD',
'#F9EBFF',
'#0C2657',
'#083C90',
'#1254B7',
'#016BF8',
'#0498EC',
'#C3E7FE',
'#E1F7FF',
'#4C2100',
'#944F01',
'#FFC010',
'#FFEC9E',
'#FEF7DB',
'#5B0000',
'#970606',
'#DB3030',
'#FF6960',
'#FFCDC7',
'#FFEAE5',
].map(toRgb);
function getUniqueMatches(txt, re) {
const colorsArray = [...txt.matchAll(re)]
.filter((matches) => matches.length)
.flatMap((match) => match[0]);
return [...new Set(colorsArray)];
}
function getReplacements(txt) {
const colors = getUniqueMatches(txt, HEX_COLOR_RE)
.map(toRgb);
const entries = colors.map(
(c) => [toHex(c), toHex(colorDiff.closest(c, palette))]
);
return entries;
}
function outputTable(entries) {
const toTd = (hex) => `<td style="background-color:${hex}">${hex}</td>`;
const rows = entries.map(([from, to]) => `<tr>${toTd(from)}${toTd(to)}</tr>`).join('\n');
const table = `<table>\n${rows}\n<table>`;
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
${table}
</body>
</html>
`;
}
const program = new Command();
program.command('replace')
.argument('<path>', 'source file path')
.option('--dry-run')
.option('--verbose')
.action((filename, options) => {
const bytes = fs.readFileSync(filename);
const size = fs.lstatSync(filename).size;
if (isBinaryFileSync(bytes, size)) {
console.error(filename, 'is a binary file.');
return;
}
if (options.verbose) {
console.debug('Replacing', filename);
}
const source = bytes.toString('utf-8');
const namedColors = getUniqueMatches(source, NAMED_COLOR_RE);
if (namedColors.length) {
console.warn(filename, chalk.yellow('potential named colors found'), namedColors);
}
const rgbColors = getUniqueMatches(source, RGB_COLOR_RE);
if (rgbColors.length) {
console.warn(filename, chalk.yellow('potential rgb colors found'));
}
const transparentColors = getUniqueMatches(source, HEX_COLOR_ALPHA_RE);
if (transparentColors.length) {
console.warn(filename, chalk.yellow('potential transparent colors found'), [transparentColors]);
}
const entries = getReplacements(source);
let dest = source;
for (const [from, to] of entries) {
dest = dest.replaceAll(from, to);
dest = dest.replaceAll(from.toUpperCase(), to);
}
if (options.dryRun) {
// console.log(dest);
} else {
fs.writeFileSync(filename, dest);
}
});
program.command('print')
.argument('<path>', 'source file path')
.option('--html')
.action((src, options) => {
const entries = getReplacements(fs.readFileSync(src, 'utf-8'));
if (options.html) {
console.log(outputTable(entries));
} else {
console.log(JSON.stringify(Object.fromEntries(entries)));
}
});
program.parse();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment