Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production) Add global file drop handler, order column normalization, SPA hash fix Serve CDN assets via /_vendor/ URLs instead of merging into bundles Add production minification with license preservation Improve JSON formatting for debugging and production optimization Add CDN asset caching with CSS URL inlining for production builds Add three-mode system (development, debug, production) Update Manifest CLAUDE.md to reflect helper class architecture Refactor Manifest.php into helper classes for better organization Pre-manifest-refactor checkpoint: Add app_mode documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
117
node_modules/svgo/lib/builtin.js
generated
vendored
Normal file
117
node_modules/svgo/lib/builtin.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
import presetDefault from '../plugins/preset-default.js';
|
||||
import * as addAttributesToSVGElement from '../plugins/addAttributesToSVGElement.js';
|
||||
import * as addClassesToSVGElement from '../plugins/addClassesToSVGElement.js';
|
||||
import * as cleanupAttrs from '../plugins/cleanupAttrs.js';
|
||||
import * as cleanupEnableBackground from '../plugins/cleanupEnableBackground.js';
|
||||
import * as cleanupIds from '../plugins/cleanupIds.js';
|
||||
import * as cleanupListOfValues from '../plugins/cleanupListOfValues.js';
|
||||
import * as cleanupNumericValues from '../plugins/cleanupNumericValues.js';
|
||||
import * as collapseGroups from '../plugins/collapseGroups.js';
|
||||
import * as convertColors from '../plugins/convertColors.js';
|
||||
import * as convertEllipseToCircle from '../plugins/convertEllipseToCircle.js';
|
||||
import * as convertOneStopGradients from '../plugins/convertOneStopGradients.js';
|
||||
import * as convertPathData from '../plugins/convertPathData.js';
|
||||
import * as convertShapeToPath from '../plugins/convertShapeToPath.js';
|
||||
import * as convertStyleToAttrs from '../plugins/convertStyleToAttrs.js';
|
||||
import * as convertTransform from '../plugins/convertTransform.js';
|
||||
import * as mergeStyles from '../plugins/mergeStyles.js';
|
||||
import * as inlineStyles from '../plugins/inlineStyles.js';
|
||||
import * as mergePaths from '../plugins/mergePaths.js';
|
||||
import * as minifyStyles from '../plugins/minifyStyles.js';
|
||||
import * as moveElemsAttrsToGroup from '../plugins/moveElemsAttrsToGroup.js';
|
||||
import * as moveGroupAttrsToElems from '../plugins/moveGroupAttrsToElems.js';
|
||||
import * as prefixIds from '../plugins/prefixIds.js';
|
||||
import * as removeAttributesBySelector from '../plugins/removeAttributesBySelector.js';
|
||||
import * as removeAttrs from '../plugins/removeAttrs.js';
|
||||
import * as removeComments from '../plugins/removeComments.js';
|
||||
import * as removeDeprecatedAttrs from '../plugins/removeDeprecatedAttrs.js';
|
||||
import * as removeDesc from '../plugins/removeDesc.js';
|
||||
import * as removeDimensions from '../plugins/removeDimensions.js';
|
||||
import * as removeDoctype from '../plugins/removeDoctype.js';
|
||||
import * as removeEditorsNSData from '../plugins/removeEditorsNSData.js';
|
||||
import * as removeElementsByAttr from '../plugins/removeElementsByAttr.js';
|
||||
import * as removeEmptyAttrs from '../plugins/removeEmptyAttrs.js';
|
||||
import * as removeEmptyContainers from '../plugins/removeEmptyContainers.js';
|
||||
import * as removeEmptyText from '../plugins/removeEmptyText.js';
|
||||
import * as removeHiddenElems from '../plugins/removeHiddenElems.js';
|
||||
import * as removeMetadata from '../plugins/removeMetadata.js';
|
||||
import * as removeNonInheritableGroupAttrs from '../plugins/removeNonInheritableGroupAttrs.js';
|
||||
import * as removeOffCanvasPaths from '../plugins/removeOffCanvasPaths.js';
|
||||
import * as removeRasterImages from '../plugins/removeRasterImages.js';
|
||||
import * as removeScripts from '../plugins/removeScripts.js';
|
||||
import * as removeStyleElement from '../plugins/removeStyleElement.js';
|
||||
import * as removeTitle from '../plugins/removeTitle.js';
|
||||
import * as removeUnknownsAndDefaults from '../plugins/removeUnknownsAndDefaults.js';
|
||||
import * as removeUnusedNS from '../plugins/removeUnusedNS.js';
|
||||
import * as removeUselessDefs from '../plugins/removeUselessDefs.js';
|
||||
import * as removeUselessStrokeAndFill from '../plugins/removeUselessStrokeAndFill.js';
|
||||
import * as removeViewBox from '../plugins/removeViewBox.js';
|
||||
import * as removeXlink from '../plugins/removeXlink.js';
|
||||
import * as removeXMLNS from '../plugins/removeXMLNS.js';
|
||||
import * as removeXMLProcInst from '../plugins/removeXMLProcInst.js';
|
||||
import * as reusePaths from '../plugins/reusePaths.js';
|
||||
import * as sortAttrs from '../plugins/sortAttrs.js';
|
||||
import * as sortDefsChildren from '../plugins/sortDefsChildren.js';
|
||||
|
||||
/**
|
||||
* Plugins that are bundled with SVGO. This includes plugin presets, and plugins
|
||||
* that are not enabled by default.
|
||||
*
|
||||
* @type {ReadonlyArray<{[Name in keyof import('./types.js').PluginsParams]: import('./types.js').BuiltinPluginOrPreset<Name, import('./types.js').PluginsParams[Name]>;}[keyof import('./types.js').PluginsParams]>}
|
||||
*/
|
||||
export const builtinPlugins = Object.freeze([
|
||||
presetDefault,
|
||||
addAttributesToSVGElement,
|
||||
addClassesToSVGElement,
|
||||
cleanupAttrs,
|
||||
cleanupEnableBackground,
|
||||
cleanupIds,
|
||||
cleanupListOfValues,
|
||||
cleanupNumericValues,
|
||||
collapseGroups,
|
||||
convertColors,
|
||||
convertEllipseToCircle,
|
||||
convertOneStopGradients,
|
||||
convertPathData,
|
||||
convertShapeToPath,
|
||||
convertStyleToAttrs,
|
||||
convertTransform,
|
||||
inlineStyles,
|
||||
mergePaths,
|
||||
mergeStyles,
|
||||
minifyStyles,
|
||||
moveElemsAttrsToGroup,
|
||||
moveGroupAttrsToElems,
|
||||
prefixIds,
|
||||
removeAttributesBySelector,
|
||||
removeAttrs,
|
||||
removeComments,
|
||||
removeDeprecatedAttrs,
|
||||
removeDesc,
|
||||
removeDimensions,
|
||||
removeDoctype,
|
||||
removeEditorsNSData,
|
||||
removeElementsByAttr,
|
||||
removeEmptyAttrs,
|
||||
removeEmptyContainers,
|
||||
removeEmptyText,
|
||||
removeHiddenElems,
|
||||
removeMetadata,
|
||||
removeNonInheritableGroupAttrs,
|
||||
removeOffCanvasPaths,
|
||||
removeRasterImages,
|
||||
removeScripts,
|
||||
removeStyleElement,
|
||||
removeTitle,
|
||||
removeUnknownsAndDefaults,
|
||||
removeUnusedNS,
|
||||
removeUselessDefs,
|
||||
removeUselessStrokeAndFill,
|
||||
removeViewBox,
|
||||
removeXlink,
|
||||
removeXMLNS,
|
||||
removeXMLProcInst,
|
||||
reusePaths,
|
||||
sortAttrs,
|
||||
sortDefsChildren,
|
||||
]);
|
||||
239
node_modules/svgo/lib/css-tools.js
generated
vendored
239
node_modules/svgo/lib/css-tools.js
generated
vendored
@@ -1,239 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var csstree = require('css-tree'),
|
||||
List = csstree.List,
|
||||
stable = require('stable'),
|
||||
specificity = require('csso/lib/restructure/prepare/specificity');
|
||||
|
||||
/**
|
||||
* Flatten a CSS AST to a selectors list.
|
||||
*
|
||||
* @param {import('css-tree').CssNode} cssAst css-tree AST to flatten
|
||||
* @return {Array} selectors
|
||||
*/
|
||||
function flattenToSelectors(cssAst) {
|
||||
var selectors = [];
|
||||
|
||||
csstree.walk(cssAst, {
|
||||
visit: 'Rule',
|
||||
enter: function (node) {
|
||||
if (node.type !== 'Rule') {
|
||||
return;
|
||||
}
|
||||
|
||||
var atrule = this.atrule;
|
||||
var rule = node;
|
||||
|
||||
node.prelude.children.each(function (selectorNode, selectorItem) {
|
||||
var selector = {
|
||||
item: selectorItem,
|
||||
atrule: atrule,
|
||||
rule: rule,
|
||||
pseudos: /** @type {{item: any; list: any[]}[]} */ ([]),
|
||||
};
|
||||
|
||||
selectorNode.children.each(function (
|
||||
selectorChildNode,
|
||||
selectorChildItem,
|
||||
selectorChildList
|
||||
) {
|
||||
if (
|
||||
selectorChildNode.type === 'PseudoClassSelector' ||
|
||||
selectorChildNode.type === 'PseudoElementSelector'
|
||||
) {
|
||||
selector.pseudos.push({
|
||||
item: selectorChildItem,
|
||||
list: selectorChildList,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
selectors.push(selector);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter selectors by Media Query.
|
||||
*
|
||||
* @param {Array} selectors to filter
|
||||
* @param {Array} useMqs Array with strings of media queries that should pass (<name> <expression>)
|
||||
* @return {Array} Filtered selectors that match the passed media queries
|
||||
*/
|
||||
function filterByMqs(selectors, useMqs) {
|
||||
return selectors.filter(function (selector) {
|
||||
if (selector.atrule === null) {
|
||||
return ~useMqs.indexOf('');
|
||||
}
|
||||
|
||||
var mqName = selector.atrule.name;
|
||||
var mqStr = mqName;
|
||||
if (
|
||||
selector.atrule.expression &&
|
||||
selector.atrule.expression.children.first().type === 'MediaQueryList'
|
||||
) {
|
||||
var mqExpr = csstree.generate(selector.atrule.expression);
|
||||
mqStr = [mqName, mqExpr].join(' ');
|
||||
}
|
||||
|
||||
return ~useMqs.indexOf(mqStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter selectors by the pseudo-elements and/or -classes they contain.
|
||||
*
|
||||
* @param {Array} selectors to filter
|
||||
* @param {Array} usePseudos Array with strings of single or sequence of pseudo-elements and/or -classes that should pass
|
||||
* @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
|
||||
*/
|
||||
function filterByPseudos(selectors, usePseudos) {
|
||||
return selectors.filter(function (selector) {
|
||||
var pseudoSelectorsStr = csstree.generate({
|
||||
type: 'Selector',
|
||||
children: new List().fromArray(
|
||||
selector.pseudos.map(function (pseudo) {
|
||||
return pseudo.item.data;
|
||||
})
|
||||
),
|
||||
});
|
||||
return ~usePseudos.indexOf(pseudoSelectorsStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove pseudo-elements and/or -classes from the selectors for proper matching.
|
||||
*
|
||||
* @param {Array} selectors to clean
|
||||
* @return {void}
|
||||
*/
|
||||
function cleanPseudos(selectors) {
|
||||
selectors.forEach(function (selector) {
|
||||
selector.pseudos.forEach(function (pseudo) {
|
||||
pseudo.list.remove(pseudo.item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two selector specificities.
|
||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||
*
|
||||
* @param {Array} aSpecificity Specificity of selector A
|
||||
* @param {Array} bSpecificity Specificity of selector B
|
||||
* @return {number} Score of selector specificity A compared to selector specificity B
|
||||
*/
|
||||
function compareSpecificity(aSpecificity, bSpecificity) {
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
if (aSpecificity[i] < bSpecificity[i]) {
|
||||
return -1;
|
||||
} else if (aSpecificity[i] > bSpecificity[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two simple selectors.
|
||||
*
|
||||
* @param {Object} aSimpleSelectorNode Simple selector A
|
||||
* @param {Object} bSimpleSelectorNode Simple selector B
|
||||
* @return {number} Score of selector A compared to selector B
|
||||
*/
|
||||
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
|
||||
var aSpecificity = specificity(aSimpleSelectorNode),
|
||||
bSpecificity = specificity(bSimpleSelectorNode);
|
||||
return compareSpecificity(aSpecificity, bSpecificity);
|
||||
}
|
||||
|
||||
function _bySelectorSpecificity(selectorA, selectorB) {
|
||||
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort selectors stably by their specificity.
|
||||
*
|
||||
* @param {Array} selectors to be sorted
|
||||
* @return {Array} Stable sorted selectors
|
||||
*/
|
||||
function sortSelectors(selectors) {
|
||||
return stable(selectors, _bySelectorSpecificity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a css-tree AST style declaration to CSSStyleDeclaration property.
|
||||
*
|
||||
* @param {import('css-tree').CssNode} declaration css-tree style declaration
|
||||
* @return {Object} CSSStyleDeclaration property
|
||||
*/
|
||||
function csstreeToStyleDeclaration(declaration) {
|
||||
var propertyName = declaration.property,
|
||||
propertyValue = csstree.generate(declaration.value),
|
||||
propertyPriority = declaration.important ? 'important' : '';
|
||||
return {
|
||||
name: propertyName,
|
||||
value: propertyValue,
|
||||
priority: propertyPriority,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSS string of a style element
|
||||
*
|
||||
* @param {Object} elem style element
|
||||
* @return {string} CSS string or empty array if no styles are set
|
||||
*/
|
||||
function getCssStr(elem) {
|
||||
if (
|
||||
elem.children.length > 0 &&
|
||||
(elem.children[0].type === 'text' || elem.children[0].type === 'cdata')
|
||||
) {
|
||||
return elem.children[0].value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CSS string of a style element
|
||||
*
|
||||
* @param {Object} elem style element
|
||||
* @param {string} css string to be set
|
||||
* @return {string} reference to field with CSS
|
||||
*/
|
||||
function setCssStr(elem, css) {
|
||||
if (elem.children.length === 0) {
|
||||
elem.children.push({
|
||||
type: 'text',
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
|
||||
if (elem.children[0].type !== 'text' && elem.children[0].type !== 'cdata') {
|
||||
return css;
|
||||
}
|
||||
|
||||
elem.children[0].value = css;
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
module.exports.flattenToSelectors = flattenToSelectors;
|
||||
|
||||
module.exports.filterByMqs = filterByMqs;
|
||||
module.exports.filterByPseudos = filterByPseudos;
|
||||
module.exports.cleanPseudos = cleanPseudos;
|
||||
|
||||
module.exports.compareSpecificity = compareSpecificity;
|
||||
module.exports.compareSimpleSelectorNode = compareSimpleSelectorNode;
|
||||
|
||||
module.exports.sortSelectors = sortSelectors;
|
||||
|
||||
module.exports.csstreeToStyleDeclaration = csstreeToStyleDeclaration;
|
||||
|
||||
module.exports.getCssStr = getCssStr;
|
||||
module.exports.setCssStr = setCssStr;
|
||||
141
node_modules/svgo/lib/parser.js
generated
vendored
141
node_modules/svgo/lib/parser.js
generated
vendored
@@ -1,29 +1,13 @@
|
||||
'use strict';
|
||||
import SAX from 'sax';
|
||||
import { textElems } from '../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').XastNode} XastNode
|
||||
* @typedef {import('./types').XastInstruction} XastInstruction
|
||||
* @typedef {import('./types').XastDoctype} XastDoctype
|
||||
* @typedef {import('./types').XastComment} XastComment
|
||||
* @typedef {import('./types').XastRoot} XastRoot
|
||||
* @typedef {import('./types').XastElement} XastElement
|
||||
* @typedef {import('./types').XastCdata} XastCdata
|
||||
* @typedef {import('./types').XastText} XastText
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
*/
|
||||
|
||||
// @ts-ignore sax will be replaced with something else later
|
||||
const SAX = require('@trysound/sax');
|
||||
const JSAPI = require('./svgo/jsAPI.js');
|
||||
const { textElems } = require('../plugins/_collections.js');
|
||||
|
||||
class SvgoParserError extends Error {
|
||||
export class SvgoParserError extends Error {
|
||||
/**
|
||||
* @param message {string}
|
||||
* @param line {number}
|
||||
* @param column {number}
|
||||
* @param source {string}
|
||||
* @param file {void | string}
|
||||
* @param {string} message
|
||||
* @param {number} line
|
||||
* @param {number} column
|
||||
* @param {string} source
|
||||
* @param {string=} file
|
||||
*/
|
||||
constructor(message, line, column, source, file) {
|
||||
super(message);
|
||||
@@ -37,6 +21,7 @@ class SvgoParserError extends Error {
|
||||
Error.captureStackTrace(this, SvgoParserError);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const lines = this.source.split(/\r?\n/);
|
||||
const startLine = Math.max(this.line - 3, 0);
|
||||
@@ -82,44 +67,34 @@ const config = {
|
||||
lowercase: true,
|
||||
xmlns: true,
|
||||
position: true,
|
||||
unparsedEntities: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert SVG (XML) string to SVG-as-JS object.
|
||||
*
|
||||
* @type {(data: string, from?: string) => XastRoot}
|
||||
* @param {string} data
|
||||
* @param {string=} from
|
||||
* @returns {import('./types.js').XastRoot}
|
||||
*/
|
||||
const parseSvg = (data, from) => {
|
||||
export const parseSvg = (data, from) => {
|
||||
const sax = SAX.parser(config.strict, config);
|
||||
/**
|
||||
* @type {XastRoot}
|
||||
*/
|
||||
const root = new JSAPI({ type: 'root', children: [] });
|
||||
/**
|
||||
* @type {XastParent}
|
||||
*/
|
||||
/** @type {import('./types.js').XastRoot} */
|
||||
const root = { type: 'root', children: [] };
|
||||
/** @type {import('./types.js').XastParent} */
|
||||
let current = root;
|
||||
/**
|
||||
* @type {Array<XastParent>}
|
||||
*/
|
||||
/** @type {import('./types.js').XastParent[]} */
|
||||
const stack = [root];
|
||||
|
||||
/**
|
||||
* @type {<T extends XastNode>(node: T) => T}
|
||||
* @param {import('./types.js').XastChild} node
|
||||
*/
|
||||
const pushToContent = (node) => {
|
||||
const wrapped = new JSAPI(node, current);
|
||||
current.children.push(wrapped);
|
||||
return wrapped;
|
||||
current.children.push(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(doctype: string) => void}
|
||||
*/
|
||||
sax.ondoctype = (doctype) => {
|
||||
/**
|
||||
* @type {XastDoctype}
|
||||
*/
|
||||
/** @type {import('./types.js').XastDoctype} */
|
||||
const node = {
|
||||
type: 'doctype',
|
||||
// TODO parse doctype for name, public and system to match xast
|
||||
@@ -140,13 +115,8 @@ const parseSvg = (data, from) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(data: { name: string, body: string }) => void}
|
||||
*/
|
||||
sax.onprocessinginstruction = (data) => {
|
||||
/**
|
||||
* @type {XastInstruction}
|
||||
*/
|
||||
/** @type {import('./types.js').XastInstruction} */
|
||||
const node = {
|
||||
type: 'instruction',
|
||||
name: data.name,
|
||||
@@ -155,13 +125,8 @@ const parseSvg = (data, from) => {
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(comment: string) => void}
|
||||
*/
|
||||
sax.oncomment = (comment) => {
|
||||
/**
|
||||
* @type {XastComment}
|
||||
*/
|
||||
/** @type {import('./types.js').XastComment} */
|
||||
const node = {
|
||||
type: 'comment',
|
||||
value: comment.trim(),
|
||||
@@ -169,13 +134,8 @@ const parseSvg = (data, from) => {
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(cdata: string) => void}
|
||||
*/
|
||||
sax.oncdata = (cdata) => {
|
||||
/**
|
||||
* @type {XastCdata}
|
||||
*/
|
||||
/** @type {import('./types.js').XastCdata} */
|
||||
const node = {
|
||||
type: 'cdata',
|
||||
value: cdata,
|
||||
@@ -183,14 +143,9 @@ const parseSvg = (data, from) => {
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(data: { name: string, attributes: Record<string, { value: string }>}) => void}
|
||||
*/
|
||||
sax.onopentag = (data) => {
|
||||
/**
|
||||
* @type {XastElement}
|
||||
*/
|
||||
let element = {
|
||||
/** @type {import('./types.js').XastElement} */
|
||||
const element = {
|
||||
type: 'element',
|
||||
name: data.name,
|
||||
attributes: {},
|
||||
@@ -199,35 +154,32 @@ const parseSvg = (data, from) => {
|
||||
for (const [name, attr] of Object.entries(data.attributes)) {
|
||||
element.attributes[name] = attr.value;
|
||||
}
|
||||
element = pushToContent(element);
|
||||
pushToContent(element);
|
||||
current = element;
|
||||
stack.push(element);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(text: string) => void}
|
||||
*/
|
||||
sax.ontext = (text) => {
|
||||
if (current.type === 'element') {
|
||||
// prevent trimming of meaningful whitespace inside textual tags
|
||||
if (textElems.includes(current.name)) {
|
||||
/**
|
||||
* @type {XastText}
|
||||
*/
|
||||
if (textElems.has(current.name)) {
|
||||
/** @type {import('./types.js').XastText} */
|
||||
const node = {
|
||||
type: 'text',
|
||||
value: text,
|
||||
};
|
||||
pushToContent(node);
|
||||
} else if (/\S/.test(text)) {
|
||||
/**
|
||||
* @type {XastText}
|
||||
*/
|
||||
const node = {
|
||||
type: 'text',
|
||||
value: text.trim(),
|
||||
};
|
||||
pushToContent(node);
|
||||
} else {
|
||||
const value = text.trim();
|
||||
|
||||
if (value !== '') {
|
||||
/** @type {import('./types.js').XastText} */
|
||||
const node = {
|
||||
type: 'text',
|
||||
value,
|
||||
};
|
||||
pushToContent(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -237,16 +189,14 @@ const parseSvg = (data, from) => {
|
||||
current = stack[stack.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(e: any) => void}
|
||||
*/
|
||||
sax.onerror = (e) => {
|
||||
const reason = e.message.split('\n')[0];
|
||||
const error = new SvgoParserError(
|
||||
e.reason,
|
||||
e.line + 1,
|
||||
e.column,
|
||||
reason,
|
||||
sax.line + 1,
|
||||
sax.column,
|
||||
data,
|
||||
from
|
||||
from,
|
||||
);
|
||||
if (e.message.indexOf('Unexpected end') === -1) {
|
||||
throw error;
|
||||
@@ -256,4 +206,3 @@ const parseSvg = (data, from) => {
|
||||
sax.write(data).close();
|
||||
return root;
|
||||
};
|
||||
exports.parseSvg = parseSvg;
|
||||
|
||||
229
node_modules/svgo/lib/path.js
generated
vendored
229
node_modules/svgo/lib/path.js
generated
vendored
@@ -1,11 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').PathDataItem} PathDataItem
|
||||
* @typedef {import('./types').PathDataCommand} PathDataCommand
|
||||
* @fileoverview Based on https://www.w3.org/TR/SVG11/paths.html#PathDataBNF.
|
||||
*/
|
||||
|
||||
// Based on https://www.w3.org/TR/SVG11/paths.html#PathDataBNF
|
||||
import { removeLeadingZero, toFixed } from './svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState
|
||||
*
|
||||
* @typedef StringifyPathDataOptions
|
||||
* @property {ReadonlyArray<import('./types.js').PathDataItem>} pathData
|
||||
* @property {number=} precision
|
||||
* @property {boolean=} disableSpaceAfterFlags
|
||||
*/
|
||||
|
||||
const argsCountPerCommand = {
|
||||
M: 2,
|
||||
@@ -31,27 +37,24 @@ const argsCountPerCommand = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(c: string) => c is PathDataCommand}
|
||||
* @param {string} c
|
||||
* @returns {c is import('./types.js').PathDataCommand}
|
||||
*/
|
||||
const isCommand = (c) => {
|
||||
return c in argsCountPerCommand;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(c: string) => boolean}
|
||||
* @param {string} c
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isWsp = (c) => {
|
||||
const codePoint = c.codePointAt(0);
|
||||
return (
|
||||
codePoint === 0x20 ||
|
||||
codePoint === 0x9 ||
|
||||
codePoint === 0xd ||
|
||||
codePoint === 0xa
|
||||
);
|
||||
const isWhiteSpace = (c) => {
|
||||
return c === ' ' || c === '\t' || c === '\r' || c === '\n';
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(c: string) => boolean}
|
||||
* @param {string} c
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isDigit = (c) => {
|
||||
const codePoint = c.codePointAt(0);
|
||||
@@ -62,16 +65,15 @@ const isDigit = (c) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(string: string, cursor: number) => [number, number | null]}
|
||||
* @param {string} string
|
||||
* @param {number} cursor
|
||||
* @returns {[number, ?number]}
|
||||
*/
|
||||
const readNumber = (string, cursor) => {
|
||||
let i = cursor;
|
||||
let value = '';
|
||||
let state = /** @type {ReadNumberState} */ ('none');
|
||||
/** @type {ReadNumberState} */
|
||||
let state = 'none';
|
||||
for (; i < string.length; i += 1) {
|
||||
const c = string[i];
|
||||
if (c === '+' || c === '-') {
|
||||
@@ -133,16 +135,13 @@ const readNumber = (string, cursor) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(string: string) => Array<PathDataItem>}
|
||||
* @param {string} string
|
||||
* @returns {import('./types.js').PathDataItem[]}
|
||||
*/
|
||||
const parsePathData = (string) => {
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
export const parsePathData = (string) => {
|
||||
/** @type {import('./types.js').PathDataItem[]} */
|
||||
const pathData = [];
|
||||
/**
|
||||
* @type {null | PathDataCommand}
|
||||
*/
|
||||
/** @type {?import('./types.js').PathDataCommand} */
|
||||
let command = null;
|
||||
let args = /** @type {number[]} */ ([]);
|
||||
let argsCount = 0;
|
||||
@@ -150,7 +149,7 @@ const parsePathData = (string) => {
|
||||
let hadComma = false;
|
||||
for (let i = 0; i < string.length; i += 1) {
|
||||
const c = string.charAt(i);
|
||||
if (isWsp(c)) {
|
||||
if (isWhiteSpace(c)) {
|
||||
continue;
|
||||
}
|
||||
// allow comma only between arguments
|
||||
@@ -170,11 +169,9 @@ const parsePathData = (string) => {
|
||||
if (c !== 'M' && c !== 'm') {
|
||||
return pathData;
|
||||
}
|
||||
} else {
|
||||
} else if (args.length !== 0) {
|
||||
// stop if previous command arguments are not flushed
|
||||
if (args.length !== 0) {
|
||||
return pathData;
|
||||
}
|
||||
return pathData;
|
||||
}
|
||||
command = c;
|
||||
args = [];
|
||||
@@ -226,7 +223,7 @@ const parsePathData = (string) => {
|
||||
// flush arguments when necessary count is reached
|
||||
if (args.length === argsCount) {
|
||||
pathData.push({ command, args });
|
||||
// subsequent moveto coordinates are threated as implicit lineto commands
|
||||
// subsequent moveto coordinates are treated as implicit lineto commands
|
||||
if (command === 'M') {
|
||||
command = 'L';
|
||||
}
|
||||
@@ -238,110 +235,128 @@ const parsePathData = (string) => {
|
||||
}
|
||||
return pathData;
|
||||
};
|
||||
exports.parsePathData = parsePathData;
|
||||
|
||||
/**
|
||||
* @type {(number: number, precision?: number) => string}
|
||||
* @param {number} number
|
||||
* @param {number=} precision
|
||||
* @returns {{ roundedStr: string, rounded: number }}
|
||||
*/
|
||||
const stringifyNumber = (number, precision) => {
|
||||
const roundAndStringify = (number, precision) => {
|
||||
if (precision != null) {
|
||||
const ratio = 10 ** precision;
|
||||
number = Math.round(number * ratio) / ratio;
|
||||
number = toFixed(number, precision);
|
||||
}
|
||||
// remove zero whole from decimal number
|
||||
return number.toString().replace(/^0\./, '.').replace(/^-0\./, '-.');
|
||||
|
||||
return {
|
||||
roundedStr: removeLeadingZero(number),
|
||||
rounded: number,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Elliptical arc large-arc and sweep flags are rendered with spaces
|
||||
* because many non-browser environments are not able to parse such paths
|
||||
*
|
||||
* @type {(
|
||||
* command: string,
|
||||
* args: number[],
|
||||
* precision?: number,
|
||||
* disableSpaceAfterFlags?: boolean
|
||||
* ) => string}
|
||||
* @param {string} command
|
||||
* @param {ReadonlyArray<number>} args
|
||||
* @param {number=} precision
|
||||
* @param {boolean=} disableSpaceAfterFlags
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => {
|
||||
let result = '';
|
||||
let prev = '';
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const number = args[i];
|
||||
const numberString = stringifyNumber(number, precision);
|
||||
let previous;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const { roundedStr, rounded } = roundAndStringify(args[i], precision);
|
||||
if (
|
||||
disableSpaceAfterFlags &&
|
||||
(command === 'A' || command === 'a') &&
|
||||
// consider combined arcs
|
||||
(i % 7 === 4 || i % 7 === 5)
|
||||
) {
|
||||
result += numberString;
|
||||
} else if (i === 0 || numberString.startsWith('-')) {
|
||||
result += roundedStr;
|
||||
} else if (i === 0 || rounded < 0) {
|
||||
// avoid space before first and negative numbers
|
||||
result += numberString;
|
||||
} else if (prev.includes('.') && numberString.startsWith('.')) {
|
||||
result += roundedStr;
|
||||
} else if (!Number.isInteger(previous) && !isDigit(roundedStr[0])) {
|
||||
// remove space before decimal with zero whole
|
||||
// only when previous number is also decimal
|
||||
result += numberString;
|
||||
result += roundedStr;
|
||||
} else {
|
||||
result += ` ${numberString}`;
|
||||
result += ` ${roundedStr}`;
|
||||
}
|
||||
prev = numberString;
|
||||
previous = rounded;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pathData: Array<PathDataItem>;
|
||||
* precision?: number;
|
||||
* disableSpaceAfterFlags?: boolean;
|
||||
* }} StringifyPathDataOptions
|
||||
* @param {StringifyPathDataOptions} options
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(options: StringifyPathDataOptions) => string}
|
||||
*/
|
||||
const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => {
|
||||
// combine sequence of the same commands
|
||||
let combined = [];
|
||||
for (let i = 0; i < pathData.length; i += 1) {
|
||||
const { command, args } = pathData[i];
|
||||
if (i === 0) {
|
||||
combined.push({ command, args });
|
||||
} else {
|
||||
/**
|
||||
* @type {PathDataItem}
|
||||
*/
|
||||
const last = combined[combined.length - 1];
|
||||
// match leading moveto with following lineto
|
||||
if (i === 1) {
|
||||
if (command === 'L') {
|
||||
last.command = 'M';
|
||||
}
|
||||
if (command === 'l') {
|
||||
last.command = 'm';
|
||||
}
|
||||
}
|
||||
if (
|
||||
(last.command === command &&
|
||||
last.command !== 'M' &&
|
||||
last.command !== 'm') ||
|
||||
// combine matching moveto and lineto sequences
|
||||
(last.command === 'M' && command === 'L') ||
|
||||
(last.command === 'm' && command === 'l')
|
||||
) {
|
||||
last.args = [...last.args, ...args];
|
||||
} else {
|
||||
combined.push({ command, args });
|
||||
}
|
||||
}
|
||||
export const stringifyPathData = ({
|
||||
pathData,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
}) => {
|
||||
if (pathData.length === 1) {
|
||||
const { command, args } = pathData[0];
|
||||
return (
|
||||
command + stringifyArgs(command, args, precision, disableSpaceAfterFlags)
|
||||
);
|
||||
}
|
||||
|
||||
let result = '';
|
||||
for (const { command, args } of combined) {
|
||||
result +=
|
||||
command + stringifyArgs(command, args, precision, disableSpaceAfterFlags);
|
||||
let prev = { ...pathData[0] };
|
||||
|
||||
// match leading moveto with following lineto
|
||||
if (pathData[1].command === 'L') {
|
||||
prev.command = 'M';
|
||||
} else if (pathData[1].command === 'l') {
|
||||
prev.command = 'm';
|
||||
}
|
||||
|
||||
for (let i = 1; i < pathData.length; i++) {
|
||||
const { command, args } = pathData[i];
|
||||
if (
|
||||
(prev.command === command &&
|
||||
prev.command !== 'M' &&
|
||||
prev.command !== 'm') ||
|
||||
// combine matching moveto and lineto sequences
|
||||
(prev.command === 'M' && command === 'L') ||
|
||||
(prev.command === 'm' && command === 'l')
|
||||
) {
|
||||
prev.args = [...prev.args, ...args];
|
||||
if (i === pathData.length - 1) {
|
||||
result +=
|
||||
prev.command +
|
||||
stringifyArgs(
|
||||
prev.command,
|
||||
prev.args,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
result +=
|
||||
prev.command +
|
||||
stringifyArgs(
|
||||
prev.command,
|
||||
prev.args,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
);
|
||||
|
||||
if (i === pathData.length - 1) {
|
||||
result +=
|
||||
command +
|
||||
stringifyArgs(command, args, precision, disableSpaceAfterFlags);
|
||||
} else {
|
||||
prev = { command, args };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
exports.stringifyPathData = stringifyPathData;
|
||||
|
||||
288
node_modules/svgo/lib/stringifier.js
generated
vendored
288
node_modules/svgo/lib/stringifier.js
generated
vendored
@@ -1,43 +1,23 @@
|
||||
'use strict';
|
||||
import { textElems } from '../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
* @typedef {import('./types').XastRoot} XastRoot
|
||||
* @typedef {import('./types').XastElement} XastElement
|
||||
* @typedef {import('./types').XastInstruction} XastInstruction
|
||||
* @typedef {import('./types').XastDoctype} XastDoctype
|
||||
* @typedef {import('./types').XastText} XastText
|
||||
* @typedef {import('./types').XastCdata} XastCdata
|
||||
* @typedef {import('./types').XastComment} XastComment
|
||||
* @typedef {import('./types').StringifyOptions} StringifyOptions
|
||||
*/
|
||||
|
||||
const { textElems } = require('../plugins/_collections.js');
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* width: void | string,
|
||||
* height: void | string,
|
||||
* indent: string,
|
||||
* textContext: null | XastElement,
|
||||
* indentLevel: number,
|
||||
* }} State
|
||||
* @typedef {Required<import('./types.js').StringifyOptions>} Options
|
||||
*
|
||||
* @typedef State
|
||||
* @property {string} indent
|
||||
* @property {?import('./types.js').XastElement} textContext
|
||||
* @property {number} indentLevel
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Required<StringifyOptions>} Options
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(char: string) => string}
|
||||
* @param {string} char
|
||||
* @returns {string}
|
||||
*/
|
||||
const encodeEntity = (char) => {
|
||||
return entities[char];
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {Options}
|
||||
*/
|
||||
/** @type {Options} */
|
||||
const defaults = {
|
||||
doctypeStart: '<!DOCTYPE',
|
||||
doctypeEnd: '>',
|
||||
@@ -60,16 +40,14 @@ const defaults = {
|
||||
indent: 4,
|
||||
regEntities: /[&'"<>]/g,
|
||||
regValEntities: /[&"<>]/g,
|
||||
encodeEntity: encodeEntity,
|
||||
encodeEntity,
|
||||
pretty: false,
|
||||
useShortTags: true,
|
||||
eol: 'lf',
|
||||
finalNewline: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {Record<string, string>}
|
||||
*/
|
||||
/** @type {Record<string, string>} */
|
||||
const entities = {
|
||||
'&': '&',
|
||||
"'": ''',
|
||||
@@ -79,20 +57,14 @@ const entities = {
|
||||
};
|
||||
|
||||
/**
|
||||
* convert XAST to SVG string
|
||||
* Converts XAST to SVG string.
|
||||
*
|
||||
* @type {(data: XastRoot, config: StringifyOptions) => {
|
||||
* data: string,
|
||||
* info: {
|
||||
* width: void | string,
|
||||
* height: void | string
|
||||
* }
|
||||
* }}
|
||||
* @param {import('./types.js').XastRoot} data
|
||||
* @param {import('./types.js').StringifyOptions=} userOptions
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifySvg = (data, userOptions = {}) => {
|
||||
/**
|
||||
* @type {Options}
|
||||
*/
|
||||
export const stringifySvg = (data, userOptions = {}) => {
|
||||
/** @type {Options} */
|
||||
const config = { ...defaults, ...userOptions };
|
||||
const indent = config.indent;
|
||||
let newIndent = ' ';
|
||||
@@ -101,13 +73,8 @@ const stringifySvg = (data, userOptions = {}) => {
|
||||
} else if (typeof indent === 'string') {
|
||||
newIndent = indent;
|
||||
}
|
||||
/**
|
||||
* @type {State}
|
||||
*/
|
||||
/** @type {State} */
|
||||
const state = {
|
||||
// TODO remove width and height in v3
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
indent: newIndent,
|
||||
textContext: null,
|
||||
indentLevel: 0,
|
||||
@@ -124,53 +91,52 @@ const stringifySvg = (data, userOptions = {}) => {
|
||||
config.textEnd += eol;
|
||||
}
|
||||
let svg = stringifyNode(data, config, state);
|
||||
if (config.finalNewline && svg.length > 0 && svg[svg.length - 1] !== '\n') {
|
||||
if (config.finalNewline && svg.length > 0 && !svg.endsWith('\n')) {
|
||||
svg += eol;
|
||||
}
|
||||
return {
|
||||
data: svg,
|
||||
info: {
|
||||
width: state.width,
|
||||
height: state.height,
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.stringifySvg = stringifySvg;
|
||||
|
||||
/**
|
||||
* @type {(node: XastParent, config: Options, state: State) => string}
|
||||
*/
|
||||
const stringifyNode = (data, config, state) => {
|
||||
let svg = '';
|
||||
state.indentLevel += 1;
|
||||
for (const item of data.children) {
|
||||
if (item.type === 'element') {
|
||||
svg += stringifyElement(item, config, state);
|
||||
}
|
||||
if (item.type === 'text') {
|
||||
svg += stringifyText(item, config, state);
|
||||
}
|
||||
if (item.type === 'doctype') {
|
||||
svg += stringifyDoctype(item, config);
|
||||
}
|
||||
if (item.type === 'instruction') {
|
||||
svg += stringifyInstruction(item, config);
|
||||
}
|
||||
if (item.type === 'comment') {
|
||||
svg += stringifyComment(item, config);
|
||||
}
|
||||
if (item.type === 'cdata') {
|
||||
svg += stringifyCdata(item, config, state);
|
||||
}
|
||||
}
|
||||
state.indentLevel -= 1;
|
||||
return svg;
|
||||
};
|
||||
|
||||
/**
|
||||
* create indent string in accordance with the current node level.
|
||||
* @param {import('./types.js').XastParent} data
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyNode = (data, config, state) => {
|
||||
let svg = '';
|
||||
state.indentLevel++;
|
||||
for (const item of data.children) {
|
||||
switch (item.type) {
|
||||
case 'element':
|
||||
svg += stringifyElement(item, config, state);
|
||||
break;
|
||||
case 'text':
|
||||
svg += stringifyText(item, config, state);
|
||||
break;
|
||||
case 'doctype':
|
||||
svg += stringifyDoctype(item, config);
|
||||
break;
|
||||
case 'instruction':
|
||||
svg += stringifyInstruction(item, config);
|
||||
break;
|
||||
case 'comment':
|
||||
svg += stringifyComment(item, config);
|
||||
break;
|
||||
case 'cdata':
|
||||
svg += stringifyCdata(item, config, state);
|
||||
}
|
||||
}
|
||||
state.indentLevel--;
|
||||
return svg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create indent string in accordance with the current node level.
|
||||
*
|
||||
* @type {(config: Options, state: State) => string}
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const createIndent = (config, state) => {
|
||||
let indent = '';
|
||||
@@ -181,14 +147,18 @@ const createIndent = (config, state) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastDoctype, config: Options) => string}
|
||||
* @param {import('./types.js').XastDoctype} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyDoctype = (node, config) => {
|
||||
return config.doctypeStart + node.data.doctype + config.doctypeEnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastInstruction, config: Options) => string}
|
||||
* @param {import('./types.js').XastInstruction} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyInstruction = (node, config) => {
|
||||
return (
|
||||
@@ -197,14 +167,19 @@ const stringifyInstruction = (node, config) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastComment, config: Options) => string}
|
||||
* @param {import('./types.js').XastComment} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyComment = (node, config) => {
|
||||
return config.commentStart + node.value + config.commentEnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastCdata, config: Options, state: State) => string}
|
||||
* @param {import('./types.js').XastCdata} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyCdata = (node, config, state) => {
|
||||
return (
|
||||
@@ -216,19 +191,12 @@ const stringifyCdata = (node, config, state) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastElement, config: Options, state: State) => string}
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyElement = (node, config, state) => {
|
||||
// beautiful injection for obtaining SVG information :)
|
||||
if (
|
||||
node.name === 'svg' &&
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null
|
||||
) {
|
||||
state.width = node.attributes.width;
|
||||
state.height = node.attributes.height;
|
||||
}
|
||||
|
||||
// empty element and short tag
|
||||
if (node.children.length === 0) {
|
||||
if (config.useShortTags) {
|
||||
@@ -239,82 +207,86 @@ const stringifyElement = (node, config, state) => {
|
||||
stringifyAttributes(node, config) +
|
||||
config.tagShortEnd
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
createIndent(config, state) +
|
||||
config.tagShortStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
config.tagOpenEnd +
|
||||
config.tagCloseStart +
|
||||
node.name +
|
||||
config.tagCloseEnd
|
||||
);
|
||||
}
|
||||
// non-empty element
|
||||
} else {
|
||||
let tagOpenStart = config.tagOpenStart;
|
||||
let tagOpenEnd = config.tagOpenEnd;
|
||||
let tagCloseStart = config.tagCloseStart;
|
||||
let tagCloseEnd = config.tagCloseEnd;
|
||||
let openIndent = createIndent(config, state);
|
||||
let closeIndent = createIndent(config, state);
|
||||
|
||||
if (state.textContext) {
|
||||
tagOpenStart = defaults.tagOpenStart;
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
tagCloseEnd = defaults.tagCloseEnd;
|
||||
openIndent = '';
|
||||
} else if (textElems.includes(node.name)) {
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
closeIndent = '';
|
||||
state.textContext = node;
|
||||
}
|
||||
|
||||
const children = stringifyNode(node, config, state);
|
||||
|
||||
if (state.textContext === node) {
|
||||
state.textContext = null;
|
||||
}
|
||||
|
||||
return (
|
||||
openIndent +
|
||||
tagOpenStart +
|
||||
createIndent(config, state) +
|
||||
config.tagShortStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
tagOpenEnd +
|
||||
children +
|
||||
closeIndent +
|
||||
tagCloseStart +
|
||||
config.tagOpenEnd +
|
||||
config.tagCloseStart +
|
||||
node.name +
|
||||
tagCloseEnd
|
||||
config.tagCloseEnd
|
||||
);
|
||||
}
|
||||
|
||||
// non-empty element
|
||||
let tagOpenStart = config.tagOpenStart;
|
||||
let tagOpenEnd = config.tagOpenEnd;
|
||||
let tagCloseStart = config.tagCloseStart;
|
||||
let tagCloseEnd = config.tagCloseEnd;
|
||||
let openIndent = createIndent(config, state);
|
||||
let closeIndent = createIndent(config, state);
|
||||
|
||||
if (state.textContext) {
|
||||
tagOpenStart = defaults.tagOpenStart;
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
tagCloseEnd = defaults.tagCloseEnd;
|
||||
openIndent = '';
|
||||
} else if (textElems.has(node.name)) {
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
closeIndent = '';
|
||||
state.textContext = node;
|
||||
}
|
||||
|
||||
const children = stringifyNode(node, config, state);
|
||||
|
||||
if (state.textContext === node) {
|
||||
state.textContext = null;
|
||||
}
|
||||
|
||||
return (
|
||||
openIndent +
|
||||
tagOpenStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
tagOpenEnd +
|
||||
children +
|
||||
closeIndent +
|
||||
tagCloseStart +
|
||||
node.name +
|
||||
tagCloseEnd
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastElement, config: Options) => string}
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyAttributes = (node, config) => {
|
||||
let attrs = '';
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
// TODO remove attributes without values support in v3
|
||||
attrs += ' ' + name;
|
||||
|
||||
if (value !== undefined) {
|
||||
const encodedValue = value
|
||||
.toString()
|
||||
.replace(config.regValEntities, config.encodeEntity);
|
||||
attrs += ' ' + name + config.attrStart + encodedValue + config.attrEnd;
|
||||
} else {
|
||||
attrs += ' ' + name;
|
||||
attrs += config.attrStart + encodedValue + config.attrEnd;
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastText, config: Options, state: State) => string}
|
||||
* @param {import('./types.js').XastText} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyText = (node, config, state) => {
|
||||
return (
|
||||
|
||||
263
node_modules/svgo/lib/style.js
generated
vendored
263
node_modules/svgo/lib/style.js
generated
vendored
@@ -1,83 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('css-tree').Rule} CsstreeRule
|
||||
* @typedef {import('./types').Specificity} Specificity
|
||||
* @typedef {import('./types').Stylesheet} Stylesheet
|
||||
* @typedef {import('./types').StylesheetRule} StylesheetRule
|
||||
* @typedef {import('./types').StylesheetDeclaration} StylesheetDeclaration
|
||||
* @typedef {import('./types').ComputedStyles} ComputedStyles
|
||||
* @typedef {import('./types').XastRoot} XastRoot
|
||||
* @typedef {import('./types').XastElement} XastElement
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
* @typedef {import('./types').XastChild} XastChild
|
||||
*/
|
||||
|
||||
const stable = require('stable');
|
||||
const csstree = require('css-tree');
|
||||
// @ts-ignore not defined in @types/csso
|
||||
const specificity = require('csso/lib/restructure/prepare/specificity');
|
||||
const { visit, matches } = require('./xast.js');
|
||||
const {
|
||||
import * as csstree from 'css-tree';
|
||||
import * as csswhat from 'css-what';
|
||||
import { syntax } from 'csso';
|
||||
import { matches } from './xast.js';
|
||||
import { visit } from './util/visit.js';
|
||||
import {
|
||||
attrsGroups,
|
||||
inheritableAttrs,
|
||||
presentationNonInheritableGroupAttrs,
|
||||
} = require('../plugins/_collections.js');
|
||||
} from '../plugins/_collections.js';
|
||||
|
||||
// @ts-ignore not defined in @types/csstree
|
||||
const csstreeWalkSkip = csstree.walk.skip;
|
||||
|
||||
/**
|
||||
* @type {(ruleNode: CsstreeRule, dynamic: boolean) => StylesheetRule}
|
||||
* @param {import('css-tree').Rule} ruleNode
|
||||
* @param {boolean} dynamic
|
||||
* @returns {import('./types.js').StylesheetRule[]}
|
||||
*/
|
||||
const parseRule = (ruleNode, dynamic) => {
|
||||
let selectors;
|
||||
let selectorsSpecificity;
|
||||
/**
|
||||
* @type {Array<StylesheetDeclaration>}
|
||||
*/
|
||||
/** @type {import('./types.js').StylesheetDeclaration[]} */
|
||||
const declarations = [];
|
||||
csstree.walk(ruleNode, (cssNode) => {
|
||||
if (cssNode.type === 'SelectorList') {
|
||||
// compute specificity from original node to consider pseudo classes
|
||||
selectorsSpecificity = specificity(cssNode);
|
||||
const newSelectorsNode = csstree.clone(cssNode);
|
||||
csstree.walk(newSelectorsNode, (pseudoClassNode, item, list) => {
|
||||
if (pseudoClassNode.type === 'PseudoClassSelector') {
|
||||
dynamic = true;
|
||||
list.remove(item);
|
||||
}
|
||||
});
|
||||
selectors = csstree.generate(newSelectorsNode);
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
// collect declarations
|
||||
ruleNode.block.children.forEach((cssNode) => {
|
||||
if (cssNode.type === 'Declaration') {
|
||||
declarations.push({
|
||||
name: cssNode.property,
|
||||
value: csstree.generate(cssNode.value),
|
||||
important: cssNode.important === true,
|
||||
});
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
});
|
||||
if (selectors == null || selectorsSpecificity == null) {
|
||||
throw Error('assert');
|
||||
}
|
||||
return {
|
||||
dynamic,
|
||||
selectors,
|
||||
specificity: selectorsSpecificity,
|
||||
declarations,
|
||||
};
|
||||
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
csstree.walk(ruleNode.prelude, (node) => {
|
||||
if (node.type === 'Selector') {
|
||||
const newNode = csstree.clone(node);
|
||||
let hasPseudoClasses = false;
|
||||
csstree.walk(newNode, (pseudoClassNode, item, list) => {
|
||||
if (pseudoClassNode.type === 'PseudoClassSelector') {
|
||||
hasPseudoClasses = true;
|
||||
list.remove(item);
|
||||
}
|
||||
});
|
||||
rules.push({
|
||||
specificity: syntax.specificity(node),
|
||||
dynamic: hasPseudoClasses || dynamic,
|
||||
// compute specificity from original node to consider pseudo classes
|
||||
selector: csstree.generate(newNode),
|
||||
declarations,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return rules;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(css: string, dynamic: boolean) => Array<StylesheetRule>}
|
||||
* @param {string} css
|
||||
* @param {boolean} dynamic
|
||||
* @returns {import('./types.js').StylesheetRule[]}
|
||||
*/
|
||||
const parseStylesheet = (css, dynamic) => {
|
||||
/**
|
||||
* @type {Array<StylesheetRule>}
|
||||
*/
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
const ast = csstree.parse(css, {
|
||||
parseValue: false,
|
||||
@@ -85,16 +69,23 @@ const parseStylesheet = (css, dynamic) => {
|
||||
});
|
||||
csstree.walk(ast, (cssNode) => {
|
||||
if (cssNode.type === 'Rule') {
|
||||
rules.push(parseRule(cssNode, dynamic || false));
|
||||
rules.push(...parseRule(cssNode, dynamic || false));
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
if (cssNode.type === 'Atrule') {
|
||||
if (cssNode.name === 'keyframes') {
|
||||
if (
|
||||
[
|
||||
'keyframes',
|
||||
'-webkit-keyframes',
|
||||
'-o-keyframes',
|
||||
'-moz-keyframes',
|
||||
].includes(cssNode.name)
|
||||
) {
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
csstree.walk(cssNode, (ruleNode) => {
|
||||
if (ruleNode.type === 'Rule') {
|
||||
rules.push(parseRule(ruleNode, dynamic || true));
|
||||
rules.push(...parseRule(ruleNode, dynamic || true));
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
});
|
||||
@@ -105,12 +96,11 @@ const parseStylesheet = (css, dynamic) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(css: string) => Array<StylesheetDeclaration>}
|
||||
* @param {string} css
|
||||
* @returns {import('./types.js').StylesheetDeclaration[]}
|
||||
*/
|
||||
const parseStyleDeclarations = (css) => {
|
||||
/**
|
||||
* @type {Array<StylesheetDeclaration>}
|
||||
*/
|
||||
/** @type {import('./types.js').StylesheetDeclaration[]} */
|
||||
const declarations = [];
|
||||
const ast = csstree.parse(css, {
|
||||
context: 'declarationList',
|
||||
@@ -129,26 +119,27 @@ const parseStyleDeclarations = (css) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
|
||||
* @param {import('./types.js').Stylesheet} stylesheet
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {import('./types.js').ComputedStyles}
|
||||
*/
|
||||
const computeOwnStyle = (stylesheet, node) => {
|
||||
/**
|
||||
* @type {ComputedStyles}
|
||||
*/
|
||||
const computeOwnStyle = (stylesheet, node, parents) => {
|
||||
/** @type {import('./types.js').ComputedStyles} */
|
||||
const computedStyle = {};
|
||||
const importantStyles = new Map();
|
||||
|
||||
// collect attributes
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (attrsGroups.presentation.includes(name)) {
|
||||
if (attrsGroups.presentation.has(name)) {
|
||||
computedStyle[name] = { type: 'static', inherited: false, value };
|
||||
importantStyles.set(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
// collect matching rules
|
||||
for (const { selectors, declarations, dynamic } of stylesheet.rules) {
|
||||
if (matches(node, selectors)) {
|
||||
for (const { selector, declarations, dynamic } of stylesheet.rules) {
|
||||
if (matches(node, selector, parents)) {
|
||||
for (const { name, value, important } of declarations) {
|
||||
const computed = computedStyle[name];
|
||||
if (computed && computed.type === 'dynamic') {
|
||||
@@ -194,13 +185,15 @@ const computeOwnStyle = (stylesheet, node) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two selector specificities.
|
||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||
* Compares selector specificities.
|
||||
* Derived from https://github.com/keeganstreet/specificity/blob/8757133ddd2ed0163f120900047ff0f92760b536/specificity.js#L207
|
||||
*
|
||||
* @type {(a: Specificity, b: Specificity) => number}
|
||||
* @param {import('./types.js').Specificity} a
|
||||
* @param {import('./types.js').Specificity} b
|
||||
* @returns {number}
|
||||
*/
|
||||
const compareSpecificity = (a, b) => {
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
export const compareSpecificity = (a, b) => {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
if (a[i] < b[i]) {
|
||||
return -1;
|
||||
} else if (a[i] > b[i]) {
|
||||
@@ -212,36 +205,35 @@ const compareSpecificity = (a, b) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(root: XastRoot) => Stylesheet}
|
||||
* @param {import('./types.js').XastRoot} root
|
||||
* @returns {import('./types.js').Stylesheet}
|
||||
*/
|
||||
const collectStylesheet = (root) => {
|
||||
/**
|
||||
* @type {Array<StylesheetRule>}
|
||||
*/
|
||||
export const collectStylesheet = (root) => {
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
/**
|
||||
* @type {Map<XastElement, XastParent>}
|
||||
*/
|
||||
/** @type {Map<import('./types.js').XastElement, import('./types.js').XastParent>} */
|
||||
const parents = new Map();
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// store parents
|
||||
parents.set(node, parentNode);
|
||||
// find and parse all styles
|
||||
if (node.name === 'style') {
|
||||
|
||||
if (node.name !== 'style') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
node.attributes.type == null ||
|
||||
node.attributes.type === '' ||
|
||||
node.attributes.type === 'text/css'
|
||||
) {
|
||||
const dynamic =
|
||||
node.attributes.media != null && node.attributes.media !== 'all';
|
||||
if (
|
||||
node.attributes.type == null ||
|
||||
node.attributes.type === '' ||
|
||||
node.attributes.type === 'text/css'
|
||||
) {
|
||||
const children = node.children;
|
||||
for (const child of children) {
|
||||
if (child.type === 'text' || child.type === 'cdata') {
|
||||
rules.push(...parseStylesheet(child.value, dynamic));
|
||||
}
|
||||
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'text' || child.type === 'cdata') {
|
||||
rules.push(...parseStylesheet(child.value, dynamic));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,29 +241,26 @@ const collectStylesheet = (root) => {
|
||||
},
|
||||
});
|
||||
// sort by selectors specificity
|
||||
stable.inplace(rules, (a, b) =>
|
||||
compareSpecificity(a.specificity, b.specificity)
|
||||
);
|
||||
rules.sort((a, b) => compareSpecificity(a.specificity, b.specificity));
|
||||
return { rules, parents };
|
||||
};
|
||||
exports.collectStylesheet = collectStylesheet;
|
||||
|
||||
/**
|
||||
* @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
|
||||
* @param {import('./types.js').Stylesheet} stylesheet
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @returns {import('./types.js').ComputedStyles}
|
||||
*/
|
||||
const computeStyle = (stylesheet, node) => {
|
||||
export const computeStyle = (stylesheet, node) => {
|
||||
const { parents } = stylesheet;
|
||||
// collect inherited styles
|
||||
const computedStyles = computeOwnStyle(stylesheet, node);
|
||||
const computedStyles = computeOwnStyle(stylesheet, node, parents);
|
||||
let parent = parents.get(node);
|
||||
while (parent != null && parent.type !== 'root') {
|
||||
const inheritedStyles = computeOwnStyle(stylesheet, parent);
|
||||
const inheritedStyles = computeOwnStyle(stylesheet, parent, parents);
|
||||
for (const [name, computed] of Object.entries(inheritedStyles)) {
|
||||
if (
|
||||
computedStyles[name] == null &&
|
||||
// ignore not inheritable styles
|
||||
inheritableAttrs.includes(name) === true &&
|
||||
presentationNonInheritableGroupAttrs.includes(name) === false
|
||||
inheritableAttrs.has(name) &&
|
||||
!presentationNonInheritableGroupAttrs.has(name)
|
||||
) {
|
||||
computedStyles[name] = { ...computed, inherited: true };
|
||||
}
|
||||
@@ -280,4 +269,56 @@ const computeStyle = (stylesheet, node) => {
|
||||
}
|
||||
return computedStyles;
|
||||
};
|
||||
exports.computeStyle = computeStyle;
|
||||
|
||||
/**
|
||||
* Determines if the CSS selector includes or traverses the given attribute.
|
||||
*
|
||||
* Classes and IDs are generated as attribute selectors, so you can check for if
|
||||
* a `.class` or `#id` is included by passing `name=class` or `name=id`
|
||||
* respectively.
|
||||
*
|
||||
* @param {csstree.ListItem<csstree.CssNode> | string} selector
|
||||
* @param {string} name
|
||||
* @param {?string} value
|
||||
* @param {boolean} traversed
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const includesAttrSelector = (
|
||||
selector,
|
||||
name,
|
||||
value = null,
|
||||
traversed = false,
|
||||
) => {
|
||||
const selectors =
|
||||
typeof selector === 'string'
|
||||
? csswhat.parse(selector)
|
||||
: csswhat.parse(csstree.generate(selector.data));
|
||||
|
||||
for (const subselector of selectors) {
|
||||
const hasAttrSelector = subselector.some((segment, index) => {
|
||||
if (traversed) {
|
||||
if (index === subselector.length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isNextTraversal = csswhat.isTraversal(subselector[index + 1]);
|
||||
|
||||
if (!isNextTraversal) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (segment.type !== 'attribute' || segment.name !== name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value == null ? true : segment.value === value;
|
||||
});
|
||||
|
||||
if (hasAttrSelector) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
96
node_modules/svgo/lib/svgo-node.js
generated
vendored
96
node_modules/svgo/lib/svgo-node.js
generated
vendored
@@ -1,100 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const { pathToFileURL } = require('url');
|
||||
const path = require('path');
|
||||
const {
|
||||
extendDefaultPlugins,
|
||||
optimize: optimizeAgnostic,
|
||||
createContentItem,
|
||||
} = require('./svgo.js');
|
||||
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
exports.createContentItem = createContentItem;
|
||||
import os from 'os';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import * as svgo from './svgo.js';
|
||||
import url from 'url';
|
||||
|
||||
/**
|
||||
* @param {string} configFile
|
||||
* @returns {Promise<import('./types.js').Config>}
|
||||
*/
|
||||
const importConfig = async (configFile) => {
|
||||
let config;
|
||||
// at the moment dynamic import may randomly fail with segfault
|
||||
// to workaround this for some users .cjs extension is loaded
|
||||
// exclusively with require
|
||||
if (configFile.endsWith('.cjs')) {
|
||||
config = require(configFile);
|
||||
} else {
|
||||
try {
|
||||
// dynamic import expects file url instead of path and may fail
|
||||
// when windows path is provided
|
||||
const { default: imported } = await import(pathToFileURL(configFile));
|
||||
config = imported;
|
||||
} catch (importError) {
|
||||
// TODO remove require in v3
|
||||
try {
|
||||
config = require(configFile);
|
||||
} catch (requireError) {
|
||||
// throw original error if es module is detected
|
||||
if (requireError.code === 'ERR_REQUIRE_ESM') {
|
||||
throw importError;
|
||||
} else {
|
||||
throw requireError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const resolvedPath = path.resolve(configFile);
|
||||
const imported = await import(url.pathToFileURL(resolvedPath).toString());
|
||||
const config = imported.default;
|
||||
|
||||
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
|
||||
throw Error(`Invalid config file "${configFile}"`);
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
const isFile = async (file) => {
|
||||
try {
|
||||
const stats = await fs.promises.stat(file);
|
||||
const stats = await fs.stat(file);
|
||||
return stats.isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadConfig = async (configFile, cwd = process.cwd()) => {
|
||||
export * from './svgo.js';
|
||||
|
||||
/**
|
||||
* If you write a tool on top of svgo you might need a way to load svgo config.
|
||||
* You can also specify relative or absolute path and customize current working
|
||||
* directory.
|
||||
*
|
||||
* @type {<T extends string | null>(configFile?: T, cwd?: string) => Promise<T extends string ? import('./svgo.js').Config : import('./svgo.js').Config | null>}
|
||||
*/
|
||||
export const loadConfig = async (configFile, cwd = process.cwd()) => {
|
||||
if (configFile != null) {
|
||||
if (path.isAbsolute(configFile)) {
|
||||
return await importConfig(configFile);
|
||||
return importConfig(configFile);
|
||||
} else {
|
||||
return await importConfig(path.join(cwd, configFile));
|
||||
return importConfig(path.join(cwd, configFile));
|
||||
}
|
||||
}
|
||||
let dir = cwd;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
|
||||
while (true) {
|
||||
const js = path.join(dir, 'svgo.config.js');
|
||||
if (await isFile(js)) {
|
||||
return await importConfig(js);
|
||||
return importConfig(js);
|
||||
}
|
||||
const mjs = path.join(dir, 'svgo.config.mjs');
|
||||
if (await isFile(mjs)) {
|
||||
return await importConfig(mjs);
|
||||
return importConfig(mjs);
|
||||
}
|
||||
const cjs = path.join(dir, 'svgo.config.cjs');
|
||||
if (await isFile(cjs)) {
|
||||
return await importConfig(cjs);
|
||||
return importConfig(cjs);
|
||||
}
|
||||
const parent = path.dirname(dir);
|
||||
if (dir === parent) {
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/33912
|
||||
return null;
|
||||
}
|
||||
dir = parent;
|
||||
}
|
||||
};
|
||||
exports.loadConfig = loadConfig;
|
||||
|
||||
const optimize = (input, config) => {
|
||||
/**
|
||||
* The core of SVGO.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {import('./svgo.js').Config=} config
|
||||
* @returns {import('./svgo.js').Output}
|
||||
*/
|
||||
export const optimize = (input, config) => {
|
||||
if (config == null) {
|
||||
config = {};
|
||||
}
|
||||
if (typeof config !== 'object') {
|
||||
throw Error('Config should be an object');
|
||||
}
|
||||
return optimizeAgnostic(input, {
|
||||
return svgo.optimize(input, {
|
||||
...config,
|
||||
js2svg: {
|
||||
// platform specific default for end of line
|
||||
@@ -103,4 +96,3 @@ const optimize = (input, config) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
exports.optimize = optimize;
|
||||
|
||||
166
node_modules/svgo/lib/svgo.js
generated
vendored
166
node_modules/svgo/lib/svgo.js
generated
vendored
@@ -1,19 +1,84 @@
|
||||
'use strict';
|
||||
import { builtinPlugins } from './builtin.js';
|
||||
import { encodeSVGDatauri } from './svgo/tools.js';
|
||||
import { invokePlugins } from './svgo/plugins.js';
|
||||
import { querySelector, querySelectorAll } from './xast.js';
|
||||
import { mapNodesToParents } from './util/map-nodes-to-parents.js';
|
||||
import { parseSvg } from './parser.js';
|
||||
import { stringifySvg } from './stringifier.js';
|
||||
import { VERSION } from './version.js';
|
||||
import * as _collections from '../plugins/_collections.js';
|
||||
|
||||
const {
|
||||
defaultPlugins,
|
||||
resolvePluginConfig,
|
||||
extendDefaultPlugins,
|
||||
} = require('./svgo/config.js');
|
||||
const { parseSvg } = require('./parser.js');
|
||||
const { stringifySvg } = require('./stringifier.js');
|
||||
const { invokePlugins } = require('./svgo/plugins.js');
|
||||
const JSAPI = require('./svgo/jsAPI.js');
|
||||
const { encodeSVGDatauri } = require('./svgo/tools.js');
|
||||
const pluginsMap = new Map();
|
||||
for (const plugin of builtinPlugins) {
|
||||
pluginsMap.set(plugin.name, plugin);
|
||||
}
|
||||
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {import('./types.js').BuiltinPluginOrPreset<?, ?>}
|
||||
*/
|
||||
function getPlugin(name) {
|
||||
if (name === 'removeScriptElement') {
|
||||
console.warn(
|
||||
'Warning: removeScriptElement has been renamed to removeScripts, please update your SVGO config',
|
||||
);
|
||||
return pluginsMap.get('removeScripts');
|
||||
}
|
||||
|
||||
const optimize = (input, config) => {
|
||||
return pluginsMap.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | import('./types.js').PluginConfig} plugin
|
||||
* @returns {?import('./types.js').PluginConfig}
|
||||
*/
|
||||
const resolvePluginConfig = (plugin) => {
|
||||
if (typeof plugin === 'string') {
|
||||
// resolve builtin plugin specified as string
|
||||
const builtinPlugin = getPlugin(plugin);
|
||||
if (builtinPlugin == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin}" specified.`);
|
||||
}
|
||||
return {
|
||||
name: plugin,
|
||||
params: {},
|
||||
fn: builtinPlugin.fn,
|
||||
};
|
||||
}
|
||||
if (typeof plugin === 'object' && plugin != null) {
|
||||
if (plugin.name == null) {
|
||||
throw Error(`Plugin name must be specified`);
|
||||
}
|
||||
// use custom plugin implementation
|
||||
// @ts-expect-error Checking for CustomPlugin with the presence of fn
|
||||
let fn = plugin.fn;
|
||||
if (fn == null) {
|
||||
// resolve builtin plugin implementation
|
||||
const builtinPlugin = getPlugin(plugin.name);
|
||||
if (builtinPlugin == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin.name}" specified.`);
|
||||
}
|
||||
fn = builtinPlugin.fn;
|
||||
}
|
||||
return {
|
||||
name: plugin.name,
|
||||
params: plugin.params,
|
||||
fn,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export * from './types.js';
|
||||
|
||||
/**
|
||||
* The core of SVGO.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {import('./types.js').Config=} config
|
||||
* @returns {import('./types.js').Output}
|
||||
*/
|
||||
export const optimize = (input, config) => {
|
||||
if (config == null) {
|
||||
config = {};
|
||||
}
|
||||
@@ -22,62 +87,57 @@ const optimize = (input, config) => {
|
||||
}
|
||||
const maxPassCount = config.multipass ? 10 : 1;
|
||||
let prevResultSize = Number.POSITIVE_INFINITY;
|
||||
let svgjs = null;
|
||||
let output = '';
|
||||
const info = {};
|
||||
if (config.path != null) {
|
||||
info.path = config.path;
|
||||
}
|
||||
for (let i = 0; i < maxPassCount; i += 1) {
|
||||
info.multipassCount = i;
|
||||
// TODO throw this error in v3
|
||||
try {
|
||||
svgjs = parseSvg(input, config.path);
|
||||
} catch (error) {
|
||||
return { error: error.toString(), modernError: error };
|
||||
}
|
||||
if (svgjs.error != null) {
|
||||
if (config.path != null) {
|
||||
svgjs.path = config.path;
|
||||
}
|
||||
return svgjs;
|
||||
}
|
||||
const plugins = config.plugins || defaultPlugins;
|
||||
if (Array.isArray(plugins) === false) {
|
||||
const ast = parseSvg(input, config.path);
|
||||
const plugins = config.plugins || ['preset-default'];
|
||||
if (!Array.isArray(plugins)) {
|
||||
throw Error(
|
||||
"Invalid plugins list. Provided 'plugins' in config should be an array."
|
||||
'malformed config, `plugins` property must be an array.\nSee more info here: https://github.com/svg/svgo#configuration',
|
||||
);
|
||||
}
|
||||
const resolvedPlugins = plugins.map(resolvePluginConfig);
|
||||
const resolvedPlugins = plugins
|
||||
.filter((plugin) => plugin != null)
|
||||
.map(resolvePluginConfig);
|
||||
|
||||
if (resolvedPlugins.length < plugins.length) {
|
||||
console.warn(
|
||||
'Warning: plugins list includes null or undefined elements, these will be ignored.',
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {import('./types.js').Config} */
|
||||
const globalOverrides = {};
|
||||
if (config.floatPrecision != null) {
|
||||
globalOverrides.floatPrecision = config.floatPrecision;
|
||||
}
|
||||
svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides);
|
||||
svgjs = stringifySvg(svgjs, config.js2svg);
|
||||
if (svgjs.data.length < prevResultSize) {
|
||||
input = svgjs.data;
|
||||
prevResultSize = svgjs.data.length;
|
||||
invokePlugins(ast, info, resolvedPlugins, null, globalOverrides);
|
||||
output = stringifySvg(ast, config.js2svg);
|
||||
if (output.length < prevResultSize) {
|
||||
input = output;
|
||||
prevResultSize = output.length;
|
||||
} else {
|
||||
if (config.datauri) {
|
||||
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
|
||||
}
|
||||
if (config.path != null) {
|
||||
svgjs.path = config.path;
|
||||
}
|
||||
return svgjs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return svgjs;
|
||||
if (config.datauri) {
|
||||
output = encodeSVGDatauri(output, config.datauri);
|
||||
}
|
||||
return {
|
||||
data: output,
|
||||
};
|
||||
};
|
||||
exports.optimize = optimize;
|
||||
|
||||
/**
|
||||
* The factory that creates a content item with the helper methods.
|
||||
*
|
||||
* @param {Object} data which is passed to jsAPI constructor
|
||||
* @returns {JSAPI} content item
|
||||
*/
|
||||
const createContentItem = (data) => {
|
||||
return new JSAPI(data);
|
||||
export {
|
||||
VERSION,
|
||||
builtinPlugins,
|
||||
mapNodesToParents,
|
||||
querySelector,
|
||||
querySelectorAll,
|
||||
_collections,
|
||||
};
|
||||
exports.createContentItem = createContentItem;
|
||||
|
||||
330
node_modules/svgo/lib/svgo/coa.js
generated
vendored
330
node_modules/svgo/lib/svgo/coa.js
generated
vendored
@@ -1,99 +1,100 @@
|
||||
'use strict';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import colors from 'picocolors';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { decodeSVGDatauri, encodeSVGDatauri } from './tools.js';
|
||||
import { loadConfig, optimize } from '../svgo-node.js';
|
||||
import { builtinPlugins } from '../builtin.js';
|
||||
import { SvgoParserError } from '../parser.js';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const colors = require('picocolors');
|
||||
const { loadConfig, optimize } = require('../svgo-node.js');
|
||||
const pluginsMap = require('../../plugins/plugins.js');
|
||||
const PKG = require('../../package.json');
|
||||
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
|
||||
|
||||
const regSVGFile = /\.svg$/i;
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const pkgPath = path.join(__dirname, '../../package.json');
|
||||
const PKG = JSON.parse(await fs.promises.readFile(pkgPath, 'utf-8'));
|
||||
|
||||
/**
|
||||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT.
|
||||
* @param {string} path
|
||||
*
|
||||
* @param {string} filePath
|
||||
*/
|
||||
function checkIsDir(path) {
|
||||
export function checkIsDir(filePath) {
|
||||
try {
|
||||
return fs.lstatSync(path).isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
return fs.lstatSync(filePath).isDirectory();
|
||||
} catch {
|
||||
return filePath.endsWith(path.sep);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function makeProgram(program) {
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
*/
|
||||
export default function makeProgram(program) {
|
||||
program
|
||||
.name(PKG.name)
|
||||
.description(PKG.description, {
|
||||
INPUT: 'Alias to --input',
|
||||
})
|
||||
.description(PKG.description)
|
||||
.version(PKG.version, '-v, --version')
|
||||
.arguments('[INPUT...]')
|
||||
.argument('[INPUT...]', 'Alias to --input')
|
||||
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
|
||||
.option('-s, --string <STRING>', 'Input SVG data string')
|
||||
.option(
|
||||
'-f, --folder <FOLDER>',
|
||||
'Input folder, optimize and rewrite all *.svg files'
|
||||
'Input folder, optimize and rewrite all *.svg files',
|
||||
)
|
||||
.option(
|
||||
'-o, --output <OUTPUT...>',
|
||||
'Output file or folder (by default the same as the input), "-" for STDOUT'
|
||||
'Output file or folder (by default the same as the input), "-" for STDOUT',
|
||||
)
|
||||
.option(
|
||||
'-p, --precision <INTEGER>',
|
||||
'Set number of digits in the fractional part, overrides plugins params'
|
||||
'Set number of digits in the fractional part, overrides plugins params',
|
||||
)
|
||||
.option(
|
||||
'--config <CONFIG>',
|
||||
'Custom config file, only .js, .mjs, and .cjs is supported',
|
||||
)
|
||||
.option('--config <CONFIG>', 'Custom config file, only .js is supported')
|
||||
.option(
|
||||
'--datauri <FORMAT>',
|
||||
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)'
|
||||
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)',
|
||||
)
|
||||
.option(
|
||||
'--multipass',
|
||||
'Pass over SVGs multiple times to ensure all optimizations are applied'
|
||||
'Pass over SVGs multiple times to ensure all optimizations are applied',
|
||||
)
|
||||
.option('--pretty', 'Make SVG pretty printed')
|
||||
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
|
||||
.option(
|
||||
'--eol <EOL>',
|
||||
'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.'
|
||||
'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.',
|
||||
)
|
||||
.option('--final-newline', 'Ensure SVG ends with a line break')
|
||||
.option(
|
||||
'-r, --recursive',
|
||||
"Use with '--folder'. Optimizes *.svg files in folders recursively."
|
||||
"Use with '--folder'. Optimizes *.svg files in folders recursively.",
|
||||
)
|
||||
.option(
|
||||
'--exclude <PATTERN...>',
|
||||
"Use with '--folder'. Exclude files matching regular expression pattern."
|
||||
"Use with '--folder'. Exclude files matching regular expression pattern.",
|
||||
)
|
||||
.option(
|
||||
'-q, --quiet',
|
||||
'Only output error messages, not regular status messages'
|
||||
'Only output error messages, not regular status messages',
|
||||
)
|
||||
.option('--show-plugins', 'Show available plugins and exit')
|
||||
// used by picocolors internally
|
||||
.option('--no-color', 'Output plain text without color')
|
||||
.action(action);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<string>} args
|
||||
* @param {any} opts
|
||||
* @param {import('commander').Command} command
|
||||
* @returns
|
||||
*/
|
||||
async function action(args, opts, command) {
|
||||
var input = opts.input || args;
|
||||
var output = opts.output;
|
||||
var config = {};
|
||||
|
||||
if (opts.precision != null) {
|
||||
const number = Number.parseInt(opts.precision, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '-p, --precision' argument must be an integer number"
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
opts.precision = number;
|
||||
}
|
||||
}
|
||||
const input = opts.input || args;
|
||||
let output = opts.output;
|
||||
/** @type {any} */
|
||||
let config = {};
|
||||
|
||||
if (opts.datauri != null) {
|
||||
if (
|
||||
@@ -102,7 +103,7 @@ async function action(args, opts, command) {
|
||||
opts.datauri !== 'unenc'
|
||||
) {
|
||||
console.error(
|
||||
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'"
|
||||
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -112,7 +113,7 @@ async function action(args, opts, command) {
|
||||
const number = Number.parseInt(opts.indent, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '--indent' argument must be an integer number"
|
||||
"error: option '--indent' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
@@ -122,7 +123,7 @@ async function action(args, opts, command) {
|
||||
|
||||
if (opts.eol != null && opts.eol !== 'lf' && opts.eol !== 'crlf') {
|
||||
console.error(
|
||||
"error: option '--eol' must have one of the following values: 'lf' or 'crlf'"
|
||||
"error: option '--eol' must have one of the following values: 'lf' or 'crlf'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -144,17 +145,12 @@ async function action(args, opts, command) {
|
||||
return command.help();
|
||||
}
|
||||
|
||||
if (
|
||||
typeof process == 'object' &&
|
||||
process.versions &&
|
||||
process.versions.node &&
|
||||
PKG &&
|
||||
PKG.engines.node
|
||||
) {
|
||||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
||||
if (process?.versions?.node && PKG.engines.node) {
|
||||
// @ts-expect-error We control this and ensure it is never null.
|
||||
const nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
||||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
|
||||
throw Error(
|
||||
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`
|
||||
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -177,13 +173,20 @@ async function action(args, opts, command) {
|
||||
|
||||
// --exclude
|
||||
config.exclude = opts.exclude
|
||||
? opts.exclude.map((pattern) => RegExp(pattern))
|
||||
? opts.exclude.map((/** @type {string} */ pattern) => RegExp(pattern))
|
||||
: [];
|
||||
|
||||
// --precision
|
||||
if (opts.precision != null) {
|
||||
var precision = Math.min(Math.max(0, opts.precision), 20);
|
||||
config.floatPrecision = precision;
|
||||
const number = Number.parseInt(opts.precision, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '-p, --precision' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
config.floatPrecision = Math.min(Math.max(0, number), 20);
|
||||
}
|
||||
}
|
||||
|
||||
// --multipass
|
||||
@@ -216,8 +219,8 @@ async function action(args, opts, command) {
|
||||
if (output) {
|
||||
if (input.length && input[0] != '-') {
|
||||
if (output.length == 1 && checkIsDir(output[0])) {
|
||||
var dir = output[0];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
const dir = output[0];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output[i] = checkIsDir(input[i])
|
||||
? input[i]
|
||||
: path.resolve(dir, path.basename(input[i]));
|
||||
@@ -238,8 +241,8 @@ async function action(args, opts, command) {
|
||||
|
||||
// --folder
|
||||
if (opts.folder) {
|
||||
var ouputFolder = (output && output[0]) || opts.folder;
|
||||
await optimizeFolder(config, opts.folder, ouputFolder);
|
||||
const outputFolder = (output && output[0]) || opts.folder;
|
||||
await optimizeFolder(config, opts.folder, outputFolder);
|
||||
}
|
||||
|
||||
// --input
|
||||
@@ -247,39 +250,39 @@ async function action(args, opts, command) {
|
||||
// STDIN
|
||||
if (input[0] === '-') {
|
||||
return new Promise((resolve, reject) => {
|
||||
var data = '',
|
||||
file = output[0];
|
||||
let data = '';
|
||||
const file = output[0];
|
||||
|
||||
process.stdin
|
||||
.on('data', (chunk) => (data += chunk))
|
||||
.once('end', () =>
|
||||
processSVGData(config, { input: 'string' }, data, file).then(
|
||||
resolve,
|
||||
reject
|
||||
)
|
||||
processSVGData(config, null, data, file).then(resolve, reject),
|
||||
);
|
||||
});
|
||||
// file
|
||||
} else {
|
||||
await Promise.all(
|
||||
input.map((file, n) => optimizeFile(config, file, output[n]))
|
||||
input.map((/** @type {string} */ file, /** @type {number} */ n) =>
|
||||
optimizeFile(config, file, output[n]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --string
|
||||
} else if (opts.string) {
|
||||
var data = decodeSVGDatauri(opts.string);
|
||||
const data = decodeSVGDatauri(opts.string);
|
||||
|
||||
return processSVGData(config, { input: 'string' }, data, output[0]);
|
||||
return processSVGData(config, null, data, output[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG files in a directory.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function optimizeFolder(config, dir, output) {
|
||||
if (!config.quiet) {
|
||||
@@ -292,15 +295,16 @@ function optimizeFolder(config, dir, output) {
|
||||
|
||||
/**
|
||||
* Process given files, take only SVG.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {ReadonlyArray<string>} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function processDirectory(config, dir, files, output) {
|
||||
// take only *.svg files, recursively if necessary
|
||||
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
const svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
|
||||
return svgFilesDescriptions.length
|
||||
? Promise.all(
|
||||
@@ -308,93 +312,105 @@ function processDirectory(config, dir, files, output) {
|
||||
optimizeFile(
|
||||
config,
|
||||
fileDescription.inputPath,
|
||||
fileDescription.outputPath
|
||||
)
|
||||
)
|
||||
fileDescription.outputPath,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Promise.reject(
|
||||
new Error(`No SVG files have been found in '${dir}' directory.`)
|
||||
new Error(`No SVG files have been found in '${dir}' directory.`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get svg files descriptions
|
||||
* @param {Object} config options
|
||||
* Get SVG files descriptions.
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {ReadonlyArray<string>} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Array}
|
||||
* @return {any[]}
|
||||
*/
|
||||
function getFilesDescriptions(config, dir, files, output) {
|
||||
const filesInThisFolder = files
|
||||
.filter(
|
||||
(name) =>
|
||||
regSVGFile.test(name) &&
|
||||
!config.exclude.some((regExclude) => regExclude.test(name))
|
||||
name.slice(-4).toLowerCase() === '.svg' &&
|
||||
!config.exclude.some((/** @type {RegExp} */ regExclude) =>
|
||||
regExclude.test(name),
|
||||
),
|
||||
)
|
||||
.map((name) => ({
|
||||
inputPath: path.resolve(dir, name),
|
||||
outputPath: path.resolve(output, name),
|
||||
}));
|
||||
|
||||
return config.recursive
|
||||
? [].concat(
|
||||
filesInThisFolder,
|
||||
files
|
||||
.filter((name) => checkIsDir(path.resolve(dir, name)))
|
||||
.map((subFolderName) => {
|
||||
const subFolderPath = path.resolve(dir, subFolderName);
|
||||
const subFolderFiles = fs.readdirSync(subFolderPath);
|
||||
const subFolderOutput = path.resolve(output, subFolderName);
|
||||
return getFilesDescriptions(
|
||||
config,
|
||||
subFolderPath,
|
||||
subFolderFiles,
|
||||
subFolderOutput
|
||||
);
|
||||
})
|
||||
.reduce((a, b) => [].concat(a, b), [])
|
||||
)
|
||||
: filesInThisFolder;
|
||||
if (!config.recursive) {
|
||||
return filesInThisFolder;
|
||||
}
|
||||
|
||||
return filesInThisFolder.concat(
|
||||
files
|
||||
.filter((name) => checkIsDir(path.resolve(dir, name)))
|
||||
.map((subFolderName) => {
|
||||
const subFolderPath = path.resolve(dir, subFolderName);
|
||||
const subFolderFiles = fs.readdirSync(subFolderPath);
|
||||
const subFolderOutput = path.resolve(output, subFolderName);
|
||||
return getFilesDescriptions(
|
||||
config,
|
||||
subFolderPath,
|
||||
subFolderFiles,
|
||||
subFolderOutput,
|
||||
);
|
||||
})
|
||||
.reduce((a, b) => a.concat(b), []),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SVG file and pass to processing.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} file
|
||||
* @param {string} output
|
||||
* @return {Promise}
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function optimizeFile(config, file, output) {
|
||||
return fs.promises.readFile(file, 'utf8').then(
|
||||
(data) =>
|
||||
processSVGData(config, { input: 'file', path: file }, data, output, file),
|
||||
(error) => checkOptimizeFileError(config, file, output, error)
|
||||
(data) => processSVGData(config, { path: file }, data, output, file),
|
||||
(error) => checkOptimizeFileError(config, file, output, error),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG data.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {?{ path: string }} info
|
||||
* @param {string} data SVG content to optimize
|
||||
* @param {string} output where to write optimized file
|
||||
* @param {string} [input] input file name (being used if output is a directory)
|
||||
* @return {Promise}
|
||||
* @param {any=} input input file name (being used if output is a directory)
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function processSVGData(config, info, data, output, input) {
|
||||
var startTime = Date.now(),
|
||||
prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
const startTime = Date.now();
|
||||
const prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
|
||||
const result = optimize(data, { ...config, ...info });
|
||||
if (result.modernError) {
|
||||
console.error(colors.red(result.modernError.toString()));
|
||||
process.exit(1);
|
||||
let result;
|
||||
try {
|
||||
result = optimize(data, { ...config, ...info });
|
||||
} catch (error) {
|
||||
if (error instanceof SvgoParserError) {
|
||||
console.error(colors.red(error.toString()));
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (config.datauri) {
|
||||
result.data = encodeSVGDatauri(result.data, config.datauri);
|
||||
}
|
||||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
|
||||
processingTime = Date.now() - startTime;
|
||||
const resultFileSize = Buffer.byteLength(result.data, 'utf8');
|
||||
const processingTime = Date.now() - startTime;
|
||||
|
||||
return writeOutput(input, output, result.data).then(
|
||||
function () {
|
||||
@@ -411,26 +427,27 @@ function processSVGData(config, info, data, output, input) {
|
||||
new Error(
|
||||
error.code === 'ENOTDIR'
|
||||
? `Error: output '${output}' is not a directory.`
|
||||
: error
|
||||
)
|
||||
)
|
||||
: error,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write result of an optimization.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output output file name. '-' for stdout
|
||||
* @param {string} data data to write
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function writeOutput(input, output, data) {
|
||||
async function writeOutput(input, output, data) {
|
||||
if (output == '-') {
|
||||
console.log(data);
|
||||
process.stdout.write(data);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(output), { recursive: true });
|
||||
await fs.promises.mkdir(path.dirname(output), { recursive: true });
|
||||
|
||||
return fs.promises
|
||||
.writeFile(output, data, 'utf8')
|
||||
@@ -438,7 +455,8 @@ function writeOutput(input, output, data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a time taken by optimization.
|
||||
* Write time taken to optimize.
|
||||
*
|
||||
* @param {number} time time in milliseconds.
|
||||
*/
|
||||
function printTimeInfo(time) {
|
||||
@@ -446,38 +464,40 @@ function printTimeInfo(time) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write optimizing information in human readable format.
|
||||
* Write optimizing stats in a human-readable format.
|
||||
*
|
||||
* @param {number} inBytes size before optimization.
|
||||
* @param {number} outBytes size after optimization.
|
||||
*/
|
||||
function printProfitInfo(inBytes, outBytes) {
|
||||
var profitPercents = 100 - (outBytes * 100) / inBytes;
|
||||
const profitPercent = 100 - (outBytes * 100) / inBytes;
|
||||
/** @type {[string, Function]} */
|
||||
const ui = profitPercent < 0 ? ['+', colors.red] : ['-', colors.green];
|
||||
|
||||
console.log(
|
||||
Math.round((inBytes / 1024) * 1000) / 1000 +
|
||||
' KiB' +
|
||||
(profitPercents < 0 ? ' + ' : ' - ') +
|
||||
colors.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
|
||||
' = ' +
|
||||
Math.round((outBytes / 1024) * 1000) / 1000 +
|
||||
' KiB'
|
||||
Math.round((inBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
ui[0],
|
||||
ui[1](Math.abs(Math.round(profitPercent * 10) / 10) + '%'),
|
||||
'=',
|
||||
Math.round((outBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for errors, if it's a dir optimize the dir.
|
||||
* @param {Object} config
|
||||
*
|
||||
* @param {any} config
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
* @param {Error & { code: string, path: string }} error
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function checkOptimizeFileError(config, input, output, error) {
|
||||
if (error.code == 'EISDIR') {
|
||||
return optimizeFolder(config, input, output);
|
||||
} else if (error.code == 'ENOENT') {
|
||||
return Promise.reject(
|
||||
new Error(`Error: no such file or directory '${error.path}'.`)
|
||||
new Error(`Error: no such file or directory '${error.path}'.`),
|
||||
);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
@@ -485,33 +505,29 @@ function checkOptimizeFileError(config, input, output, error) {
|
||||
|
||||
/**
|
||||
* Check for saving file error. If the output is a dir, then write file there.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {string} data
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
* @param {Error & { code: string }} error
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function checkWriteFileError(input, output, data, error) {
|
||||
if (error.code == 'EISDIR' && input) {
|
||||
return fs.promises.writeFile(
|
||||
path.resolve(output, path.basename(input)),
|
||||
data,
|
||||
'utf8'
|
||||
'utf8',
|
||||
);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show list of available plugins with short description.
|
||||
*/
|
||||
/** Show list of available plugins with short description. */
|
||||
function showAvailablePlugins() {
|
||||
const list = Object.entries(pluginsMap)
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([name, plugin]) => ` [ ${colors.green(name)} ] ${plugin.description}`)
|
||||
const list = builtinPlugins
|
||||
.map((plugin) => ` [ ${colors.green(plugin.name)} ] ${plugin.description}`)
|
||||
.join('\n');
|
||||
console.log('Currently available plugins:\n' + list);
|
||||
}
|
||||
|
||||
module.exports.checkIsDir = checkIsDir;
|
||||
|
||||
138
node_modules/svgo/lib/svgo/config.js
generated
vendored
138
node_modules/svgo/lib/svgo/config.js
generated
vendored
@@ -1,138 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const pluginsMap = require('../../plugins/plugins.js');
|
||||
|
||||
const pluginsOrder = [
|
||||
'removeDoctype',
|
||||
'removeXMLProcInst',
|
||||
'removeComments',
|
||||
'removeMetadata',
|
||||
'removeXMLNS',
|
||||
'removeEditorsNSData',
|
||||
'cleanupAttrs',
|
||||
'mergeStyles',
|
||||
'inlineStyles',
|
||||
'minifyStyles',
|
||||
'convertStyleToAttrs',
|
||||
'cleanupIDs',
|
||||
'prefixIds',
|
||||
'removeRasterImages',
|
||||
'removeUselessDefs',
|
||||
'cleanupNumericValues',
|
||||
'cleanupListOfValues',
|
||||
'convertColors',
|
||||
'removeUnknownsAndDefaults',
|
||||
'removeNonInheritableGroupAttrs',
|
||||
'removeUselessStrokeAndFill',
|
||||
'removeViewBox',
|
||||
'cleanupEnableBackground',
|
||||
'removeHiddenElems',
|
||||
'removeEmptyText',
|
||||
'convertShapeToPath',
|
||||
'convertEllipseToCircle',
|
||||
'moveElemsAttrsToGroup',
|
||||
'moveGroupAttrsToElems',
|
||||
'collapseGroups',
|
||||
'convertPathData',
|
||||
'convertTransform',
|
||||
'removeEmptyAttrs',
|
||||
'removeEmptyContainers',
|
||||
'mergePaths',
|
||||
'removeUnusedNS',
|
||||
'sortAttrs',
|
||||
'sortDefsChildren',
|
||||
'removeTitle',
|
||||
'removeDesc',
|
||||
'removeDimensions',
|
||||
'removeAttrs',
|
||||
'removeAttributesBySelector',
|
||||
'removeElementsByAttr',
|
||||
'addClassesToSVGElement',
|
||||
'removeStyleElement',
|
||||
'removeScriptElement',
|
||||
'addAttributesToSVGElement',
|
||||
'removeOffCanvasPaths',
|
||||
'reusePaths',
|
||||
];
|
||||
const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
|
||||
exports.defaultPlugins = defaultPlugins;
|
||||
|
||||
const extendDefaultPlugins = (plugins) => {
|
||||
console.warn(
|
||||
'\n"extendDefaultPlugins" utility is deprecated.\n' +
|
||||
'Use "preset-default" plugin with overrides instead.\n' +
|
||||
'For example:\n' +
|
||||
`{\n` +
|
||||
` name: 'preset-default',\n` +
|
||||
` params: {\n` +
|
||||
` overrides: {\n` +
|
||||
` // customize plugin options\n` +
|
||||
` convertShapeToPath: {\n` +
|
||||
` convertArcs: true\n` +
|
||||
` },\n` +
|
||||
` // disable plugins\n` +
|
||||
` convertPathData: false\n` +
|
||||
` }\n` +
|
||||
` }\n` +
|
||||
`}\n`
|
||||
);
|
||||
const extendedPlugins = pluginsOrder.map((name) => ({
|
||||
name,
|
||||
active: pluginsMap[name].active,
|
||||
}));
|
||||
for (const plugin of plugins) {
|
||||
const resolvedPlugin = resolvePluginConfig(plugin);
|
||||
const index = pluginsOrder.indexOf(resolvedPlugin.name);
|
||||
if (index === -1) {
|
||||
extendedPlugins.push(plugin);
|
||||
} else {
|
||||
extendedPlugins[index] = plugin;
|
||||
}
|
||||
}
|
||||
return extendedPlugins;
|
||||
};
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
|
||||
const resolvePluginConfig = (plugin) => {
|
||||
let configParams = {};
|
||||
if (typeof plugin === 'string') {
|
||||
// resolve builtin plugin specified as string
|
||||
const pluginConfig = pluginsMap[plugin];
|
||||
if (pluginConfig == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin}" specified.`);
|
||||
}
|
||||
return {
|
||||
...pluginConfig,
|
||||
name: plugin,
|
||||
active: true,
|
||||
params: { ...pluginConfig.params, ...configParams },
|
||||
};
|
||||
}
|
||||
if (typeof plugin === 'object' && plugin != null) {
|
||||
if (plugin.name == null) {
|
||||
throw Error(`Plugin name should be specified`);
|
||||
}
|
||||
if (plugin.fn) {
|
||||
// resolve custom plugin with implementation
|
||||
return {
|
||||
active: true,
|
||||
...plugin,
|
||||
params: { ...configParams, ...plugin.params },
|
||||
};
|
||||
} else {
|
||||
// resolve builtin plugin specified as object without implementation
|
||||
const pluginConfig = pluginsMap[plugin.name];
|
||||
if (pluginConfig == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin.name}" specified.`);
|
||||
}
|
||||
return {
|
||||
...pluginConfig,
|
||||
active: true,
|
||||
...plugin,
|
||||
params: { ...pluginConfig.params, ...configParams, ...plugin.params },
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.resolvePluginConfig = resolvePluginConfig;
|
||||
72
node_modules/svgo/lib/svgo/css-class-list.js
generated
vendored
72
node_modules/svgo/lib/svgo/css-class-list.js
generated
vendored
@@ -1,72 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var CSSClassList = function (node) {
|
||||
this.parentNode = node;
|
||||
this.classNames = new Set();
|
||||
const value = node.attributes.class;
|
||||
if (value != null) {
|
||||
this.addClassValueHandler();
|
||||
this.setClassValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
// attr.class.value
|
||||
|
||||
CSSClassList.prototype.addClassValueHandler = function () {
|
||||
Object.defineProperty(this.parentNode.attributes, 'class', {
|
||||
get: this.getClassValue.bind(this),
|
||||
set: this.setClassValue.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
};
|
||||
|
||||
CSSClassList.prototype.getClassValue = function () {
|
||||
var arrClassNames = Array.from(this.classNames);
|
||||
return arrClassNames.join(' ');
|
||||
};
|
||||
|
||||
CSSClassList.prototype.setClassValue = function (newValue) {
|
||||
if (typeof newValue === 'undefined') {
|
||||
this.classNames.clear();
|
||||
return;
|
||||
}
|
||||
var arrClassNames = newValue.split(' ');
|
||||
this.classNames = new Set(arrClassNames);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.add = function (/* variadic */) {
|
||||
this.addClassValueHandler();
|
||||
Object.values(arguments).forEach(this._addSingle.bind(this));
|
||||
};
|
||||
|
||||
CSSClassList.prototype._addSingle = function (className) {
|
||||
this.classNames.add(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.remove = function (/* variadic */) {
|
||||
this.addClassValueHandler();
|
||||
Object.values(arguments).forEach(this._removeSingle.bind(this));
|
||||
};
|
||||
|
||||
CSSClassList.prototype._removeSingle = function (className) {
|
||||
this.classNames.delete(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.item = function (index) {
|
||||
var arrClassNames = Array.from(this.classNames);
|
||||
return arrClassNames[index];
|
||||
};
|
||||
|
||||
CSSClassList.prototype.toggle = function (className, force) {
|
||||
if (this.contains(className) || force === false) {
|
||||
this.classNames.delete(className);
|
||||
}
|
||||
this.classNames.add(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.contains = function (className) {
|
||||
return this.classNames.has(className);
|
||||
};
|
||||
|
||||
module.exports = CSSClassList;
|
||||
2
node_modules/svgo/lib/svgo/css-select-adapter.d.ts
generated
vendored
2
node_modules/svgo/lib/svgo/css-select-adapter.d.ts
generated
vendored
@@ -1,2 +0,0 @@
|
||||
declare let obj: any;
|
||||
export = obj;
|
||||
141
node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
141
node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
@@ -1,79 +1,46 @@
|
||||
'use strict';
|
||||
import { mapNodesToParents } from '../util/map-nodes-to-parents.js';
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['isTag']} */
|
||||
const isTag = (node) => {
|
||||
return node.type === 'element';
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['existsOne']} */
|
||||
const existsOne = (test, elems) => {
|
||||
return elems.some((elem) => {
|
||||
if (isTag(elem)) {
|
||||
return test(elem) || existsOne(test, getChildren(elem));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return isTag(elem) && (test(elem) || existsOne(test, getChildren(elem)));
|
||||
});
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getAttributeValue']} */
|
||||
const getAttributeValue = (elem, name) => {
|
||||
return elem.attributes[name];
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getChildren']} */
|
||||
const getChildren = (node) => {
|
||||
return node.children || [];
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getName']} */
|
||||
const getName = (elemAst) => {
|
||||
return elemAst.name;
|
||||
};
|
||||
|
||||
const getParent = (node) => {
|
||||
return node.parentNode || null;
|
||||
};
|
||||
|
||||
const getSiblings = (elem) => {
|
||||
var parent = getParent(elem);
|
||||
return parent ? getChildren(parent) : [];
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getText']} */
|
||||
const getText = (node) => {
|
||||
if (node.children[0].type === 'text' && node.children[0].type === 'cdata') {
|
||||
if (node.children[0].type === 'text' || node.children[0].type === 'cdata') {
|
||||
return node.children[0].value;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['hasAttrib']} */
|
||||
const hasAttrib = (elem, name) => {
|
||||
return elem.attributes[name] !== undefined;
|
||||
};
|
||||
|
||||
const removeSubsets = (nodes) => {
|
||||
let idx = nodes.length;
|
||||
let node;
|
||||
let ancestor;
|
||||
let replace;
|
||||
// Check if each node (or one of its ancestors) is already contained in the
|
||||
// array.
|
||||
while (--idx > -1) {
|
||||
node = ancestor = nodes[idx];
|
||||
// Temporarily remove the node under consideration
|
||||
nodes[idx] = null;
|
||||
replace = true;
|
||||
while (ancestor) {
|
||||
if (nodes.includes(ancestor)) {
|
||||
replace = false;
|
||||
nodes.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
ancestor = getParent(ancestor);
|
||||
}
|
||||
// If the node has been found to be unique, re-insert it.
|
||||
if (replace) {
|
||||
nodes[idx] = node;
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['findAll']} */
|
||||
const findAll = (test, elems) => {
|
||||
const result = [];
|
||||
for (const elem of elems) {
|
||||
@@ -87,6 +54,7 @@ const findAll = (test, elems) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['findOne']} */
|
||||
const findOne = (test, elems) => {
|
||||
for (const elem of elems) {
|
||||
if (isTag(elem)) {
|
||||
@@ -102,19 +70,74 @@ const findOne = (test, elems) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const svgoCssSelectAdapter = {
|
||||
isTag,
|
||||
existsOne,
|
||||
getAttributeValue,
|
||||
getChildren,
|
||||
getName,
|
||||
getParent,
|
||||
getSiblings,
|
||||
getText,
|
||||
hasAttrib,
|
||||
removeSubsets,
|
||||
findAll,
|
||||
findOne,
|
||||
};
|
||||
/**
|
||||
* @param {import('../types.js').XastParent} relativeNode
|
||||
* @param {Map<import('../types.js').XastNode, import('../types.js').XastParent>=} parents
|
||||
* @returns {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']}
|
||||
*/
|
||||
export function createAdapter(relativeNode, parents) {
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getParent']} */
|
||||
const getParent = (node) => {
|
||||
if (!parents) {
|
||||
parents = mapNodesToParents(relativeNode);
|
||||
}
|
||||
|
||||
module.exports = svgoCssSelectAdapter;
|
||||
return parents.get(node) || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} elem
|
||||
* @returns {any}
|
||||
*/
|
||||
const getSiblings = (elem) => {
|
||||
const parent = getParent(elem);
|
||||
return parent ? getChildren(parent) : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} nodes
|
||||
* @returns {any}
|
||||
*/
|
||||
const removeSubsets = (nodes) => {
|
||||
let idx = nodes.length;
|
||||
let node;
|
||||
let ancestor;
|
||||
let replace;
|
||||
// Check if each node (or one of its ancestors) is already contained in the
|
||||
// array.
|
||||
while (--idx > -1) {
|
||||
node = ancestor = nodes[idx];
|
||||
// Temporarily remove the node under consideration
|
||||
nodes[idx] = null;
|
||||
replace = true;
|
||||
while (ancestor) {
|
||||
if (nodes.includes(ancestor)) {
|
||||
replace = false;
|
||||
nodes.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
ancestor = getParent(ancestor);
|
||||
}
|
||||
// If the node has been found to be unique, re-insert it.
|
||||
if (replace) {
|
||||
nodes[idx] = node;
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
return {
|
||||
isTag,
|
||||
existsOne,
|
||||
getAttributeValue,
|
||||
getChildren,
|
||||
getName,
|
||||
getParent,
|
||||
getSiblings,
|
||||
getText,
|
||||
hasAttrib,
|
||||
removeSubsets,
|
||||
findAll,
|
||||
findOne,
|
||||
};
|
||||
}
|
||||
|
||||
232
node_modules/svgo/lib/svgo/css-style-declaration.js
generated
vendored
232
node_modules/svgo/lib/svgo/css-style-declaration.js
generated
vendored
@@ -1,232 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var csstree = require('css-tree'),
|
||||
csstools = require('../css-tools');
|
||||
|
||||
var CSSStyleDeclaration = function (node) {
|
||||
this.parentNode = node;
|
||||
|
||||
this.properties = new Map();
|
||||
this.hasSynced = false;
|
||||
|
||||
this.styleValue = null;
|
||||
|
||||
this.parseError = false;
|
||||
const value = node.attributes.style;
|
||||
if (value != null) {
|
||||
this.addStyleValueHandler();
|
||||
this.setStyleValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
// attr.style.value
|
||||
|
||||
CSSStyleDeclaration.prototype.addStyleValueHandler = function () {
|
||||
Object.defineProperty(this.parentNode.attributes, 'style', {
|
||||
get: this.getStyleValue.bind(this),
|
||||
set: this.setStyleValue.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.getStyleValue = function () {
|
||||
return this.getCssText();
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.setStyleValue = function (newValue) {
|
||||
this.properties.clear(); // reset all existing properties
|
||||
this.styleValue = newValue;
|
||||
this.hasSynced = false; // raw css changed
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._loadCssText = function () {
|
||||
if (this.hasSynced) {
|
||||
return;
|
||||
}
|
||||
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
|
||||
|
||||
if (!this.styleValue || this.styleValue.length === 0) {
|
||||
return;
|
||||
}
|
||||
var inlineCssStr = this.styleValue;
|
||||
|
||||
var declarations = {};
|
||||
try {
|
||||
declarations = csstree.parse(inlineCssStr, {
|
||||
context: 'declarationList',
|
||||
parseValue: false,
|
||||
});
|
||||
} catch (parseError) {
|
||||
this.parseError = parseError;
|
||||
return;
|
||||
}
|
||||
this.parseError = false;
|
||||
|
||||
var self = this;
|
||||
declarations.children.each(function (declaration) {
|
||||
try {
|
||||
var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
|
||||
self.setProperty(
|
||||
styleDeclaration.name,
|
||||
styleDeclaration.value,
|
||||
styleDeclaration.priority
|
||||
);
|
||||
} catch (styleError) {
|
||||
if (styleError.message !== 'Unknown node type: undefined') {
|
||||
self.parseError = styleError;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// only reads from properties
|
||||
|
||||
/**
|
||||
* Get the textual representation of the declaration block (equivalent to .cssText attribute).
|
||||
*
|
||||
* @return {string} Textual representation of the declaration block (empty string for no properties)
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getCssText = function () {
|
||||
var properties = this.getProperties();
|
||||
|
||||
if (this.parseError) {
|
||||
// in case of a parse error, pass through original styles
|
||||
return this.styleValue;
|
||||
}
|
||||
|
||||
var cssText = [];
|
||||
properties.forEach(function (property, propertyName) {
|
||||
var strImportant = property.priority === 'important' ? '!important' : '';
|
||||
cssText.push(
|
||||
propertyName.trim() + ':' + property.value.trim() + strImportant
|
||||
);
|
||||
});
|
||||
return cssText.join(';');
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._handleParseError = function () {
|
||||
if (this.parseError) {
|
||||
console.warn(
|
||||
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
|
||||
this.parseError
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._getProperty = function (propertyName) {
|
||||
if (typeof propertyName === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var property = properties.get(propertyName.trim());
|
||||
return property;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the optional priority, "important".
|
||||
*
|
||||
* @param {string} propertyName representing the property name to be checked.
|
||||
* @return {string} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) {
|
||||
var property = this._getProperty(propertyName);
|
||||
return property ? property.priority : '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the property value given a property name.
|
||||
*
|
||||
* @param {string} propertyName representing the property name to be checked.
|
||||
* @return {string} value containing the value of the property. If not set, returns the empty string.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) {
|
||||
var property = this._getProperty(propertyName);
|
||||
return property ? property.value : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a property name.
|
||||
*
|
||||
* @param {number} index of the node to be fetched. The index is zero-based.
|
||||
* @return {string} propertyName that is the name of the CSS property at the specified index.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.item = function (index) {
|
||||
if (typeof index === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
return Array.from(properties.keys())[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all properties of the node.
|
||||
*
|
||||
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getProperties = function () {
|
||||
this._loadCssText();
|
||||
return this.properties;
|
||||
};
|
||||
|
||||
// writes to properties
|
||||
|
||||
/**
|
||||
* Remove a property from the CSS declaration block.
|
||||
*
|
||||
* @param {string} propertyName representing the property name to be removed.
|
||||
* @return {string} oldValue equal to the value of the CSS property before it was removed.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.removeProperty = function (propertyName) {
|
||||
if (typeof propertyName === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
this.addStyleValueHandler();
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var oldValue = this.getPropertyValue(propertyName);
|
||||
properties.delete(propertyName.trim());
|
||||
return oldValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify an existing CSS property or creates a new CSS property in the declaration block.
|
||||
*
|
||||
* @param {string} propertyName representing the CSS property name to be modified.
|
||||
* @param {string} value containing the new property value. If not specified, treated as the empty string. value must not contain "!important" -- that should be set using the priority parameter.
|
||||
* @param {string} priority allowing the "important" CSS priority to be set. If not specified, treated as the empty string.
|
||||
* @return {{value: string, priority: string}}
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.setProperty = function (
|
||||
propertyName,
|
||||
value,
|
||||
priority
|
||||
) {
|
||||
if (typeof propertyName === 'undefined') {
|
||||
throw Error('propertyName argument required, but only not present.');
|
||||
}
|
||||
|
||||
this.addStyleValueHandler();
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var property = {
|
||||
value: value.trim(),
|
||||
priority: priority.trim(),
|
||||
};
|
||||
properties.set(propertyName.trim(), property);
|
||||
|
||||
return property;
|
||||
};
|
||||
|
||||
module.exports = CSSStyleDeclaration;
|
||||
2
node_modules/svgo/lib/svgo/jsAPI.d.ts
generated
vendored
2
node_modules/svgo/lib/svgo/jsAPI.d.ts
generated
vendored
@@ -1,2 +0,0 @@
|
||||
declare let obj: any;
|
||||
export = obj;
|
||||
443
node_modules/svgo/lib/svgo/jsAPI.js
generated
vendored
443
node_modules/svgo/lib/svgo/jsAPI.js
generated
vendored
@@ -1,443 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { selectAll, selectOne, is } = require('css-select');
|
||||
const svgoCssSelectAdapter = require('./css-select-adapter');
|
||||
const CSSClassList = require('./css-class-list');
|
||||
const CSSStyleDeclaration = require('./css-style-declaration');
|
||||
|
||||
/**
|
||||
* @type {(name: string) => { prefix: string, local: string }}
|
||||
*/
|
||||
const parseName = (name) => {
|
||||
if (name == null) {
|
||||
return {
|
||||
prefix: '',
|
||||
local: '',
|
||||
};
|
||||
}
|
||||
if (name === 'xmlns') {
|
||||
return {
|
||||
prefix: 'xmlns',
|
||||
local: '',
|
||||
};
|
||||
}
|
||||
const chunks = name.split(':');
|
||||
if (chunks.length === 1) {
|
||||
return {
|
||||
prefix: '',
|
||||
local: chunks[0],
|
||||
};
|
||||
}
|
||||
return {
|
||||
prefix: chunks[0],
|
||||
local: chunks[1],
|
||||
};
|
||||
};
|
||||
|
||||
var cssSelectOpts = {
|
||||
xmlMode: true,
|
||||
adapter: svgoCssSelectAdapter,
|
||||
};
|
||||
|
||||
const attrsHandler = {
|
||||
get: (attributes, name) => {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (attributes.hasOwnProperty(name)) {
|
||||
return {
|
||||
name,
|
||||
get value() {
|
||||
return attributes[name];
|
||||
},
|
||||
set value(value) {
|
||||
attributes[name] = value;
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
set: (attributes, name, attr) => {
|
||||
attributes[name] = attr.value;
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
var JSAPI = function (data, parentNode) {
|
||||
Object.assign(this, data);
|
||||
if (this.type === 'element') {
|
||||
if (this.attributes == null) {
|
||||
this.attributes = {};
|
||||
}
|
||||
if (this.children == null) {
|
||||
this.children = [];
|
||||
}
|
||||
Object.defineProperty(this, 'class', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: new CSSClassList(this),
|
||||
});
|
||||
Object.defineProperty(this, 'style', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: new CSSStyleDeclaration(this),
|
||||
});
|
||||
Object.defineProperty(this, 'parentNode', {
|
||||
writable: true,
|
||||
value: parentNode,
|
||||
});
|
||||
|
||||
// temporary attrs polyfill
|
||||
// TODO remove after migration
|
||||
const element = this;
|
||||
Object.defineProperty(this, 'attrs', {
|
||||
configurable: true,
|
||||
get() {
|
||||
return new Proxy(element.attributes, attrsHandler);
|
||||
},
|
||||
set(value) {
|
||||
const newAttributes = {};
|
||||
for (const attr of Object.values(value)) {
|
||||
newAttributes[attr.name] = attr.value;
|
||||
}
|
||||
element.attributes = newAttributes;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
module.exports = JSAPI;
|
||||
|
||||
/**
|
||||
* Perform a deep clone of this node.
|
||||
*
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.clone = function () {
|
||||
const { children, ...nodeData } = this;
|
||||
// Deep-clone node data.
|
||||
const clonedNode = new JSAPI(JSON.parse(JSON.stringify(nodeData)), null);
|
||||
if (children) {
|
||||
clonedNode.children = children.map((child) => {
|
||||
const clonedChild = child.clone();
|
||||
clonedChild.parentNode = clonedNode;
|
||||
return clonedChild;
|
||||
});
|
||||
}
|
||||
return clonedNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if item is an element
|
||||
* (any, with a specific name or in a names array).
|
||||
*
|
||||
* @param {String|Array} [param] element name or names arrays
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.isElem = function (param) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (param == null) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(param)) {
|
||||
return param.includes(this.name);
|
||||
}
|
||||
return this.name === param;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renames an element
|
||||
*
|
||||
* @param {String} name new element name
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.renameElem = function (name) {
|
||||
if (name && typeof name === 'string') this.name = name;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element is empty.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.isEmpty = function () {
|
||||
return !this.children || !this.children.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the closest ancestor of the current element.
|
||||
* @param elemName
|
||||
*
|
||||
* @return {?Object}
|
||||
*/
|
||||
JSAPI.prototype.closestElem = function (elemName) {
|
||||
var elem = this;
|
||||
|
||||
while ((elem = elem.parentNode) && !elem.isElem(elemName));
|
||||
|
||||
return elem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes children by removing elements and/or adding new elements.
|
||||
*
|
||||
* @param {Number} start Index at which to start changing the children.
|
||||
* @param {Number} n Number of elements to remove.
|
||||
* @param {Array|Object} [insertion] Elements to add to the children.
|
||||
* @return {Array} Removed elements.
|
||||
*/
|
||||
JSAPI.prototype.spliceContent = function (start, n, insertion) {
|
||||
if (arguments.length < 2) return [];
|
||||
|
||||
if (!Array.isArray(insertion))
|
||||
insertion = Array.apply(null, arguments).slice(2);
|
||||
|
||||
insertion.forEach(function (inner) {
|
||||
inner.parentNode = this;
|
||||
}, this);
|
||||
|
||||
return this.children.splice.apply(
|
||||
this.children,
|
||||
[start, n].concat(insertion)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String} [name] attribute name
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.hasAttr = function (name, val) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (Object.keys(this.attributes).length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
return true;
|
||||
}
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (this.attributes.hasOwnProperty(name) === false) {
|
||||
return false;
|
||||
}
|
||||
if (val !== undefined) {
|
||||
return this.attributes[name] === val.toString();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute by local name
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String} [localName] local attribute name
|
||||
* @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.hasAttrLocal = function (localName, val) {
|
||||
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||
|
||||
if (!arguments.length) return !!this.attrs;
|
||||
|
||||
var callback;
|
||||
|
||||
switch (val != null && val.constructor && val.constructor.name) {
|
||||
case 'Number': // same as String
|
||||
case 'String':
|
||||
callback = stringValueTest;
|
||||
break;
|
||||
case 'RegExp':
|
||||
callback = regexpValueTest;
|
||||
break;
|
||||
case 'Function':
|
||||
callback = funcValueTest;
|
||||
break;
|
||||
default:
|
||||
callback = nameTest;
|
||||
}
|
||||
return this.someAttr(callback);
|
||||
|
||||
function nameTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName;
|
||||
}
|
||||
|
||||
function stringValueTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName && val == attr.value;
|
||||
}
|
||||
|
||||
function regexpValueTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName && val.test(attr.value);
|
||||
}
|
||||
|
||||
function funcValueTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName && val(attr.value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a specific attribute from an element
|
||||
* (by name or name + value).
|
||||
*
|
||||
* @param {String} name attribute name
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Object|Undefined}
|
||||
*/
|
||||
JSAPI.prototype.attr = function (name, val) {
|
||||
if (this.hasAttr(name, val)) {
|
||||
return this.attrs[name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get computed attribute value from an element
|
||||
*
|
||||
* @param {String} name attribute name
|
||||
* @return {Object|Undefined}
|
||||
*/
|
||||
JSAPI.prototype.computedAttr = function (name, val) {
|
||||
if (!arguments.length) return;
|
||||
|
||||
for (
|
||||
var elem = this;
|
||||
elem && (!elem.hasAttr(name) || !elem.attributes[name]);
|
||||
elem = elem.parentNode
|
||||
);
|
||||
|
||||
if (val != null) {
|
||||
return elem ? elem.hasAttr(name, val) : false;
|
||||
} else if (elem && elem.hasAttr(name)) {
|
||||
return elem.attributes[name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a specific attribute.
|
||||
*
|
||||
* @param {String|Array} name attribute name
|
||||
* @param {String} [val] attribute value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.removeAttr = function (name, val) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (arguments.length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(name)) {
|
||||
for (const nameItem of name) {
|
||||
this.removeAttr(nameItem, val);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (this.hasAttr(name, val) === false) {
|
||||
return false;
|
||||
}
|
||||
delete this.attributes[name];
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add attribute.
|
||||
*
|
||||
* @param {Object} [attr={}] attribute object
|
||||
* @return {Object|Boolean} created attribute or false if no attr was passed in
|
||||
*/
|
||||
JSAPI.prototype.addAttr = function (attr) {
|
||||
attr = attr || {};
|
||||
|
||||
if (attr.name === undefined) return false;
|
||||
|
||||
this.attributes[attr.name] = attr.value;
|
||||
|
||||
if (attr.name === 'class') {
|
||||
// newly added class attribute
|
||||
this.class.addClassValueHandler();
|
||||
}
|
||||
|
||||
if (attr.name === 'style') {
|
||||
// newly added style attribute
|
||||
this.style.addStyleValueHandler();
|
||||
}
|
||||
|
||||
return this.attrs[attr.name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates over all attributes.
|
||||
*
|
||||
* @param {Function} callback callback
|
||||
* @param {Object} [context] callback context
|
||||
* @return {Boolean} false if there are no any attributes
|
||||
*/
|
||||
JSAPI.prototype.eachAttr = function (callback, context) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (callback == null) {
|
||||
return false;
|
||||
}
|
||||
for (const attr of Object.values(this.attrs)) {
|
||||
callback.call(context, attr);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests whether some attribute passes the test.
|
||||
*
|
||||
* @param {Function} callback callback
|
||||
* @param {Object} [context] callback context
|
||||
* @return {Boolean} false if there are no any attributes
|
||||
*/
|
||||
JSAPI.prototype.someAttr = function (callback, context) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const attr of Object.values(this.attrs)) {
|
||||
if (callback.call(context, attr)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a string of CSS selectors against the element and returns matched elements.
|
||||
*
|
||||
* @param {String} selectors CSS selector(s) string
|
||||
* @return {Array} null if no elements matched
|
||||
*/
|
||||
JSAPI.prototype.querySelectorAll = function (selectors) {
|
||||
var matchedEls = selectAll(selectors, this, cssSelectOpts);
|
||||
|
||||
return matchedEls.length > 0 ? matchedEls : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a string of CSS selectors against the element and returns only the first matched element.
|
||||
*
|
||||
* @param {String} selectors CSS selector(s) string
|
||||
* @return {Array} null if no element matched
|
||||
*/
|
||||
JSAPI.prototype.querySelector = function (selectors) {
|
||||
return selectOne(selectors, this, cssSelectOpts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if a selector matches a given element.
|
||||
*
|
||||
* @param {String} selector CSS selector string
|
||||
* @return {Boolean} true if element would be selected by selector string, false if it does not
|
||||
*/
|
||||
JSAPI.prototype.matches = function (selector) {
|
||||
return is(this, selector, cssSelectOpts);
|
||||
};
|
||||
102
node_modules/svgo/lib/svgo/plugins.js
generated
vendored
102
node_modules/svgo/lib/svgo/plugins.js
generated
vendored
@@ -1,85 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
const { visit } = require('../xast.js');
|
||||
import { visit } from '../util/visit.js';
|
||||
|
||||
/**
|
||||
* Plugins engine.
|
||||
*
|
||||
* @module plugins
|
||||
*
|
||||
* @param {Object} ast input ast
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins object from config
|
||||
* @return {Object} output ast
|
||||
* @param {import('../types.js').XastNode} ast Input AST.
|
||||
* @param {any} info Extra information.
|
||||
* @param {ReadonlyArray<any>} plugins Plugins property from config.
|
||||
* @param {any} overrides
|
||||
* @param {any} globalOverrides
|
||||
*/
|
||||
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => {
|
||||
export const invokePlugins = (
|
||||
ast,
|
||||
info,
|
||||
plugins,
|
||||
overrides,
|
||||
globalOverrides,
|
||||
) => {
|
||||
for (const plugin of plugins) {
|
||||
const override = overrides == null ? null : overrides[plugin.name];
|
||||
const override = overrides?.[plugin.name];
|
||||
if (override === false) {
|
||||
continue;
|
||||
}
|
||||
const params = { ...plugin.params, ...globalOverrides, ...override };
|
||||
|
||||
if (plugin.type === 'perItem') {
|
||||
ast = perItem(ast, info, plugin, params);
|
||||
}
|
||||
if (plugin.type === 'perItemReverse') {
|
||||
ast = perItem(ast, info, plugin, params, true);
|
||||
}
|
||||
if (plugin.type === 'full') {
|
||||
if (plugin.active) {
|
||||
ast = plugin.fn(ast, params, info);
|
||||
}
|
||||
}
|
||||
if (plugin.type === 'visitor') {
|
||||
if (plugin.active) {
|
||||
const visitor = plugin.fn(ast, params, info);
|
||||
if (visitor != null) {
|
||||
visit(ast, visitor);
|
||||
}
|
||||
}
|
||||
const visitor = plugin.fn(ast, params, info);
|
||||
if (visitor != null) {
|
||||
visit(ast, visitor);
|
||||
}
|
||||
}
|
||||
return ast;
|
||||
};
|
||||
exports.invokePlugins = invokePlugins;
|
||||
|
||||
/**
|
||||
* Direct or reverse per-item loop.
|
||||
*
|
||||
* @param {Object} data input data
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins list to process
|
||||
* @param {boolean} [reverse] reverse pass?
|
||||
* @return {Object} output data
|
||||
* @template {string} T
|
||||
* @param {{ name: T, plugins: ReadonlyArray<import('../types.js').BuiltinPlugin<string, any>> }} arg0
|
||||
* @returns {import('../types.js').BuiltinPluginOrPreset<T, any>}
|
||||
*/
|
||||
function perItem(data, info, plugin, params, reverse) {
|
||||
function monkeys(items) {
|
||||
items.children = items.children.filter(function (item) {
|
||||
// reverse pass
|
||||
if (reverse && item.children) {
|
||||
monkeys(item);
|
||||
}
|
||||
// main filter
|
||||
let kept = true;
|
||||
if (plugin.active) {
|
||||
kept = plugin.fn(item, params, info) !== false;
|
||||
}
|
||||
// direct pass
|
||||
if (!reverse && item.children) {
|
||||
monkeys(item);
|
||||
}
|
||||
return kept;
|
||||
});
|
||||
return items;
|
||||
}
|
||||
return monkeys(data);
|
||||
}
|
||||
|
||||
const createPreset = ({ name, plugins }) => {
|
||||
export const createPreset = ({ name, plugins }) => {
|
||||
return {
|
||||
name,
|
||||
type: 'full',
|
||||
isPreset: true,
|
||||
plugins: Object.freeze(plugins),
|
||||
fn: (ast, params, info) => {
|
||||
const { floatPrecision, overrides } = params;
|
||||
const globalOverrides = {};
|
||||
@@ -87,23 +49,23 @@ const createPreset = ({ name, plugins }) => {
|
||||
globalOverrides.floatPrecision = floatPrecision;
|
||||
}
|
||||
if (overrides) {
|
||||
for (const [pluginName, override] of Object.entries(overrides)) {
|
||||
if (override === true) {
|
||||
const pluginNames = plugins.map(({ name }) => name);
|
||||
for (const pluginName of Object.keys(overrides)) {
|
||||
if (!pluginNames.includes(pluginName)) {
|
||||
console.warn(
|
||||
`You are trying to enable ${pluginName} which is not part of preset.\n` +
|
||||
`Try to put it before or after preset, for example\n\n` +
|
||||
`You are trying to configure ${pluginName} which is not part of ${name}.\n` +
|
||||
`Try to put it before or after, for example\n\n` +
|
||||
`plugins: [\n` +
|
||||
` {\n` +
|
||||
` name: 'preset-default',\n` +
|
||||
` name: '${name}',\n` +
|
||||
` },\n` +
|
||||
` 'cleanupListOfValues'\n` +
|
||||
`]\n`
|
||||
` '${pluginName}'\n` +
|
||||
`]\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invokePlugins(ast, info, plugins, overrides, globalOverrides);
|
||||
invokePlugins(ast, info, plugins, overrides, globalOverrides);
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.createPreset = createPreset;
|
||||
|
||||
184
node_modules/svgo/lib/svgo/tools.js
generated
vendored
184
node_modules/svgo/lib/svgo/tools.js
generated
vendored
@@ -1,16 +1,25 @@
|
||||
'use strict';
|
||||
import { attrsGroups, referencesProps } from '../../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../types').PathDataCommand} PathDataCommand
|
||||
* @typedef CleanupOutDataParams
|
||||
* @property {boolean=} noSpaceAfterFlags
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} negativeExtraSpace
|
||||
*/
|
||||
|
||||
const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g;
|
||||
const regReferencesHref = /^#(.+?)$/;
|
||||
const regReferencesBegin = /(\w+)\.[a-zA-Z]/;
|
||||
|
||||
/**
|
||||
* Encode plain SVG data string into Data URI string.
|
||||
*
|
||||
* @type {(str: string, type?: 'base64' | 'enc' | 'unenc') => string}
|
||||
* @param {string} str
|
||||
* @param {import('../types.js').DataUri=} type
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.encodeSVGDatauri = (str, type) => {
|
||||
var prefix = 'data:image/svg+xml';
|
||||
export const encodeSVGDatauri = (str, type) => {
|
||||
let prefix = 'data:image/svg+xml';
|
||||
if (!type || type === 'base64') {
|
||||
// base64
|
||||
prefix += ';base64,';
|
||||
@@ -28,16 +37,19 @@ exports.encodeSVGDatauri = (str, type) => {
|
||||
/**
|
||||
* Decode SVG Data URI string into plain SVG string.
|
||||
*
|
||||
* @type {(str: string) => string}
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.decodeSVGDatauri = (str) => {
|
||||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
var match = regexp.exec(str);
|
||||
export const decodeSVGDatauri = (str) => {
|
||||
const regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
const match = regexp.exec(str);
|
||||
|
||||
// plain string
|
||||
if (!match) return str;
|
||||
if (!match) {
|
||||
return str;
|
||||
}
|
||||
|
||||
var data = match[3];
|
||||
const data = match[3];
|
||||
|
||||
if (match[2]) {
|
||||
// base64
|
||||
@@ -52,28 +64,21 @@ exports.decodeSVGDatauri = (str) => {
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* noSpaceAfterFlags?: boolean,
|
||||
* leadingZero?: boolean,
|
||||
* negativeExtraSpace?: boolean
|
||||
* }} CleanupOutDataParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a row of numbers to an optimized string view.
|
||||
*
|
||||
* @example
|
||||
* [0, -1, .5, .5] → "0-1 .5.5"
|
||||
*
|
||||
* @type {(data: Array<number>, params: CleanupOutDataParams, command?: PathDataCommand) => string}
|
||||
* @param {ReadonlyArray<number>} data
|
||||
* @param {CleanupOutDataParams} params
|
||||
* @param {import('../types.js').PathDataCommand=} command
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.cleanupOutData = (data, params, command) => {
|
||||
export const cleanupOutData = (data, params, command) => {
|
||||
let str = '';
|
||||
let delimiter;
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
/** @type {number} */
|
||||
let prev;
|
||||
|
||||
data.forEach((item, i) => {
|
||||
@@ -81,13 +86,17 @@ exports.cleanupOutData = (data, params, command) => {
|
||||
delimiter = ' ';
|
||||
|
||||
// no extra space in front of first number
|
||||
if (i == 0) delimiter = '';
|
||||
if (i == 0) {
|
||||
delimiter = '';
|
||||
}
|
||||
|
||||
// no extra space after 'arcto' command flags(large-arc and sweep flags)
|
||||
// no extra space after arc command flags (large-arc and sweep flags)
|
||||
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
|
||||
var pos = i % 7;
|
||||
if (pos == 4 || pos == 5) delimiter = '';
|
||||
const pos = i % 7;
|
||||
if (pos == 4 || pos == 5) {
|
||||
delimiter = '';
|
||||
}
|
||||
}
|
||||
|
||||
// remove floating-point numbers leading zeros
|
||||
@@ -116,22 +125,117 @@ exports.cleanupOutData = (data, params, command) => {
|
||||
/**
|
||||
* Remove floating-point numbers leading zero.
|
||||
*
|
||||
* @param {number} value
|
||||
* @returns {string}
|
||||
* @example
|
||||
* 0.5 → .5
|
||||
*
|
||||
* @example
|
||||
* -0.5 → -.5
|
||||
*
|
||||
* @type {(num: number) => string}
|
||||
*/
|
||||
const removeLeadingZero = (num) => {
|
||||
var strNum = num.toString();
|
||||
export const removeLeadingZero = (value) => {
|
||||
const strValue = value.toString();
|
||||
|
||||
if (0 < num && num < 1 && strNum.charAt(0) === '0') {
|
||||
strNum = strNum.slice(1);
|
||||
} else if (-1 < num && num < 0 && strNum.charAt(1) === '0') {
|
||||
strNum = strNum.charAt(0) + strNum.slice(2);
|
||||
if (0 < value && value < 1 && strValue.startsWith('0')) {
|
||||
return strValue.slice(1);
|
||||
}
|
||||
return strNum;
|
||||
|
||||
if (-1 < value && value < 0 && strValue[1] === '0') {
|
||||
return strValue[0] + strValue.slice(2);
|
||||
}
|
||||
|
||||
return strValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* If the current node contains any scripts. This does not check parents or
|
||||
* children of the node, only the properties and attributes of the node itself.
|
||||
*
|
||||
* @param {import('../types.js').XastElement} node Current node to check against.
|
||||
* @returns {boolean} If the current node contains scripts.
|
||||
*/
|
||||
export const hasScripts = (node) => {
|
||||
if (node.name === 'script' && node.children.length !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.name === 'a') {
|
||||
const hasJsLinks = Object.entries(node.attributes).some(
|
||||
([attrKey, attrValue]) =>
|
||||
(attrKey === 'href' || attrKey.endsWith(':href')) &&
|
||||
attrValue != null &&
|
||||
attrValue.trimStart().startsWith('javascript:'),
|
||||
);
|
||||
|
||||
if (hasJsLinks) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const eventAttrs = [
|
||||
...attrsGroups.animationEvent,
|
||||
...attrsGroups.documentEvent,
|
||||
...attrsGroups.documentElementEvent,
|
||||
...attrsGroups.globalEvent,
|
||||
...attrsGroups.graphicalEvent,
|
||||
];
|
||||
|
||||
return eventAttrs.some((attr) => node.attributes[attr] != null);
|
||||
};
|
||||
|
||||
/**
|
||||
* For example, a string that contains one or more of following would match and
|
||||
* return true:
|
||||
*
|
||||
* * `url(#gradient001)`
|
||||
* * `url('#gradient001')`
|
||||
*
|
||||
* @param {string} body
|
||||
* @returns {boolean} If the given string includes a URL reference.
|
||||
*/
|
||||
export const includesUrlReference = (body) => {
|
||||
return new RegExp(regReferencesUrl).test(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} attribute
|
||||
* @param {string} value
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export const findReferences = (attribute, value) => {
|
||||
const results = [];
|
||||
|
||||
if (referencesProps.has(attribute)) {
|
||||
const matches = value.matchAll(regReferencesUrl);
|
||||
for (const match of matches) {
|
||||
results.push(match[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute === 'href' || attribute.endsWith(':href')) {
|
||||
const match = regReferencesHref.exec(value);
|
||||
if (match != null) {
|
||||
results.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute === 'begin') {
|
||||
const match = regReferencesBegin.exec(value);
|
||||
if (match != null) {
|
||||
results.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return results.map((body) => decodeURI(body));
|
||||
};
|
||||
|
||||
/**
|
||||
* Does the same as {@link Number.toFixed} but without casting
|
||||
* the return value to a string.
|
||||
*
|
||||
* @param {number} num
|
||||
* @param {number} precision
|
||||
* @returns {number}
|
||||
*/
|
||||
export const toFixed = (num, precision) => {
|
||||
const pow = 10 ** precision;
|
||||
return Math.round(num * pow) / pow;
|
||||
};
|
||||
exports.removeLeadingZero = removeLeadingZero;
|
||||
|
||||
1
node_modules/svgo/lib/types.js
generated
vendored
Normal file
1
node_modules/svgo/lib/types.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
208
node_modules/svgo/lib/types.ts
generated
vendored
208
node_modules/svgo/lib/types.ts
generated
vendored
@@ -1,3 +1,154 @@
|
||||
import { AddAttributesToSVGElementParams } from '../plugins/addAttributesToSVGElement.js';
|
||||
import { AddClassesToSVGElementParams } from '../plugins/addClassesToSVGElement.js';
|
||||
import { CleanupAttrsParams } from '../plugins/cleanupAttrs.js';
|
||||
import { CleanupIdsParams } from '../plugins/cleanupIds.js';
|
||||
import { CleanupListOfValuesParams } from '../plugins/cleanupListOfValues.js';
|
||||
import { CleanupNumericValuesParams } from '../plugins/cleanupNumericValues.js';
|
||||
import { ConvertColorsParams } from '../plugins/convertColors.js';
|
||||
import { ConvertPathDataParams } from '../plugins/convertPathData.js';
|
||||
import { ConvertShapeToPathParams } from '../plugins/convertShapeToPath.js';
|
||||
import { ConvertStyleToAttrsParams } from '../plugins/convertStyleToAttrs.js';
|
||||
import { ConvertTransformParams } from '../plugins/convertTransform.js';
|
||||
import { InlineStylesParams } from '../plugins/inlineStyles.js';
|
||||
import { MergePathsParams } from '../plugins/mergePaths.js';
|
||||
import { MinifyStylesParams } from '../plugins/minifyStyles.js';
|
||||
import { PrefixIdsParams } from '../plugins/prefixIds.js';
|
||||
import { RemoveAttrsParams } from '../plugins/removeAttrs.js';
|
||||
import { RemoveCommentsParams } from '../plugins/removeComments.js';
|
||||
import { RemoveDeprecatedAttrsParams } from '../plugins/removeDeprecatedAttrs.js';
|
||||
import { RemoveDescParams } from '../plugins/removeDesc.js';
|
||||
import { RemoveEditorsNSDataParams } from '../plugins/removeEditorsNSData.js';
|
||||
import { RemoveElementsByAttrParams } from '../plugins/removeElementsByAttr.js';
|
||||
import { RemoveEmptyTextParams } from '../plugins/removeEmptyText.js';
|
||||
import { RemoveHiddenElemsParams } from '../plugins/removeHiddenElems.js';
|
||||
import { RemoveUnknownsAndDefaultsParams } from '../plugins/removeUnknownsAndDefaults.js';
|
||||
import { RemoveUselessStrokeAndFillParams } from '../plugins/removeUselessStrokeAndFill.js';
|
||||
import { RemoveXlinkParams } from '../plugins/removeXlink.js';
|
||||
import { SortAttrsParams } from '../plugins/sortAttrs.js';
|
||||
|
||||
export type DefaultPlugins = {
|
||||
cleanupAttrs: CleanupAttrsParams;
|
||||
cleanupEnableBackground: null;
|
||||
cleanupIds: CleanupIdsParams;
|
||||
cleanupNumericValues: CleanupNumericValuesParams;
|
||||
collapseGroups: null;
|
||||
convertColors: ConvertColorsParams;
|
||||
convertEllipseToCircle: null;
|
||||
convertPathData: ConvertPathDataParams;
|
||||
convertShapeToPath: ConvertShapeToPathParams;
|
||||
convertTransform: ConvertTransformParams;
|
||||
mergeStyles: null;
|
||||
inlineStyles: InlineStylesParams;
|
||||
mergePaths: MergePathsParams;
|
||||
minifyStyles: MinifyStylesParams;
|
||||
moveElemsAttrsToGroup: null;
|
||||
moveGroupAttrsToElems: null;
|
||||
removeComments: RemoveCommentsParams;
|
||||
removeDeprecatedAttrs: RemoveDeprecatedAttrsParams;
|
||||
removeDesc: RemoveDescParams;
|
||||
removeDoctype: null;
|
||||
removeEditorsNSData: RemoveEditorsNSDataParams;
|
||||
removeEmptyAttrs: null;
|
||||
removeEmptyContainers: null;
|
||||
removeEmptyText: RemoveEmptyTextParams;
|
||||
removeHiddenElems: RemoveHiddenElemsParams;
|
||||
removeMetadata: null;
|
||||
removeNonInheritableGroupAttrs: null;
|
||||
removeUnknownsAndDefaults: RemoveUnknownsAndDefaultsParams;
|
||||
removeUnusedNS: null;
|
||||
removeUselessDefs: null;
|
||||
removeUselessStrokeAndFill: RemoveUselessStrokeAndFillParams;
|
||||
removeXMLProcInst: null;
|
||||
sortAttrs: SortAttrsParams;
|
||||
sortDefsChildren: null;
|
||||
};
|
||||
|
||||
export type PresetDefaultOverrides = {
|
||||
[Name in keyof DefaultPlugins]?: DefaultPlugins[Name] | false;
|
||||
};
|
||||
|
||||
export type BuiltinsWithOptionalParams = DefaultPlugins & {
|
||||
'preset-default': {
|
||||
floatPrecision?: number;
|
||||
/**
|
||||
* All default plugins can be customized or disabled here
|
||||
* for example
|
||||
* {
|
||||
* sortAttrs: { xmlnsOrder: "alphabetical" },
|
||||
* cleanupAttrs: false,
|
||||
* }
|
||||
*/
|
||||
overrides?: PresetDefaultOverrides;
|
||||
};
|
||||
cleanupListOfValues: CleanupListOfValuesParams;
|
||||
convertOneStopGradients: null;
|
||||
convertStyleToAttrs: ConvertStyleToAttrsParams;
|
||||
prefixIds: PrefixIdsParams;
|
||||
removeDimensions: null;
|
||||
removeOffCanvasPaths: null;
|
||||
removeRasterImages: null;
|
||||
removeScripts: null;
|
||||
removeStyleElement: null;
|
||||
removeTitle: null;
|
||||
removeViewBox: null;
|
||||
removeXlink: RemoveXlinkParams;
|
||||
removeXMLNS: null;
|
||||
reusePaths: null;
|
||||
};
|
||||
|
||||
export type BuiltinsWithRequiredParams = {
|
||||
addAttributesToSVGElement: AddAttributesToSVGElementParams;
|
||||
addClassesToSVGElement: AddClassesToSVGElementParams;
|
||||
removeAttributesBySelector: any;
|
||||
removeAttrs: RemoveAttrsParams;
|
||||
removeElementsByAttr: RemoveElementsByAttrParams;
|
||||
};
|
||||
|
||||
export type PluginsParams = BuiltinsWithOptionalParams &
|
||||
BuiltinsWithRequiredParams;
|
||||
|
||||
export type CustomPlugin<T = any> = {
|
||||
name: string;
|
||||
fn: Plugin<T>;
|
||||
params?: T;
|
||||
};
|
||||
|
||||
export type PluginConfig =
|
||||
| keyof BuiltinsWithOptionalParams
|
||||
| {
|
||||
[Name in keyof BuiltinsWithOptionalParams]: {
|
||||
name: Name;
|
||||
params?: BuiltinsWithOptionalParams[Name];
|
||||
};
|
||||
}[keyof BuiltinsWithOptionalParams]
|
||||
| {
|
||||
[Name in keyof BuiltinsWithRequiredParams]: {
|
||||
name: Name;
|
||||
params: BuiltinsWithRequiredParams[Name];
|
||||
};
|
||||
}[keyof BuiltinsWithRequiredParams]
|
||||
| CustomPlugin;
|
||||
|
||||
export type BuiltinPlugin<Name extends string, Params> = {
|
||||
/** Name of the plugin, also known as the plugin ID. */
|
||||
name: Name;
|
||||
description?: string;
|
||||
fn: Plugin<Params>;
|
||||
};
|
||||
|
||||
export type BuiltinPluginOrPreset<Name extends string, Params> = BuiltinPlugin<
|
||||
Name,
|
||||
Params
|
||||
> & {
|
||||
/** If the plugin is itself a preset that invokes other plugins. */
|
||||
isPreset?: true;
|
||||
/**
|
||||
* If the plugin is a preset that invokes other plugins, this returns an
|
||||
* array of the plugins in the preset in the order that they are invoked.
|
||||
*/
|
||||
plugins?: ReadonlyArray<BuiltinPlugin<string, Object>>;
|
||||
};
|
||||
|
||||
export type XastDoctype = {
|
||||
type: 'doctype';
|
||||
name: string;
|
||||
@@ -31,7 +182,7 @@ export type XastElement = {
|
||||
type: 'element';
|
||||
name: string;
|
||||
attributes: Record<string, string>;
|
||||
children: Array<XastChild>;
|
||||
children: XastChild[];
|
||||
};
|
||||
|
||||
export type XastChild =
|
||||
@@ -44,7 +195,7 @@ export type XastChild =
|
||||
|
||||
export type XastRoot = {
|
||||
type: 'root';
|
||||
children: Array<XastChild>;
|
||||
children: XastChild[];
|
||||
};
|
||||
|
||||
export type XastParent = XastRoot | XastElement;
|
||||
@@ -80,12 +231,12 @@ export type StringifyOptions = {
|
||||
finalNewline?: boolean;
|
||||
};
|
||||
|
||||
type VisitorNode<Node> = {
|
||||
export type VisitorNode<Node> = {
|
||||
enter?: (node: Node, parentNode: XastParent) => void | symbol;
|
||||
exit?: (node: Node, parentNode: XastParent) => void;
|
||||
};
|
||||
|
||||
type VisitorRoot = {
|
||||
export type VisitorRoot = {
|
||||
enter?: (node: XastRoot, parentNode: null) => void;
|
||||
exit?: (node: XastRoot, parentNode: null) => void;
|
||||
};
|
||||
@@ -105,13 +256,13 @@ export type PluginInfo = {
|
||||
multipassCount: number;
|
||||
};
|
||||
|
||||
export type Plugin<Params> = (
|
||||
export type Plugin<P = null> = (
|
||||
root: XastRoot,
|
||||
params: Params,
|
||||
info: PluginInfo
|
||||
) => null | Visitor;
|
||||
params: P,
|
||||
info: PluginInfo,
|
||||
) => Visitor | null | void;
|
||||
|
||||
export type Specificity = [number, number, number, number];
|
||||
export type Specificity = [number, number, number];
|
||||
|
||||
export type StylesheetDeclaration = {
|
||||
name: string;
|
||||
@@ -121,23 +272,23 @@ export type StylesheetDeclaration = {
|
||||
|
||||
export type StylesheetRule = {
|
||||
dynamic: boolean;
|
||||
selectors: string;
|
||||
selector: string;
|
||||
specificity: Specificity;
|
||||
declarations: Array<StylesheetDeclaration>;
|
||||
declarations: StylesheetDeclaration[];
|
||||
};
|
||||
|
||||
export type Stylesheet = {
|
||||
rules: Array<StylesheetRule>;
|
||||
rules: StylesheetRule[];
|
||||
parents: Map<XastElement, XastParent>;
|
||||
};
|
||||
|
||||
type StaticStyle = {
|
||||
export type StaticStyle = {
|
||||
type: 'static';
|
||||
inherited: boolean;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type DynamicStyle = {
|
||||
export type DynamicStyle = {
|
||||
type: 'dynamic';
|
||||
inherited: boolean;
|
||||
};
|
||||
@@ -168,5 +319,32 @@ export type PathDataCommand =
|
||||
|
||||
export type PathDataItem = {
|
||||
command: PathDataCommand;
|
||||
args: Array<number>;
|
||||
args: number[];
|
||||
};
|
||||
|
||||
export type DataUri = 'base64' | 'enc' | 'unenc';
|
||||
|
||||
export type Config = {
|
||||
/** Can be used by plugins, for example prefixIds. */
|
||||
path?: string;
|
||||
/** Pass over SVGs multiple times to ensure all optimizations are applied. */
|
||||
multipass?: boolean;
|
||||
/**
|
||||
* Precision of floating point numbers. Will be passed to each plugin that
|
||||
* supports this param.
|
||||
*/
|
||||
floatPrecision?: number;
|
||||
/**
|
||||
* Plugins configuration. By default SVGO uses `preset-default`, but may
|
||||
* contain builtin or custom plugins.
|
||||
*/
|
||||
plugins?: PluginConfig[];
|
||||
/** Options for rendering optimized SVG from AST. */
|
||||
js2svg?: StringifyOptions;
|
||||
/** Output as Data URI string. */
|
||||
datauri?: DataUri;
|
||||
};
|
||||
|
||||
export type Output = {
|
||||
data: string;
|
||||
};
|
||||
|
||||
29
node_modules/svgo/lib/util/map-nodes-to-parents.js
generated
vendored
Normal file
29
node_modules/svgo/lib/util/map-nodes-to-parents.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import { visit } from './visit.js';
|
||||
|
||||
/**
|
||||
* Maps all nodes to their parent node recursively.
|
||||
*
|
||||
* @param {import('../types.js').XastParent} node
|
||||
* @returns {Map<import('../types.js').XastNode, import('../types.js').XastParent>}
|
||||
*/
|
||||
export function mapNodesToParents(node) {
|
||||
/** @type {Map<import('../types.js').XastNode, import('../types.js').XastParent>} */
|
||||
const parents = new Map();
|
||||
|
||||
for (const child of node.children) {
|
||||
parents.set(child, node);
|
||||
visit(
|
||||
child,
|
||||
{
|
||||
element: {
|
||||
enter: (child, parent) => {
|
||||
parents.set(child, parent);
|
||||
},
|
||||
},
|
||||
},
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
return parents;
|
||||
}
|
||||
36
node_modules/svgo/lib/util/visit.js
generated
vendored
Normal file
36
node_modules/svgo/lib/util/visit.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
export const visitSkip = Symbol();
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').XastNode} node
|
||||
* @param {import('../types.js').Visitor} visitor
|
||||
* @param {any=} parentNode
|
||||
*/
|
||||
export const visit = (node, visitor, parentNode) => {
|
||||
const callbacks = visitor[node.type];
|
||||
if (callbacks?.enter) {
|
||||
// @ts-expect-error 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 lose 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?.exit) {
|
||||
// @ts-expect-error hard to infer
|
||||
callbacks.exit(node, parentNode);
|
||||
}
|
||||
};
|
||||
7
node_modules/svgo/lib/version.js
generated
vendored
Normal file
7
node_modules/svgo/lib/version.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Version of SVGO.
|
||||
*
|
||||
* @type {string}
|
||||
* @since 4.0.0
|
||||
*/
|
||||
export const VERSION = '4.0.0';
|
||||
117
node_modules/svgo/lib/xast.js
generated
vendored
117
node_modules/svgo/lib/xast.js
generated
vendored
@@ -1,102 +1,53 @@
|
||||
'use strict';
|
||||
import { is, selectAll, selectOne } from 'css-select';
|
||||
import { createAdapter } from './svgo/css-select-adapter.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').XastNode} XastNode
|
||||
* @typedef {import('./types').XastChild} XastChild
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
* @typedef {import('./types').Visitor} Visitor
|
||||
* @param {import('./types.js').XastParent} relativeNode
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {import('css-select').Options<import('./types.js').XastNode & { children?: any }, import('./types.js').XastElement>}
|
||||
*/
|
||||
function createCssSelectOptions(relativeNode, parents) {
|
||||
return {
|
||||
xmlMode: true,
|
||||
adapter: createAdapter(relativeNode, parents),
|
||||
};
|
||||
}
|
||||
|
||||
const { selectAll, selectOne, is } = require('css-select');
|
||||
const xastAdaptor = require('./svgo/css-select-adapter.js');
|
||||
|
||||
const cssSelectOptions = {
|
||||
xmlMode: true,
|
||||
adapter: xastAdaptor,
|
||||
/**
|
||||
* @param {import('./types.js').XastParent} node Element to query the children of.
|
||||
* @param {string} selector CSS selector string.
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {import('./types.js').XastChild[]} All matching elements.
|
||||
*/
|
||||
export const querySelectorAll = (node, selector, parents) => {
|
||||
return selectAll(selector, node, createCssSelectOptions(node, parents));
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastNode, selector: string) => Array<XastChild>}
|
||||
* @param {import('./types.js').XastParent} node Element to query the children of.
|
||||
* @param {string} selector CSS selector string.
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {?import('./types.js').XastChild} First match, or null if there was no match.
|
||||
*/
|
||||
const querySelectorAll = (node, selector) => {
|
||||
return selectAll(selector, node, cssSelectOptions);
|
||||
export const querySelector = (node, selector, parents) => {
|
||||
return selectOne(selector, node, createCssSelectOptions(node, parents));
|
||||
};
|
||||
exports.querySelectorAll = querySelectorAll;
|
||||
|
||||
/**
|
||||
* @type {(node: XastNode, selector: string) => null | XastChild}
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {string} selector
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const querySelector = (node, selector) => {
|
||||
return selectOne(selector, node, cssSelectOptions);
|
||||
export const matches = (node, selector, parents) => {
|
||||
return is(node, selector, createCssSelectOptions(node, parents));
|
||||
};
|
||||
exports.querySelector = querySelector;
|
||||
|
||||
/**
|
||||
* @type {(node: XastChild, selector: string) => boolean}
|
||||
* @param {import('./types.js').XastChild} node
|
||||
* @param {import('./types.js').XastParent} parentNode
|
||||
*/
|
||||
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;
|
||||
};
|
||||
exports.closestByName = closestByName;
|
||||
|
||||
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) => {
|
||||
export const detachNodeFromParent = (node, parentNode) => {
|
||||
// avoid splice to not break for loops
|
||||
parentNode.children = parentNode.children.filter((child) => child !== node);
|
||||
};
|
||||
exports.detachNodeFromParent = detachNodeFromParent;
|
||||
|
||||
Reference in New Issue
Block a user