diff --git a/lib/style.test.js b/lib/style.test.js
index 97c0900b..9171ffe1 100644
--- a/lib/style.test.js
+++ b/lib/style.test.js
@@ -2,6 +2,7 @@
const { expect } = require('chai');
const { computeStyle } = require('./style.js');
+const { querySelector } = require('./xast.js');
const svg2js = require('./svgo/svg2js.js');
describe('computeStyle', () => {
@@ -30,24 +31,24 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#class'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#class'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'red' },
});
- expect(computeStyle(root.querySelector('#two-classes'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#two-classes'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'green' },
stroke: { type: 'static', inherited: false, value: 'black' },
});
- expect(computeStyle(root.querySelector('#attribute'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#attribute'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'purple' },
});
- expect(computeStyle(root.querySelector('#inline-style'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#inline-style'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'grey' },
});
- expect(computeStyle(root.querySelector('#inheritance'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#inheritance'))).to.deep.equal({
fill: { type: 'static', inherited: true, value: 'yellow' },
});
expect(
- computeStyle(root.querySelector('#nested-inheritance'))
+ computeStyle(querySelector(root, '#nested-inheritance'))
).to.deep.equal({
fill: { type: 'static', inherited: true, value: 'blue' },
});
@@ -69,23 +70,23 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#complex-selector'))).to.deep.equal(
- {
- fill: { type: 'static', inherited: false, value: 'red' },
- }
- );
expect(
- computeStyle(root.querySelector('#attribute-over-inheritance'))
+ computeStyle(querySelector(root, '#complex-selector'))
+ ).to.deep.equal({
+ fill: { type: 'static', inherited: false, value: 'red' },
+ });
+ expect(
+ computeStyle(querySelector(root, '#attribute-over-inheritance'))
).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'orange' },
});
expect(
- computeStyle(root.querySelector('#style-rule-over-attribute'))
+ computeStyle(querySelector(root, '#style-rule-over-attribute'))
).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'blue' },
});
expect(
- computeStyle(root.querySelector('#inline-style-over-style-rule'))
+ computeStyle(querySelector(root, '#inline-style-over-style-rule'))
).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'purple' },
});
@@ -103,18 +104,18 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#complex-selector'))).to.deep.equal(
- {
- fill: { type: 'static', inherited: false, value: 'green' },
- }
- );
expect(
- computeStyle(root.querySelector('#style-rule-over-inline-style'))
+ computeStyle(querySelector(root, '#complex-selector'))
).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'green' },
});
expect(
- computeStyle(root.querySelector('#inline-style-over-style-rule'))
+ computeStyle(querySelector(root, '#style-rule-over-inline-style'))
+ ).to.deep.equal({
+ fill: { type: 'static', inherited: false, value: 'green' },
+ });
+ expect(
+ computeStyle(querySelector(root, '#inline-style-over-style-rule'))
).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'purple' },
});
@@ -140,21 +141,21 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#media-query'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#media-query'))).to.deep.equal({
fill: { type: 'dynamic', inherited: false },
});
- expect(computeStyle(root.querySelector('#hover'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#hover'))).to.deep.equal({
fill: { type: 'dynamic', inherited: false },
});
- expect(computeStyle(root.querySelector('#inherited'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#inherited'))).to.deep.equal({
fill: { type: 'dynamic', inherited: true },
});
expect(
- computeStyle(root.querySelector('#inherited-overriden'))
+ computeStyle(querySelector(root, '#inherited-overriden'))
).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'blue' },
});
- expect(computeStyle(root.querySelector('#static'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#static'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'black' },
});
});
@@ -176,13 +177,13 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#media-query'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#media-query'))).to.deep.equal({
fill: { type: 'dynamic', inherited: false },
});
- expect(computeStyle(root.querySelector('#kinda-static'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#kinda-static'))).to.deep.equal({
fill: { type: 'dynamic', inherited: false },
});
- expect(computeStyle(root.querySelector('#static'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#static'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'blue' },
});
});
@@ -204,13 +205,15 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#valid-type'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#valid-type'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'red' },
});
- expect(computeStyle(root.querySelector('#empty-type'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#empty-type'))).to.deep.equal({
fill: { type: 'static', inherited: false, value: 'green' },
});
- expect(computeStyle(root.querySelector('#invalid-type'))).to.deep.equal({});
+ expect(computeStyle(querySelector(root, '#invalid-type'))).to.deep.equal(
+ {}
+ );
});
it('ignores keyframes atrule', () => {
@@ -235,7 +238,7 @@ describe('computeStyle', () => {
`);
- expect(computeStyle(root.querySelector('#element'))).to.deep.equal({
+ expect(computeStyle(querySelector(root, '#element'))).to.deep.equal({
animation: {
type: 'static',
inherited: false,
diff --git a/lib/xast.js b/lib/xast.js
new file mode 100644
index 00000000..8a8118bc
--- /dev/null
+++ b/lib/xast.js
@@ -0,0 +1,53 @@
+'use strict';
+
+const { selectAll, selectOne, is } = require('css-select');
+const xastAdaptor = require('./svgo/css-select-adapter.js');
+
+const cssSelectOptions = {
+ xmlMode: true,
+ adapter: xastAdaptor,
+};
+
+const querySelectorAll = (node, selector) => {
+ return selectAll(selector, node, cssSelectOptions);
+};
+exports.querySelectorAll = querySelectorAll;
+
+const querySelector = (node, selector) => {
+ return selectOne(selector, node, cssSelectOptions);
+};
+exports.querySelector = querySelector;
+
+const matches = (node, selector) => {
+ return is(node, selector, cssSelectOptions);
+};
+exports.matches = matches;
+
+const closestByName = (node, name) => {
+ let currentNode = node;
+ while (currentNode) {
+ if (currentNode.type === 'element' && currentNode.name === name) {
+ return currentNode;
+ }
+ currentNode = currentNode.parentNode;
+ }
+ return null;
+};
+exports.closestByName = closestByName;
+
+const traverseBreak = Symbol();
+exports.traverseBreak = traverseBreak;
+
+const traverse = (node, fn) => {
+ if (fn(node) === traverseBreak) {
+ return traverseBreak;
+ }
+ if (node.type === 'root' || node.type === 'element') {
+ for (const child of node.children) {
+ if (traverse(child, fn) === traverseBreak) {
+ return traverseBreak;
+ }
+ }
+ }
+};
+exports.traverse = traverse;
diff --git a/plugins/cleanupEnableBackground.js b/plugins/cleanupEnableBackground.js
index 5ef55193..3abae8bb 100644
--- a/plugins/cleanupEnableBackground.js
+++ b/plugins/cleanupEnableBackground.js
@@ -1,5 +1,7 @@
'use strict';
+const { traverse } = require('../lib/xast.js');
+
exports.type = 'full';
exports.active = true;
@@ -17,72 +19,55 @@ exports.description =
* ⬇
*