1
0
mirror of https://github.com/svg/svgo.git synced 2025-08-09 02:22:08 +03:00

Add visitSkip symbol (#1547)

This should help to avoid node.parentNode and closestByName
in some cases by skiping visiting children of current node.

Works only in `enter` listener.
This commit is contained in:
Bogdan Chadkin
2021-08-27 17:01:29 +03:00
committed by GitHub
parent ac8edbaf41
commit 3ca57d1856
4 changed files with 121 additions and 100 deletions

View File

@@ -52,7 +52,7 @@ export type XastParent = XastRoot | XastElement;
export type XastNode = XastRoot | XastChild;
type VisitorNode<Node> = {
enter?: (node: Node, parentNode: XastParent) => void;
enter?: (node: Node, parentNode: XastParent) => void | symbol;
exit?: (node: Node, parentNode: XastParent) => void;
};

View File

@@ -75,6 +75,9 @@ const traverse = (node, fn) => {
};
exports.traverse = traverse;
const visitSkip = Symbol();
exports.visitSkip = visitSkip;
/**
* @type {(node: XastNode, visitor: Visitor, parentNode?: any) => void}
*/
@@ -82,7 +85,10 @@ const visit = (node, visitor, parentNode) => {
const callbacks = visitor[node.type];
if (callbacks && callbacks.enter) {
// @ts-ignore hard to infer
callbacks.enter(node, parentNode);
const symbol = callbacks.enter(node, parentNode);
if (symbol === visitSkip) {
return;
}
}
// visit root children
if (node.type === 'root') {

View File

@@ -1,50 +1,37 @@
'use strict';
const { visit, detachNodeFromParent } = require('./xast.js');
/**
* @typedef {import('./types').XastRoot} XastRoot
* @typedef {import('./types').XastElement} XastElement
*/
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;
const { visit, visitSkip, detachNodeFromParent } = require('./xast.js');
/**
* @type {(children: Array<XastElement>) => XastRoot}
*/
const root = (children) => {
return { type: 'root', children };
};
describe('xast', () => {
it('enter into nodes', () => {
const root = getAst();
/**
* @type {(
* name: string,
* attrs?: null | Record<string, string>,
* children?: Array<XastElement>
* ) => XastElement}
*/
const x = (name, attrs = null, children = []) => {
return { type: 'element', name, attributes: attrs || {}, children };
};
test('visit enters into nodes', () => {
const ast = root([x('g', null, [x('rect'), x('circle')]), x('ellipse')]);
/**
* @type {Array<string>}
*/
const entered = [];
visit(root, {
visit(ast, {
root: {
enter: (node) => {
entered.push(node.type);
@@ -63,12 +50,15 @@ describe('xast', () => {
'element:circle',
'element:ellipse',
]);
});
});
it('exit from nodes', () => {
const root = getAst();
test('visit exits from nodes', () => {
const ast = root([x('g', null, [x('rect'), x('circle')]), x('ellipse')]);
/**
* @type {Array<string>}
*/
const exited = [];
visit(root, {
visit(ast, {
root: {
exit: (node) => {
exited.push(node.type);
@@ -87,12 +77,15 @@ describe('xast', () => {
'element:ellipse',
'root',
]);
});
});
it('skip entering children if node is detached', () => {
const root = getAst();
test('visit skips entering children if node is detached', () => {
const ast = root([x('g', null, [x('rect'), x('circle')]), x('ellipse')]);
/**
* @type {Array<string>}
*/
const entered = [];
visit(root, {
visit(ast, {
element: {
enter: (node, parentNode) => {
entered.push(node.name);
@@ -103,5 +96,27 @@ describe('xast', () => {
},
});
expect(entered).toEqual(['g', 'ellipse']);
});
expect(ast).toEqual(root([x('ellipse')]));
});
test('visit skips entering children when symbol is passed', () => {
const ast = root([x('g', null, [x('rect'), x('circle')]), x('ellipse')]);
/**
* @type {Array<string>}
*/
const entered = [];
visit(ast, {
element: {
enter: (node) => {
entered.push(node.name);
if (node.name === 'g') {
return visitSkip;
}
},
},
});
expect(entered).toEqual(['g', 'ellipse']);
expect(ast).toEqual(
root([x('g', null, [x('rect'), x('circle')]), x('ellipse')])
);
});

View File

@@ -10,7 +10,7 @@
"resolveJsonModule": true,
"noImplicitAny": true
},
"include": ["plugins/**/*"],
"include": ["plugins/**/*", "lib/xast.test.js"],
"exclude": [
"plugins/_applyTransforms.js",
"plugins/cleanupIDs.js",