diff --git a/source/app/metrics/index.mjs b/source/app/metrics/index.mjs index ebb3c986..736ab872 100644 --- a/source/app/metrics/index.mjs +++ b/source/app/metrics/index.mjs @@ -1,8 +1,6 @@ //Imports import ejs from "ejs" -import SVGO from "svgo" import util from "util" -import xmlformat from "xml-formatter" import * as utils from "./utils.mjs" //Setup @@ -174,32 +172,12 @@ export default async function metrics({login, q}, {graphql, rest, plugins, conf, if (q["config.gemoji"]) rendered = await imports.svg.gemojis(rendered, {rest}) //Optimize rendering - if (!q.raw) - rendered = xmlformat(rendered, {lineSeparator:"\n", collapseContent:true}) - if ((conf.settings?.optimize) && (!q.raw)) { - console.debug(`metrics/compute/${login} > optimize`) - if (experimental.has("--optimize")) { - const {error, data:optimized} = await SVGO.optimize(rendered, { - multipass:true, - plugins:SVGO.extendDefaultPlugins([ - //Additional cleanup - {name:"cleanupListOfValues"}, - {name:"removeRasterImages"}, - {name:"removeScriptElement"}, - //Force CSS style consistency - {name:"inlineStyles", active:false}, - {name:"removeViewBox", active:false}, - ]), - }) - if (error) - throw new Error(`Could not optimize SVG: \n${error}`) - rendered = optimized - console.debug(`metrics/compute/${login} > optimize > success`) - } - else - console.debug(`metrics/compute/${login} > optimize > this feature is currently disabled due to display issues (use --optimize flag in experimental features to force enable it)`) - - } + if ((conf.settings?.optimize === true) || (conf.settings?.optimize?.includes?.("css"))) + rendered = await imports.svg.optimize.css(rendered) + if ((conf.settings?.optimize === true) || (conf.settings?.optimize?.includes?.("xml"))) + rendered = await imports.svg.optimize.xml(rendered, q) + if ((conf.settings?.optimize === true) || (conf.settings?.optimize?.includes?.("svg"))) + rendered = await imports.svg.optimize.svg(rendered, q, experimental) //Verify svg if (verify) { console.debug(`metrics/compute/${login} > verify SVG`) diff --git a/source/app/metrics/utils.mjs b/source/app/metrics/utils.mjs index 16d1a92e..4b2f42b9 100644 --- a/source/app/metrics/utils.mjs +++ b/source/app/metrics/utils.mjs @@ -26,6 +26,11 @@ import emoji from "emoji-name-map" import minimatch from "minimatch" import crypto from "crypto" import linguist from "linguist-js" +import purgecss from "purgecss" +import csso from "csso" +import SVGO from "svgo" +import xmlformat from "xml-formatter" + prism_lang() //Exports @@ -155,7 +160,7 @@ export function htmlunescape(string, u = {"&":true, "<":true, ">":true, '"':true /**Chartist */ export async function chartist() { - const css = `` + const css = `` return (await nodechartist(...arguments)) .replace(/class="ct-chart-line">/, `class="ct-chart-line">${css}`) } @@ -510,6 +515,64 @@ export const svg = { rendered = rendered.replace(new RegExp(emoji, "g"), gemoji) return rendered }, + /**Optimizers */ + optimize:{ + /**CSS optimizer */ + async css(rendered) { + //Extract styles + console.debug("metrics/svg/optimize/css > optimizing") + const regex = /` + return rendered.replace(cleaned, optimized) + }, + /**XML optimizer */ + async xml(rendered, {raw = false} = {}) { + console.debug("metrics/svg/optimize/xml > optimizing") + if (raw) { + console.debug("metrics/svg/optimize/xml > skipped as raw option is enabled") + return rendered + } + return xmlformat(rendered, {lineSeparator:"\n", collapseContent:true}) + }, + /**SVG optimizer */ + async svg(rendered, {raw = false} = {}, experimental = new Set()) { + console.debug("metrics/svg/optimize/svg > optimizing") + if (raw) { + console.debug("metrics/svg/optimize/svg > skipped as raw option is enabled") + return rendered + } + if (!experimental.has("--optimize")) { + console.debug("metrics/svg/optimize/svg > this feature require experimental feature flag --optimize-svg") + return rendered + } + const {error, data:optimized} = await SVGO.optimize(rendered, { + multipass:true, + plugins:SVGO.extendDefaultPlugins([ + //Additional cleanup + {name:"cleanupListOfValues"}, + {name:"removeRasterImages"}, + {name:"removeScriptElement"}, + //Force CSS style consistency + {name:"inlineStyles", active:false}, + {name:"removeViewBox", active:false}, + ]), + }) + if (error) + throw new Error(`Could not optimize SVG: \n${error}`) + return optimized + } + } } /**Wait */ diff --git a/source/plugins/core/metadata.yml b/source/plugins/core/metadata.yml index e01779d5..763ad86d 100644 --- a/source/plugins/core/metadata.yml +++ b/source/plugins/core/metadata.yml @@ -106,8 +106,14 @@ inputs: # Some templates may not support this option optimize: description: SVG optimization - type: boolean - default: yes + type: array + default: css, xml + format: + - comma-separated + values: + - css # Purge and minify CSS styles + - xml # Pretty-print XML + - svg # Optimize SVG with SVGO (experimental, require --optimize-svg flag) # Setup additional templates from remote repositories setup_community_templates: @@ -280,13 +286,14 @@ inputs: default: no # Experimental features + # Note that no backward compatibility are guaranteed for these features experimental_features: description: Experimental features type: array format: space-separated default: "" values: - - --optimize + - --optimize-svg # Use mocked data to bypass external APIs use_mocked_data: