Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 'use strict' const fs = require('fs') const path = require('path') const { resolveColorHex } = require('./colors') const { checkDimensions } = require('./dimensions') const { REPO_TYPES } = require('./types') function validateName(name) { const errors = [] if (name !== name.toLowerCase()) { errors.push('Icon name should be in lowercase.') return errors } if (name.includes(' ')) { errors.push('Icon name should not contain spaces.') return errors } if (!/^[a-z_]+$/.test(name)) { errors.push('Only underscores are allowed as special characters in icon name.') } return errors } function validateSynonyms(synonyms) { const errors = [] ;(synonyms || []).forEach(syn => { if (!/^[a-z\s]+$/.test(syn)) { errors.push(`Synonym "${syn}" must be lowercase without special characters.`) } }) return errors } function validateColor(icon) { const errors = [] const warnings = [] const color = String(icon.color || '') const isUtilityOrDoctype = icon.icon_type === 'utility' || icon.icon_type === 'doctype' if (isUtilityOrDoctype && color.length !== 0) { warnings.push('Color is present for Utility/Doctype icon but will be ignored.') } if (!isUtilityOrDoctype) { const isStandardOrAction = icon.icon_type === 'standard' || icon.icon_type === 'action' if (isStandardOrAction && color.length === 0) { errors.push('Color is required for Standard or Action icons.') } if (color && !resolveColorHex(color)) { errors.push('Color reference not found in color palette.') } } return { errors, warnings } } function validateSize(icon, svgContent) { if (!svgContent) return [] return checkDimensions(icon.icon_type, svgContent).map( msg => `${icon.icon_name}.svg: ${msg}` ) } /** * Validate one icon record against its SVG content and the repo state. * * @param {object} icon { icon_name, icon_type, color, synonyms } * @param {object} ctx { svgContent, repoRoot } * @returns {{ errors: string[], warnings: string[] }} */ function validateIcon(icon, ctx = {}) { const { svgContent = null, repoRoot = '.' } = ctx let errors = [] let warnings = [] if (!REPO_TYPES.includes(icon.icon_type)) { return { errors: [`Unsupported icon type "${icon.icon_type}".`], warnings } } errors = errors.concat(validateName(icon.icon_name)) errors = errors.concat(validateSynonyms(icon.synonyms)) const colorResult = validateColor(icon) errors = errors.concat(colorResult.errors) warnings = warnings.concat(colorResult.warnings) if (!svgContent) { errors.push('Missing SVG.') } else { errors = errors.concat(validateSize(icon, svgContent)) } // Already present in the repo? (we should not re-add an existing icon) const repoCommon = path.join(repoRoot, 'svg', icon.icon_type, 'common', `${icon.icon_name}.svg`) const repoDirect = path.join(repoRoot, 'svg', icon.icon_type, `${icon.icon_name}.svg`) if (fs.existsSync(repoCommon) || fs.existsSync(repoDirect)) { errors.push(`Already present in repo at svg/${icon.icon_type}/.`) } return { errors, warnings } } /** * Return the set of "<name>--<type>" ids that appear more than once. */ function findDuplicates(icons) { const seen = new Set() const dupes = new Set() icons.forEach(icon => { const key = `${icon.icon_name}--${icon.icon_type}` if (seen.has(key)) dupes.add(key) else seen.add(key) }) return dupes } module.exports = { validateIcon, findDuplicates } |