[new plugin] reimplement inlineDefs
The code is taken from https://github.com/svg/svgo/pull/976, refactored with new api, covered types and simplified. Plugin has no dependencies so can be used without changing. ``` const inlineDefs = require('./inlineDefs.js'); module.exports = { plugins: [ 'preset-default', inlineDefs ] }; ```
166
plugins/inlineDefs.js
Normal file
@ -0,0 +1,166 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastParent} XastParent
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
*/
|
||||
|
||||
exports.name = 'inlineDefs';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'inlines svg definitions';
|
||||
|
||||
/**
|
||||
* @typedef {(element: XastElement, parentNode: XastParent) => void} VisitCallback
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(element: XastParent, fn: VisitCallback) => void}
|
||||
*/
|
||||
const visitElements = (node, fn) => {
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element') {
|
||||
fn(child, node);
|
||||
visitElements(child, fn);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Replaces use tag with the corresponding definitions
|
||||
* if onlyUnique is enabled, replaces only use tags with definitions referred to only once
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* onlyUnique?: boolean
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const { onlyUnique = true } = params;
|
||||
// hacky extract JSAPI class to avoid imports from other modules
|
||||
const JSAPI = root.constructor;
|
||||
|
||||
/**
|
||||
* @type {[XastElement, XastParent][]}
|
||||
*/
|
||||
const uses = [];
|
||||
/**
|
||||
* @type {Map<string, number>}
|
||||
*/
|
||||
const useCounts = new Map();
|
||||
/**
|
||||
* @type {Map<string, XastElement>}
|
||||
*/
|
||||
const referencedElements = new Map();
|
||||
|
||||
// collect defs container and all uses
|
||||
visitElements(root, (node, parentNode) => {
|
||||
if (node.name === 'use') {
|
||||
uses.push([node, parentNode]);
|
||||
const href = node.attributes['xlink:href'] || node.attributes.href;
|
||||
const count = useCounts.get(href) || 0;
|
||||
useCounts.set(href, count + 1);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// find elements referenced by all <use>
|
||||
if (node.attributes.id == null) {
|
||||
return;
|
||||
}
|
||||
const href = `#${node.attributes.id}`;
|
||||
const count = useCounts.get(href);
|
||||
// not referenced
|
||||
if (count == null) {
|
||||
return;
|
||||
}
|
||||
referencedElements.set(href, node);
|
||||
/// remove id attribute when referenced yb <use> more than once
|
||||
if (onlyUnique === false && count > 1) {
|
||||
delete node.attributes.id;
|
||||
}
|
||||
// remove elements referenced by <use> only once
|
||||
if (onlyUnique === true && count === 1) {
|
||||
parentNode.children = parentNode.children.filter(
|
||||
(child) => child !== node
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
exit(node, parentNode) {
|
||||
// remove empty <defs> container
|
||||
if (node.name === 'defs') {
|
||||
if (onlyUnique === false || node.children.length === 0) {
|
||||
parentNode.children = parentNode.children.filter(
|
||||
(child) => child !== node
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
root: {
|
||||
exit: () => {
|
||||
for (const [use, useParentNode] of uses) {
|
||||
const href = use.attributes['xlink:href'] || use.attributes['href'];
|
||||
const count = useCounts.get(href) || 0;
|
||||
const referenced = referencedElements.get(href);
|
||||
|
||||
if (onlyUnique === true && count > 1) {
|
||||
continue;
|
||||
}
|
||||
if (referenced == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy attrubutes from <use> to referenced element
|
||||
for (const [name, value] of Object.entries(use.attributes)) {
|
||||
if (
|
||||
name !== 'x' &&
|
||||
name !== 'y' &&
|
||||
name !== 'xlink:href' &&
|
||||
name !== 'href'
|
||||
) {
|
||||
referenced.attributes[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const x = use.attributes.x;
|
||||
const y = use.attributes.y;
|
||||
let attrValue = null;
|
||||
if (x != null && y != null) {
|
||||
attrValue = `translate(${x}, ${y})`;
|
||||
} else if (x != null) {
|
||||
attrValue = `translate(${x})`;
|
||||
}
|
||||
|
||||
let replacement = referenced;
|
||||
// wrap referenced element with <g> when <use> had coordinates
|
||||
if (attrValue != null) {
|
||||
/**
|
||||
* @type {XastElement}
|
||||
*/
|
||||
const g = {
|
||||
type: 'element',
|
||||
name: 'g',
|
||||
attributes: {
|
||||
transform: attrValue,
|
||||
},
|
||||
children: [referenced],
|
||||
};
|
||||
// @ts-ignore
|
||||
replacement = new JSAPI(g);
|
||||
}
|
||||
useParentNode.children = useParentNode.children.map((child) => {
|
||||
if (child === use) {
|
||||
return replacement;
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
@ -54,3 +54,4 @@ exports.removeXMLProcInst = require('./removeXMLProcInst.js');
|
||||
exports.reusePaths = require('./reusePaths.js');
|
||||
exports.sortAttrs = require('./sortAttrs.js');
|
||||
exports.sortDefsChildren = require('./sortDefsChildren.js');
|
||||
exports.inlineDefs = require('./inlineDefs.js');
|
||||
|
12
test/plugins/inlineDefs.01.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg id="test01" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use xlink:href="#rect1"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test01" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 419 B |
20
test/plugins/inlineDefs.02.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<svg id="test02" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</defs>
|
||||
<use xlink:href="#rect1"></use>
|
||||
<use xlink:href="#rect1"></use>
|
||||
<use xlink:href="#rect2"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test02" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use xlink:href="#rect1"/>
|
||||
<use xlink:href="#rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 680 B |
15
test/plugins/inlineDefs.03.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg id="test03" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</defs>
|
||||
<use xlink:href="#rect1"></use>
|
||||
<use xlink:href="#rect2"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test03" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 555 B |
14
test/plugins/inlineDefs.04.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg id="test04" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use xlink:href="#rect1" x="50" y="60"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test04" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<g transform="translate(50, 60)">
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 480 B |
17
test/plugins/inlineDefs.05.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<svg id="test05" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3"/>
|
||||
<mask id="m" x="0" y="0" width="301" height="31" fill="#fff">
|
||||
<use xlink:href="#a" stroke-width="4" opacity=".4"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<use xlink:href="#m"/>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test05" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<mask id="m" x="0" y="0" width="301" height="31" fill="#fff">
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3" stroke-width="4" opacity=".4"/>
|
||||
</mask>
|
||||
</svg>
|
After Width: | Height: | Size: 747 B |
22
test/plugins/inlineDefs.06.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg id="test06" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3"/>
|
||||
<g id="c">
|
||||
<use xlink:href="#b"/>
|
||||
</g>
|
||||
<g id="b">
|
||||
<use xlink:href="#a"/>
|
||||
</g>
|
||||
</defs>
|
||||
<use xlink:href="#c"/>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test06" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="c">
|
||||
<g id="b">
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 682 B |
18
test/plugins/inlineDefs.07.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg id="test07" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use xlink:href="#rect1"></use>
|
||||
<use xlink:href="#rect1"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test07" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100"/>
|
||||
<rect width="100" height="100"/>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
{ "onlyUnique": false }
|
After Width: | Height: | Size: 515 B |
12
test/plugins/inlineDefs.08.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg id="test08" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use href="#rect1"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test08" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 327 B |
20
test/plugins/inlineDefs.09.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<svg id="test09" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</defs>
|
||||
<use href="#rect1"></use>
|
||||
<use href="#rect1"></use>
|
||||
<use href="#rect2"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test09" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use href="#rect1"/>
|
||||
<use href="#rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 564 B |
15
test/plugins/inlineDefs.10.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg id="test10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</defs>
|
||||
<use href="#rect1"></use>
|
||||
<use href="#rect2"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test10" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
<rect width="200" height="200" id="rect2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 457 B |
14
test/plugins/inlineDefs.11.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg id="test11" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use href="#rect1" x="50" y="60"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test11" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<g transform="translate(50, 60)">
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 392 B |
17
test/plugins/inlineDefs.12.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<svg id="test12" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3"/>
|
||||
<mask id="m" x="0" y="0" width="301" height="31" fill="#fff">
|
||||
<use href="#a" stroke-width="4" opacity=".4"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<use href="#m"/>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test12" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="m" x="0" y="0" width="301" height="31" fill="#fff">
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3" stroke-width="4" opacity=".4"/>
|
||||
</mask>
|
||||
</svg>
|
After Width: | Height: | Size: 649 B |
22
test/plugins/inlineDefs.13.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg id="test13" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3"/>
|
||||
<g id="c">
|
||||
<use href="#b"/>
|
||||
</g>
|
||||
<g id="b">
|
||||
<use href="#a"/>
|
||||
</g>
|
||||
</defs>
|
||||
<use href="#c"/>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test13" width="315" height="92" viewBox="0 0 315 92" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="c">
|
||||
<g id="b">
|
||||
<rect id="a" x="7" y="5" width="301" height="31" rx="3"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 578 B |
18
test/plugins/inlineDefs.14.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg id="test14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<rect width="100" height="100" id="rect1"/>
|
||||
</defs>
|
||||
<use href="#rect1"></use>
|
||||
<use href="#rect1"></use>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
<svg id="test14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100"/>
|
||||
<rect width="100" height="100"/>
|
||||
</svg>
|
||||
|
||||
@@@
|
||||
|
||||
{ "onlyUnique": false }
|
After Width: | Height: | Size: 417 B |