[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
---|
| 4 | const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
---|
| 5 | const { elemsGroups } = require('./_collections.js');
|
---|
| 6 |
|
---|
| 7 | exports.type = 'visitor';
|
---|
| 8 | exports.name = 'removeUselessStrokeAndFill';
|
---|
| 9 | exports.active = true;
|
---|
| 10 | exports.description = 'removes useless stroke and fill attributes';
|
---|
| 11 |
|
---|
| 12 | /**
|
---|
| 13 | * Remove useless stroke and fill attrs.
|
---|
| 14 | *
|
---|
| 15 | * @author Kir Belevich
|
---|
| 16 | *
|
---|
| 17 | * @type {import('../lib/types').Plugin<{
|
---|
| 18 | * stroke?: boolean,
|
---|
| 19 | * fill?: boolean,
|
---|
| 20 | * removeNone?: boolean
|
---|
| 21 | * }>}
|
---|
| 22 | */
|
---|
| 23 | exports.fn = (root, params) => {
|
---|
| 24 | const {
|
---|
| 25 | stroke: removeStroke = true,
|
---|
| 26 | fill: removeFill = true,
|
---|
| 27 | removeNone = false,
|
---|
| 28 | } = params;
|
---|
| 29 |
|
---|
| 30 | // style and script elements deoptimise this plugin
|
---|
| 31 | let hasStyleOrScript = false;
|
---|
| 32 | visit(root, {
|
---|
| 33 | element: {
|
---|
| 34 | enter: (node) => {
|
---|
| 35 | if (node.name === 'style' || node.name === 'script') {
|
---|
| 36 | hasStyleOrScript = true;
|
---|
| 37 | }
|
---|
| 38 | },
|
---|
| 39 | },
|
---|
| 40 | });
|
---|
| 41 | if (hasStyleOrScript) {
|
---|
| 42 | return null;
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | const stylesheet = collectStylesheet(root);
|
---|
| 46 |
|
---|
| 47 | return {
|
---|
| 48 | element: {
|
---|
| 49 | enter: (node, parentNode) => {
|
---|
| 50 | // id attribute deoptimise the whole subtree
|
---|
| 51 | if (node.attributes.id != null) {
|
---|
| 52 | return visitSkip;
|
---|
| 53 | }
|
---|
| 54 | if (elemsGroups.shape.includes(node.name) == false) {
|
---|
| 55 | return;
|
---|
| 56 | }
|
---|
| 57 | const computedStyle = computeStyle(stylesheet, node);
|
---|
| 58 | const stroke = computedStyle.stroke;
|
---|
| 59 | const strokeOpacity = computedStyle['stroke-opacity'];
|
---|
| 60 | const strokeWidth = computedStyle['stroke-width'];
|
---|
| 61 | const markerEnd = computedStyle['marker-end'];
|
---|
| 62 | const fill = computedStyle.fill;
|
---|
| 63 | const fillOpacity = computedStyle['fill-opacity'];
|
---|
| 64 | const computedParentStyle =
|
---|
| 65 | parentNode.type === 'element'
|
---|
| 66 | ? computeStyle(stylesheet, parentNode)
|
---|
| 67 | : null;
|
---|
| 68 | const parentStroke =
|
---|
| 69 | computedParentStyle == null ? null : computedParentStyle.stroke;
|
---|
| 70 |
|
---|
| 71 | // remove stroke*
|
---|
| 72 | if (removeStroke) {
|
---|
| 73 | if (
|
---|
| 74 | stroke == null ||
|
---|
| 75 | (stroke.type === 'static' && stroke.value == 'none') ||
|
---|
| 76 | (strokeOpacity != null &&
|
---|
| 77 | strokeOpacity.type === 'static' &&
|
---|
| 78 | strokeOpacity.value === '0') ||
|
---|
| 79 | (strokeWidth != null &&
|
---|
| 80 | strokeWidth.type === 'static' &&
|
---|
| 81 | strokeWidth.value === '0')
|
---|
| 82 | ) {
|
---|
| 83 | // stroke-width may affect the size of marker-end
|
---|
| 84 | // marker is not visible when stroke-width is 0
|
---|
| 85 | if (
|
---|
| 86 | (strokeWidth != null &&
|
---|
| 87 | strokeWidth.type === 'static' &&
|
---|
| 88 | strokeWidth.value === '0') ||
|
---|
| 89 | markerEnd == null
|
---|
| 90 | ) {
|
---|
| 91 | for (const name of Object.keys(node.attributes)) {
|
---|
| 92 | if (name.startsWith('stroke')) {
|
---|
| 93 | delete node.attributes[name];
|
---|
| 94 | }
|
---|
| 95 | }
|
---|
| 96 | // set explicit none to not inherit from parent
|
---|
| 97 | if (
|
---|
| 98 | parentStroke != null &&
|
---|
| 99 | parentStroke.type === 'static' &&
|
---|
| 100 | parentStroke.value !== 'none'
|
---|
| 101 | ) {
|
---|
| 102 | node.attributes.stroke = 'none';
|
---|
| 103 | }
|
---|
| 104 | }
|
---|
| 105 | }
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | // remove fill*
|
---|
| 109 | if (removeFill) {
|
---|
| 110 | if (
|
---|
| 111 | (fill != null && fill.type === 'static' && fill.value === 'none') ||
|
---|
| 112 | (fillOpacity != null &&
|
---|
| 113 | fillOpacity.type === 'static' &&
|
---|
| 114 | fillOpacity.value === '0')
|
---|
| 115 | ) {
|
---|
| 116 | for (const name of Object.keys(node.attributes)) {
|
---|
| 117 | if (name.startsWith('fill-')) {
|
---|
| 118 | delete node.attributes[name];
|
---|
| 119 | }
|
---|
| 120 | }
|
---|
| 121 | if (
|
---|
| 122 | fill == null ||
|
---|
| 123 | (fill.type === 'static' && fill.value !== 'none')
|
---|
| 124 | ) {
|
---|
| 125 | node.attributes.fill = 'none';
|
---|
| 126 | }
|
---|
| 127 | }
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | if (removeNone) {
|
---|
| 131 | if (
|
---|
| 132 | (stroke == null || node.attributes.stroke === 'none') &&
|
---|
| 133 | ((fill != null &&
|
---|
| 134 | fill.type === 'static' &&
|
---|
| 135 | fill.value === 'none') ||
|
---|
| 136 | node.attributes.fill === 'none')
|
---|
| 137 | ) {
|
---|
| 138 | detachNodeFromParent(node, parentNode);
|
---|
| 139 | }
|
---|
| 140 | }
|
---|
| 141 | },
|
---|
| 142 | },
|
---|
| 143 | };
|
---|
| 144 | };
|
---|