mirror of
https://github.com/svg/svgo.git
synced 2025-07-29 20:21:14 +03:00
Add plugin types (#1527)
Covered following plugins - addAttributesToSVGElement.js - addClassesToSVGElement.js - cleanupAttrs.js - convertEllipseToCircle.js - removeAttributesBySelector.js - removeAttrs.js - removeComments.js - removeDesc.js - removeDoctype.js - removeElementsByAttr.js - removeEmptyText.js - removeMetadata.js - removeRasterImages.js - removeScriptElement.js - removeStyleElement.js - removeTitle.js - removeXMLProcInst.js
This commit is contained in:
2
lib/svgo/css-select-adapter.d.ts
vendored
Normal file
2
lib/svgo/css-select-adapter.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
declare let obj: any;
|
||||
export = obj;
|
@ -1,9 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {any} node
|
||||
* @return {node is any}
|
||||
*/
|
||||
const isTag = (node) => {
|
||||
return node.type === 'element';
|
||||
};
|
||||
|
74
lib/types.ts
Normal file
74
lib/types.ts
Normal file
@ -0,0 +1,74 @@
|
||||
type XastDoctype = {
|
||||
type: 'doctype';
|
||||
name: string;
|
||||
data: {
|
||||
doctype: string;
|
||||
};
|
||||
};
|
||||
|
||||
type XastInstruction = {
|
||||
type: 'instruction';
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type XastComment = {
|
||||
type: 'comment';
|
||||
value: string;
|
||||
};
|
||||
|
||||
type XastCdata = {
|
||||
type: 'cdata';
|
||||
value: string;
|
||||
};
|
||||
|
||||
type XastText = {
|
||||
type: 'text';
|
||||
value: string;
|
||||
};
|
||||
|
||||
type XastElement = {
|
||||
type: 'element';
|
||||
name: string;
|
||||
attributes: Record<string, string>;
|
||||
children: Array<XastChild>;
|
||||
};
|
||||
|
||||
export type XastChild =
|
||||
| XastDoctype
|
||||
| XastInstruction
|
||||
| XastComment
|
||||
| XastCdata
|
||||
| XastText
|
||||
| XastElement;
|
||||
|
||||
type XastRoot = {
|
||||
type: 'root';
|
||||
children: Array<XastChild>;
|
||||
};
|
||||
|
||||
export type XastParent = XastRoot | XastElement;
|
||||
|
||||
export type XastNode = XastRoot | XastChild;
|
||||
|
||||
type VisitorNode<Node> = {
|
||||
enter?: (node: Node, parentNode: XastParent) => void;
|
||||
leave?: (node: Node, parentNode: XastParent) => void;
|
||||
};
|
||||
|
||||
type VisitorRoot = {
|
||||
enter?: (node: XastRoot, parentNode: null) => void;
|
||||
leave?: (node: XastRoot, parentNode: null) => void;
|
||||
};
|
||||
|
||||
export type Visitor = {
|
||||
doctype?: VisitorNode<XastDoctype>;
|
||||
instruction?: VisitorNode<XastInstruction>;
|
||||
comment?: VisitorNode<XastComment>;
|
||||
cdata?: VisitorNode<XastCdata>;
|
||||
text?: VisitorNode<XastText>;
|
||||
element?: VisitorNode<XastElement>;
|
||||
root?: VisitorRoot;
|
||||
};
|
||||
|
||||
export type Plugin<Params> = (root: XastRoot, params: Params) => null | Visitor;
|
31
lib/xast.js
31
lib/xast.js
@ -1,5 +1,12 @@
|
||||
'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');
|
||||
|
||||
@ -8,27 +15,40 @@ const cssSelectOptions = {
|
||||
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;
|
||||
|
||||
/**
|
||||
* @type {(node: XastChild, name: string) => null | XastChild}
|
||||
*/
|
||||
const closestByName = (node, name) => {
|
||||
let currentNode = node;
|
||||
while (currentNode) {
|
||||
if (currentNode.type === 'element' && currentNode.name === name) {
|
||||
return currentNode;
|
||||
}
|
||||
// @ts-ignore parentNode is hidden from public usage
|
||||
currentNode = currentNode.parentNode;
|
||||
}
|
||||
return null;
|
||||
@ -38,6 +58,9 @@ exports.closestByName = closestByName;
|
||||
const traverseBreak = Symbol();
|
||||
exports.traverseBreak = traverseBreak;
|
||||
|
||||
/**
|
||||
* @type {(node: any, fn: any) => any}
|
||||
*/
|
||||
const traverse = (node, fn) => {
|
||||
if (fn(node) === traverseBreak) {
|
||||
return traverseBreak;
|
||||
@ -52,7 +75,10 @@ const traverse = (node, fn) => {
|
||||
};
|
||||
exports.traverse = traverse;
|
||||
|
||||
const visit = (node, visitor, parentNode = null) => {
|
||||
/**
|
||||
* @type {(node: any, visitor: any, parentNode: any) => void}
|
||||
*/
|
||||
const visit = (node, visitor, parentNode) => {
|
||||
const callbacks = visitor[node.type];
|
||||
if (callbacks && callbacks.enter) {
|
||||
callbacks.enter(node, parentNode);
|
||||
@ -78,6 +104,9 @@ const visit = (node, visitor, parentNode = null) => {
|
||||
};
|
||||
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);
|
||||
|
@ -48,11 +48,16 @@ plugins: [
|
||||
* Add attributes to an outer <svg> element. Example config:
|
||||
*
|
||||
* @author April Arcus
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* attribute?: string | Record<string, null | string>,
|
||||
* attributes?: Array<string | Record<string, null | string>>
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
if (!Array.isArray(params.attributes) && !params.attribute) {
|
||||
console.error(ENOCLS);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const attributes = params.attributes || [params.attribute];
|
||||
return {
|
||||
@ -62,12 +67,14 @@ exports.fn = (root, params) => {
|
||||
for (const attribute of attributes) {
|
||||
if (typeof attribute === 'string') {
|
||||
if (node.attributes[attribute] == null) {
|
||||
// @ts-ignore disallow explicit nullable attribute value
|
||||
node.attributes[attribute] = undefined;
|
||||
}
|
||||
}
|
||||
if (typeof attribute === 'object') {
|
||||
for (const key of Object.keys(attribute)) {
|
||||
if (node.attributes[key] == null) {
|
||||
// @ts-ignore disallow explicit nullable attribute value
|
||||
node.attributes[key] = attribute[key];
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,11 @@ plugins: [
|
||||
* ]
|
||||
*
|
||||
* @author April Arcus
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* className?: string,
|
||||
* classNames?: Array<string>
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
if (
|
||||
@ -57,13 +62,14 @@ exports.fn = (root, params) => {
|
||||
!params.className
|
||||
) {
|
||||
console.error(ENOCLS);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const classNames = params.classNames || [params.className];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
// @ts-ignore class attribute will be just a string eventually
|
||||
node.class.add(...classNames);
|
||||
}
|
||||
},
|
||||
|
@ -14,6 +14,12 @@ const regSpaces = /\s{2,}/g;
|
||||
* Cleanup attributes values from newlines, trailing and repeating spaces.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* newlines?: boolean,
|
||||
* trim?: boolean,
|
||||
* spaces?: boolean
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const { newlines = true, trim = true, spaces = true } = params;
|
||||
|
@ -11,14 +11,16 @@ exports.description = 'converts non-eccentric <ellipse>s to <circle>s';
|
||||
* @see https://www.w3.org/TR/SVG11/shapes.html
|
||||
*
|
||||
* @author Taylor Hunt
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'ellipse') {
|
||||
const rx = node.attributes.rx || 0;
|
||||
const ry = node.attributes.ry || 0;
|
||||
const rx = node.attributes.rx || '0';
|
||||
const ry = node.attributes.ry || '0';
|
||||
if (
|
||||
rx === ry ||
|
||||
rx === 'auto' ||
|
||||
|
@ -71,9 +71,11 @@ exports.description =
|
||||
* ↓
|
||||
* <rect x="0" y="0" width="100" height="100"/>
|
||||
*
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors}
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors
|
||||
*
|
||||
* @author Bradley Mease
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<any>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const selectors = Array.isArray(params.selectors)
|
||||
@ -82,6 +84,7 @@ exports.fn = (root, params) => {
|
||||
for (const { selector, attributes } of selectors) {
|
||||
const nodes = querySelectorAll(root, selector);
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'element') {
|
||||
if (Array.isArray(attributes)) {
|
||||
for (const name of attributes) {
|
||||
delete node.attributes[name];
|
||||
@ -91,5 +94,6 @@ exports.fn = (root, params) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
@ -10,13 +10,13 @@ const DEFAULT_SEPARATOR = ':';
|
||||
/**
|
||||
* Remove attributes
|
||||
*
|
||||
* @param elemSeparator
|
||||
* @example elemSeparator
|
||||
* format: string
|
||||
*
|
||||
* @param preserveCurrentColor
|
||||
* @example preserveCurrentColor
|
||||
* format: boolean
|
||||
*
|
||||
* @param attrs:
|
||||
* @example attrs:
|
||||
*
|
||||
* format: [ element* : attribute* : value* ]
|
||||
*
|
||||
@ -69,6 +69,12 @@ const DEFAULT_SEPARATOR = ':';
|
||||
*
|
||||
*
|
||||
* @author Benny Schudel
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* elemSeparator?: string,
|
||||
* preserveCurrentColor?: boolean,
|
||||
* attrs: string | Array<string>
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
// wrap into an array if params is not
|
||||
|
@ -15,6 +15,8 @@ exports.description = 'removes comments';
|
||||
* Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -17,6 +17,8 @@ const standardDescs = /^(Created with|Created using)/;
|
||||
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc
|
||||
*
|
||||
* @author Daniel Wabyick
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{ removeAny?: boolean }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const { removeAny = true } = params;
|
||||
|
@ -28,6 +28,8 @@ exports.description = 'removes doctype declaration';
|
||||
* ]>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -11,9 +11,7 @@ exports.description =
|
||||
/**
|
||||
* Remove arbitrary SVG elements by ID or className.
|
||||
*
|
||||
* @param id
|
||||
* examples:
|
||||
*
|
||||
* @example id
|
||||
* > single: remove element with ID of `elementID`
|
||||
* ---
|
||||
* removeElementsByAttr:
|
||||
@ -26,9 +24,7 @@ exports.description =
|
||||
* - 'elementID'
|
||||
* - 'anotherID'
|
||||
*
|
||||
* @param class
|
||||
* examples:
|
||||
*
|
||||
* @example class
|
||||
* > single: remove all elements with class of `elementClass`
|
||||
* ---
|
||||
* removeElementsByAttr:
|
||||
@ -42,6 +38,11 @@ exports.description =
|
||||
* - 'anotherClass'
|
||||
*
|
||||
* @author Eli Dupuis (@elidupuis)
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* id?: string | Array<string>,
|
||||
* class?: string | Array<string>
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const ids =
|
||||
|
@ -23,6 +23,12 @@ exports.description = 'removes empty <text> elements';
|
||||
* <tref xlink:href=""/>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* text?: boolean,
|
||||
* tspan?: boolean,
|
||||
* tref?: boolean
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const { text = true, tspan = true, tref = true } = params;
|
||||
|
@ -13,6 +13,8 @@ exports.description = 'removes <metadata>';
|
||||
* https://www.w3.org/TR/SVG11/metadata.html
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -13,6 +13,8 @@ exports.description = 'removes raster images (disabled by default)';
|
||||
* @see https://bugs.webkit.org/show_bug.cgi?id=63548
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -12,8 +12,9 @@ exports.description = 'removes <script> elements (disabled by default)';
|
||||
*
|
||||
* https://www.w3.org/TR/SVG11/script.html
|
||||
*
|
||||
*
|
||||
* @author Patrick Klingemann
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -13,6 +13,8 @@ exports.description = 'removes <style> element (disabled by default)';
|
||||
* https://www.w3.org/TR/SVG11/styling.html#StyleElement
|
||||
*
|
||||
* @author Betsy Dupuis
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -13,6 +13,8 @@ exports.description = 'removes <title>';
|
||||
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
|
||||
*
|
||||
* @author Igor Kalashnikov
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -14,6 +14,8 @@ exports.description = 'removes XML processing instructions';
|
||||
* <?xml version="1.0" encoding="utf-8"?>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
|
@ -8,29 +8,19 @@
|
||||
"checkJs": true,
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"noImplicitAny": false
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": ["lib/**/*", "plugins/**/*", "test/**/*"],
|
||||
"include": ["plugins/**/*"],
|
||||
"exclude": [
|
||||
"**/*.test.js",
|
||||
"lib/svgo-node.js",
|
||||
"lib/svgo/coa.js",
|
||||
"lib/svgo/config.js",
|
||||
"lib/svgo/js2svg.js",
|
||||
"lib/svgo/svg2js.js",
|
||||
"lib/svgo/jsAPI.js",
|
||||
"lib/svgo.js",
|
||||
"plugins/_path.js",
|
||||
"plugins/_transforms.js",
|
||||
"plugins/_applyTransforms.js",
|
||||
"plugins/cleanupAttrs.js",
|
||||
"plugins/cleanupEnableBackground.js",
|
||||
"plugins/cleanupIDs.js",
|
||||
"plugins/cleanupListOfValues.js",
|
||||
"plugins/cleanupNumericValues.js",
|
||||
"plugins/collapseGroups.js",
|
||||
"plugins/convertColors.js",
|
||||
"plugins/convertEllipseToCircle.js",
|
||||
"plugins/convertPathData.js",
|
||||
"plugins/convertShapeToPath.js",
|
||||
"plugins/convertStyleToAttrs.js",
|
||||
@ -41,19 +31,12 @@
|
||||
"plugins/moveGroupAttrsToElems.js",
|
||||
"plugins/plugins.js",
|
||||
"plugins/prefixIds.js",
|
||||
"plugins/removeAttributesBySelector.js",
|
||||
"plugins/removeAttrs.js",
|
||||
"plugins/removeComments.js",
|
||||
"plugins/removeDimensions.js",
|
||||
"plugins/removeDoctype.js",
|
||||
"plugins/removeEditorsNSData.js",
|
||||
"plugins/removeElementsByAttr.js",
|
||||
"plugins/removeEmptyAttrs.js",
|
||||
"plugins/removeEmptyText.js",
|
||||
"plugins/removeHiddenElems.js",
|
||||
"plugins/removeNonInheritableGroupAttrs.js",
|
||||
"plugins/removeOffCanvasPaths.js",
|
||||
"plugins/removeRasterImages.js",
|
||||
"plugins/removeUnknownsAndDefaults.js",
|
||||
"plugins/removeUnusedNS.js",
|
||||
"plugins/removeUselessDefs.js",
|
||||
@ -62,11 +45,10 @@
|
||||
"plugins/removeXMLNS.js",
|
||||
"plugins/reusePaths.js",
|
||||
"plugins/sortDefsChildren.js",
|
||||
"plugins/preset-default.js",
|
||||
"lib/svgo/jsAPI.js",
|
||||
"test/browser.js",
|
||||
"test/config/fixtures/**/*.js",
|
||||
"test/regression.js",
|
||||
"lib/svgo/plugins.js"
|
||||
"plugins/sortAttrs.js",
|
||||
"plugins/removeEmptyContainers.js",
|
||||
"plugins/minifyStyles.js",
|
||||
"plugins/inlineStyles.js",
|
||||
"plugins/preset-default.js"
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user