-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(removeDeprecatedAttrs): new removeDeprecatedAttrs plugin #1869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| --- | ||
| title: Remove Deprecated Attributes | ||
| svgo: | ||
| pluginId: removeDeprecatedAttrs | ||
| defaultPlugin: true | ||
| parameters: | ||
| removeAny: | ||
| description: By default, this plugin only removes safe deprecated attributes that do not change the rendered image. Enabling this will remove all deprecated attributes which may impact rendering. | ||
| type: boolean | ||
| default: false | ||
| --- | ||
|
|
||
| Removes deprecated attributes from elements in the document. | ||
|
|
||
| This plugin does not remove attributes from the deprecated XLink namespace. To remove them, use the [Remove XLink](/docs/plugins/remove-xlink/) plugin. | ||
|
|
||
| ## Usage | ||
|
|
||
| <PluginUsage /> | ||
|
|
||
| ## Demo | ||
|
|
||
| <PluginDemo /> | ||
|
|
||
| ## Implementation | ||
|
|
||
| - https://github.com/svg/svgo/blob/main/plugins/removeDeprecatedAttrs.js | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -375,11 +375,35 @@ export const attrsGroupsDefaults = { | |
| }, | ||
| }; | ||
|
|
||
| /** | ||
| * @type {Record<string, { safe?: Set<string>, unsafe?: Set<string> }>} | ||
| * @see https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
| */ | ||
| export const attrsGroupsDeprecated = { | ||
| animationAttributeTarget: { unsafe: new Set(['attributeType']) }, | ||
| conditionalProcessing: { unsafe: new Set(['requiredFeatures']) }, | ||
| core: { unsafe: new Set(['xml:base', 'xml:lang', 'xml:space']) }, | ||
| presentation: { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Presentation attributes can be used in styles as well. We should probably be removing them too. Do you have time to add a check, where if the attribute being removed is a presentation attribute, also parse the I worked on something like this recently in the Basically, if the node defines the Instead of collecting the declarations like I did, you can just remove them immediately from the list from inside Important to note that the same attribute can be redundantly defined more than once in CSS, so it's worth looping through all styles and not exit early if the attribute is found. It could be worth updating the stylesheet ( |
||
| unsafe: new Set([ | ||
| 'clip', | ||
| 'color-profile', | ||
| 'enable-background', | ||
| 'glyph-orientation-horizontal', | ||
| 'glyph-orientation-vertical', | ||
| 'kerning', | ||
| ]), | ||
| }, | ||
| }; | ||
|
|
||
| /** | ||
| * @type {Record<string, { | ||
| * attrsGroups: Set<string>, | ||
| * attrs?: Set<string>, | ||
| * defaults?: Record<string, string>, | ||
| * deprecated?: { | ||
| * safe?: Set<string>, | ||
| * unsafe?: Set<string>, | ||
| * }, | ||
| * contentGroups?: Set<string>, | ||
| * content?: Set<string>, | ||
| * }>} | ||
|
|
@@ -574,6 +598,7 @@ export const elems = { | |
| name: 'sRGB', | ||
| 'rendering-intent': 'auto', | ||
| }, | ||
| deprecated: { unsafe: new Set(['name']) }, | ||
| contentGroups: new Set(['descriptive']), | ||
| }, | ||
| cursor: { | ||
|
|
@@ -958,6 +983,7 @@ export const elems = { | |
| width: '120%', | ||
| height: '120%', | ||
| }, | ||
| deprecated: { unsafe: new Set(['filterRes']) }, | ||
| contentGroups: new Set(['descriptive', 'filterPrimitive']), | ||
| content: new Set(['animate', 'set']), | ||
| }, | ||
|
|
@@ -978,6 +1004,15 @@ export const elems = { | |
| 'horiz-origin-x': '0', | ||
| 'horiz-origin-y': '0', | ||
| }, | ||
| deprecated: { | ||
| unsafe: new Set([ | ||
| 'horiz-origin-x', | ||
| 'horiz-origin-y', | ||
| 'vert-adv-y', | ||
| 'vert-origin-x', | ||
| 'vert-origin-y', | ||
| ]), | ||
| }, | ||
| contentGroups: new Set(['descriptive']), | ||
| content: new Set(['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern']), | ||
| }, | ||
|
|
@@ -1028,6 +1063,31 @@ export const elems = { | |
| 'panose-1': '0 0 0 0 0 0 0 0 0 0', | ||
| slope: '0', | ||
| }, | ||
| deprecated: { | ||
| unsafe: new Set([ | ||
| 'accent-height', | ||
| 'alphabetic', | ||
| 'ascent', | ||
| 'bbox', | ||
| 'cap-height', | ||
| 'descent', | ||
| 'hanging', | ||
| 'ideographic', | ||
| 'mathematical', | ||
| 'panose-1', | ||
| 'slope', | ||
| 'stemh', | ||
| 'stemv', | ||
| 'unicode-range', | ||
| 'units-per-em', | ||
| 'v-alphabetic', | ||
| 'v-hanging', | ||
| 'v-ideographic', | ||
| 'v-mathematical', | ||
| 'widths', | ||
| 'x-height', | ||
| ]), | ||
| }, | ||
| contentGroups: new Set(['descriptive']), | ||
| content: new Set([ | ||
| // TODO: "at most one 'font-face-src' element" | ||
|
|
@@ -1038,10 +1098,12 @@ export const elems = { | |
| 'font-face-format': { | ||
| attrsGroups: new Set(['core']), | ||
| attrs: new Set(['string']), | ||
| deprecated: { unsafe: new Set(['string']) }, | ||
| }, | ||
| 'font-face-name': { | ||
| attrsGroups: new Set(['core']), | ||
| attrs: new Set(['name']), | ||
| deprecated: { unsafe: new Set(['name']) }, | ||
| }, | ||
| 'font-face-src': { | ||
| attrsGroups: new Set(['core']), | ||
|
|
@@ -1134,6 +1196,18 @@ export const elems = { | |
| defaults: { | ||
| 'arabic-form': 'initial', | ||
| }, | ||
| deprecated: { | ||
| unsafe: new Set([ | ||
| 'arabic-form', | ||
| 'glyph-name', | ||
| 'horiz-adv-x', | ||
| 'orientation', | ||
| 'unicode', | ||
| 'vert-adv-y', | ||
| 'vert-origin-x', | ||
| 'vert-origin-y', | ||
| ]), | ||
| }, | ||
| contentGroups: new Set([ | ||
| 'animation', | ||
| 'descriptive', | ||
|
|
@@ -1173,6 +1247,14 @@ export const elems = { | |
| 'vert-origin-x', | ||
| 'vert-origin-y', | ||
| ]), | ||
| deprecated: { | ||
| unsafe: new Set([ | ||
| 'horiz-adv-x', | ||
| 'vert-adv-y', | ||
| 'vert-origin-x', | ||
| 'vert-origin-y', | ||
| ]), | ||
| }, | ||
| contentGroups: new Set([ | ||
| 'animation', | ||
| 'descriptive', | ||
|
|
@@ -1236,6 +1318,7 @@ export const elems = { | |
| hkern: { | ||
| attrsGroups: new Set(['core']), | ||
| attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']), | ||
| deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) }, | ||
| }, | ||
| image: { | ||
| attrsGroups: new Set([ | ||
|
|
@@ -1430,6 +1513,14 @@ export const elems = { | |
| 'vert-origin-x', | ||
| 'vert-origin-y', | ||
| ]), | ||
| deprecated: { | ||
| unsafe: new Set([ | ||
| 'horiz-adv-x', | ||
| 'vert-adv-y', | ||
| 'vert-origin-x', | ||
| 'vert-origin-y', | ||
| ]), | ||
| }, | ||
| contentGroups: new Set([ | ||
| 'animation', | ||
| 'descriptive', | ||
|
|
@@ -1710,6 +1801,15 @@ export const elems = { | |
| contentScriptType: 'application/ecmascript', | ||
| contentStyleType: 'text/css', | ||
| }, | ||
| deprecated: { | ||
| safe: new Set(['version']), | ||
| unsafe: new Set([ | ||
| 'baseProfile', | ||
| 'contentScriptType', | ||
| 'contentStyleType', | ||
| 'zoomAndPan', | ||
| ]), | ||
| }, | ||
| contentGroups: new Set([ | ||
| 'animation', | ||
| 'descriptive', | ||
|
|
@@ -1956,11 +2056,13 @@ export const elems = { | |
| 'viewTarget', | ||
| 'zoomAndPan', | ||
| ]), | ||
| deprecated: { unsafe: new Set(['viewTarget', 'zoomAndPan']) }, | ||
| contentGroups: new Set(['descriptive']), | ||
| }, | ||
| vkern: { | ||
| attrsGroups: new Set(['core']), | ||
| attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']), | ||
| deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) }, | ||
| }, | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| import * as csswhat from 'css-what'; | ||
| import { attrsGroupsDeprecated, elems } from './_collections.js'; | ||
| import { collectStylesheet } from '../lib/style.js'; | ||
|
|
||
| export const name = 'removeDeprecatedAttrs'; | ||
| export const description = 'removes deprecated attributes'; | ||
|
|
||
| /** | ||
| * @typedef {{ safe?: Set<string>; unsafe?: Set<string> }} DeprecatedAttrs | ||
| * @typedef {import('../lib/types.js').XastElement} XastElement | ||
| */ | ||
|
|
||
| /** | ||
| * @param {import('../lib/types.js').Stylesheet} stylesheet | ||
| * @returns {Set<string>} | ||
| */ | ||
| function extractAttributesInStylesheet(stylesheet) { | ||
| const attributesInStylesheet = new Set(); | ||
|
|
||
| stylesheet.rules.forEach((rule) => { | ||
| const selectors = csswhat.parse(rule.selector); | ||
| selectors.forEach((subselector) => { | ||
| subselector.forEach((segment) => { | ||
| if (segment.type !== 'attribute') { | ||
| return; | ||
| } | ||
|
|
||
| attributesInStylesheet.add(segment.name); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| return attributesInStylesheet; | ||
| } | ||
|
|
||
| /** | ||
| * @param {XastElement} node | ||
| * @param {DeprecatedAttrs | undefined} deprecatedAttrs | ||
| * @param {import('./plugins-types.js').DefaultPlugins['removeDeprecatedAttrs']} params | ||
| * @param {Set<string>} attributesInStylesheet | ||
| */ | ||
| function processAttributes( | ||
| node, | ||
| deprecatedAttrs, | ||
| params, | ||
| attributesInStylesheet, | ||
| ) { | ||
| if (!deprecatedAttrs) { | ||
| return; | ||
| } | ||
|
|
||
| if (deprecatedAttrs.safe) { | ||
| deprecatedAttrs.safe.forEach((name) => { | ||
| if (attributesInStylesheet.has(name)) { | ||
| return; | ||
| } | ||
| delete node.attributes[name]; | ||
| }); | ||
| } | ||
|
|
||
| if (params.removeUnsafe && deprecatedAttrs.unsafe) { | ||
| deprecatedAttrs.unsafe.forEach((name) => { | ||
| if (attributesInStylesheet.has(name)) { | ||
| return; | ||
| } | ||
| delete node.attributes[name]; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Remove deprecated attributes. | ||
| * | ||
| * @type {import('./plugins-types.js').Plugin<'removeDeprecatedAttrs'>} | ||
| */ | ||
| export function fn(root, params) { | ||
| const stylesheet = collectStylesheet(root); | ||
| const attributesInStylesheet = extractAttributesInStylesheet(stylesheet); | ||
|
|
||
| return { | ||
| element: { | ||
| enter: (node) => { | ||
| const elemConfig = elems[node.name]; | ||
| if (!elemConfig) { | ||
| return; | ||
| } | ||
|
|
||
| // Special cases | ||
|
|
||
| // Removing deprecated xml:lang is safe when the lang attribute exists. | ||
| if ( | ||
| elemConfig.attrsGroups.has('core') && | ||
| node.attributes['xml:lang'] && | ||
| !attributesInStylesheet.has('xml:lang') && | ||
| node.attributes['lang'] | ||
| ) { | ||
| delete node.attributes['xml:lang']; | ||
| } | ||
|
|
||
| // General cases | ||
|
|
||
| elemConfig.attrsGroups.forEach((attrsGroup) => { | ||
| processAttributes( | ||
| node, | ||
| attrsGroupsDeprecated[attrsGroup], | ||
| params, | ||
| attributesInStylesheet, | ||
| ); | ||
| }); | ||
|
|
||
| processAttributes( | ||
| node, | ||
| elemConfig.deprecated, | ||
| params, | ||
| attributesInStylesheet, | ||
| ); | ||
| }, | ||
| }, | ||
| }; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.