summaryrefslogtreecommitdiff
path: root/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/svgo/plugins/removeUnknownsAndDefaults.js')
-rw-r--r--node_modules/svgo/plugins/removeUnknownsAndDefaults.js208
1 files changed, 208 insertions, 0 deletions
diff --git a/node_modules/svgo/plugins/removeUnknownsAndDefaults.js b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
new file mode 100644
index 00000000..c67845cd
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
@@ -0,0 +1,208 @@
+'use strict';
+
+const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
+const { collectStylesheet, computeStyle } = require('../lib/style.js');
+const {
+ elems,
+ attrsGroups,
+ elemsGroups,
+ attrsGroupsDefaults,
+ presentationNonInheritableGroupAttrs,
+} = require('./_collections');
+
+exports.name = 'removeUnknownsAndDefaults';
+exports.description =
+ 'removes unknown elements content and attributes, removes attrs with default values';
+
+// resolve all groups references
+
+/**
+ * @type {Map<string, Set<string>>}
+ */
+const allowedChildrenPerElement = new Map();
+/**
+ * @type {Map<string, Set<string>>}
+ */
+const allowedAttributesPerElement = new Map();
+/**
+ * @type {Map<string, Map<string, string>>}
+ */
+const attributesDefaultsPerElement = new Map();
+
+for (const [name, config] of Object.entries(elems)) {
+ /**
+ * @type {Set<string>}
+ */
+ const allowedChildren = new Set();
+ if (config.content) {
+ for (const elementName of config.content) {
+ allowedChildren.add(elementName);
+ }
+ }
+ if (config.contentGroups) {
+ for (const contentGroupName of config.contentGroups) {
+ const elemsGroup = elemsGroups[contentGroupName];
+ if (elemsGroup) {
+ for (const elementName of elemsGroup) {
+ allowedChildren.add(elementName);
+ }
+ }
+ }
+ }
+ /**
+ * @type {Set<string>}
+ */
+ const allowedAttributes = new Set();
+ if (config.attrs) {
+ for (const attrName of config.attrs) {
+ allowedAttributes.add(attrName);
+ }
+ }
+ /**
+ * @type {Map<string, string>}
+ */
+ const attributesDefaults = new Map();
+ if (config.defaults) {
+ for (const [attrName, defaultValue] of Object.entries(config.defaults)) {
+ attributesDefaults.set(attrName, defaultValue);
+ }
+ }
+ for (const attrsGroupName of config.attrsGroups) {
+ const attrsGroup = attrsGroups[attrsGroupName];
+ if (attrsGroup) {
+ for (const attrName of attrsGroup) {
+ allowedAttributes.add(attrName);
+ }
+ }
+ const groupDefaults = attrsGroupsDefaults[attrsGroupName];
+ if (groupDefaults) {
+ for (const [attrName, defaultValue] of Object.entries(groupDefaults)) {
+ attributesDefaults.set(attrName, defaultValue);
+ }
+ }
+ }
+ allowedChildrenPerElement.set(name, allowedChildren);
+ allowedAttributesPerElement.set(name, allowedAttributes);
+ attributesDefaultsPerElement.set(name, attributesDefaults);
+}
+
+/**
+ * Remove unknown elements content and attributes,
+ * remove attributes with default values.
+ *
+ * @author Kir Belevich
+ *
+ * @type {import('./plugins-types').Plugin<'removeUnknownsAndDefaults'>}
+ */
+exports.fn = (root, params) => {
+ const {
+ unknownContent = true,
+ unknownAttrs = true,
+ defaultAttrs = true,
+ uselessOverrides = true,
+ keepDataAttrs = true,
+ keepAriaAttrs = true,
+ keepRoleAttr = false,
+ } = params;
+ const stylesheet = collectStylesheet(root);
+
+ return {
+ element: {
+ enter: (node, parentNode) => {
+ // skip namespaced elements
+ if (node.name.includes(':')) {
+ return;
+ }
+ // skip visiting foreignObject subtree
+ if (node.name === 'foreignObject') {
+ return visitSkip;
+ }
+
+ // remove unknown element's content
+ if (unknownContent && parentNode.type === 'element') {
+ const allowedChildren = allowedChildrenPerElement.get(
+ parentNode.name
+ );
+ if (allowedChildren == null || allowedChildren.size === 0) {
+ // remove unknown elements
+ if (allowedChildrenPerElement.get(node.name) == null) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ } else {
+ // remove not allowed children
+ if (allowedChildren.has(node.name) === false) {
+ detachNodeFromParent(node, parentNode);
+ return;
+ }
+ }
+ }
+
+ const allowedAttributes = allowedAttributesPerElement.get(node.name);
+ const attributesDefaults = attributesDefaultsPerElement.get(node.name);
+ const computedParentStyle =
+ parentNode.type === 'element'
+ ? computeStyle(stylesheet, parentNode)
+ : null;
+
+ // remove element's unknown attrs and attrs with default values
+ for (const [name, value] of Object.entries(node.attributes)) {
+ if (keepDataAttrs && name.startsWith('data-')) {
+ continue;
+ }
+ if (keepAriaAttrs && name.startsWith('aria-')) {
+ continue;
+ }
+ if (keepRoleAttr && name === 'role') {
+ continue;
+ }
+ // skip xmlns attribute
+ if (name === 'xmlns') {
+ continue;
+ }
+ // skip namespaced attributes except xml:* and xlink:*
+ if (name.includes(':')) {
+ const [prefix] = name.split(':');
+ if (prefix !== 'xml' && prefix !== 'xlink') {
+ continue;
+ }
+ }
+
+ if (
+ unknownAttrs &&
+ allowedAttributes &&
+ allowedAttributes.has(name) === false
+ ) {
+ delete node.attributes[name];
+ }
+ if (
+ defaultAttrs &&
+ node.attributes.id == null &&
+ attributesDefaults &&
+ attributesDefaults.get(name) === value
+ ) {
+ // keep defaults if parent has own or inherited style
+ if (
+ computedParentStyle == null ||
+ computedParentStyle[name] == null
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ if (uselessOverrides && node.attributes.id == null) {
+ const style =
+ computedParentStyle == null ? null : computedParentStyle[name];
+ if (
+ presentationNonInheritableGroupAttrs.includes(name) === false &&
+ style != null &&
+ style.type === 'static' &&
+ style.value === value
+ ) {
+ delete node.attributes[name];
+ }
+ }
+ }
+ },
+ },
+ };
+};