1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-29 20:21:14 +03:00

Add "visitor" plugins support (#1454)

Visitor is a simple pattern which helps to avoid many type checks
and provide both "perItem" and "perItemReverse" functionality without
fragmentation.

The most important case is an ability to define state which in many
plugins specified either on module level or by polluting `params`.

In this diff I added visit and detachFromParent utilities and refactored
new mergeStyles plugin with it.

Also fixed bug when cdata content is merged into "text" node which is
not always valid.
This commit is contained in:
Bogdan Chadkin
2021-03-28 11:20:17 +03:00
committed by GitHub
parent 19c77d2398
commit 27bef1a954
6 changed files with 234 additions and 60 deletions

108
lib/xast.test.js Normal file
View File

@ -0,0 +1,108 @@
'use strict';
const { expect } = require('chai');
const { visit, detachNodeFromParent } = require('./xast.js');
const getAst = () => {
const ast = {
type: 'root',
children: [
{
type: 'element',
name: 'g',
attributes: {},
children: [
{
type: 'element',
name: 'rect',
attributes: {},
children: [],
},
{
type: 'element',
name: 'circle',
attributes: {},
children: [],
},
],
},
{
type: 'element',
name: 'ellipse',
attributes: {},
children: [],
},
],
};
ast.children[0].parentNode = ast;
ast.children[0].children[0].parentNode = ast.children[0];
ast.children[0].children[1].parentNode = ast.children[0];
ast.children[1].parentNode = ast;
return ast;
};
describe('xast', () => {
it('enter into nodes', () => {
const root = getAst();
const entered = [];
visit(root, {
root: {
enter: (node) => {
entered.push(node.type);
},
},
element: {
enter: (node) => {
entered.push(`${node.type}:${node.name}`);
},
},
});
expect(entered).to.deep.equal([
'root',
'element:g',
'element:rect',
'element:circle',
'element:ellipse',
]);
});
it('exit from nodes', () => {
const root = getAst();
const exited = [];
visit(root, {
root: {
exit: (node) => {
exited.push(node.type);
},
},
element: {
exit: (node) => {
exited.push(`${node.type}:${node.name}`);
},
},
});
expect(exited).to.deep.equal([
'element:rect',
'element:circle',
'element:g',
'element:ellipse',
'root',
]);
});
it('skip entering children if node is detached', () => {
const root = getAst();
const entered = [];
visit(root, {
element: {
enter: (node) => {
entered.push(node.name);
if (node.name === 'g') {
detachNodeFromParent(node);
}
},
},
});
expect(entered).to.deep.equal(['g', 'ellipse']);
});
});