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