Skip to content

Commit

Permalink
feat(cleanupStyleAttributes): minify numeric values (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnkenny54 authored Nov 14, 2024
1 parent 9a38290 commit 44d2109
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 134 deletions.
55 changes: 0 additions & 55 deletions lib/attvalue.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { minifyNumber } from './svgo/tools.js';

export class AttValue {
#strVal;
/**
Expand All @@ -26,56 +24,3 @@ export class AttValue {
return this.#strVal;
}
}

export class OpacityValue extends AttValue {
/** @type {string|undefined} */
#strVal;
/** @type {number|undefined} */
#opacity;

/**
* @param {string|undefined} strVal
* @param {number} [opacity]
*/
constructor(strVal, opacity) {
super(strVal);
this.#opacity = opacity;
}

/**
* @returns {number}
*/
getOpacity() {
if (this.#opacity === undefined) {
// If opacity is not set, set it from the original string.
this.#opacity = parseFloat(super.toString());
}
return this.#opacity;
}

/**
* @param {import('./types.js').SVGAttValue} value
* @returns {OpacityValue}
*/
static getOpacityObj(value) {
if (typeof value === 'string') {
return new OpacityValue(value);
}
if (value instanceof OpacityValue) {
return value;
}
throw value;
}

/**
* Override parent method to insure string is minified.
* @returns {string}
*/
toString() {
if (this.#strVal === undefined) {
// Minified string hasn't been generated yet.
this.#strVal = minifyNumber(this.getOpacity());
}
return this.#strVal;
}
}
53 changes: 53 additions & 0 deletions lib/length.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AttValue } from './attvalue.js';
import { ExactNum } from './exactnum.js';
import { isDigit, minifyNumber, toFixed } from './svgo/tools.js';

export class LengthValue extends AttValue {
Expand All @@ -20,6 +21,18 @@ export class LengthValue extends AttValue {
if (isDigit(lastChar) || lastChar === '.') {
return new PixelLengthValue(value, undefined);
}
let units = '';
for (let index = value.length - 1; index >= 0; index--) {
const char = value[index];
if (isDigit(char) || char === '.') {
const num = value.substring(0, index + 1);
if (units === 'px') {
return new PixelLengthValue(num, undefined);
}
return new UnitLengthValue(num, units);
}
units = char + units;
}
}
return new LengthValue(value);
}
Expand All @@ -38,6 +51,13 @@ export class LengthValue extends AttValue {
throw value;
}

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

/**
* @returns {number|null}
*/
Expand Down Expand Up @@ -74,6 +94,17 @@ class PixelLengthValue extends LengthValue {
return minifyNumber(this.#pixels);
}

/**
* @returns {LengthValue}
*/
getMinifiedValue() {
const pixels = this.getPixels();
if (pixels === null) {
throw new Error();
}
return new PixelLengthValue(minifyNumber(pixels), this.#pixels);
}

/**
* @returns {number|null}
*/
Expand All @@ -96,3 +127,25 @@ class PixelLengthValue extends LengthValue {
return new PixelLengthValue(undefined, toFixed(pixels, digits));
}
}

class UnitLengthValue extends LengthValue {
#value;
#units;

/**
* @param {string} value
* @param {string} units
*/
constructor(value, units) {
super(value + units);
this.#value = new ExactNum(value);
this.#units = units;
}

/**
* @returns {LengthValue}
*/
getMinifiedValue() {
return new UnitLengthValue(this.#value.getMinifiedString(), this.#units);
}
}
22 changes: 22 additions & 0 deletions lib/lengthOrPct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { LengthValue } from './length.js';
import { PctValue } from './numericvalue.js';

export class LengthOrPctValue {
/**
* @param {import('./types.js').SVGAttValue} value
* @returns {LengthValue|PctValue}
*/
static getLengthOrPctObj(value) {
if (typeof value === 'string') {
const v = value.trim();
if (v.endsWith('%')) {
const pct = PctValue.createPctValue(v);
if (pct) {
return pct;
}
}
}

return LengthValue.getLengthObj(value);
}
}
83 changes: 83 additions & 0 deletions lib/numericvalue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { AttValue } from './attvalue.js';
import { ExactNum } from './exactnum.js';
import { isNumber, toFixed } from './svgo/tools.js';

export class NumericValue extends AttValue {
#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 PctValue(new ExactNum(value * 100));
}
return this;
}

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

export class PctValue extends AttValue {
#n;

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

/**
* @param {string} value
* @returns {PctValue|undefined}
*/
static createPctValue(value) {
const pct = value.substring(0, value.length - 1);
if (isNumber(pct)) {
return new PctValue(new ExactNum(pct));
}
}

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 NumericValue(new ExactNum(pct / 100));
}

/**
* @param {number} digits
* @returns {AttValue}
*/
round(digits) {
return new NumericValue(new ExactNum(this.#n.getValue() / 100)).round(
digits,
);
}
}
55 changes: 55 additions & 0 deletions lib/opacity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { AttValue } from './attvalue.js';
import { minifyNumber } from './svgo/tools.js';

export class OpacityValue extends AttValue {
/** @type {string|undefined} */
#strVal;
/** @type {number|undefined} */
#opacity;

/**
* @param {string|undefined} strVal
* @param {number} [opacity]
*/
constructor(strVal, opacity) {
super(strVal);
this.#opacity = opacity;
}

/**
* @returns {number}
*/
getOpacity() {
if (this.#opacity === undefined) {
// If opacity is not set, set it from the original string.
this.#opacity = parseFloat(super.toString());
}
return this.#opacity;
}

/**
* @param {import('./types.js').SVGAttValue} value
* @returns {OpacityValue}
*/
static getOpacityObj(value) {
if (typeof value === 'string') {
return new OpacityValue(value);
}
if (value instanceof OpacityValue) {
return value;
}
throw value;
}

/**
* Override parent method to insure string is minified.
* @returns {string}
*/
toString() {
if (this.#strVal === undefined) {
// Minified string hasn't been generated yet.
this.#strVal = minifyNumber(this.getOpacity());
}
return this.#strVal;
}
}
Loading

0 comments on commit 44d2109

Please sign in to comment.