Skip to content

Commit

Permalink
Add minifyGradients plugin. (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnkenny54 authored Oct 19, 2024
1 parent b590a57 commit 9db08dc
Show file tree
Hide file tree
Showing 30 changed files with 850 additions and 239 deletions.
11 changes: 0 additions & 11 deletions docs/04-plugins/convertOneStopGradients.mdx

This file was deleted.

4 changes: 2 additions & 2 deletions lib/builtin.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import * as collapseGroups from '../plugins/collapseGroups.js';
import * as combinePaths from '../plugins/combinePaths.js';
import * as combineStyleElements from '../plugins/combineStyleElements.js';
import * as convertEllipseToCircle from '../plugins/convertEllipseToCircle.js';
import * as convertOneStopGradients from '../plugins/convertOneStopGradients.js';
import * as convertPathData from '../plugins/convertPathData.js';
import * as convertShapeToPath from '../plugins/convertShapeToPath.js';
import * as convertStyleToAttrs from '../plugins/convertStyleToAttrs.js';
Expand All @@ -23,6 +22,7 @@ import * as inlineStyles from '../plugins/inlineStyles.js';
import * as inlineUse from '../plugins/inlineUse.js';
import * as mergePaths from '../plugins/mergePaths.js';
import * as minifyColors from '../plugins/minifyColors.js';
import * as minifyGradients from '../plugins/minifyGradients.js';
import * as minifyPathData from '../plugins/minifyPathData.js';
import * as minifyStyles from '../plugins/minifyStyles.js';
import * as minifyTransforms from '../plugins/minifyTransforms.js';
Expand Down Expand Up @@ -81,7 +81,6 @@ export const builtin = Object.freeze([
collapseGroups,
combinePaths,
convertEllipseToCircle,
convertOneStopGradients,
convertPathData,
convertShapeToPath,
convertStyleToAttrs,
Expand All @@ -90,6 +89,7 @@ export const builtin = Object.freeze([
inlineUse,
mergePaths,
minifyColors,
minifyGradients,
minifyPathData,
minifyStyles,
minifyTransforms,
Expand Down
130 changes: 130 additions & 0 deletions lib/stop-offset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { AttValue } from './attvalue.js';
import { ExactNum } from './exactnum.js';
import { isNumber, toFixed } from './svgo/tools.js';

export class StopOffsetValue extends AttValue {
/**
* @param {string|undefined} strVal
*/
constructor(strVal) {
super(strVal);
}

/**
* @param {string} value
* @returns {StopOffsetValue}
*/
static #createStopOffsetObj(value) {
value = value.trim();
if (value.endsWith('%')) {
const pct = value.substring(0, value.length - 1);
if (isNumber(pct)) {
return new PctStopOffsetValue(new ExactNum(pct));
}
} else {
if (isNumber(value)) {
return new NumericStopOffsetValue(new ExactNum(value));
}
}
return new StopOffsetValue(value);
}

/**
* @param {string|AttValue} value
* @returns {StopOffsetValue}
*/
static getStopOffsetObj(value) {
if (typeof value === 'string') {
return this.#createStopOffsetObj(value);
}
if (value instanceof StopOffsetValue) {
return value;
}
throw value;
}

/**
* @returns {StopOffsetValue}
*/
getMinifiedValue() {
return this;
}

/**
* @param {number} digits
* @returns {StopOffsetValue}
*/
// eslint-disable-next-line no-unused-vars
round(digits) {
return this;
}
}

class NumericStopOffsetValue extends StopOffsetValue {
#n;

/**
* @param {ExactNum} n
*/
constructor(n) {
super(undefined);
this.#n = n;
}

generateString() {
return this.#n.getMinifiedString();
}

getMinifiedValue() {
const value = this.#n.getValue();
// Use % if it can be represented as a single digit.
if (value >= 0.01 && value <= 0.09 && this.#n.getNumberOfDigits() === 2) {
return new PctStopOffsetValue(new ExactNum(value * 100));
}
return this;
}

/**
* @param {number} digits
* @returns {StopOffsetValue}
*/
round(digits) {
const value = toFixed(this.#n.getValue(), digits);
return new NumericStopOffsetValue(new ExactNum(value)).getMinifiedValue();
}
}

class PctStopOffsetValue extends StopOffsetValue {
#n;

/**
* @param {ExactNum} n
*/
constructor(n) {
super(undefined);
this.#n = n;
}

generateString() {
return this.#n.getMinifiedString() + '%';
}

getMinifiedValue() {
const pct = this.#n.getValue();
// Use % if it can be represented as a single digit.
if (pct >= 1 && pct <= 9 && Number.isInteger(pct)) {
return this;
}
return new NumericStopOffsetValue(new ExactNum(pct / 100));
}

/**
* @param {number} digits
* @returns {StopOffsetValue}
*/
round(digits) {
return new NumericStopOffsetValue(
new ExactNum(this.#n.getValue() / 100),
).round(digits);
}
}
90 changes: 90 additions & 0 deletions lib/svgo/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { referencesProps } from '../../plugins/_collections.js';
import { cssPropToString, getStyleDeclarations } from '../css-tools.js';

/**
* @typedef {Map<string,{referencingEl:import('../types.js').XastElement,referencingAtt:string}[]>} IdReferenceMap
* @typedef {import('../types.js').DataUri} DataUri
* @typedef {import('../types.js').PathDataCommand} PathDataCommand
*/
Expand Down Expand Up @@ -193,6 +194,79 @@ export function getNumberOfDecimalDigits(str) {
return str.slice(str.indexOf('.')).length - 1;
}

/**
* @param {string} str
* @returns {boolean}
*/
export function isNumber(str) {
/** @type {'start'|'number'|'decimal'|'exp'|'expdigits'|'end'} */
let state = 'start';
let hasDigits = false;
let hasExp = false;
let hasExpDigits = false;
for (let index = 0; index < str.length; index++) {
const char = str[index];
switch (char) {
case ' ':
if (state === 'start' || state === 'end') {
continue;
} else if (state === 'number') {
state = 'end';
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (state === 'start') {
hasDigits = true;
state = 'number';
} else if (state === 'decimal') {
hasDigits = true;
} else if (state === 'exp' || state === 'expdigits') {
hasExpDigits = true;
state = 'expdigits';
}
break;
case '.':
if (state === 'start' || state === 'number') {
state = 'decimal';
} else {
return false;
}
break;
case '-':
case '+':
if (state === 'start') {
continue;
} else if (state === 'exp') {
state = 'expdigits';
} else {
return false;
}
break;
case 'e':
case 'E':
if (state === 'decimal' || state === 'number') {
state = 'exp';
hasExp = true;
} else {
return false;
}
break;
default:
return false;
}
}
return state !== 'start' && hasDigits && (!hasExp || hasExpDigits);
}

/**
* Return the number as a string in shortest form.
*
Expand All @@ -206,6 +280,22 @@ export const minifyNumber = (n) => {
return removeLeadingZero(n);
};

/**
* @param {import('../types.js').XastElement} element
* @param {IdReferenceMap} allReferencedIds
*/
export function recordReferencedIds(element, allReferencedIds) {
const referencedIds = getReferencedIds(element);
for (const { id, attName } of referencedIds) {
let references = allReferencedIds.get(id);
if (!references) {
references = [];
allReferencedIds.set(id, references);
}
references.push({ referencingEl: element, referencingAtt: attName });
}
}

/**
* Remove floating-point numbers leading zero.
*
Expand Down
10 changes: 1 addition & 9 deletions lib/xast.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { selectAll, selectOne, is } from 'css-select';
import { selectAll, is } from 'css-select';
import xastAdaptor from './svgo/css-select-adapter.js';

/**
Expand All @@ -24,14 +24,6 @@ export const querySelectorAll = (node, selector) => {
return selectAll(selector, node, cssSelectOptions);
};

/**
* @type {(node: XastNode, selector: string) => ?XastChild}
* @deprecated
*/
export const querySelector = (node, selector) => {
return selectOne(selector, node, cssSelectOptions);
};

/**
* @type {(node: XastChild, selector: string) => boolean}
* @deprecated
Expand Down
1 change: 1 addition & 0 deletions plugins/_collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,7 @@ export const elems = {
attrsGroups: new Set(['core', 'presentation']),
attrs: new Set(['class', 'style', 'offset', 'path']),
content: new Set(['animate', 'animateColor', 'set']),
defaults: { offset: '0' },
},
style: {
attrsGroups: new Set(['core']),
Expand Down
24 changes: 6 additions & 18 deletions plugins/cleanupIds.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
*/

import { getStyleDeclarations } from '../lib/css-tools.js';
import { writeStyleAttribute } from '../lib/css.js';
import {
generateId,
getReferencedIds,
getReferencedIdsInAttribute,
recordReferencedIds,
SVGOError,
updateReferencedDeclarationIds,
} from '../lib/svgo/tools.js';
Expand All @@ -18,7 +14,7 @@ export const name = 'cleanupIds';
export const description = 'removes unused IDs and minifies used';

/**
* @param {XastElement} element
* @param {import('../lib/types.js').XastElement} element
* @param {string} attName
* @param {Map<string,string>} idMap
*/
Expand All @@ -44,7 +40,7 @@ function updateReferencedId(element, attName, idMap) {
}

/**
* @param {XastElement} element
* @param {import('../lib/types.js').XastElement} element
* @param {Map<string,string>} idMap
*/
function updateReferencedStyleId(element, idMap) {
Expand Down Expand Up @@ -133,9 +129,9 @@ export const fn = (_root, params, info) => {
return idMap;
}

/** @type {Map<string,XastElement>} */
/** @type {Map<string,import('../lib/types.js').XastElement>} */
const foundIds = new Map();
/** @type {Map<string,{referencingEl:XastElement,referencingAtt:string}[]>} */
/** @type {import('../lib/svgo/tools.js').IdReferenceMap} */
const allReferencedIds = new Map();

const preserveIds = new Set(
Expand Down Expand Up @@ -170,15 +166,7 @@ export const fn = (_root, params, info) => {
foundIds.set(element.attributes.id, element);
}

const referencedIds = getReferencedIds(element);
for (const { id, attName } of referencedIds) {
let references = allReferencedIds.get(id);
if (!references) {
references = [];
allReferencedIds.set(id, references);
}
references.push({ referencingEl: element, referencingAtt: attName });
}
recordReferencedIds(element, allReferencedIds);
},
},

Expand Down
Loading

0 comments on commit 9db08dc

Please sign in to comment.