diff --git a/.editorconfig b/.editorconfig index ee20bec..7059661 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,26 +2,17 @@ root = true [*] charset = utf-8 +indent_size = 4 indent_style = space -indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true -[*.json] -indent_style = tab -indent_size = 4 - [*.md] -indent_size = 4 +indent_size = unset trim_trailing_whitespace = false -[*.py] -indent_size = 4 - -[*.sublime-*] -indent_style = tab -indent_size = 4 +[*.{html,svg,toml,xhtml,yaml,yml}] +indent_size = 2 -[*.tmPreferences] +[*.{json,sublime-,tmPreferences}*] indent_style = tab -indent_size = 4 diff --git a/.gitattributes b/.gitattributes index 22a1d19..7f37c44 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,5 +12,8 @@ /.flake8 export-ignore /.gitattributes export-ignore /.gitignore export-ignore +/biome.json export-ignore /pyproject.toml export-ignore /requirements-dev.txt export-ignore +/svglint.config.mjs export-ignore +/svgo.config.mjs export-ignore diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..4c28def --- /dev/null +++ b/biome.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.7.0/schema.json", + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { "useLiteralKeys": "off" } + } + }, + "formatter": { + "enabled": false + }, + "organizeImports": { + "enabled": false + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + } +} diff --git a/svglint.config.mjs b/svglint.config.mjs new file mode 100644 index 0000000..7570880 --- /dev/null +++ b/svglint.config.mjs @@ -0,0 +1,71 @@ +const NEWLINE_INDENT_REGEX = /(?:\n(?:\s*))+/g; + +function t(strings, ...values) { + if (typeof strings === 'function') { + return (...args) => t(['', ''], strings(...args)); + } + if (!Array.isArray(strings)) { + return t([strings]); + } + return strings + .reduce((result, string, index) => result + values[index - 1] + string) + .replace(NEWLINE_INDENT_REGEX, ' ') + .trim(); +} + +const SVG_PATH_REGEX = /^m[-mzlhvcsqtae\d,. ]+$/i; + +export default { + rules: { + elm: { + 'svg': 1, + 'svg > path': 1, + '*': false, + }, + attr: [ + { + 'fill' :'#000', + 'fill-rule': 'evenodd', + 'viewBox': '0 0 16 16', + 'xmlns': 'http://www.w3.org/2000/svg', + 'rule::selector': 'svg', + 'rule::whitelist': true, + 'rule::order': ['xmlns', 'fill', 'fill-rule', 'viewBox'], + }, + { + 'd': SVG_PATH_REGEX, + 'rule::selector': 'svg > path', + 'rule::whitelist': true, + 'rule::order': true, + }, + ], + valid: true, + custom: [ + (reporter, $, ast, { filename }) => { + reporter.name = 'no-self-closing-path'; + + // Don't allow explicit '' closing tag + if (!ast.source.includes('')) { + return; + } + + const index = ast.source.indexOf(''); + const message = 'Invalid SVG content format'; + const reason = t` + found a closing "path" tag at index ${index}. + The path should be self-closing, + use "/>" instead of ">" + `; + reporter.error(`${message}: ${reason}`); + }, + ] + }, + ignore: [ + // TODO: go through these and simplify/remove fill paths; + // I've barely skimmed all svgs so there are probably more + 'icons/svg/file_type_fsharp.svg', + 'icons/svg/file_type_nix.svg', + 'icons/svg/file_type_python.svg', + 'icons/svg/file_type_sublime.svg', + ], +}; diff --git a/svgo.config.mjs b/svgo.config.mjs new file mode 100644 index 0000000..a04b1d1 --- /dev/null +++ b/svgo.config.mjs @@ -0,0 +1,88 @@ +export default { + multipass: true, + js2svg: { + indent: 2, + pretty: true, + }, + plugins: [ + 'removeDoctype', + 'removeXMLProcInst', + 'removeComments', + 'removeMetadata', + 'removeEditorsNSData', + 'cleanupAttrs', + 'cleanupIds', + 'cleanupNumericValues', + 'removeUselessDefs', + 'convertColors', + 'removeUnknownsAndDefaults', + 'removeNonInheritableGroupAttrs', + { + name: 'removeUselessStrokeAndFill', + params: { + removeNone: true, + }, + }, + 'removeDimensions', + 'cleanupEnableBackground', + 'removeHiddenElems', + 'removeEmptyText', + 'cleanupListOfValues', + { + name: 'convertShapeToPath', + params: { + convertArcs: true, + }, + }, + 'convertEllipseToCircle', + 'moveElemsAttrsToGroup', + 'moveGroupAttrsToElems', + 'collapseGroups', + { + name: 'convertPathData', + params: { + floatPrecision: 3, + noSpaceAfterFlags: false, + }, + }, + 'convertTransform', + 'removeEmptyAttrs', + 'removeEmptyContainers', + 'removeUnusedNS', + { + name: 'mergePaths', + params: { + force: true, + noSpaceAfterFlags: true, + }, + }, + { + name: 'addAttributesToSVGElement', + params: { + attributes: [{ + 'fill' :'#000', + 'fill-rule': 'evenodd', + 'viewBox': '0 0 16 16', + 'xmlns': 'http://www.w3.org/2000/svg', + }], + }, + }, + { + name: 'removeAttrs', + params: { + attrs: [ + 'svg:.*(?