[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | /**
|
---|
| 4 | * @typedef {import('../lib/types').XastElement} XastElement
|
---|
| 5 | * @typedef {import('../lib/types').XastParent} XastParent
|
---|
| 6 | * @typedef {import('../lib/types').XastNode} XastNode
|
---|
| 7 | */
|
---|
| 8 |
|
---|
| 9 | const JSAPI = require('../lib/svgo/jsAPI.js');
|
---|
| 10 |
|
---|
| 11 | exports.type = 'visitor';
|
---|
| 12 | exports.name = 'reusePaths';
|
---|
| 13 | exports.active = false;
|
---|
| 14 | exports.description =
|
---|
| 15 | 'Finds <path> elements with the same d, fill, and ' +
|
---|
| 16 | 'stroke, and converts them to <use> elements ' +
|
---|
| 17 | 'referencing a single <path> def.';
|
---|
| 18 |
|
---|
| 19 | /**
|
---|
| 20 | * Finds <path> elements with the same d, fill, and stroke, and converts them to
|
---|
| 21 | * <use> elements referencing a single <path> def.
|
---|
| 22 | *
|
---|
| 23 | * @author Jacob Howcroft
|
---|
| 24 | *
|
---|
| 25 | * @type {import('../lib/types').Plugin<void>}
|
---|
| 26 | */
|
---|
| 27 | exports.fn = () => {
|
---|
| 28 | /**
|
---|
| 29 | * @type {Map<string, Array<XastElement>>}
|
---|
| 30 | */
|
---|
| 31 | const paths = new Map();
|
---|
| 32 |
|
---|
| 33 | return {
|
---|
| 34 | element: {
|
---|
| 35 | enter: (node) => {
|
---|
| 36 | if (node.name === 'path' && node.attributes.d != null) {
|
---|
| 37 | const d = node.attributes.d;
|
---|
| 38 | const fill = node.attributes.fill || '';
|
---|
| 39 | const stroke = node.attributes.stroke || '';
|
---|
| 40 | const key = d + ';s:' + stroke + ';f:' + fill;
|
---|
| 41 | let list = paths.get(key);
|
---|
| 42 | if (list == null) {
|
---|
| 43 | list = [];
|
---|
| 44 | paths.set(key, list);
|
---|
| 45 | }
|
---|
| 46 | list.push(node);
|
---|
| 47 | }
|
---|
| 48 | },
|
---|
| 49 |
|
---|
| 50 | exit: (node, parentNode) => {
|
---|
| 51 | if (node.name === 'svg' && parentNode.type === 'root') {
|
---|
| 52 | /**
|
---|
| 53 | * @type {XastElement}
|
---|
| 54 | */
|
---|
| 55 | const rawDefs = {
|
---|
| 56 | type: 'element',
|
---|
| 57 | name: 'defs',
|
---|
| 58 | attributes: {},
|
---|
| 59 | children: [],
|
---|
| 60 | };
|
---|
| 61 | /**
|
---|
| 62 | * @type {XastElement}
|
---|
| 63 | */
|
---|
| 64 | const defsTag = new JSAPI(rawDefs, node);
|
---|
| 65 | let index = 0;
|
---|
| 66 | for (const list of paths.values()) {
|
---|
| 67 | if (list.length > 1) {
|
---|
| 68 | // add reusable path to defs
|
---|
| 69 | /**
|
---|
| 70 | * @type {XastElement}
|
---|
| 71 | */
|
---|
| 72 | const rawPath = {
|
---|
| 73 | type: 'element',
|
---|
| 74 | name: 'path',
|
---|
| 75 | attributes: { ...list[0].attributes },
|
---|
| 76 | children: [],
|
---|
| 77 | };
|
---|
| 78 | delete rawPath.attributes.transform;
|
---|
| 79 | let id;
|
---|
| 80 | if (rawPath.attributes.id == null) {
|
---|
| 81 | id = 'reuse-' + index;
|
---|
| 82 | index += 1;
|
---|
| 83 | rawPath.attributes.id = id;
|
---|
| 84 | } else {
|
---|
| 85 | id = rawPath.attributes.id;
|
---|
| 86 | delete list[0].attributes.id;
|
---|
| 87 | }
|
---|
| 88 | /**
|
---|
| 89 | * @type {XastElement}
|
---|
| 90 | */
|
---|
| 91 | const reusablePath = new JSAPI(rawPath, defsTag);
|
---|
| 92 | defsTag.children.push(reusablePath);
|
---|
| 93 | // convert paths to <use>
|
---|
| 94 | for (const pathNode of list) {
|
---|
| 95 | pathNode.name = 'use';
|
---|
| 96 | pathNode.attributes['xlink:href'] = '#' + id;
|
---|
| 97 | delete pathNode.attributes.d;
|
---|
| 98 | delete pathNode.attributes.stroke;
|
---|
| 99 | delete pathNode.attributes.fill;
|
---|
| 100 | }
|
---|
| 101 | }
|
---|
| 102 | }
|
---|
| 103 | if (defsTag.children.length !== 0) {
|
---|
| 104 | if (node.attributes['xmlns:xlink'] == null) {
|
---|
| 105 | node.attributes['xmlns:xlink'] = 'http://www.w3.org/1999/xlink';
|
---|
| 106 | }
|
---|
| 107 | node.children.unshift(defsTag);
|
---|
| 108 | }
|
---|
| 109 | }
|
---|
| 110 | },
|
---|
| 111 | },
|
---|
| 112 | };
|
---|
| 113 | };
|
---|