diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2023-12-21 22:46:53 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2023-12-21 22:46:53 +0800 |
| commit | f06956efd2dc4bf77244a9fcce2b883a00b925af (patch) | |
| tree | 0123cb73ce67f1f4b17e1d6cbabff076ee44e31b /node_modules/svgo/plugins/removeXlink.js | |
| parent | 8d980c13970c15b6d2312b6fc4ac51698dd0d220 (diff) | |
updated node modules
Diffstat (limited to 'node_modules/svgo/plugins/removeXlink.js')
| -rw-r--r-- | node_modules/svgo/plugins/removeXlink.js | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/node_modules/svgo/plugins/removeXlink.js b/node_modules/svgo/plugins/removeXlink.js new file mode 100644 index 00000000..d341b673 --- /dev/null +++ b/node_modules/svgo/plugins/removeXlink.js @@ -0,0 +1,226 @@ +'use strict'; + +const { elems } = require('./_collections'); + +/** + * @typedef {import('../lib/types').XastElement} XastElement + */ + +exports.name = 'removeXlink'; +exports.description = + 'remove xlink namespace and replaces attributes with the SVG 2 equivalent where applicable'; + +/** URI indicating the Xlink namespace. */ +const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'; + +/** + * Map of `xlink:show` values to the SVG 2 `target` attribute values. + * + * @type {Record<string, string>} + * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:show#usage_notes + */ +const SHOW_TO_TARGET = { + new: '_blank', + replace: '_self', +}; + +/** + * Elements that use xlink:href, but were deprecated in SVG 2 and therefore + * don't support the SVG 2 href attribute. + * + * @type {string[]} + * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href + * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/href + */ +const LEGACY_ELEMENTS = [ + 'cursor', + 'filter', + 'font-face-uri', + 'glyphRef', + 'tref', +]; + +/** + * @param {XastElement} node + * @param {string[]} prefixes + * @param {string} attr + * @returns {string[]} + */ +const findPrefixedAttrs = (node, prefixes, attr) => { + return prefixes + .map((prefix) => `${prefix}:${attr}`) + .filter((attr) => node.attributes[attr] != null); +}; + +/** + * Removes XLink namespace prefixes and converts references to XLink attributes + * to the native SVG equivalent. + * + * The XLink namespace is deprecated in SVG 2. + * + * @type {import('./plugins-types').Plugin<'removeXlink'>} + * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href + */ +exports.fn = (_, params) => { + const { includeLegacy } = params; + + /** + * XLink namespace prefixes that are currently in the stack. + * + * @type {string[]} + */ + const xlinkPrefixes = []; + + /** + * Namespace prefixes that exist in {@link xlinkPrefixes} but were overridden + * in a child element to point to another namespace, and so is not treated as + * an XLink attribute. + * + * @type {string[]} + */ + const overriddenPrefixes = []; + + /** + * Namespace prefixes that were used in one of the {@link LEGACY_ELEMENTS}. + * + * @type {string[]} + */ + const usedInLegacyElement = []; + + return { + element: { + enter: (node) => { + for (const [key, value] of Object.entries(node.attributes)) { + if (key.startsWith('xmlns:')) { + const prefix = key.split(':', 2)[1]; + + if (value === XLINK_NAMESPACE) { + xlinkPrefixes.push(prefix); + continue; + } + + if (xlinkPrefixes.includes(prefix)) { + overriddenPrefixes.push(prefix); + } + } + } + + if ( + overriddenPrefixes.some((prefix) => xlinkPrefixes.includes(prefix)) + ) { + return; + } + + const showAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'show'); + let showHandled = node.attributes.target != null; + for (let i = showAttrs.length - 1; i >= 0; i--) { + const attr = showAttrs[i]; + const value = node.attributes[attr]; + const mapping = SHOW_TO_TARGET[value]; + + if (showHandled || mapping == null) { + delete node.attributes[attr]; + continue; + } + + if (mapping !== elems[node.name]?.defaults?.target) { + node.attributes.target = mapping; + } + + delete node.attributes[attr]; + showHandled = true; + } + + const titleAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'title'); + for (let i = titleAttrs.length - 1; i >= 0; i--) { + const attr = titleAttrs[i]; + const value = node.attributes[attr]; + const hasTitle = node.children.filter( + (child) => child.type === 'element' && child.name === 'title' + ); + + if (hasTitle.length > 0) { + delete node.attributes[attr]; + continue; + } + + /** @type {XastElement} */ + const titleTag = { + type: 'element', + name: 'title', + attributes: {}, + children: [ + { + type: 'text', + value, + }, + ], + }; + + Object.defineProperty(titleTag, 'parentNode', { + writable: true, + value: node, + }); + + node.children.unshift(titleTag); + delete node.attributes[attr]; + } + + const hrefAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'href'); + + if ( + hrefAttrs.length > 0 && + LEGACY_ELEMENTS.includes(node.name) && + !includeLegacy + ) { + hrefAttrs + .map((attr) => attr.split(':', 1)[0]) + .forEach((prefix) => usedInLegacyElement.push(prefix)); + return; + } + + for (let i = hrefAttrs.length - 1; i >= 0; i--) { + const attr = hrefAttrs[i]; + const value = node.attributes[attr]; + + if (node.attributes.href != null) { + delete node.attributes[attr]; + continue; + } + + node.attributes.href = value; + delete node.attributes[attr]; + } + }, + exit: (node) => { + for (const [key, value] of Object.entries(node.attributes)) { + const [prefix, attr] = key.split(':', 2); + + if ( + xlinkPrefixes.includes(prefix) && + !overriddenPrefixes.includes(prefix) && + !usedInLegacyElement.includes(prefix) && + !includeLegacy + ) { + delete node.attributes[key]; + continue; + } + + if (key.startsWith('xmlns:') && !usedInLegacyElement.includes(attr)) { + if (value === XLINK_NAMESPACE) { + const index = xlinkPrefixes.indexOf(attr); + xlinkPrefixes.splice(index, 1); + delete node.attributes[key]; + continue; + } + + if (overriddenPrefixes.includes(prefix)) { + const index = overriddenPrefixes.indexOf(attr); + overriddenPrefixes.splice(index, 1); + } + } + } + }, + }, + }; +}; |
