Skip to content
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

Add dereferenceUses plugin #1279

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
782a87b
Add plugin file and tests.
strarsis Sep 2, 2020
8594b3a
Add code to replace the `<symbol>` element with configurable wrapper …
strarsis Sep 4, 2020
3bd002b
Fix test.
strarsis Sep 4, 2020
cb915dc
Add to plugins.js.
strarsis Mar 19, 2021
48d7c21
Apply linter fix to dereferenceUses plugin.
strarsis Mar 19, 2021
2de6eaa
Resolve package lock conflict with upstream.
strarsis Mar 22, 2021
c4397c7
Add new test for plugin (1279#issuecomment-803706532).
strarsis Mar 22, 2021
fa93ae0
Apply styles of <use/> element on top of styles of referenced element.
strarsis Mar 22, 2021
137341e
Fix expected result in test.
strarsis Mar 22, 2021
4d658eb
Merge branch 'master' of https://github.com/svg/svgo into dereference…
strarsis Mar 27, 2021
8fd5222
Apply recommended code style.
strarsis Mar 27, 2021
7d9135a
Improve constant and variable names.
strarsis Mar 27, 2021
0e426f6
Improve comment.
strarsis Mar 27, 2021
a37eb7b
Fix attribute override logic.
strarsis Mar 27, 2021
45244fc
Merge branch 'master' into dereferenceUses-plugin
strarsis Mar 29, 2021
60088b0
Simplify plugin test description.
strarsis Mar 30, 2021
8a497ef
Simplify plugin tests descriptions.
strarsis Mar 30, 2021
5dc8c65
Use href attribute names array for finding href attribute.
strarsis Mar 30, 2021
246d861
Remove large, rather redundant plugin test.
strarsis Mar 30, 2021
454e338
Rename options argument/variable to params.
strarsis Mar 30, 2021
96d0f7e
Use destructuring for plugin params.
strarsis Mar 30, 2021
470ab35
Merge branch 'main' into dereferenceUses-plugin
strarsis Mar 23, 2023
bb15cbe
Add param typings for dereferenceUses plugin.
strarsis Mar 23, 2023
e7226cb
Rewrite dereferenceUses plugin for new `svgo` architecture.
strarsis Mar 24, 2023
8db961d
Improve test comments.
strarsis Mar 24, 2023
cd30067
Increase test coverage.
strarsis Mar 24, 2023
92dcf8e
Fix `yarn` lockfile.
strarsis Mar 24, 2023
5712fee
Appy prettier.
strarsis Mar 24, 2023
314dd8e
Add `structuredClone` polyfill for older `node`s.
strarsis Mar 24, 2023
869c113
Add TS definitions for `@ungap/structured-clone`.
strarsis Mar 24, 2023
e797bb0
Merge remote-tracking branch 'origin' into dereferenceUses-plugin
strarsis Aug 29, 2024
99a01d4
Update imports and plugin declaration for current `svgo`.
strarsis Aug 29, 2024
886ade6
Add plugin documentation in new `svgo` documentation format.
strarsis Aug 29, 2024
bdde2ef
Update test fixture files to new `svgo` conventions.
strarsis Aug 29, 2024
bb4adde
Add `dereferenceUses` plugin as built-in `svgo` plugin.
strarsis Aug 29, 2024
58697fd
Remove npm lockfile.
strarsis Aug 29, 2024
eed23f1
Fix code lint errors in plugin code.
strarsis Aug 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,174 changes: 3,163 additions & 11 deletions package-lock.json

Large diffs are not rendered by default.

116 changes: 116 additions & 0 deletions plugins/dereferenceUses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use strict';

const { querySelectorAll, querySelector } = require('../lib/xast.js');

exports.type = 'full';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor with visitor please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I am eager to try it with a plugin.


exports.active = true;

exports.description = 'dereferences <use/> elements';

exports.params = {
keepHref: false, // keep (xlink:)href attributes

symbolContainer: 'svg', // browsers use <svg/> as container of <symbol/> content (`g` could also be used)
};

const OverridingUseAttributeNames = [
'x',
'y',
'width',
'height',
'href',
'xlink:href',
];
const HrefAttributeNames = ['href', 'xlink:href'];

/**
* Dereferences <use> elements
*
*
* @param {Object} document document element
* @param {Object} options plugin params
*
* @author strarsis <strarsis@gmail.com>
*/
exports.fn = function (document, options) {
options = options || {};
strarsis marked this conversation as resolved.
Show resolved Hide resolved

// collect <use/>s
const useElements = querySelectorAll(document, 'use');

// no <use/>s, nothing to do
if (useElements === null) {
return document;
}

// replace each <use/> with its referenced node
for (const useElement of useElements) {
// `href`/`xlink:href` value
const hrefAttribute = useElement.attr('href')
? useElement.attr('href')
: useElement.attr('xlink:href');
strarsis marked this conversation as resolved.
Show resolved Hide resolved
if (!hrefAttribute || hrefAttribute.value.length === 0) continue;
const href = hrefAttribute.value;

// look up referenced element
const targetElement = querySelector(document, href);
if (!targetElement) continue;

// clone referenced element for insertion
const insertElement = targetElement.clone();

// Attribute inheritance of the referenced element
// @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use
// "Only the attributes x, y, width, height and href on the use element will override those set on the referenced element.
// However, any other attributes not set on the referenced element will be applied to the use element."
const insertElementAttributeNames = Object.keys(insertElement.attributes);
for (const attributeName in useElement.attributes) {
// don't remove attributes from the referenced element that, by specs, override the one of the <use> element
if (
insertElementAttributeNames.includes(attributeName) &&
!OverridingUseAttributeNames.includes(attributeName)
)
continue;

// don't remove href attribute with keepHref option turned on
if (!options.keepHref && HrefAttributeNames.includes(attributeName))
continue;

// set overriding attributes from referenced node
insertElement.attributes[attributeName] =
useElement.attributes[attributeName];
}

// only the original node is allowed to have this ID (IDs must be unique)
delete insertElement.attributes['id'];

// <symbol/> elements are template elements (hence not visible),
// browsers would place a <symbol/> element as a different element
if (insertElement.isElem('symbol')) {
insertElement.name = options.symbolContainer;
}

// apply styles of <use/> element on top of the referenced Element
const useElementProperties = useElement.style.getProperties();
for (const propertyName in useElementProperties) {
const property = useElementProperties[propertyName];

insertElement.style.setProperty(
propertyName,
property.value,
property.priority
);
}

// replace the <use/> element with the referenced element
const useParentElement = useElement.parentNode;

// position of <use/> in parent
const useElementPosition = useParentElement.children.indexOf(useElement);

useParentElement.children.splice(useElementPosition, 1, insertElement);
}

return document;
};
1 change: 1 addition & 0 deletions plugins/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ exports.removeEmptyContainers = require('./removeEmptyContainers.js');
exports.removeEmptyText = require('./removeEmptyText.js');
exports.removeHiddenElems = require('./removeHiddenElems.js');
exports.removeMetadata = require('./removeMetadata.js');
exports.dereferenceUses = require('./dereferenceUses.js');
exports.removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js');
exports.removeOffCanvasPaths = require('./removeOffCanvasPaths.js');
exports.removeRasterImages = require('./removeRasterImages.js');
Expand Down
21 changes: 21 additions & 0 deletions test/plugins/dereferenceUses.01.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions test/plugins/dereferenceUses.02.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions test/plugins/dereferenceUses.03.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions test/plugins/dereferenceUses.04.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions test/plugins/dereferenceUses.05.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions test/plugins/dereferenceUses.06.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading