diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2026-05-18 23:10:59 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2026-05-18 23:41:44 +0800 |
| commit | fc87c009806dcc98867f6b58efe29b042aa01dee (patch) | |
| tree | 8f59b3f4a5b66e9dfef2d40c8d318e258a521228 /node_modules/svgo/lib | |
| parent | 3336b2356ee6152d9c6ae59bd9b87ccbc0958407 (diff) | |
node: updatemain
Diffstat (limited to 'node_modules/svgo/lib')
| -rw-r--r-- | node_modules/svgo/lib/builtin.js | 57 | ||||
| -rw-r--r-- | node_modules/svgo/lib/parser.js | 262 | ||||
| -rw-r--r-- | node_modules/svgo/lib/path.js | 380 | ||||
| -rw-r--r-- | node_modules/svgo/lib/stringifier.js | 295 | ||||
| -rw-r--r-- | node_modules/svgo/lib/style.js | 336 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo-node.js | 85 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo.d.ts | 69 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo.js | 102 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo/coa.js | 528 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo/css-select-adapter.d.ts | 2 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo/css-select-adapter.js | 120 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo/plugins.js | 61 | ||||
| -rw-r--r-- | node_modules/svgo/lib/svgo/tools.js | 245 | ||||
| -rw-r--r-- | node_modules/svgo/lib/types.d.ts | 174 | ||||
| -rw-r--r-- | node_modules/svgo/lib/xast.js | 87 |
15 files changed, 0 insertions, 2803 deletions
diff --git a/node_modules/svgo/lib/builtin.js b/node_modules/svgo/lib/builtin.js deleted file mode 100644 index 28a380ec..00000000 --- a/node_modules/svgo/lib/builtin.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -exports.builtin = [ - require('../plugins/preset-default.js'), - require('../plugins/addAttributesToSVGElement.js'), - require('../plugins/addClassesToSVGElement.js'), - require('../plugins/cleanupAttrs.js'), - require('../plugins/cleanupEnableBackground.js'), - require('../plugins/cleanupIds.js'), - require('../plugins/cleanupListOfValues.js'), - require('../plugins/cleanupNumericValues.js'), - require('../plugins/collapseGroups.js'), - require('../plugins/convertColors.js'), - require('../plugins/convertEllipseToCircle.js'), - require('../plugins/convertOneStopGradients.js'), - require('../plugins/convertPathData.js'), - require('../plugins/convertShapeToPath.js'), - require('../plugins/convertStyleToAttrs.js'), - require('../plugins/convertTransform.js'), - require('../plugins/mergeStyles.js'), - require('../plugins/inlineStyles.js'), - require('../plugins/mergePaths.js'), - require('../plugins/minifyStyles.js'), - require('../plugins/moveElemsAttrsToGroup.js'), - require('../plugins/moveGroupAttrsToElems.js'), - require('../plugins/prefixIds.js'), - require('../plugins/removeAttributesBySelector.js'), - require('../plugins/removeAttrs.js'), - require('../plugins/removeComments.js'), - require('../plugins/removeDesc.js'), - require('../plugins/removeDimensions.js'), - require('../plugins/removeDoctype.js'), - require('../plugins/removeEditorsNSData.js'), - require('../plugins/removeElementsByAttr.js'), - require('../plugins/removeEmptyAttrs.js'), - require('../plugins/removeEmptyContainers.js'), - require('../plugins/removeEmptyText.js'), - require('../plugins/removeHiddenElems.js'), - require('../plugins/removeMetadata.js'), - require('../plugins/removeNonInheritableGroupAttrs.js'), - require('../plugins/removeOffCanvasPaths.js'), - require('../plugins/removeRasterImages.js'), - require('../plugins/removeScriptElement.js'), - require('../plugins/removeStyleElement.js'), - require('../plugins/removeTitle.js'), - require('../plugins/removeUnknownsAndDefaults.js'), - require('../plugins/removeUnusedNS.js'), - require('../plugins/removeUselessDefs.js'), - require('../plugins/removeUselessStrokeAndFill.js'), - require('../plugins/removeViewBox.js'), - require('../plugins/removeXlink.js'), - require('../plugins/removeXMLNS.js'), - require('../plugins/removeXMLProcInst.js'), - require('../plugins/reusePaths.js'), - require('../plugins/sortAttrs.js'), - require('../plugins/sortDefsChildren.js'), -]; diff --git a/node_modules/svgo/lib/parser.js b/node_modules/svgo/lib/parser.js deleted file mode 100644 index 4637acf1..00000000 --- a/node_modules/svgo/lib/parser.js +++ /dev/null @@ -1,262 +0,0 @@ -'use strict'; - -/** - * @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 - * @typedef {import('./types').XastChild} XastChild - */ - -// @ts-ignore sax will be replaced with something else later -const SAX = require('@trysound/sax'); -const { textElems } = require('../plugins/_collections'); - -class SvgoParserError extends Error { - /** - * @param message {string} - * @param line {number} - * @param column {number} - * @param source {string} - * @param file {void | string} - */ - constructor(message, line, column, source, file) { - super(message); - this.name = 'SvgoParserError'; - this.message = `${file || '<input>'}:${line}:${column}: ${message}`; - this.reason = message; - this.line = line; - this.column = column; - this.source = source; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, SvgoParserError); - } - } - toString() { - const lines = this.source.split(/\r?\n/); - const startLine = Math.max(this.line - 3, 0); - const endLine = Math.min(this.line + 2, lines.length); - const lineNumberWidth = String(endLine).length; - const startColumn = Math.max(this.column - 54, 0); - const endColumn = Math.max(this.column + 20, 80); - const code = lines - .slice(startLine, endLine) - .map((line, index) => { - const lineSlice = line.slice(startColumn, endColumn); - let ellipsisPrefix = ''; - let ellipsisSuffix = ''; - if (startColumn !== 0) { - ellipsisPrefix = startColumn > line.length - 1 ? ' ' : '…'; - } - if (endColumn < line.length - 1) { - ellipsisSuffix = '…'; - } - const number = startLine + 1 + index; - const gutter = ` ${number.toString().padStart(lineNumberWidth)} | `; - if (number === this.line) { - const gutterSpacing = gutter.replace(/[^|]/g, ' '); - const lineSpacing = ( - ellipsisPrefix + line.slice(startColumn, this.column - 1) - ).replace(/[^\t]/g, ' '); - const spacing = gutterSpacing + lineSpacing; - return `>${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}\n ${spacing}^`; - } - return ` ${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}`; - }) - .join('\n'); - return `${this.name}: ${this.message}\n\n${code}\n`; - } -} - -const entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g; - -const config = { - strict: true, - trim: false, - normalize: false, - lowercase: true, - xmlns: true, - position: true, -}; - -/** - * Convert SVG (XML) string to SVG-as-JS object. - * - * @type {(data: string, from?: string) => XastRoot} - */ -const parseSvg = (data, from) => { - const sax = SAX.parser(config.strict, config); - /** - * @type {XastRoot} - */ - const root = { type: 'root', children: [] }; - /** - * @type {XastParent} - */ - let current = root; - /** - * @type {XastParent[]} - */ - const stack = [root]; - - /** - * @type {(node: XastChild) => void} - */ - const pushToContent = (node) => { - // TODO remove legacy parentNode in v4 - Object.defineProperty(node, 'parentNode', { - writable: true, - value: current, - }); - current.children.push(node); - }; - - /** - * @type {(doctype: string) => void} - */ - sax.ondoctype = (doctype) => { - /** - * @type {XastDoctype} - */ - const node = { - type: 'doctype', - // TODO parse doctype for name, public and system to match xast - name: 'svg', - data: { - doctype, - }, - }; - pushToContent(node); - const subsetStart = doctype.indexOf('['); - if (subsetStart >= 0) { - entityDeclaration.lastIndex = subsetStart; - let entityMatch = entityDeclaration.exec(data); - while (entityMatch != null) { - sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3]; - entityMatch = entityDeclaration.exec(data); - } - } - }; - - /** - * @type {(data: { name: string, body: string }) => void} - */ - sax.onprocessinginstruction = (data) => { - /** - * @type {XastInstruction} - */ - const node = { - type: 'instruction', - name: data.name, - value: data.body, - }; - pushToContent(node); - }; - - /** - * @type {(comment: string) => void} - */ - sax.oncomment = (comment) => { - /** - * @type {XastComment} - */ - const node = { - type: 'comment', - value: comment.trim(), - }; - pushToContent(node); - }; - - /** - * @type {(cdata: string) => void} - */ - sax.oncdata = (cdata) => { - /** - * @type {XastCdata} - */ - const node = { - type: 'cdata', - value: cdata, - }; - pushToContent(node); - }; - - /** - * @type {(data: { name: string, attributes: Record<string, { value: string }>}) => void} - */ - sax.onopentag = (data) => { - /** - * @type {XastElement} - */ - let element = { - type: 'element', - name: data.name, - attributes: {}, - children: [], - }; - for (const [name, attr] of Object.entries(data.attributes)) { - element.attributes[name] = attr.value; - } - 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.has(current.name)) { - /** - * @type {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); - } - } - }; - - sax.onclosetag = () => { - stack.pop(); - current = stack[stack.length - 1]; - }; - - /** - * @type {(e: any) => void} - */ - sax.onerror = (e) => { - const error = new SvgoParserError( - e.reason, - e.line + 1, - e.column, - data, - from, - ); - if (e.message.indexOf('Unexpected end') === -1) { - throw error; - } - }; - - sax.write(data).close(); - return root; -}; -exports.parseSvg = parseSvg; diff --git a/node_modules/svgo/lib/path.js b/node_modules/svgo/lib/path.js deleted file mode 100644 index 814ee2e3..00000000 --- a/node_modules/svgo/lib/path.js +++ /dev/null @@ -1,380 +0,0 @@ -'use strict'; - -const { removeLeadingZero, toFixed } = require('./svgo/tools'); - -/** - * @typedef {import('./types').PathDataItem} PathDataItem - * @typedef {import('./types').PathDataCommand} PathDataCommand - */ - -// Based on https://www.w3.org/TR/SVG11/paths.html#PathDataBNF - -const argsCountPerCommand = { - M: 2, - m: 2, - Z: 0, - z: 0, - L: 2, - l: 2, - H: 1, - h: 1, - V: 1, - v: 1, - C: 6, - c: 6, - S: 4, - s: 4, - Q: 4, - q: 4, - T: 2, - t: 2, - A: 7, - a: 7, -}; - -/** - * @type {(c: string) => c is PathDataCommand} - */ -const isCommand = (c) => { - return c in argsCountPerCommand; -}; - -/** - * @type {(c: string) => boolean} - */ -const isWsp = (c) => { - const codePoint = c.codePointAt(0); - return ( - codePoint === 0x20 || - codePoint === 0x9 || - codePoint === 0xd || - codePoint === 0xa - ); -}; - -/** - * @type {(c: string) => boolean} - */ -const isDigit = (c) => { - const codePoint = c.codePointAt(0); - if (codePoint == null) { - return false; - } - return 48 <= codePoint && codePoint <= 57; -}; - -/** - * @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState - */ - -/** - * @type {(string: string, cursor: number) => [number, ?number]} - */ -const readNumber = (string, cursor) => { - let i = cursor; - let value = ''; - let state = /** @type {ReadNumberState} */ ('none'); - for (; i < string.length; i += 1) { - const c = string[i]; - if (c === '+' || c === '-') { - if (state === 'none') { - state = 'sign'; - value += c; - continue; - } - if (state === 'e') { - state = 'exponent_sign'; - value += c; - continue; - } - } - if (isDigit(c)) { - if (state === 'none' || state === 'sign' || state === 'whole') { - state = 'whole'; - value += c; - continue; - } - if (state === 'decimal_point' || state === 'decimal') { - state = 'decimal'; - value += c; - continue; - } - if (state === 'e' || state === 'exponent_sign' || state === 'exponent') { - state = 'exponent'; - value += c; - continue; - } - } - if (c === '.') { - if (state === 'none' || state === 'sign' || state === 'whole') { - state = 'decimal_point'; - value += c; - continue; - } - } - if (c === 'E' || c == 'e') { - if ( - state === 'whole' || - state === 'decimal_point' || - state === 'decimal' - ) { - state = 'e'; - value += c; - continue; - } - } - break; - } - const number = Number.parseFloat(value); - if (Number.isNaN(number)) { - return [cursor, null]; - } else { - // step back to delegate iteration to parent loop - return [i - 1, number]; - } -}; - -/** - * @type {(string: string) => PathDataItem[]} - */ -const parsePathData = (string) => { - /** - * @type {PathDataItem[]} - */ - const pathData = []; - /** - * @type {?PathDataCommand} - */ - let command = null; - let args = /** @type {number[]} */ ([]); - let argsCount = 0; - let canHaveComma = false; - let hadComma = false; - for (let i = 0; i < string.length; i += 1) { - const c = string.charAt(i); - if (isWsp(c)) { - continue; - } - // allow comma only between arguments - if (canHaveComma && c === ',') { - if (hadComma) { - break; - } - hadComma = true; - continue; - } - if (isCommand(c)) { - if (hadComma) { - return pathData; - } - if (command == null) { - // moveto should be leading command - if (c !== 'M' && c !== 'm') { - return pathData; - } - } else { - // stop if previous command arguments are not flushed - if (args.length !== 0) { - return pathData; - } - } - command = c; - args = []; - argsCount = argsCountPerCommand[command]; - canHaveComma = false; - // flush command without arguments - if (argsCount === 0) { - pathData.push({ command, args }); - } - continue; - } - // avoid parsing arguments if no command detected - if (command == null) { - return pathData; - } - // read next argument - let newCursor = i; - let number = null; - if (command === 'A' || command === 'a') { - const position = args.length; - if (position === 0 || position === 1) { - // allow only positive number without sign as first two arguments - if (c !== '+' && c !== '-') { - [newCursor, number] = readNumber(string, i); - } - } - if (position === 2 || position === 5 || position === 6) { - [newCursor, number] = readNumber(string, i); - } - if (position === 3 || position === 4) { - // read flags - if (c === '0') { - number = 0; - } - if (c === '1') { - number = 1; - } - } - } else { - [newCursor, number] = readNumber(string, i); - } - if (number == null) { - return pathData; - } - args.push(number); - canHaveComma = true; - hadComma = false; - i = newCursor; - // flush arguments when necessary count is reached - if (args.length === argsCount) { - pathData.push({ command, args }); - // subsequent moveto coordinates are treated as implicit lineto commands - if (command === 'M') { - command = 'L'; - } - if (command === 'm') { - command = 'l'; - } - args = []; - } - } - return pathData; -}; -exports.parsePathData = parsePathData; - -/** - * @type {(number: number, precision?: number) => { - * roundedStr: string, - * rounded: number - * }} - */ -const roundAndStringify = (number, precision) => { - if (precision != null) { - number = toFixed(number, precision); - } - - 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} - */ -const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => { - let result = ''; - 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 += roundedStr; - } else if (i === 0 || rounded < 0) { - // avoid space before first and negative numbers - result += roundedStr; - } else if ( - !Number.isInteger(previous) && - rounded != 0 && - rounded < 1 && - rounded > -1 - ) { - // remove space before decimal with zero whole - // only when previous number is also decimal - result += roundedStr; - } else { - result += ` ${roundedStr}`; - } - previous = rounded; - } - - return result; -}; - -/** - * @typedef {{ - * pathData: PathDataItem[]; - * precision?: number; - * disableSpaceAfterFlags?: boolean; - * }} StringifyPathDataOptions - */ - -/** - * @param {StringifyPathDataOptions} options - * @returns {string} - */ -const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => { - if (pathData.length === 1) { - const { command, args } = pathData[0]; - return ( - command + stringifyArgs(command, args, precision, disableSpaceAfterFlags) - ); - } - - let result = ''; - 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; diff --git a/node_modules/svgo/lib/stringifier.js b/node_modules/svgo/lib/stringifier.js deleted file mode 100644 index 1a710869..00000000 --- a/node_modules/svgo/lib/stringifier.js +++ /dev/null @@ -1,295 +0,0 @@ -'use strict'; - -/** - * @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'); - -/** - * @typedef {{ - * indent: string, - * textContext: ?XastElement, - * indentLevel: number, - * }} State - */ - -/** - * @typedef {Required<StringifyOptions>} Options - */ - -/** - * @type {(char: string) => string} - */ -const encodeEntity = (char) => { - return entities[char]; -}; - -/** @type {Options} */ -const defaults = { - doctypeStart: '<!DOCTYPE', - doctypeEnd: '>', - procInstStart: '<?', - procInstEnd: '?>', - tagOpenStart: '<', - tagOpenEnd: '>', - tagCloseStart: '</', - tagCloseEnd: '>', - tagShortStart: '<', - tagShortEnd: '/>', - attrStart: '="', - attrEnd: '"', - commentStart: '<!--', - commentEnd: '-->', - cdataStart: '<![CDATA[', - cdataEnd: ']]>', - textStart: '', - textEnd: '', - indent: 4, - regEntities: /[&'"<>]/g, - regValEntities: /[&"<>]/g, - encodeEntity, - pretty: false, - useShortTags: true, - eol: 'lf', - finalNewline: false, -}; - -/** @type {Record<string, string>} */ -const entities = { - '&': '&', - "'": ''', - '"': '"', - '>': '>', - '<': '<', -}; - -/** - * convert XAST to SVG string - * - * @type {(data: XastRoot, config: StringifyOptions) => string} - */ -const stringifySvg = (data, userOptions = {}) => { - /** - * @type {Options} - */ - const config = { ...defaults, ...userOptions }; - const indent = config.indent; - let newIndent = ' '; - if (typeof indent === 'number' && Number.isNaN(indent) === false) { - newIndent = indent < 0 ? '\t' : ' '.repeat(indent); - } else if (typeof indent === 'string') { - newIndent = indent; - } - /** - * @type {State} - */ - const state = { - indent: newIndent, - textContext: null, - indentLevel: 0, - }; - const eol = config.eol === 'crlf' ? '\r\n' : '\n'; - if (config.pretty) { - config.doctypeEnd += eol; - config.procInstEnd += eol; - config.commentEnd += eol; - config.cdataEnd += eol; - config.tagShortEnd += eol; - config.tagOpenEnd += eol; - config.tagCloseEnd += eol; - config.textEnd += eol; - } - let svg = stringifyNode(data, config, state); - if (config.finalNewline && svg.length > 0 && !svg.endsWith('\n')) { - svg += eol; - } - return svg; -}; -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. - * - * @type {(config: Options, state: State) => string} - */ -const createIndent = (config, state) => { - let indent = ''; - if (config.pretty && state.textContext == null) { - indent = state.indent.repeat(state.indentLevel - 1); - } - return indent; -}; - -/** - * @type {(node: XastDoctype, config: Options) => string} - */ -const stringifyDoctype = (node, config) => { - return config.doctypeStart + node.data.doctype + config.doctypeEnd; -}; - -/** - * @type {(node: XastInstruction, config: Options) => string} - */ -const stringifyInstruction = (node, config) => { - return ( - config.procInstStart + node.name + ' ' + node.value + config.procInstEnd - ); -}; - -/** - * @type {(node: XastComment, config: Options) => string} - */ -const stringifyComment = (node, config) => { - return config.commentStart + node.value + config.commentEnd; -}; - -/** - * @type {(node: XastCdata, config: Options, state: State) => string} - */ -const stringifyCdata = (node, config, state) => { - return ( - createIndent(config, state) + - config.cdataStart + - node.value + - config.cdataEnd - ); -}; - -/** - * @type {(node: XastElement, config: Options, state: State) => string} - */ -const stringifyElement = (node, config, state) => { - // empty element and short tag - if (node.children.length === 0) { - if (config.useShortTags) { - return ( - createIndent(config, state) + - config.tagShortStart + - node.name + - 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.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} - */ -const stringifyAttributes = (node, config) => { - let attrs = ''; - for (const [name, value] of Object.entries(node.attributes)) { - // TODO remove attributes without values support in v3 - if (value !== undefined) { - const encodedValue = value - .toString() - .replace(config.regValEntities, config.encodeEntity); - attrs += ' ' + name + config.attrStart + encodedValue + config.attrEnd; - } else { - attrs += ' ' + name; - } - } - return attrs; -}; - -/** - * @type {(node: XastText, config: Options, state: State) => string} - */ -const stringifyText = (node, config, state) => { - return ( - createIndent(config, state) + - config.textStart + - node.value.replace(config.regEntities, config.encodeEntity) + - (state.textContext ? '' : config.textEnd) - ); -}; diff --git a/node_modules/svgo/lib/style.js b/node_modules/svgo/lib/style.js deleted file mode 100644 index 0e62afb9..00000000 --- a/node_modules/svgo/lib/style.js +++ /dev/null @@ -1,336 +0,0 @@ -'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 csstree = require('css-tree'); -const csswhat = require('css-what'); -const { - syntax: { specificity }, -} = require('csso'); -const { visit, matches } = require('./xast.js'); -const { - attrsGroups, - inheritableAttrs, - presentationNonInheritableGroupAttrs, -} = require('../plugins/_collections.js'); - -// @ts-ignore not defined in @types/csstree -const csstreeWalkSkip = csstree.walk.skip; - -/** - * @type {(ruleNode: CsstreeRule, dynamic: boolean) => StylesheetRule[]} - */ -const parseRule = (ruleNode, dynamic) => { - /** - * @type {StylesheetDeclaration[]} - */ - const declarations = []; - // 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, - }); - } - }); - - /** @type {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: 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) => StylesheetRule[]} - */ -const parseStylesheet = (css, dynamic) => { - /** @type {StylesheetRule[]} */ - const rules = []; - const ast = csstree.parse(css, { - parseValue: false, - parseAtrulePrelude: false, - }); - csstree.walk(ast, (cssNode) => { - if (cssNode.type === 'Rule') { - rules.push(...parseRule(cssNode, dynamic || false)); - return csstreeWalkSkip; - } - if (cssNode.type === 'Atrule') { - if ( - cssNode.name === 'keyframes' || - cssNode.name === '-webkit-keyframes' - ) { - return csstreeWalkSkip; - } - csstree.walk(cssNode, (ruleNode) => { - if (ruleNode.type === 'Rule') { - rules.push(...parseRule(ruleNode, dynamic || true)); - return csstreeWalkSkip; - } - }); - return csstreeWalkSkip; - } - }); - return rules; -}; - -/** - * @type {(css: string) => StylesheetDeclaration[]} - */ -const parseStyleDeclarations = (css) => { - /** @type {StylesheetDeclaration[]} */ - const declarations = []; - const ast = csstree.parse(css, { - context: 'declarationList', - parseValue: false, - }); - csstree.walk(ast, (cssNode) => { - if (cssNode.type === 'Declaration') { - declarations.push({ - name: cssNode.property, - value: csstree.generate(cssNode.value), - important: cssNode.important === true, - }); - } - }); - return declarations; -}; - -/** - * @param {Stylesheet} stylesheet - * @param {XastElement} node - * @returns {ComputedStyles} - */ -const computeOwnStyle = (stylesheet, node) => { - /** @type {ComputedStyles} */ - const computedStyle = {}; - const importantStyles = new Map(); - - // collect attributes - for (const [name, value] of Object.entries(node.attributes)) { - if (attrsGroups.presentation.has(name)) { - computedStyle[name] = { type: 'static', inherited: false, value }; - importantStyles.set(name, false); - } - } - - // collect matching rules - for (const { selector, declarations, dynamic } of stylesheet.rules) { - if (matches(node, selector)) { - for (const { name, value, important } of declarations) { - const computed = computedStyle[name]; - if (computed && computed.type === 'dynamic') { - continue; - } - if (dynamic) { - computedStyle[name] = { type: 'dynamic', inherited: false }; - continue; - } - if ( - computed == null || - important === true || - importantStyles.get(name) === false - ) { - computedStyle[name] = { type: 'static', inherited: false, value }; - importantStyles.set(name, important); - } - } - } - } - - // collect inline styles - const styleDeclarations = - node.attributes.style == null - ? [] - : parseStyleDeclarations(node.attributes.style); - for (const { name, value, important } of styleDeclarations) { - const computed = computedStyle[name]; - if (computed && computed.type === 'dynamic') { - continue; - } - if ( - computed == null || - important === true || - importantStyles.get(name) === false - ) { - computedStyle[name] = { type: 'static', inherited: false, value }; - importantStyles.set(name, important); - } - } - - return computedStyle; -}; - -/** - * Compares selector specificities. - * Derived from https://github.com/keeganstreet/specificity/blob/8757133ddd2ed0163f120900047ff0f92760b536/specificity.js#L207 - * - * @param {Specificity} a - * @param {Specificity} b - * @returns {number} - */ -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]) { - return 1; - } - } - - return 0; -}; -exports.compareSpecificity = compareSpecificity; - -/** - * @type {(root: XastRoot) => Stylesheet} - */ -const collectStylesheet = (root) => { - /** @type {StylesheetRule[]} */ - const rules = []; - /** @type {Map<XastElement, XastParent>} */ - const parents = new Map(); - - visit(root, { - element: { - enter: (node, parentNode) => { - parents.set(node, parentNode); - - 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'; - - for (const child of node.children) { - if (child.type === 'text' || child.type === 'cdata') { - rules.push(...parseStylesheet(child.value, dynamic)); - } - } - } - }, - }, - }); - // sort by selectors specificity - rules.sort((a, b) => compareSpecificity(a.specificity, b.specificity)); - return { rules, parents }; -}; -exports.collectStylesheet = collectStylesheet; - -/** - * @param {Stylesheet} stylesheet - * @param {XastElement} node - * @returns {ComputedStyles} - */ -const computeStyle = (stylesheet, node) => { - const { parents } = stylesheet; - const computedStyles = computeOwnStyle(stylesheet, node); - let parent = parents.get(node); - while (parent != null && parent.type !== 'root') { - const inheritedStyles = computeOwnStyle(stylesheet, parent); - for (const [name, computed] of Object.entries(inheritedStyles)) { - if ( - computedStyles[name] == null && - inheritableAttrs.has(name) && - !presentationNonInheritableGroupAttrs.has(name) - ) { - computedStyles[name] = { ...computed, inherited: true }; - } - } - parent = parents.get(parent); - } - 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} - */ -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; -}; -exports.includesAttrSelector = includesAttrSelector; diff --git a/node_modules/svgo/lib/svgo-node.js b/node_modules/svgo/lib/svgo-node.js deleted file mode 100644 index fc3ba8ab..00000000 --- a/node_modules/svgo/lib/svgo-node.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -const os = require('os'); -const fs = require('fs'); -const { pathToFileURL } = require('url'); -const path = require('path'); -const { optimize: optimizeAgnostic } = require('./svgo.js'); - -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 { - // 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; - } - if (config == null || typeof config !== 'object' || Array.isArray(config)) { - throw Error(`Invalid config file "${configFile}"`); - } - return config; -}; - -const isFile = async (file) => { - try { - const stats = await fs.promises.stat(file); - return stats.isFile(); - } catch { - return false; - } -}; - -const loadConfig = async (configFile, cwd = process.cwd()) => { - if (configFile != null) { - if (path.isAbsolute(configFile)) { - return await importConfig(configFile); - } else { - return await 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); - } - const mjs = path.join(dir, 'svgo.config.mjs'); - if (await isFile(mjs)) { - return await importConfig(mjs); - } - const cjs = path.join(dir, 'svgo.config.cjs'); - if (await isFile(cjs)) { - return await importConfig(cjs); - } - const parent = path.dirname(dir); - if (dir === parent) { - return null; - } - dir = parent; - } -}; -exports.loadConfig = loadConfig; - -const optimize = (input, config) => { - if (config == null) { - config = {}; - } - if (typeof config !== 'object') { - throw Error('Config should be an object'); - } - return optimizeAgnostic(input, { - ...config, - js2svg: { - // platform specific default for end of line - eol: os.EOL === '\r\n' ? 'crlf' : 'lf', - ...config.js2svg, - }, - }); -}; -exports.optimize = optimize; diff --git a/node_modules/svgo/lib/svgo.d.ts b/node_modules/svgo/lib/svgo.d.ts deleted file mode 100644 index d4e9e3c2..00000000 --- a/node_modules/svgo/lib/svgo.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { StringifyOptions, DataUri, Plugin as PluginFn } from './types'; -import type { - BuiltinsWithOptionalParams, - BuiltinsWithRequiredParams, -} from '../plugins/plugins-types'; - -type CustomPlugin = { - name: string; - fn: PluginFn<void>; -}; - -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 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 - * ['preset-default'] is default - * Can also specify any builtin plugin - * ['sortAttrs', { name: 'prefixIds', params: { prefix: 'my-prefix' } }] - * Or custom - * [{ name: 'myPlugin', fn: () => ({}) }] - */ - plugins?: PluginConfig[]; - /** Options for rendering optimized SVG from AST. */ - js2svg?: StringifyOptions; - /** Output as Data URI string. */ - datauri?: DataUri; -}; - -type Output = { - data: string; -}; - -/** The core of SVGO */ -export declare function optimize(input: string, config?: Config): Output; - -/** - * 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. - */ -export declare function loadConfig( - configFile: string, - cwd?: string, -): Promise<Config>; -export declare function loadConfig( - configFile?: null, - cwd?: string, -): Promise<Config | null>; diff --git a/node_modules/svgo/lib/svgo.js b/node_modules/svgo/lib/svgo.js deleted file mode 100644 index acb1f72e..00000000 --- a/node_modules/svgo/lib/svgo.js +++ /dev/null @@ -1,102 +0,0 @@ -'use strict'; - -const { parseSvg } = require('./parser.js'); -const { stringifySvg } = require('./stringifier.js'); -const { builtin } = require('./builtin.js'); -const { invokePlugins } = require('./svgo/plugins.js'); -const { encodeSVGDatauri } = require('./svgo/tools.js'); - -const pluginsMap = {}; -for (const plugin of builtin) { - pluginsMap[plugin.name] = plugin; -} - -const resolvePluginConfig = (plugin) => { - if (typeof plugin === 'string') { - // resolve builtin plugin specified as string - const builtinPlugin = pluginsMap[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 should be specified`); - } - // use custom plugin implementation - let fn = plugin.fn; - if (fn == null) { - // resolve builtin plugin implementation - const builtinPlugin = pluginsMap[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; -}; - -const optimize = (input, config) => { - if (config == null) { - config = {}; - } - if (typeof config !== 'object') { - throw Error('Config should be an object'); - } - const maxPassCount = config.multipass ? 10 : 1; - let prevResultSize = Number.POSITIVE_INFINITY; - let output = ''; - const info = {}; - if (config.path != null) { - info.path = config.path; - } - for (let i = 0; i < maxPassCount; i += 1) { - info.multipassCount = i; - const ast = parseSvg(input, config.path); - const plugins = config.plugins || ['preset-default']; - if (!Array.isArray(plugins)) { - throw Error( - 'malformed config, `plugins` property must be an array.\nSee more info here: https://github.com/svg/svgo#configuration', - ); - } - 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.', - ); - } - const globalOverrides = {}; - if (config.floatPrecision != null) { - globalOverrides.floatPrecision = config.floatPrecision; - } - invokePlugins(ast, info, resolvedPlugins, null, globalOverrides); - output = stringifySvg(ast, config.js2svg); - if (output.length < prevResultSize) { - input = output; - prevResultSize = output.length; - } else { - break; - } - } - if (config.datauri) { - output = encodeSVGDatauri(output, config.datauri); - } - return { - data: output, - }; -}; -exports.optimize = optimize; diff --git a/node_modules/svgo/lib/svgo/coa.js b/node_modules/svgo/lib/svgo/coa.js deleted file mode 100644 index 8e638d06..00000000 --- a/node_modules/svgo/lib/svgo/coa.js +++ /dev/null @@ -1,528 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const colors = require('picocolors'); -const { loadConfig, optimize } = require('../svgo-node.js'); -const { builtin } = require('../builtin.js'); -const PKG = require('../../package.json'); -const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js'); - -const regSVGFile = /\.svg$/i; - -/** - * Synchronously check if path is a directory. Tolerant to errors like ENOENT. - * - * @param {string} path - */ -function checkIsDir(path) { - try { - return fs.lstatSync(path).isDirectory(); - } catch (e) { - return false; - } -} - -module.exports = function makeProgram(program) { - program - .name(PKG.name) - .description(PKG.description, { - INPUT: 'Alias to --input', - }) - .version(PKG.version, '-v, --version') - .arguments('[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', - ) - .option( - '-o, --output <OUTPUT...>', - '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', - ) - .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)', - ) - .option( - '--multipass', - '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.', - ) - .option('--final-newline', 'Ensure SVG ends with a line break') - .option( - '-r, --recursive', - "Use with '--folder'. Optimizes *.svg files in folders recursively.", - ) - .option( - '--exclude <PATTERN...>', - "Use with '--folder'. Exclude files matching regular expression pattern.", - ) - .option( - '-q, --quiet', - '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); -}; - -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; - } - } - - if (opts.datauri != null) { - if ( - opts.datauri !== 'base64' && - opts.datauri !== 'enc' && - opts.datauri !== 'unenc' - ) { - console.error( - "error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'", - ); - process.exit(1); - } - } - - if (opts.indent != null) { - const number = Number.parseInt(opts.indent, 10); - if (Number.isNaN(number)) { - console.error( - "error: option '--indent' argument must be an integer number", - ); - process.exit(1); - } else { - opts.indent = number; - } - } - - 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'", - ); - process.exit(1); - } - - // --show-plugins - if (opts.showPlugins) { - showAvailablePlugins(); - return; - } - - // w/o anything - if ( - (input.length === 0 || input[0] === '-') && - !opts.string && - !opts.stdin && - !opts.folder && - process.stdin.isTTY === true - ) { - 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 (parseFloat(process.versions.node) < parseFloat(nodeVersion)) { - throw Error( - `${PKG.name} requires Node.js version ${nodeVersion} or higher.`, - ); - } - } - - // --config - const loadedConfig = await loadConfig(opts.config); - if (loadedConfig != null) { - config = loadedConfig; - } - - // --quiet - if (opts.quiet) { - config.quiet = opts.quiet; - } - - // --recursive - if (opts.recursive) { - config.recursive = opts.recursive; - } - - // --exclude - config.exclude = opts.exclude - ? opts.exclude.map((pattern) => RegExp(pattern)) - : []; - - // --precision - if (opts.precision != null) { - var precision = Math.min(Math.max(0, opts.precision), 20); - config.floatPrecision = precision; - } - - // --multipass - if (opts.multipass) { - config.multipass = true; - } - - // --pretty - if (opts.pretty) { - config.js2svg = config.js2svg || {}; - config.js2svg.pretty = true; - if (opts.indent != null) { - config.js2svg.indent = opts.indent; - } - } - - // --eol - if (opts.eol) { - config.js2svg = config.js2svg || {}; - config.js2svg.eol = opts.eol; - } - - // --final-newline - if (opts.finalNewline) { - config.js2svg = config.js2svg || {}; - config.js2svg.finalNewline = true; - } - - // --output - 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++) { - output[i] = checkIsDir(input[i]) - ? input[i] - : path.resolve(dir, path.basename(input[i])); - } - } else if (output.length < input.length) { - output = output.concat(input.slice(output.length)); - } - } - } else if (input.length) { - output = input; - } else if (opts.string) { - output = '-'; - } - - if (opts.datauri) { - config.datauri = opts.datauri; - } - - // --folder - if (opts.folder) { - var ouputFolder = (output && output[0]) || opts.folder; - await optimizeFolder(config, opts.folder, ouputFolder); - } - - // --input - if (input.length !== 0) { - // STDIN - if (input[0] === '-') { - return new Promise((resolve, reject) => { - var data = '', - file = output[0]; - - process.stdin - .on('data', (chunk) => (data += chunk)) - .once('end', () => - processSVGData(config, null, data, file).then(resolve, reject), - ); - }); - // file - } else { - await Promise.all( - input.map((file, n) => optimizeFile(config, file, output[n])), - ); - } - - // --string - } else if (opts.string) { - var data = decodeSVGDatauri(opts.string); - - return processSVGData(config, null, data, output[0]); - } -} - -/** - * Optimize SVG files in a directory. - * - * @param {Object} config options - * @param {string} dir input directory - * @param {string} output output directory - * @return {Promise} - */ -function optimizeFolder(config, dir, output) { - if (!config.quiet) { - console.log(`Processing directory '${dir}':\n`); - } - return fs.promises - .readdir(dir) - .then((files) => processDirectory(config, dir, files, output)); -} - -/** - * Process given files, take only SVG. - * - * @param {Object} config options - * @param {string} dir input directory - * @param {Array} files list of file names in the directory - * @param {string} output output directory - * @return {Promise} - */ -function processDirectory(config, dir, files, output) { - // take only *.svg files, recursively if necessary - var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output); - - return svgFilesDescriptions.length - ? Promise.all( - svgFilesDescriptions.map((fileDescription) => - optimizeFile( - config, - fileDescription.inputPath, - fileDescription.outputPath, - ), - ), - ) - : Promise.reject( - new Error(`No SVG files have been found in '${dir}' directory.`), - ); -} - -/** - * Get SVG files descriptions. - * - * @param {Object} config options - * @param {string} dir input directory - * @param {Array} files list of file names in the directory - * @param {string} output output directory - * @return {Array} - */ -function getFilesDescriptions(config, dir, files, output) { - const filesInThisFolder = files - .filter( - (name) => - regSVGFile.test(name) && - !config.exclude.some((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; -} - -/** - * Read SVG file and pass to processing. - * - * @param {Object} config options - * @param {string} file - * @param {string} output - * @return {Promise} - */ -function optimizeFile(config, file, output) { - return fs.promises.readFile(file, 'utf8').then( - (data) => processSVGData(config, { path: file }, data, output, file), - (error) => checkOptimizeFileError(config, file, output, error), - ); -} - -/** - * Optimize SVG data. - * - * @param {Object} config options - * @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} - */ -function processSVGData(config, info, data, output, input) { - var startTime = Date.now(), - prevFileSize = Buffer.byteLength(data, 'utf8'); - - let result; - try { - result = optimize(data, { ...config, ...info }); - } catch (error) { - if (error.name === '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; - - return writeOutput(input, output, result.data).then( - function () { - if (!config.quiet && output != '-') { - if (input) { - console.log(`\n${path.basename(input)}:`); - } - printTimeInfo(processingTime); - printProfitInfo(prevFileSize, resultFileSize); - } - }, - (error) => - Promise.reject( - new Error( - error.code === 'ENOTDIR' - ? `Error: output '${output}' is not a directory.` - : 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} - */ -function writeOutput(input, output, data) { - if (output == '-') { - process.stdout.write(data); - return Promise.resolve(); - } - - fs.mkdirSync(path.dirname(output), { recursive: true }); - - return fs.promises - .writeFile(output, data, 'utf8') - .catch((error) => checkWriteFileError(input, output, data, error)); -} - -/** - * Write time taken to optimize. - * - * @param {number} time time in milliseconds. - */ -function printTimeInfo(time) { - console.log(`Done in ${time} ms!`); -} - -/** - * Write optimizing stats in a human-readable format. - * - * @param {number} inBytes size before optimization. - * @param {number} outBytes size after optimization. - */ -function printProfitInfo(inBytes, outBytes) { - 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', - 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 {string} input - * @param {string} output - * @param {Error} error - * @return {Promise} - */ -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}'.`), - ); - } - return Promise.reject(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} - */ -function checkWriteFileError(input, output, data, error) { - if (error.code == 'EISDIR' && input) { - return fs.promises.writeFile( - path.resolve(output, path.basename(input)), - data, - 'utf8', - ); - } else { - return Promise.reject(error); - } -} - -/** Show list of available plugins with short description. */ -function showAvailablePlugins() { - const list = builtin - .sort((a, b) => a.name.localeCompare(b.name)) - .map((plugin) => ` [ ${colors.green(plugin.name)} ] ${plugin.description}`) - .join('\n'); - console.log('Currently available plugins:\n' + list); -} - -module.exports.checkIsDir = checkIsDir; diff --git a/node_modules/svgo/lib/svgo/css-select-adapter.d.ts b/node_modules/svgo/lib/svgo/css-select-adapter.d.ts deleted file mode 100644 index a11ba35f..00000000 --- a/node_modules/svgo/lib/svgo/css-select-adapter.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare let obj: any; -export = obj; diff --git a/node_modules/svgo/lib/svgo/css-select-adapter.js b/node_modules/svgo/lib/svgo/css-select-adapter.js deleted file mode 100644 index e9f1024a..00000000 --- a/node_modules/svgo/lib/svgo/css-select-adapter.js +++ /dev/null @@ -1,120 +0,0 @@ -'use strict'; - -const isTag = (node) => { - return node.type === 'element'; -}; - -const existsOne = (test, elems) => { - return elems.some((elem) => { - if (isTag(elem)) { - return test(elem) || existsOne(test, getChildren(elem)); - } else { - return false; - } - }); -}; - -const getAttributeValue = (elem, name) => { - return elem.attributes[name]; -}; - -const getChildren = (node) => { - return node.children || []; -}; - -const getName = (elemAst) => { - return elemAst.name; -}; - -const getParent = (node) => { - return node.parentNode || null; -}; - -const getSiblings = (elem) => { - var parent = getParent(elem); - return parent ? getChildren(parent) : []; -}; - -const getText = (node) => { - if (node.children[0].type === 'text' && node.children[0].type === 'cdata') { - return node.children[0].value; - } - return ''; -}; - -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; -}; - -const findAll = (test, elems) => { - const result = []; - for (const elem of elems) { - if (isTag(elem)) { - if (test(elem)) { - result.push(elem); - } - result.push(...findAll(test, getChildren(elem))); - } - } - return result; -}; - -const findOne = (test, elems) => { - for (const elem of elems) { - if (isTag(elem)) { - if (test(elem)) { - return elem; - } - const result = findOne(test, getChildren(elem)); - if (result) { - return result; - } - } - } - return null; -}; - -const svgoCssSelectAdapter = { - isTag, - existsOne, - getAttributeValue, - getChildren, - getName, - getParent, - getSiblings, - getText, - hasAttrib, - removeSubsets, - findAll, - findOne, -}; - -module.exports = svgoCssSelectAdapter; diff --git a/node_modules/svgo/lib/svgo/plugins.js b/node_modules/svgo/lib/svgo/plugins.js deleted file mode 100644 index 10e93e9c..00000000 --- a/node_modules/svgo/lib/svgo/plugins.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -const { visit } = require('../xast.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 - */ -const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => { - for (const plugin of plugins) { - const override = overrides?.[plugin.name]; - if (override === false) { - continue; - } - const params = { ...plugin.params, ...globalOverrides, ...override }; - - const visitor = plugin.fn(ast, params, info); - if (visitor != null) { - visit(ast, visitor); - } - } -}; -exports.invokePlugins = invokePlugins; - -const createPreset = ({ name, plugins }) => { - return { - name, - fn: (ast, params, info) => { - const { floatPrecision, overrides } = params; - const globalOverrides = {}; - if (floatPrecision != null) { - globalOverrides.floatPrecision = floatPrecision; - } - if (overrides) { - const pluginNames = plugins.map(({ name }) => name); - for (const pluginName of Object.keys(overrides)) { - if (!pluginNames.includes(pluginName)) { - console.warn( - `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: '${name}',\n` + - ` },\n` + - ` '${pluginName}'\n` + - `]\n`, - ); - } - } - } - invokePlugins(ast, info, plugins, overrides, globalOverrides); - }, - }; -}; -exports.createPreset = createPreset; diff --git a/node_modules/svgo/lib/svgo/tools.js b/node_modules/svgo/lib/svgo/tools.js deleted file mode 100644 index 2d5269ec..00000000 --- a/node_modules/svgo/lib/svgo/tools.js +++ /dev/null @@ -1,245 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../../lib/types').XastElement} XastElement - * @typedef {import('../types').PathDataCommand} PathDataCommand - * @typedef {import('../types').DataUri} DataUri - */ - -const { attrsGroups, referencesProps } = require('../../plugins/_collections'); - -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?: DataUri) => string} - */ -exports.encodeSVGDatauri = (str, type) => { - var prefix = 'data:image/svg+xml'; - if (!type || type === 'base64') { - // base64 - prefix += ';base64,'; - str = prefix + Buffer.from(str).toString('base64'); - } else if (type === 'enc') { - // URI encoded - str = prefix + ',' + encodeURIComponent(str); - } else if (type === 'unenc') { - // unencoded - str = prefix + ',' + str; - } - return str; -}; - -/** - * Decode SVG Data URI string into plain SVG string. - * - * @type {(str: string) => string} - */ -exports.decodeSVGDatauri = (str) => { - var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/; - var match = regexp.exec(str); - - // plain string - if (!match) return str; - - var data = match[3]; - - if (match[2]) { - // base64 - str = Buffer.from(data, 'base64').toString('utf8'); - } else if (data.charAt(0) === '%') { - // URI encoded - str = decodeURIComponent(data); - } else if (data.charAt(0) === '<') { - // unencoded - str = data; - } - 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: number[], params: CleanupOutDataParams, command?: PathDataCommand) => string} - */ -exports.cleanupOutData = (data, params, command) => { - let str = ''; - let delimiter; - /** - * @type {number} - */ - let prev; - - data.forEach((item, i) => { - // space delimiter by default - delimiter = ' '; - - // no extra space in front of first number - if (i == 0) delimiter = ''; - - // no extra space after 'arcto' 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 = ''; - } - - // remove floating-point numbers leading zeros - // 0.5 → .5 - // -0.5 → -.5 - const itemStr = params.leadingZero - ? removeLeadingZero(item) - : item.toString(); - - // no extra space in front of negative number or - // in front of a floating number if a previous number is floating too - if ( - params.negativeExtraSpace && - delimiter != '' && - (item < 0 || (itemStr.charAt(0) === '.' && prev % 1 !== 0)) - ) { - delimiter = ''; - } - // save prev item value - prev = item; - str += delimiter + itemStr; - }); - return str; -}; - -/** - * Remove floating-point numbers leading zero. - * - * @param {number} value - * @returns {string} - * @example - * 0.5 → .5 - * -0.5 → -.5 - */ -const removeLeadingZero = (value) => { - const strValue = value.toString(); - - if (0 < value && value < 1 && strValue.startsWith('0')) { - return strValue.slice(1); - } - - if (-1 < value && value < 0 && strValue[1] === '0') { - return strValue[0] + strValue.slice(2); - } - - return strValue; -}; -exports.removeLeadingZero = removeLeadingZero; - -/** - * 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 {XastElement} node Current node to check against. - * @returns {boolean} If the current node contains scripts. - */ -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); -}; -exports.hasScripts = hasScripts; - -/** - * 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. - */ -const includesUrlReference = (body) => { - return new RegExp(regReferencesUrl).test(body); -}; -exports.includesUrlReference = includesUrlReference; - -/** - * @param {string} attribute - * @param {string} value - * @returns {string[]} - */ -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)); -}; -exports.findReferences = findReferences; - -/** - * Does the same as {@link Number.toFixed} but without casting - * the return value to a string. - * - * @param {number} num - * @param {number} precision - * @returns {number} - */ -const toFixed = (num, precision) => { - const pow = 10 ** precision; - return Math.round(num * pow) / pow; -}; -exports.toFixed = toFixed; diff --git a/node_modules/svgo/lib/types.d.ts b/node_modules/svgo/lib/types.d.ts deleted file mode 100644 index f61d9f18..00000000 --- a/node_modules/svgo/lib/types.d.ts +++ /dev/null @@ -1,174 +0,0 @@ -export type XastDoctype = { - type: 'doctype'; - name: string; - data: { - doctype: string; - }; -}; - -export type XastInstruction = { - type: 'instruction'; - name: string; - value: string; -}; - -export type XastComment = { - type: 'comment'; - value: string; -}; - -export type XastCdata = { - type: 'cdata'; - value: string; -}; - -export type XastText = { - type: 'text'; - value: string; -}; - -export type XastElement = { - type: 'element'; - name: string; - attributes: Record<string, string>; - children: XastChild[]; -}; - -export type XastChild = - | XastDoctype - | XastInstruction - | XastComment - | XastCdata - | XastText - | XastElement; - -export type XastRoot = { - type: 'root'; - children: XastChild[]; -}; - -export type XastParent = XastRoot | XastElement; - -export type XastNode = XastRoot | XastChild; - -export type StringifyOptions = { - doctypeStart?: string; - doctypeEnd?: string; - procInstStart?: string; - procInstEnd?: string; - tagOpenStart?: string; - tagOpenEnd?: string; - tagCloseStart?: string; - tagCloseEnd?: string; - tagShortStart?: string; - tagShortEnd?: string; - attrStart?: string; - attrEnd?: string; - commentStart?: string; - commentEnd?: string; - cdataStart?: string; - cdataEnd?: string; - textStart?: string; - textEnd?: string; - indent?: number | string; - regEntities?: RegExp; - regValEntities?: RegExp; - encodeEntity?: (char: string) => string; - pretty?: boolean; - useShortTags?: boolean; - eol?: 'lf' | 'crlf'; - finalNewline?: boolean; -}; - -type VisitorNode<Node> = { - enter?: (node: Node, parentNode: XastParent) => void | symbol; - exit?: (node: Node, parentNode: XastParent) => void; -}; - -type VisitorRoot = { - enter?: (node: XastRoot, parentNode: null) => void; - exit?: (node: XastRoot, parentNode: null) => void; -}; - -export type Visitor = { - doctype?: VisitorNode<XastDoctype>; - instruction?: VisitorNode<XastInstruction>; - comment?: VisitorNode<XastComment>; - cdata?: VisitorNode<XastCdata>; - text?: VisitorNode<XastText>; - element?: VisitorNode<XastElement>; - root?: VisitorRoot; -}; - -export type PluginInfo = { - path?: string; - multipassCount: number; -}; - -export type Plugin<Params> = ( - root: XastRoot, - params: Params, - info: PluginInfo, -) => null | Visitor; - -export type Specificity = [number, number, number]; - -export type StylesheetDeclaration = { - name: string; - value: string; - important: boolean; -}; - -export type StylesheetRule = { - dynamic: boolean; - selector: string; - specificity: Specificity; - declarations: StylesheetDeclaration[]; -}; - -export type Stylesheet = { - rules: StylesheetRule[]; - parents: Map<XastElement, XastParent>; -}; - -type StaticStyle = { - type: 'static'; - inherited: boolean; - value: string; -}; - -type DynamicStyle = { - type: 'dynamic'; - inherited: boolean; -}; - -export type ComputedStyles = Record<string, StaticStyle | DynamicStyle>; - -export type PathDataCommand = - | 'M' - | 'm' - | 'Z' - | 'z' - | 'L' - | 'l' - | 'H' - | 'h' - | 'V' - | 'v' - | 'C' - | 'c' - | 'S' - | 's' - | 'Q' - | 'q' - | 'T' - | 't' - | 'A' - | 'a'; - -export type PathDataItem = { - command: PathDataCommand; - args: number[]; -}; - -export type DataUri = 'base64' | 'enc' | 'unenc'; diff --git a/node_modules/svgo/lib/xast.js b/node_modules/svgo/lib/xast.js deleted file mode 100644 index 9dca9646..00000000 --- a/node_modules/svgo/lib/xast.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -/** - * @typedef {import('./types').XastNode} XastNode - * @typedef {import('./types').XastChild} XastChild - * @typedef {import('./types').XastParent} XastParent - * @typedef {import('./types').Visitor} Visitor - */ - -const { selectAll, selectOne, is } = require('css-select'); -const xastAdaptor = require('./svgo/css-select-adapter.js'); - -const cssSelectOptions = { - xmlMode: true, - adapter: xastAdaptor, -}; - -/** - * @type {(node: XastNode, selector: string) => XastChild[]} - */ -const querySelectorAll = (node, selector) => { - return selectAll(selector, node, cssSelectOptions); -}; -exports.querySelectorAll = querySelectorAll; - -/** - * @type {(node: XastNode, selector: string) => ?XastChild} - */ -const querySelector = (node, selector) => { - return selectOne(selector, node, cssSelectOptions); -}; -exports.querySelector = querySelector; - -/** - * @type {(node: XastChild, selector: string) => boolean} - */ -const matches = (node, selector) => { - return is(node, selector, cssSelectOptions); -}; -exports.matches = matches; - -const visitSkip = Symbol(); -exports.visitSkip = visitSkip; - -/** - * @type {(node: XastNode, visitor: Visitor, parentNode?: any) => void} - */ -const visit = (node, visitor, parentNode) => { - const callbacks = visitor[node.type]; - if (callbacks && callbacks.enter) { - // @ts-ignore hard to infer - const symbol = callbacks.enter(node, parentNode); - if (symbol === visitSkip) { - return; - } - } - // visit root children - if (node.type === 'root') { - // copy children array to not loose cursor when children is spliced - for (const child of node.children) { - visit(child, visitor, node); - } - } - // visit element children if still attached to parent - if (node.type === 'element') { - if (parentNode.children.includes(node)) { - for (const child of node.children) { - visit(child, visitor, node); - } - } - } - if (callbacks && callbacks.exit) { - // @ts-ignore hard to infer - callbacks.exit(node, parentNode); - } -}; -exports.visit = visit; - -/** - * @param {XastChild} node - * @param {XastParent} parentNode - */ -const detachNodeFromParent = (node, parentNode) => { - // avoid splice to not break for loops - parentNode.children = parentNode.children.filter((child) => child !== node); -}; -exports.detachNodeFromParent = detachNodeFromParent; |
