From e447c7adaeb162115d0485c00cdef869c95d7220 Mon Sep 17 00:00:00 2001 From: korki Date: Sun, 17 Apr 2022 07:24:52 +0200 Subject: Use a custom svgo plugin to optimize svg attributes (#1148) * Use custom svgo plugin to optimize svg attrs * Remove `finalNewline` since it doesn't seem to have any effect * Minor tweaks Co-authored-by: XhmikosR --- build/build-svgs.js | 39 +++------------- package-lock.json | 127 ---------------------------------------------------- package.json | 1 - svgo.config.js | 42 ++++++++++++++++- 4 files changed, 46 insertions(+), 163 deletions(-) diff --git a/build/build-svgs.js b/build/build-svgs.js index 886af3532..c889ce59f 100644 --- a/build/build-svgs.js +++ b/build/build-svgs.js @@ -4,52 +4,23 @@ const fs = require('fs').promises const path = require('path') +const process = require('process') const picocolors = require('picocolors') -const cheerio = require('cheerio') const { loadConfig, optimize } = require('svgo') const iconsDir = path.join(__dirname, '../icons/') const VERBOSE = process.argv.includes('--verbose') -const svgAttributes = { - xmlns: 'http://www.w3.org/2000/svg', - width: '16', - height: '16', - fill: 'currentColor', - class: '', - viewBox: '0 0 16 16' -} - async function processFile(file, config) { const filepath = path.join(iconsDir, file) const basename = path.basename(file, '.svg') const originalSvg = await fs.readFile(filepath, 'utf8') - const optimizedSvg = await optimize(originalSvg, { - path: filepath, - ...config - }) - - const $ = await cheerio.load(optimizedSvg.data, { - xml: { - xmlMode: true - } - }) - const $svgElement = $('svg') - - // We keep all SVG contents apart from the `` element. - // `$(this)` refers to the original object not the replaced one! - $svgElement.replaceWith($('').append($(this).html())) - - // Then we set the `svgAttributes` in the order we want to, - // hence why we remove the attributes and add them back - for (const [attribute, value] of Object.entries(svgAttributes)) { - $svgElement.removeAttr(attribute) - $svgElement.attr(attribute, attribute === 'class' ? `bi bi-${basename}` : value) - } + const { data: optimizedSvg } = await optimize(originalSvg, { path: filepath, ...config }) - const resultSvg = $svgElement.toString().replace(/\r\n?/g, '\n') + // svgo will always add a final newline when in pretty mode + const resultSvg = optimizedSvg.trim() if (resultSvg !== originalSvg) { await fs.writeFile(filepath, resultSvg, 'utf8') @@ -75,7 +46,7 @@ async function processFile(file, config) { const filesLength = files.length - console.log(picocolors.green('\nSuccess, %s icon%s prepared!'), filesLength, filesLength !== 1 ? 's' : '') + console.log(picocolors.green('\nSuccess, prepared %s icon%s!'), filesLength, filesLength !== 1 ? 's' : '') console.timeEnd(timeLabel) } catch (error) { console.error(error) diff --git a/package-lock.json b/package-lock.json index 12c9e3e53..73ed15e81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "MIT", "devDependencies": { "autoprefixer": "^10.4.4", - "cheerio": "^1.0.0-rc.10", "cross-env": "^7.0.3", "eslint": "^8.12.0", "fantasticon": "^1.2.3", @@ -1370,43 +1369,6 @@ "tslib": "^2.0.3" } }, - "node_modules/cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "dev": true, - "dependencies": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", - "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", - "dev": true, - "dependencies": { - "css-select": "^4.1.3", - "css-what": "^5.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -3464,25 +3426,6 @@ "node": ">=8" } }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, "node_modules/http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", @@ -5302,21 +5245,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "dependencies": { - "parse5": "^6.0.1" - } - }, "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", @@ -8899,34 +8827,6 @@ "tslib": "^2.0.3" } }, - "cheerio": { - "version": "1.0.0-rc.10", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", - "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", - "dev": true, - "requires": { - "cheerio-select": "^1.5.0", - "dom-serializer": "^1.3.2", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - } - }, - "cheerio-select": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", - "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", - "dev": true, - "requires": { - "css-select": "^4.1.3", - "css-what": "^5.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.7.0" - } - }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -10555,18 +10455,6 @@ "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", "dev": true }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, "http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", @@ -11949,21 +11837,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - } - }, "pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", diff --git a/package.json b/package.json index 2f83a7808..9c3a4ea08 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "sass": "font/bootstrap-icons.scss", "devDependencies": { "autoprefixer": "^10.4.4", - "cheerio": "^1.0.0-rc.10", "cross-env": "^7.0.3", "eslint": "^8.12.0", "fantasticon": "^1.2.3", diff --git a/svgo.config.js b/svgo.config.js index 62bf8f654..19c41a86d 100644 --- a/svgo.config.js +++ b/svgo.config.js @@ -1,10 +1,13 @@ 'use strict' +const path = require('path') + module.exports = { multipass: true, js2svg: { pretty: true, - indent: 2 + indent: 2, + eol: 'lf' }, plugins: [ { @@ -31,6 +34,43 @@ module.exports = { 'fill' ] } + }, + // Custom plugin which resets the SVG attributes to explicit values + { + name: 'explicitAttrs', + type: 'visitor', + params: { + attributes: { + xmlns: 'http://www.w3.org/2000/svg', + width: '16', + height: '16', + fill: 'currentColor', + class: '', // We replace the class with the correct one based on filename later + viewBox: '0 0 16 16' + } + }, + fn(_root, params, info) { + if (!params.attributes) { + return null + } + + const basename = path.basename(info.path, '.svg') + + return { + element: { + enter(node, parentNode) { + if (node.name === 'svg' && parentNode.type === 'root') { + // We set the `svgAttributes` in the order we want to, + // hence why we remove the attributes and add them back + node.attributes = {} + for (const [key, value] of Object.entries(params.attributes)) { + node.attributes[key] = key === 'class' ? `bi bi-${basename}` : value + } + } + } + } + } + } } ] } -- cgit v1.2.3