[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.reusePaths = require('./reusePaths.js');
|
||||||
exports.sortAttrs = require('./sortAttrs.js');
|
exports.sortAttrs = require('./sortAttrs.js');
|
||||||
exports.sortDefsChildren = require('./sortDefsChildren.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 |