source: trip-planner-front/node_modules/svgo/plugins/removeUnknownsAndDefaults.js@ 571e0df

Last change on this file since 571e0df was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 6.2 KB
RevLine 
[6a3a178]1'use strict';
2
3const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
4const { collectStylesheet, computeStyle } = require('../lib/style.js');
5const {
6 elems,
7 attrsGroups,
8 elemsGroups,
9 attrsGroupsDefaults,
10 presentationNonInheritableGroupAttrs,
11} = require('./_collections');
12
13exports.type = 'visitor';
14exports.name = 'removeUnknownsAndDefaults';
15exports.active = true;
16exports.description =
17 'removes unknown elements content and attributes, removes attrs with default values';
18
19// resolve all groups references
20
21/**
22 * @type {Map<string, Set<string>>}
23 */
24const allowedChildrenPerElement = new Map();
25/**
26 * @type {Map<string, Set<string>>}
27 */
28const allowedAttributesPerElement = new Map();
29/**
30 * @type {Map<string, Map<string, string>>}
31 */
32const attributesDefaultsPerElement = new Map();
33
34for (const [name, config] of Object.entries(elems)) {
35 /**
36 * @type {Set<string>}
37 */
38 const allowedChildren = new Set();
39 if (config.content) {
40 for (const elementName of config.content) {
41 allowedChildren.add(elementName);
42 }
43 }
44 if (config.contentGroups) {
45 for (const contentGroupName of config.contentGroups) {
46 const elemsGroup = elemsGroups[contentGroupName];
47 if (elemsGroup) {
48 for (const elementName of elemsGroup) {
49 allowedChildren.add(elementName);
50 }
51 }
52 }
53 }
54 /**
55 * @type {Set<string>}
56 */
57 const allowedAttributes = new Set();
58 if (config.attrs) {
59 for (const attrName of config.attrs) {
60 allowedAttributes.add(attrName);
61 }
62 }
63 /**
64 * @type {Map<string, string>}
65 */
66 const attributesDefaults = new Map();
67 if (config.defaults) {
68 for (const [attrName, defaultValue] of Object.entries(config.defaults)) {
69 attributesDefaults.set(attrName, defaultValue);
70 }
71 }
72 for (const attrsGroupName of config.attrsGroups) {
73 const attrsGroup = attrsGroups[attrsGroupName];
74 if (attrsGroup) {
75 for (const attrName of attrsGroup) {
76 allowedAttributes.add(attrName);
77 }
78 }
79 const groupDefaults = attrsGroupsDefaults[attrsGroupName];
80 if (groupDefaults) {
81 for (const [attrName, defaultValue] of Object.entries(groupDefaults)) {
82 attributesDefaults.set(attrName, defaultValue);
83 }
84 }
85 }
86 allowedChildrenPerElement.set(name, allowedChildren);
87 allowedAttributesPerElement.set(name, allowedAttributes);
88 attributesDefaultsPerElement.set(name, attributesDefaults);
89}
90
91/**
92 * Remove unknown elements content and attributes,
93 * remove attributes with default values.
94 *
95 * @author Kir Belevich
96 *
97 * @type {import('../lib/types').Plugin<{
98 * unknownContent?: boolean,
99 * unknownAttrs?: boolean,
100 * defaultAttrs?: boolean,
101 * uselessOverrides?: boolean,
102 * keepDataAttrs?: boolean,
103 * keepAriaAttrs?: boolean,
104 * keepRoleAttr?: boolean,
105 * }>}
106 */
107exports.fn = (root, params) => {
108 const {
109 unknownContent = true,
110 unknownAttrs = true,
111 defaultAttrs = true,
112 uselessOverrides = true,
113 keepDataAttrs = true,
114 keepAriaAttrs = true,
115 keepRoleAttr = false,
116 } = params;
117 const stylesheet = collectStylesheet(root);
118
119 return {
120 element: {
121 enter: (node, parentNode) => {
122 // skip namespaced elements
123 if (node.name.includes(':')) {
124 return;
125 }
126 // skip visiting foreignObject subtree
127 if (node.name === 'foreignObject') {
128 return visitSkip;
129 }
130
131 // remove unknown element's content
132 if (unknownContent && parentNode.type === 'element') {
133 const allowedChildren = allowedChildrenPerElement.get(
134 parentNode.name
135 );
136 if (allowedChildren == null || allowedChildren.size === 0) {
137 // remove unknown elements
138 if (allowedChildrenPerElement.get(node.name) == null) {
139 detachNodeFromParent(node, parentNode);
140 return;
141 }
142 } else {
143 // remove not allowed children
144 if (allowedChildren.has(node.name) === false) {
145 detachNodeFromParent(node, parentNode);
146 return;
147 }
148 }
149 }
150
151 const allowedAttributes = allowedAttributesPerElement.get(node.name);
152 const attributesDefaults = attributesDefaultsPerElement.get(node.name);
153 const computedParentStyle =
154 parentNode.type === 'element'
155 ? computeStyle(stylesheet, parentNode)
156 : null;
157
158 // remove element's unknown attrs and attrs with default values
159 for (const [name, value] of Object.entries(node.attributes)) {
160 if (keepDataAttrs && name.startsWith('data-')) {
161 continue;
162 }
163 if (keepAriaAttrs && name.startsWith('aria-')) {
164 continue;
165 }
166 if (keepRoleAttr && name === 'role') {
167 continue;
168 }
169 // skip xmlns attribute
170 if (name === 'xmlns') {
171 continue;
172 }
173 // skip namespaced attributes except xml:* and xlink:*
174 if (name.includes(':')) {
175 const [prefix] = name.split(':');
176 if (prefix !== 'xml' && prefix !== 'xlink') {
177 continue;
178 }
179 }
180
181 if (
182 unknownAttrs &&
183 allowedAttributes &&
184 allowedAttributes.has(name) === false
185 ) {
186 delete node.attributes[name];
187 }
188 if (
189 defaultAttrs &&
190 node.attributes.id == null &&
191 attributesDefaults &&
192 attributesDefaults.get(name) === value
193 ) {
194 // keep defaults if parent has own or inherited style
195 if (
196 computedParentStyle == null ||
197 computedParentStyle[name] == null
198 ) {
199 delete node.attributes[name];
200 }
201 }
202 if (uselessOverrides && node.attributes.id == null) {
203 const style =
204 computedParentStyle == null ? null : computedParentStyle[name];
205 if (
206 presentationNonInheritableGroupAttrs.includes(name) === false &&
207 style != null &&
208 style.type === 'static' &&
209 style.value === value
210 ) {
211 delete node.attributes[name];
212 }
213 }
214 }
215 },
216 },
217 };
218};
Note: See TracBrowser for help on using the repository browser.