Files
rspade_system/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
root d523f0f600 Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production)
Add global file drop handler, order column normalization, SPA hash fix
Serve CDN assets via /_vendor/ URLs instead of merging into bundles
Add production minification with license preservation
Improve JSON formatting for debugging and production optimization
Add CDN asset caching with CSS URL inlining for production builds
Add three-mode system (development, debug, production)
Update Manifest CLAUDE.md to reflect helper class architecture
Refactor Manifest.php into helper classes for better organization
Pre-manifest-refactor checkpoint: Add app_mode documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 10:38:22 +00:00

130 lines
3.6 KiB
JavaScript
Executable File

import { visit } from '../lib/util/visit.js';
import { inheritableAttrs, pathElems } from './_collections.js';
export const name = 'moveElemsAttrsToGroup';
export const description =
'Move common attributes of group children to the group';
/**
* Move common attributes of group children to the group
*
* @example
* <g attr1="val1">
* <g attr2="val2">
* text
* </g>
* <circle attr2="val2" attr3="val3"/>
* </g>
* ⬇
* <g attr1="val1" attr2="val2">
* <g>
* text
* </g>
* <circle attr3="val3"/>
* </g>
*
* @author Kir Belevich
*
* @type {import('../lib/types.js').Plugin}
*/
export const fn = (root) => {
// find if any style element is present
let deoptimizedWithStyles = false;
visit(root, {
element: {
enter: (node) => {
if (node.name === 'style') {
deoptimizedWithStyles = true;
}
},
},
});
return {
element: {
exit: (node) => {
// process only groups with more than 1 child
if (node.name !== 'g' || node.children.length <= 1) {
return;
}
// deoptimize the plugin when style elements are present
// selectors may rely on id, classes or tag names
if (deoptimizedWithStyles) {
return;
}
/**
* Find common attributes in group children.
*
* @type {Map<string, string>}
*/
const commonAttributes = new Map();
let initial = true;
let everyChildIsPath = true;
for (const child of node.children) {
if (child.type === 'element') {
if (!pathElems.has(child.name)) {
everyChildIsPath = false;
}
if (initial) {
initial = false;
// collect all inheritable attributes from first child element
for (const [name, value] of Object.entries(child.attributes)) {
// consider only inheritable attributes
if (inheritableAttrs.has(name)) {
commonAttributes.set(name, value);
}
}
} else {
// exclude uncommon attributes from initial list
for (const [name, value] of commonAttributes) {
if (child.attributes[name] !== value) {
commonAttributes.delete(name);
}
}
}
}
}
// preserve transform on children when group has filter or clip-path or mask
if (
node.attributes['filter'] != null ||
node.attributes['clip-path'] != null ||
node.attributes.mask != null
) {
commonAttributes.delete('transform');
}
// preserve transform when all children are paths
// so the transform could be applied to path data by other plugins
if (everyChildIsPath) {
commonAttributes.delete('transform');
}
// add common children attributes to group
for (const [name, value] of commonAttributes) {
if (name === 'transform') {
if (node.attributes.transform != null) {
node.attributes.transform = `${node.attributes.transform} ${value}`;
} else {
node.attributes.transform = value;
}
} else {
node.attributes[name] = value;
}
}
// delete common attributes from children
for (const child of node.children) {
if (child.type === 'element') {
for (const [name] of commonAttributes) {
delete child.attributes[name];
}
}
}
},
},
};
};