1
0
mirror of https://github.com/simple-icons/simple-icons.git synced 2025-01-05 01:20:39 +02:00

Memoize functions in SVG linting (#9233)

This commit is contained in:
Álvaro Mondéjar 2023-08-07 22:35:36 -06:00 committed by GitHub
parent d690e11c4d
commit 8abcd9c8b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -31,6 +31,7 @@ const negativeZerosRegexp = /-0(?=[^\.]|[\s\d\w]|$)/g;
const svgPathRegexp = /^[Mm][MmZzLlHhVvCcSsQqTtAaEe0-9\-,. ]+$/;
const iconSize = 24;
const iconTargetCenter = iconSize / 2;
const iconFloatPrecision = 3;
const iconMaxFloatPrecision = 5;
const iconTolerance = 0.001;
@ -117,6 +118,27 @@ const maybeShortenedWithEllipsis = (str) => {
return str.length > 20 ? `${str.substring(0, 20)}...` : str;
};
/**
* Memoize a function which accepts a single argument.
* A second argument can be passed to be used as key.
*/
const memoize = (func) => {
const results = {};
return (arg, defaultKey = null) => {
const key = defaultKey || arg;
if (!results[key]) {
results[key] = func(arg);
}
return results[key];
};
};
const getIconPath = memoize(($icon, filepath) => $icon.find('path').attr('d'));
const getIconPathSegments = memoize((iconPath) => parsePath(iconPath));
const getIconPathBbox = memoize((iconPath) => svgPathBbox(iconPath));
if (updateIgnoreFile) {
process.on('exit', () => {
// ensure object output order is consistent due to async svglint processing
@ -345,15 +367,15 @@ export default {
}
}
},
(reporter, $) => {
(reporter, $, ast, filepath) => {
reporter.name = 'icon-size';
const iconPath = $.find('path').attr('d');
const iconPath = getIconPath($, filepath);
if (!updateIgnoreFile && isIgnored(reporter.name, iconPath)) {
return;
}
const [minX, minY, maxX, maxY] = svgPathBbox(iconPath);
const [minX, minY, maxX, maxY] = getIconPathBbox(iconPath);
const width = +(maxX - minX).toFixed(iconFloatPrecision);
const height = +(maxY - minY).toFixed(iconFloatPrecision);
@ -374,11 +396,11 @@ export default {
}
}
},
(reporter, $, ast) => {
(reporter, $, ast, filepath) => {
reporter.name = 'icon-precision';
const iconPath = $.find('path').attr('d');
const segments = parsePath(iconPath);
const iconPath = getIconPath($, filepath);
const segments = getIconPathSegments(iconPath);
for (const segment of segments) {
const precisionMax = Math.max(
@ -404,12 +426,11 @@ export default {
}
}
},
(reporter, $, ast) => {
(reporter, $, ast, filepath) => {
reporter.name = 'ineffective-segments';
const iconPath = $.find('path').attr('d');
const segments = parsePath(iconPath);
const iconPath = getIconPath($, filepath);
const segments = getIconPathSegments(iconPath);
const absSegments = svgpath(iconPath).abs().unshort().segments;
const lowerMovementCommands = ['m', 'l'];
@ -625,17 +646,15 @@ export default {
}
}
},
(reporter, $, ast) => {
(reporter, $, ast, filepath) => {
reporter.name = 'collinear-segments';
const iconPath = $.find('path').attr('d');
/**
* Extracts collinear coordinates from SVG path straight lines
* (does not extracts collinear coordinates from curves).
**/
const getCollinearSegments = (iconPath) => {
const segments = parsePath(iconPath),
const segments = getIconPathSegments(iconPath),
collinearSegments = [],
straightLineCommands = 'HhVvLlMm';
@ -792,8 +811,12 @@ export default {
return collinearSegments;
};
const collinearSegments = getCollinearSegments(iconPath),
pathDIndex = getPathDIndex(ast.source);
const iconPath = getIconPath($, filepath),
collinearSegments = getCollinearSegments(iconPath);
if (collinearSegments.length === 0) {
return;
}
const pathDIndex = getPathDIndex(ast.source);
for (const segment of collinearSegments) {
let errorMsg = `Collinear segment "${iconPath.substring(
segment.start,
@ -827,10 +850,10 @@ export default {
}
}
},
(reporter, $, ast) => {
(reporter, $, ast, filepath) => {
reporter.name = 'negative-zeros';
const iconPath = $.find('path').attr('d');
const iconPath = getIconPath($, filepath);
// Find negative zeros inside path
const negativeZeroMatches = Array.from(
@ -853,27 +876,26 @@ export default {
}
}
},
(reporter, $) => {
(reporter, $, ast, filepath) => {
reporter.name = 'icon-centered';
const iconPath = $.find('path').attr('d');
const iconPath = getIconPath($, filepath);
if (!updateIgnoreFile && isIgnored(reporter.name, iconPath)) {
return;
}
const [minX, minY, maxX, maxY] = svgPathBbox(iconPath);
const targetCenter = iconSize / 2;
const [minX, minY, maxX, maxY] = getIconPathBbox(iconPath);
const centerX = +((minX + maxX) / 2).toFixed(iconFloatPrecision);
const devianceX = centerX - targetCenter;
const devianceX = centerX - iconTargetCenter;
const centerY = +((minY + maxY) / 2).toFixed(iconFloatPrecision);
const devianceY = centerY - targetCenter;
const devianceY = centerY - iconTargetCenter;
if (
Math.abs(devianceX) > iconTolerance ||
Math.abs(devianceY) > iconTolerance
) {
reporter.error(
`<path> must be centered at (${targetCenter}, ${targetCenter});` +
`<path> must be centered at (${iconTargetCenter}, ${iconTargetCenter});` +
` the center is currently (${centerX}, ${centerY})`,
);
if (updateIgnoreFile) {
@ -881,16 +903,16 @@ export default {
}
}
},
(reporter, $, ast) => {
(reporter, $, ast, filepath) => {
reporter.name = 'path-format';
const iconPath = $.find('path').attr('d');
const iconPath = getIconPath($, filepath);
if (!svgPathRegexp.test(iconPath)) {
let errorMsg = 'Invalid path format',
reason;
if (!/^[Mm]/.test(iconPath)) {
if (!iconPath.startsWith('M') && !iconPath.startsWith('m')) {
// doesn't start with moveto
reason =
'should start with "moveto" command ("M" or "m"),' +
@ -917,8 +939,7 @@ export default {
if (invalidCharactersMsgs.length > 0) {
reason = `unexpected character${
invalidCharactersMsgs.length > 1 ? 's' : ''
} found`;
reason += ` (${invalidCharactersMsgs.join(', ')})`;
} found (${invalidCharactersMsgs.join(', ')})`;
reporter.error(`${errorMsg}: ${reason}`);
}
}