Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
459
Frontend-Learner/node_modules/svgo/plugins/removeHiddenElems.js
generated
vendored
Normal file
459
Frontend-Learner/node_modules/svgo/plugins/removeHiddenElems.js
generated
vendored
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
import { elemsGroups } from './_collections.js';
|
||||
import { detachNodeFromParent, querySelector } from '../lib/xast.js';
|
||||
import { visit, visitSkip } from '../lib/util/visit.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { parsePathData } from '../lib/path.js';
|
||||
import { findReferences, hasScripts } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveHiddenElemsParams
|
||||
* @property {boolean=} isHidden
|
||||
* @property {boolean=} displayNone
|
||||
* @property {boolean=} opacity0
|
||||
* @property {boolean=} circleR0
|
||||
* @property {boolean=} ellipseRX0
|
||||
* @property {boolean=} ellipseRY0
|
||||
* @property {boolean=} rectWidth0
|
||||
* @property {boolean=} rectHeight0
|
||||
* @property {boolean=} patternWidth0
|
||||
* @property {boolean=} patternHeight0
|
||||
* @property {boolean=} imageWidth0
|
||||
* @property {boolean=} imageHeight0
|
||||
* @property {boolean=} pathEmptyD
|
||||
* @property {boolean=} polylineEmptyPoints
|
||||
* @property {boolean=} polygonEmptyPoints
|
||||
*/
|
||||
|
||||
const nonRendering = elemsGroups.nonRendering;
|
||||
|
||||
export const name = 'removeHiddenElems';
|
||||
export const description =
|
||||
'removes hidden elements (zero sized, with absent attributes)';
|
||||
|
||||
/**
|
||||
* Remove hidden elements with disabled rendering:
|
||||
* - display="none"
|
||||
* - opacity="0"
|
||||
* - circle with zero radius
|
||||
* - ellipse with zero x-axis or y-axis radius
|
||||
* - rectangle with zero width or height
|
||||
* - pattern with zero width or height
|
||||
* - image with zero width or height
|
||||
* - path with empty data
|
||||
* - polyline with empty points
|
||||
* - polygon with empty points
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveHiddenElemsParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
isHidden = true,
|
||||
displayNone = true,
|
||||
opacity0 = true,
|
||||
circleR0 = true,
|
||||
ellipseRX0 = true,
|
||||
ellipseRY0 = true,
|
||||
rectWidth0 = true,
|
||||
rectHeight0 = true,
|
||||
patternWidth0 = true,
|
||||
patternHeight0 = true,
|
||||
imageWidth0 = true,
|
||||
imageHeight0 = true,
|
||||
pathEmptyD = true,
|
||||
polylineEmptyPoints = true,
|
||||
polygonEmptyPoints = true,
|
||||
} = params;
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
/**
|
||||
* Skip non-rendered nodes initially, and only detach if they have no ID, or
|
||||
* their ID is not referenced by another node.
|
||||
*
|
||||
* @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>}
|
||||
*/
|
||||
const nonRenderedNodes = new Map();
|
||||
|
||||
/**
|
||||
* IDs for removed hidden definitions.
|
||||
*
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const removedDefIds = new Set();
|
||||
|
||||
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>} */
|
||||
const allDefs = new Map();
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const allReferences = new Set();
|
||||
|
||||
/** @type {Map<string, Array<{ node: import('../lib/types.js').XastElement, parentNode: import('../lib/types.js').XastParent }>>} */
|
||||
const referencesById = new Map();
|
||||
|
||||
/**
|
||||
* If styles are present, we can't be sure if a definition is unused or not
|
||||
*/
|
||||
let deoptimized = false;
|
||||
|
||||
/**
|
||||
* Nodes can't be removed if they or any of their children have an id attribute that is referenced.
|
||||
* @param {import('../lib/types.js').XastElement} node
|
||||
* @returns boolean
|
||||
*/
|
||||
function canRemoveNonRenderingNode(node) {
|
||||
if (allReferences.has(node.attributes.id)) {
|
||||
return false;
|
||||
}
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element' && !canRemoveNonRenderingNode(child)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastChild} node
|
||||
* @param {import('../lib/types.js').XastParent} parentNode
|
||||
*/
|
||||
function removeElement(node, parentNode) {
|
||||
if (
|
||||
node.type === 'element' &&
|
||||
node.attributes.id != null &&
|
||||
parentNode.type === 'element' &&
|
||||
parentNode.name === 'defs'
|
||||
) {
|
||||
removedDefIds.add(node.attributes.id);
|
||||
}
|
||||
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// transparent non-rendering elements still apply where referenced
|
||||
if (nonRendering.has(node.name)) {
|
||||
nonRenderedNodes.set(node, parentNode);
|
||||
return visitSkip;
|
||||
}
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
// opacity="0"
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
|
||||
if (
|
||||
opacity0 &&
|
||||
computedStyle.opacity &&
|
||||
computedStyle.opacity.type === 'static' &&
|
||||
computedStyle.opacity.value === '0'
|
||||
) {
|
||||
if (node.name === 'path') {
|
||||
nonRenderedNodes.set(node, parentNode);
|
||||
return visitSkip;
|
||||
}
|
||||
removeElement(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (
|
||||
(node.name === 'style' && node.children.length !== 0) ||
|
||||
hasScripts(node)
|
||||
) {
|
||||
deoptimized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.name === 'defs') {
|
||||
allDefs.set(node, parentNode);
|
||||
}
|
||||
|
||||
if (node.name === 'use') {
|
||||
for (const attr of Object.keys(node.attributes)) {
|
||||
if (attr !== 'href' && !attr.endsWith(':href')) {
|
||||
continue;
|
||||
}
|
||||
const value = node.attributes[attr];
|
||||
const id = value.slice(1);
|
||||
|
||||
let refs = referencesById.get(id);
|
||||
if (!refs) {
|
||||
refs = [];
|
||||
referencesById.set(id, refs);
|
||||
}
|
||||
refs.push({ node, parentNode });
|
||||
}
|
||||
}
|
||||
|
||||
// Removes hidden elements
|
||||
// https://www.w3schools.com/cssref/pr_class_visibility.asp
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
if (
|
||||
isHidden &&
|
||||
computedStyle.visibility &&
|
||||
computedStyle.visibility.type === 'static' &&
|
||||
computedStyle.visibility.value === 'hidden' &&
|
||||
// keep if any descendant enables visibility
|
||||
querySelector(node, '[visibility=visible]') == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// display="none"
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
|
||||
// "A value of display: none indicates that the given element
|
||||
// and its children shall not be rendered directly"
|
||||
if (
|
||||
displayNone &&
|
||||
computedStyle.display &&
|
||||
computedStyle.display.type === 'static' &&
|
||||
computedStyle.display.value === 'none' &&
|
||||
// markers with display: none still rendered
|
||||
node.name !== 'marker'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Circles with zero radius
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <circle r="0">
|
||||
if (
|
||||
circleR0 &&
|
||||
node.name === 'circle' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.r === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ellipse with zero x-axis radius
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <ellipse rx="0">
|
||||
if (
|
||||
ellipseRX0 &&
|
||||
node.name === 'ellipse' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.rx === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ellipse with zero y-axis radius
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <ellipse ry="0">
|
||||
if (
|
||||
ellipseRY0 &&
|
||||
node.name === 'ellipse' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.ry === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rectangle with zero width
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <rect width="0">
|
||||
if (
|
||||
rectWidth0 &&
|
||||
node.name === 'rect' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rectangle with zero height
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <rect height="0">
|
||||
if (
|
||||
rectHeight0 &&
|
||||
rectWidth0 &&
|
||||
node.name === 'rect' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pattern with zero width
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
||||
//
|
||||
// <pattern width="0">
|
||||
if (
|
||||
patternWidth0 &&
|
||||
node.name === 'pattern' &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pattern with zero height
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
||||
//
|
||||
// <pattern height="0">
|
||||
if (
|
||||
patternHeight0 &&
|
||||
node.name === 'pattern' &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Image with zero width
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <image width="0">
|
||||
if (
|
||||
imageWidth0 &&
|
||||
node.name === 'image' &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Image with zero height
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <image height="0">
|
||||
if (
|
||||
imageHeight0 &&
|
||||
node.name === 'image' &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Path with empty data
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/paths.html#DAttribute
|
||||
//
|
||||
// <path d=""/>
|
||||
if (pathEmptyD && node.name === 'path') {
|
||||
if (node.attributes.d == null) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
const pathData = parsePathData(node.attributes.d);
|
||||
if (pathData.length === 0) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
// keep single point paths for markers
|
||||
if (
|
||||
pathData.length === 1 &&
|
||||
computedStyle['marker-start'] == null &&
|
||||
computedStyle['marker-end'] == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Polyline with empty points
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute
|
||||
//
|
||||
// <polyline points="">
|
||||
if (
|
||||
polylineEmptyPoints &&
|
||||
node.name === 'polyline' &&
|
||||
node.attributes.points == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Polygon with empty points
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
|
||||
//
|
||||
// <polygon points="">
|
||||
if (
|
||||
polygonEmptyPoints &&
|
||||
node.name === 'polygon' &&
|
||||
node.attributes.points == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
const ids = findReferences(name, value);
|
||||
|
||||
for (const id of ids) {
|
||||
allReferences.add(id);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
root: {
|
||||
exit: () => {
|
||||
for (const id of removedDefIds) {
|
||||
const refs = referencesById.get(id);
|
||||
if (refs) {
|
||||
for (const { node, parentNode } of refs) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!deoptimized) {
|
||||
for (const [
|
||||
nonRenderedNode,
|
||||
nonRenderedParent,
|
||||
] of nonRenderedNodes.entries()) {
|
||||
if (canRemoveNonRenderingNode(nonRenderedNode)) {
|
||||
detachNodeFromParent(nonRenderedNode, nonRenderedParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [node, parentNode] of allDefs.entries()) {
|
||||
if (node.children.length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue