UNPKG

color-name-list

Version:
444 lines (393 loc) 12.3 kB
import fs from 'fs'; import path from 'path'; import { parseCSVString, findDuplicates, objArrToString } from './lib.js'; import { exec } from 'child_process'; const args = process.argv; const isTestRun = !!args.find((arg) => (arg === '--testOnly')); // only hex colors with 6 values const hexColorValidation = /^#[0-9a-f]{6}$/; const errors = []; // spaces regex const spacesValidation = /^\s+|\s{2,}|\s$/; // quote regex const quoteValidation = /"|'|`/; // setting const __dirname = path.dirname(new URL(import.meta.url).pathname); const baseFolder = __dirname + '/../'; const folderSrc = 'src/'; const folderDist = 'dist/'; const fileNameSrc = 'colornames'; const fileNameBestOfPostfix = '.bestof'; const readmeFileName = 'README.md'; const fileNameShortPostfix = '.short'; const maxShortNameLength = 12; const sortBy = 'name'; const csvKeys = ['name', 'hex']; const bestOfKey = 'good name'; // reads the CSV file contents const src = fs.readFileSync( path.normalize(`${baseFolder}${folderSrc}${fileNameSrc}.csv`), 'utf8' ).toString(); const colorsSrc = parseCSVString(src); // sort by sorting criteria colorsSrc.entries.sort((a, b) => { return a[sortBy].localeCompare(b[sortBy]); }); csvKeys.forEach((key) => { // find duplicates const dupes = findDuplicates(colorsSrc.values[key]); dupes.forEach((dupe) => { log(key, dupe, `found a double ${key}`); }); }); // loop hex values for validations colorsSrc.values['hex'].forEach((hex) => { // validate HEX values if ( !hexColorValidation.test(hex) ) { log('hex', hex, `${hex} is not a valid hex value. (Or to short, we avoid using the hex shorthands, no capital letters)`); } }); // loop names colorsSrc.values['name'].forEach((name) => { // check for spaces if (spacesValidation.test(name)) { log('name', name, `${name} found either a leading or trailing space (or both)`); } if (quoteValidation.test(name)) { log('name', name, `${name} found a quote character, should be an apostrophe ’`); } }); // loop good name markers colorsSrc.values[bestOfKey].forEach((str) => { // check for spaces if (spacesValidation.test(str)) { log( `"${bestOfKey}" marker'`, str, `${str} found either a leading or trailing space (or both)` ); } if (!(str == "x" || str == "")) { log( `"${bestOfKey}" marker`, str, `${str} must be a lowercase "x" character or empty` ); } }); showLog(); if (isTestRun) { console.log('⇪ See test results above ⇪'); process.exit(); } // creates JS related files const JSONExportString = JSON.stringify( [...colorsSrc.entries].map( // removes good name attributes (val) => ({ name: val.name, hex: val.hex, }) ) ); const JSONExportStringBestOf = JSON.stringify( [...colorsSrc.entries].filter( (val) => (val[bestOfKey]) ).map( // removes good name attributes (val) => ({ name: val.name, hex: val.hex, }) ) ); const JSONExportStringShort = JSON.stringify( [...colorsSrc.entries] .filter( // make sure its only one word long (val) => val[bestOfKey] && //val.name.split(" ").length === 1 && val.name.length < maxShortNameLength + 1 ) .map( // removes good name attributes (val) => ({ name: val.name, hex: val.hex, }) ) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.json`), JSONExportString ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameBestOfPostfix}.json`), JSONExportStringBestOf ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameShortPostfix}.json`), JSONExportStringShort ); // creates a more compact JSON file, where the HEX color serves as an id const miniJSONExportObj = colorsSrc.entries.reduce((obj, entry) => { obj[entry.hex.replace('#', '')] = entry.name; return obj; }, {}); const miniJSONExportObjBestOf = colorsSrc.entries.reduce((obj, entry) => { if(entry[bestOfKey]) { obj[entry.hex.replace('#', '')] = entry.name; } return obj; }, {}); const miniJSONExportObjShort = colorsSrc.entries.reduce((obj, entry) => { if ( entry[bestOfKey] && //entry.name.split(" ").length === 1 && entry.name.length < maxShortNameLength + 1 ) { obj[entry.hex.replace("#", "")] = entry.name; } return obj; }, {}); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.min.json`), JSON.stringify(miniJSONExportObj) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameBestOfPostfix}.min.json`), JSON.stringify(miniJSONExportObjBestOf) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameShortPostfix}.min.json`), JSON.stringify(miniJSONExportObjShort) ); // gets UMD template const umdTpl = fs.readFileSync( path.normalize(__dirname + '/umd.js.tpl'), 'utf8' ).toString(); // create UMD fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.umd.js`), umdTpl.replace('"{{COLORS}}"', JSONExportString) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameBestOfPostfix}.umd.js`), umdTpl.replace('"{{COLORS}}"', JSONExportStringBestOf) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameShortPostfix}.umd.js`), umdTpl.replace('"{{COLORS}}"', JSONExportStringShort) ); // gets ESM template const esmTpl = fs.readFileSync( path.normalize(__dirname + '/esm.js.tpl'), 'utf8' ).toString(); // create ESM fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.esm.js`), esmTpl.replace('"{{COLORS}}"', JSONExportString) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.esm.mjs`), esmTpl.replace('"{{COLORS}}"', JSONExportString) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameBestOfPostfix}.esm.js`), esmTpl.replace('"{{COLORS}}"', JSONExportStringBestOf) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameBestOfPostfix}.esm.mjs`), esmTpl.replace('"{{COLORS}}"', JSONExportStringBestOf) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameShortPostfix}.esm.js`), esmTpl.replace('"{{COLORS}}"', JSONExportStringShort) ); fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameShortPostfix}.esm.mjs`), esmTpl.replace('"{{COLORS}}"', JSONExportStringShort) ); // create foreign formats // configuration for the file outputs const outputFormats = { 'csv': { insertBefore: csvKeys.join(',') + '\r\n', }, 'yaml': { insertBefore: '-\r\n ', beforeValue: '"', afterValue: '"', includeKeyPerItem: true, rowDelimitor: '\r\n-\r\n ', itemDelimitor: '\r\n ', }, 'scss': { insertBefore: '$color-name-list: (', beforeValue: '"', afterValue: '"', insertAfter: ');', itemDelimitor: ':', rowDelimitor: ',', }, 'html': { insertBefore: `<table><thead><tr><th>${csvKeys.join('</th><th>')}</th></tr><thead><tbody><tr><td>`, itemDelimitor: '</td><td>', rowDelimitor: '</td></tr><tr><td>', insertAfter: `</td></tr></tbody></table>`, }, 'xml': { insertBefore: `<?xml version='1.0'?>\r\n<colors>\r\n<color>\r\n<${csvKeys[0]}>`, itemDelimitor: `</${csvKeys[0]}>\r\n<${csvKeys[1]}>`, rowDelimitor: `</${csvKeys[1]}>\r\n</color>\r\n<color>\r\n<${csvKeys[0]}>`, insertAfter: `</${csvKeys[1]}>\r\n</color>\r\n</colors>`, }, }; for (const outputFormat in outputFormats) { if (outputFormats[outputFormat]) { let outputString = objArrToString( colorsSrc.entries, csvKeys, outputFormats[outputFormat] ); if (outputFormat === 'html' || outputFormat === 'xml') { outputString = outputString.replace(/&/g, '&amp;'); } fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.${outputFormat}`), outputString ); } } // bestOf files for (const outputFormat in outputFormats) { if (outputFormats[outputFormat]) { let outputString = objArrToString( colorsSrc.entries.filter((val) => (val[bestOfKey])), csvKeys, outputFormats[outputFormat] ); if (outputFormat === 'html' || outputFormat === 'xml') { outputString = outputString.replace(/&/g, '&amp;'); } fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameBestOfPostfix}.${outputFormat}`), outputString ); } } // short files for (const outputFormat in outputFormats) { if (outputFormats[outputFormat]) { let outputString = objArrToString( colorsSrc.entries.filter( (val) => val[bestOfKey] && //val.name.split(" ").length === 1 && val.name.length < maxShortNameLength + 1 ), csvKeys, outputFormats[outputFormat] ); if (outputFormat === 'html' || outputFormat === 'xml') { outputString = outputString.replace(/&/g, '&amp;'); } fs.writeFileSync( path.normalize(`${baseFolder}${folderDist}${fileNameSrc}${fileNameShortPostfix}.${outputFormat}`), outputString ); } } // updates the color count in readme file const readme = fs.readFileSync( path.normalize(`${baseFolder}${readmeFileName}`), 'utf8' ).toString(); fs.writeFileSync( path.normalize(`${baseFolder}${readmeFileName}`), readme.replace( // update color count in text /__\d+__/g, `__${colorsSrc.entries.length}__` ).replace( // update color count in badge /\d+-colors-orange/, `${colorsSrc.entries.length}-colors-orange` ).replace( // update color count in percentage /__\d+(\.\d+)?%__/, `__${((colorsSrc.entries.length / (256 * 256 * 256)) * 100).toFixed(2)}%__` ).replace( // update file size /\d+(\.\d+)? MB\)__/g, `${ (fs.statSync(path.normalize(`${baseFolder}${folderDist}${fileNameSrc}.json`)).size / 1024 / 1024).toFixed(2) } MB)__` ), 'utf8' ); /** * outputs the collected logs */ function showLog() { let errorLevel = 0; let totalErrors = 0; errors.forEach((error, i) => { totalErrors = i + 1; errorLevel = error.errorLevel || errorLevel; console.log(`${error.errorLevel ? '⛔' : '⚠'} ${error.message}`); console.log(JSON.stringify(error.entries)); console.log('*-------------------------*'); }); if (errorLevel) { throw `⚠ failed because of the ${totalErrors} error${totalErrors > 1 ? 's' : ''} above ⚠`; } return totalErrors; } /** * logs errors and warning * @param {string} key key to look for in input * @param {string} value value to look for * @param {string} message error message * @param {Number} errorLevel if any error is set to 1, the program will exit */ function log(key, value, message, errorLevel = 1) { const error = {}; // looks for the original item that caused the error error.entries = colorsSrc.entries.filter((entry) => { return entry[key] === value; }); error.message = message; error.errorLevel = errorLevel; errors.push(error); } // gets SVG template const svgTpl = fs.readFileSync( path.normalize(__dirname + '/changes.svg.tpl'), 'utf8' ).toString(); // generates an SVG image with the new color based on the diff ot the last commit to the current function diffSVG() { exec( `git diff -U0 HEAD ${baseFolder}${folderDist}${fileNameSrc}.csv`, function (err, stdout, stderr) { const diffTxt = stdout; if (!/(?<=^[\+])[^\+].*/gm.test(diffTxt)) return; const changes = diffTxt.match(/(?<=^[\+])[^\+].*/gm).filter((i) => i); const svgTxtStr = changes.reduce((str, change, i) => { const changeParts = change.split(","); return `${str}<text x="40" y="${20 + (i + 1) * 70}" fill="${ changeParts[1] }">${changeParts[0].replace(/&/g, "&amp;")}</text>`; }, ""); fs.writeFileSync( path.normalize(`${baseFolder}changes.svg`), svgTpl .replace(/{height}/g, changes.length * 70 + 80) .replace(/{items}/g, svgTxtStr) ); } ); }; diffSVG();