mirror of
https://github.com/svg/svgo.git
synced 2025-07-03 05:42:32 +03:00
The last usage of closestByName utility based on node.parentNode is removed here. One step closer to clean ast in v3.
87 lines
2.3 KiB
JavaScript
87 lines
2.3 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @typedef {import('./types').XastNode} XastNode
|
|
* @typedef {import('./types').XastChild} XastChild
|
|
* @typedef {import('./types').XastParent} XastParent
|
|
* @typedef {import('./types').Visitor} Visitor
|
|
*/
|
|
|
|
const { selectAll, selectOne, is } = require('css-select');
|
|
const xastAdaptor = require('./svgo/css-select-adapter.js');
|
|
|
|
const cssSelectOptions = {
|
|
xmlMode: true,
|
|
adapter: xastAdaptor,
|
|
};
|
|
|
|
/**
|
|
* @type {(node: XastNode, selector: string) => Array<XastChild>}
|
|
*/
|
|
const querySelectorAll = (node, selector) => {
|
|
return selectAll(selector, node, cssSelectOptions);
|
|
};
|
|
exports.querySelectorAll = querySelectorAll;
|
|
|
|
/**
|
|
* @type {(node: XastNode, selector: string) => null | XastChild}
|
|
*/
|
|
const querySelector = (node, selector) => {
|
|
return selectOne(selector, node, cssSelectOptions);
|
|
};
|
|
exports.querySelector = querySelector;
|
|
|
|
/**
|
|
* @type {(node: XastChild, selector: string) => boolean}
|
|
*/
|
|
const matches = (node, selector) => {
|
|
return is(node, selector, cssSelectOptions);
|
|
};
|
|
exports.matches = matches;
|
|
|
|
const visitSkip = Symbol();
|
|
exports.visitSkip = visitSkip;
|
|
|
|
/**
|
|
* @type {(node: XastNode, visitor: Visitor, parentNode?: any) => void}
|
|
*/
|
|
const visit = (node, visitor, parentNode) => {
|
|
const callbacks = visitor[node.type];
|
|
if (callbacks && callbacks.enter) {
|
|
// @ts-ignore hard to infer
|
|
const symbol = callbacks.enter(node, parentNode);
|
|
if (symbol === visitSkip) {
|
|
return;
|
|
}
|
|
}
|
|
// visit root children
|
|
if (node.type === 'root') {
|
|
// copy children array to not loose cursor when children is spliced
|
|
for (const child of node.children) {
|
|
visit(child, visitor, node);
|
|
}
|
|
}
|
|
// visit element children if still attached to parent
|
|
if (node.type === 'element') {
|
|
if (parentNode.children.includes(node)) {
|
|
for (const child of node.children) {
|
|
visit(child, visitor, node);
|
|
}
|
|
}
|
|
}
|
|
if (callbacks && callbacks.exit) {
|
|
// @ts-ignore hard to infer
|
|
callbacks.exit(node, parentNode);
|
|
}
|
|
};
|
|
exports.visit = visit;
|
|
|
|
/**
|
|
* @type {(node: XastChild, parentNode: XastParent) => void}
|
|
*/
|
|
const detachNodeFromParent = (node, parentNode) => {
|
|
// avoid splice to not break for loops
|
|
parentNode.children = parentNode.children.filter((child) => child !== node);
|
|
};
|
|
exports.detachNodeFromParent = detachNodeFromParent;
|