svgo
Advanced tools
Comparing version 3.3.1 to 3.3.2
@@ -1,111 +0,57 @@ | ||
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 removeScriptElement from '../plugins/removeScriptElement.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'; | ||
'use strict'; | ||
export const builtin = [ | ||
presetDefault, | ||
addAttributesToSVGElement, | ||
addClassesToSVGElement, | ||
cleanupAttrs, | ||
cleanupEnableBackground, | ||
cleanupIds, | ||
cleanupListOfValues, | ||
cleanupNumericValues, | ||
collapseGroups, | ||
convertColors, | ||
convertEllipseToCircle, | ||
convertOneStopGradients, | ||
convertPathData, | ||
convertShapeToPath, | ||
convertStyleToAttrs, | ||
convertTransform, | ||
mergeStyles, | ||
inlineStyles, | ||
mergePaths, | ||
minifyStyles, | ||
moveElemsAttrsToGroup, | ||
moveGroupAttrsToElems, | ||
prefixIds, | ||
removeAttributesBySelector, | ||
removeAttrs, | ||
removeComments, | ||
removeDeprecatedAttrs, | ||
removeDesc, | ||
removeDimensions, | ||
removeDoctype, | ||
removeEditorsNSData, | ||
removeElementsByAttr, | ||
removeEmptyAttrs, | ||
removeEmptyContainers, | ||
removeEmptyText, | ||
removeHiddenElems, | ||
removeMetadata, | ||
removeNonInheritableGroupAttrs, | ||
removeOffCanvasPaths, | ||
removeRasterImages, | ||
removeScriptElement, | ||
removeStyleElement, | ||
removeTitle, | ||
removeUnknownsAndDefaults, | ||
removeUnusedNS, | ||
removeUselessDefs, | ||
removeUselessStrokeAndFill, | ||
removeViewBox, | ||
removeXlink, | ||
removeXMLNS, | ||
removeXMLProcInst, | ||
reusePaths, | ||
sortAttrs, | ||
sortDefsChildren, | ||
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'), | ||
]; |
@@ -0,17 +1,19 @@ | ||
'use strict'; | ||
/** | ||
* @typedef {import('./types.js').XastNode} XastNode | ||
* @typedef {import('./types.js').XastInstruction} XastInstruction | ||
* @typedef {import('./types.js').XastDoctype} XastDoctype | ||
* @typedef {import('./types.js').XastComment} XastComment | ||
* @typedef {import('./types.js').XastRoot} XastRoot | ||
* @typedef {import('./types.js').XastElement} XastElement | ||
* @typedef {import('./types.js').XastCdata} XastCdata | ||
* @typedef {import('./types.js').XastText} XastText | ||
* @typedef {import('./types.js').XastParent} XastParent | ||
* @typedef {import('./types.js').XastChild} XastChild | ||
* @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 | ||
import SAX from '@trysound/sax'; | ||
import { textElems } from '../plugins/_collections.js'; | ||
const SAX = require('@trysound/sax'); | ||
const { textElems } = require('../plugins/_collections'); | ||
@@ -90,3 +92,3 @@ class SvgoParserError extends Error { | ||
*/ | ||
export const parseSvg = (data, from) => { | ||
const parseSvg = (data, from) => { | ||
const sax = SAX.parser(config.strict, config); | ||
@@ -261,1 +263,2 @@ /** | ||
}; | ||
exports.parseSvg = parseSvg; |
@@ -1,6 +0,8 @@ | ||
import { removeLeadingZero, toFixed } from './svgo/tools.js'; | ||
'use strict'; | ||
const { removeLeadingZero, toFixed } = require('./svgo/tools'); | ||
/** | ||
* @typedef {import('./types.js').PathDataItem} PathDataItem | ||
* @typedef {import('./types.js').PathDataCommand} PathDataCommand | ||
* @typedef {import('./types').PathDataItem} PathDataItem | ||
* @typedef {import('./types').PathDataCommand} PathDataCommand | ||
*/ | ||
@@ -138,3 +140,3 @@ | ||
*/ | ||
export const parsePathData = (string) => { | ||
const parsePathData = (string) => { | ||
/** | ||
@@ -242,2 +244,3 @@ * @type {PathDataItem[]} | ||
}; | ||
exports.parsePathData = parsePathData; | ||
@@ -288,3 +291,8 @@ /** | ||
result += roundedStr; | ||
} else if (!Number.isInteger(previous) && !isDigit(roundedStr[0])) { | ||
} else if ( | ||
!Number.isInteger(previous) && | ||
rounded != 0 && | ||
rounded < 1 && | ||
rounded > -1 | ||
) { | ||
// remove space before decimal with zero whole | ||
@@ -314,7 +322,3 @@ // only when previous number is also decimal | ||
*/ | ||
export const stringifyPathData = ({ | ||
pathData, | ||
precision, | ||
disableSpaceAfterFlags, | ||
}) => { | ||
const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => { | ||
if (pathData.length === 1) { | ||
@@ -380,1 +384,2 @@ const { command, args } = pathData[0]; | ||
}; | ||
exports.stringifyPathData = stringifyPathData; |
@@ -1,13 +0,18 @@ | ||
import { textElems } from '../plugins/_collections.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('./types.js').XastParent} XastParent | ||
* @typedef {import('./types.js').XastRoot} XastRoot | ||
* @typedef {import('./types.js').XastElement} XastElement | ||
* @typedef {import('./types.js').XastInstruction} XastInstruction | ||
* @typedef {import('./types.js').XastDoctype} XastDoctype | ||
* @typedef {import('./types.js').XastText} XastText | ||
* @typedef {import('./types.js').XastCdata} XastCdata | ||
* @typedef {import('./types.js').XastComment} XastComment | ||
* @typedef {import('./types.js').StringifyOptions} StringifyOptions | ||
* @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 {{ | ||
@@ -18,2 +23,5 @@ * indent: string, | ||
* }} State | ||
*/ | ||
/** | ||
* @typedef {Required<StringifyOptions>} Options | ||
@@ -73,3 +81,3 @@ */ | ||
*/ | ||
export const stringifySvg = (data, userOptions = {}) => { | ||
const stringifySvg = (data, userOptions = {}) => { | ||
/** | ||
@@ -111,2 +119,3 @@ * @type {Options} | ||
}; | ||
exports.stringifySvg = stringifySvg; | ||
@@ -113,0 +122,0 @@ /** |
@@ -1,24 +0,29 @@ | ||
import * as csstree from 'css-tree'; | ||
import * as csswhat from 'css-what'; | ||
import { syntax } from 'csso'; | ||
import { visit, matches } from './xast.js'; | ||
import { | ||
attrsGroups, | ||
inheritableAttrs, | ||
presentationNonInheritableGroupAttrs, | ||
} from '../plugins/_collections.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('css-tree').Rule} CsstreeRule | ||
* @typedef {import('./types.js').Specificity} Specificity | ||
* @typedef {import('./types.js').Stylesheet} Stylesheet | ||
* @typedef {import('./types.js').StylesheetRule} StylesheetRule | ||
* @typedef {import('./types.js').StylesheetDeclaration} StylesheetDeclaration | ||
* @typedef {import('./types.js').ComputedStyles} ComputedStyles | ||
* @typedef {import('./types.js').XastRoot} XastRoot | ||
* @typedef {import('./types.js').XastElement} XastElement | ||
* @typedef {import('./types.js').XastParent} XastParent | ||
* @typedef {import('./types.js').XastChild} XastChild | ||
* @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; | ||
@@ -58,3 +63,3 @@ | ||
rules.push({ | ||
specificity: syntax.specificity(node), | ||
specificity: specificity(node), | ||
dynamic: hasPseudoClasses || dynamic, | ||
@@ -200,3 +205,3 @@ // compute specificity from original node to consider pseudo classes | ||
*/ | ||
export const compareSpecificity = (a, b) => { | ||
const compareSpecificity = (a, b) => { | ||
for (let i = 0; i < 4; i += 1) { | ||
@@ -212,2 +217,3 @@ if (a[i] < b[i]) { | ||
}; | ||
exports.compareSpecificity = compareSpecificity; | ||
@@ -217,3 +223,3 @@ /** | ||
*/ | ||
export const collectStylesheet = (root) => { | ||
const collectStylesheet = (root) => { | ||
/** @type {StylesheetRule[]} */ | ||
@@ -254,2 +260,3 @@ const rules = []; | ||
}; | ||
exports.collectStylesheet = collectStylesheet; | ||
@@ -261,3 +268,3 @@ /** | ||
*/ | ||
export const computeStyle = (stylesheet, node) => { | ||
const computeStyle = (stylesheet, node) => { | ||
const { parents } = stylesheet; | ||
@@ -281,2 +288,3 @@ const computedStyles = computeOwnStyle(stylesheet, node); | ||
}; | ||
exports.computeStyle = computeStyle; | ||
@@ -296,3 +304,3 @@ /** | ||
*/ | ||
export const includesAttrSelector = ( | ||
const includesAttrSelector = ( | ||
selector, | ||
@@ -336,1 +344,2 @@ name, | ||
}; | ||
exports.includesAttrSelector = includesAttrSelector; |
@@ -1,13 +0,22 @@ | ||
import os from 'os'; | ||
import fs from 'fs'; | ||
import { pathToFileURL } from 'url'; | ||
import path from 'path'; | ||
import { optimize as optimizeAgnostic } from './svgo.js'; | ||
'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) => { | ||
// dynamic import expects file url instead of path and may fail | ||
// when windows path is provided | ||
const imported = await import(pathToFileURL(configFile)); | ||
const config = imported.default; | ||
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)) { | ||
@@ -28,3 +37,3 @@ throw Error(`Invalid config file "${configFile}"`); | ||
export const loadConfig = async (configFile, cwd = process.cwd()) => { | ||
const loadConfig = async (configFile, cwd = process.cwd()) => { | ||
if (configFile != null) { | ||
@@ -59,4 +68,5 @@ if (path.isAbsolute(configFile)) { | ||
}; | ||
exports.loadConfig = loadConfig; | ||
export const optimize = (input, config) => { | ||
const optimize = (input, config) => { | ||
if (config == null) { | ||
@@ -77,1 +87,2 @@ config = {}; | ||
}; | ||
exports.optimize = optimize; |
@@ -1,11 +0,10 @@ | ||
import type { StringifyOptions, DataUri, Plugin } from './types.js'; | ||
import type { StringifyOptions, DataUri, Plugin as PluginFn } from './types'; | ||
import type { | ||
BuiltinsWithOptionalParams, | ||
BuiltinsWithRequiredParams, | ||
} from '../plugins/plugins-types.js'; | ||
} from '../plugins/plugins-types'; | ||
type CustomPlugin<T = any> = { | ||
type CustomPlugin = { | ||
name: string; | ||
fn: Plugin<T>; | ||
params?: T; | ||
fn: PluginFn<void>; | ||
}; | ||
@@ -57,1 +56,15 @@ | ||
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>; |
@@ -1,7 +0,9 @@ | ||
import { parseSvg } from './parser.js'; | ||
import { stringifySvg } from './stringifier.js'; | ||
import { builtin } from './builtin.js'; | ||
import { invokePlugins } from './svgo/plugins.js'; | ||
import { encodeSVGDatauri } from './svgo/tools.js'; | ||
'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 = {}; | ||
@@ -48,3 +50,3 @@ for (const plugin of builtin) { | ||
export const optimize = (input, config) => { | ||
const optimize = (input, config) => { | ||
if (config == null) { | ||
@@ -101,1 +103,2 @@ config = {}; | ||
}; | ||
exports.optimize = optimize; |
@@ -1,12 +0,10 @@ | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
import colors from 'picocolors'; | ||
import { fileURLToPath } from 'url'; | ||
import { encodeSVGDatauri, decodeSVGDatauri } from './tools.js'; | ||
import { loadConfig, optimize } from '../svgo-node.js'; | ||
import { builtin } from '../builtin.js'; | ||
'use strict'; | ||
const __dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
const pkgPath = path.join(__dirname, '../../package.json'); | ||
const PKG = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); | ||
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'); | ||
@@ -18,13 +16,13 @@ const regSVGFile = /\.svg$/i; | ||
* | ||
* @param {string} filePath | ||
* @param {string} path | ||
*/ | ||
export function checkIsDir(filePath) { | ||
function checkIsDir(path) { | ||
try { | ||
return fs.lstatSync(filePath).isDirectory(); | ||
return fs.lstatSync(path).isDirectory(); | ||
} catch (e) { | ||
return filePath.endsWith(path.sep); | ||
return false; | ||
} | ||
} | ||
export default function makeProgram(program) { | ||
module.exports = function makeProgram(program) { | ||
program | ||
@@ -51,7 +49,4 @@ .name(PKG.name) | ||
) | ||
.option('--config <CONFIG>', 'Custom config file, only .js is supported') | ||
.option( | ||
'--config <CONFIG>', | ||
'Custom config file, only .js, .mjs, and .cjs is supported', | ||
) | ||
.option( | ||
'--datauri <FORMAT>', | ||
@@ -87,3 +82,3 @@ 'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)', | ||
.action(action); | ||
} | ||
}; | ||
@@ -535,1 +530,3 @@ async function action(args, opts, command) { | ||
} | ||
module.exports.checkIsDir = checkIsDir; |
declare let obj: any; | ||
export default obj; | ||
export = obj; |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
const isTag = (node) => { | ||
@@ -118,2 +120,2 @@ return node.type === 'element'; | ||
export default svgoCssSelectAdapter; | ||
module.exports = svgoCssSelectAdapter; |
@@ -1,3 +0,5 @@ | ||
import { visit } from '../xast.js'; | ||
'use strict'; | ||
const { visit } = require('../xast.js'); | ||
/** | ||
@@ -13,9 +15,3 @@ * Plugins engine. | ||
*/ | ||
export const invokePlugins = ( | ||
ast, | ||
info, | ||
plugins, | ||
overrides, | ||
globalOverrides, | ||
) => { | ||
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => { | ||
for (const plugin of plugins) { | ||
@@ -34,4 +30,5 @@ const override = overrides?.[plugin.name]; | ||
}; | ||
exports.invokePlugins = invokePlugins; | ||
export const createPreset = ({ name, plugins }) => { | ||
const createPreset = ({ name, plugins }) => { | ||
return { | ||
@@ -66,1 +63,2 @@ name, | ||
}; | ||
exports.createPreset = createPreset; |
@@ -0,8 +1,10 @@ | ||
'use strict'; | ||
/** | ||
* @typedef {import('../types.js').DataUri} DataUri | ||
* @typedef {import('../types.js').PathDataCommand} PathDataCommand | ||
* @typedef {import('../../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../../lib/types').XastElement} XastElement | ||
* @typedef {import('../types').PathDataCommand} PathDataCommand | ||
* @typedef {import('../types').DataUri} DataUri | ||
*/ | ||
import { attrsGroups, referencesProps } from '../../plugins/_collections.js'; | ||
const { attrsGroups, referencesProps } = require('../../plugins/_collections'); | ||
@@ -18,3 +20,3 @@ const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g; | ||
*/ | ||
export const encodeSVGDatauri = (str, type) => { | ||
exports.encodeSVGDatauri = (str, type) => { | ||
var prefix = 'data:image/svg+xml'; | ||
@@ -40,3 +42,3 @@ if (!type || type === 'base64') { | ||
*/ | ||
export const decodeSVGDatauri = (str) => { | ||
exports.decodeSVGDatauri = (str) => { | ||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/; | ||
@@ -79,3 +81,3 @@ var match = regexp.exec(str); | ||
*/ | ||
export const cleanupOutData = (data, params, command) => { | ||
exports.cleanupOutData = (data, params, command) => { | ||
let str = ''; | ||
@@ -134,3 +136,3 @@ let delimiter; | ||
*/ | ||
export const removeLeadingZero = (value) => { | ||
const removeLeadingZero = (value) => { | ||
const strValue = value.toString(); | ||
@@ -148,2 +150,3 @@ | ||
}; | ||
exports.removeLeadingZero = removeLeadingZero; | ||
@@ -157,3 +160,3 @@ /** | ||
*/ | ||
export const hasScripts = (node) => { | ||
const hasScripts = (node) => { | ||
if (node.name === 'script' && node.children.length !== 0) { | ||
@@ -186,2 +189,3 @@ return true; | ||
}; | ||
exports.hasScripts = hasScripts; | ||
@@ -198,5 +202,6 @@ /** | ||
*/ | ||
export const includesUrlReference = (body) => { | ||
const includesUrlReference = (body) => { | ||
return new RegExp(regReferencesUrl).test(body); | ||
}; | ||
exports.includesUrlReference = includesUrlReference; | ||
@@ -208,3 +213,3 @@ /** | ||
*/ | ||
export const findReferences = (attribute, value) => { | ||
const findReferences = (attribute, value) => { | ||
const results = []; | ||
@@ -235,2 +240,3 @@ | ||
}; | ||
exports.findReferences = findReferences; | ||
@@ -245,5 +251,6 @@ /** | ||
*/ | ||
export const toFixed = (num, precision) => { | ||
const toFixed = (num, precision) => { | ||
const pow = 10 ** precision; | ||
return Math.round(num * pow) / pow; | ||
}; | ||
exports.toFixed = toFixed; |
@@ -112,3 +112,3 @@ export type XastDoctype = { | ||
info: PluginInfo, | ||
) => Visitor | null | void; | ||
) => null | Visitor; | ||
@@ -115,0 +115,0 @@ export type Specificity = [number, number, number]; |
@@ -1,11 +0,13 @@ | ||
import { selectAll, selectOne, is } from 'css-select'; | ||
import xastAdaptor from './svgo/css-select-adapter.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('./types.js').XastNode} XastNode | ||
* @typedef {import('./types.js').XastChild} XastChild | ||
* @typedef {import('./types.js').XastParent} XastParent | ||
* @typedef {import('./types.js').Visitor} Visitor | ||
* @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 = { | ||
@@ -19,5 +21,6 @@ xmlMode: true, | ||
*/ | ||
export const querySelectorAll = (node, selector) => { | ||
const querySelectorAll = (node, selector) => { | ||
return selectAll(selector, node, cssSelectOptions); | ||
}; | ||
exports.querySelectorAll = querySelectorAll; | ||
@@ -27,5 +30,6 @@ /** | ||
*/ | ||
export const querySelector = (node, selector) => { | ||
const querySelector = (node, selector) => { | ||
return selectOne(selector, node, cssSelectOptions); | ||
}; | ||
exports.querySelector = querySelector; | ||
@@ -35,7 +39,9 @@ /** | ||
*/ | ||
export const matches = (node, selector) => { | ||
const matches = (node, selector) => { | ||
return is(node, selector, cssSelectOptions); | ||
}; | ||
exports.matches = matches; | ||
export const visitSkip = Symbol(); | ||
const visitSkip = Symbol(); | ||
exports.visitSkip = visitSkip; | ||
@@ -45,3 +51,3 @@ /** | ||
*/ | ||
export const visit = (node, visitor, parentNode) => { | ||
const visit = (node, visitor, parentNode) => { | ||
const callbacks = visitor[node.type]; | ||
@@ -75,2 +81,3 @@ if (callbacks && callbacks.enter) { | ||
}; | ||
exports.visit = visit; | ||
@@ -81,5 +88,6 @@ /** | ||
*/ | ||
export const detachNodeFromParent = (node, parentNode) => { | ||
const detachNodeFromParent = (node, parentNode) => { | ||
// avoid splice to not break for loops | ||
parentNode.children = parentNode.children.filter((child) => child !== node); | ||
}; | ||
exports.detachNodeFromParent = detachNodeFromParent; |
{ | ||
"packageManager": "yarn@2.4.3", | ||
"name": "svgo", | ||
"version": "3.3.1", | ||
"version": "3.3.2", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
"license": "MIT", | ||
"type": "module", | ||
"keywords": [ | ||
@@ -54,25 +53,4 @@ "svgo", | ||
"main": "./lib/svgo-node.js", | ||
"bin": "./bin/svgo.js", | ||
"types": "./lib/svgo-node.d.ts", | ||
"exports": { | ||
".": { | ||
"import": "./lib/svgo-node.js", | ||
"require": "./dist/svgo-node.cjs", | ||
"types": "./lib/svgo-node.d.ts" | ||
}, | ||
"./browser": { | ||
"import": "./dist/svgo.browser.js", | ||
"types": "./lib/svgo.d.ts" | ||
} | ||
}, | ||
"typesVersions": { | ||
"*": { | ||
".": [ | ||
"./lib/svgo-node.d.ts" | ||
], | ||
"browser": [ | ||
"./lib/svgo.d.ts" | ||
] | ||
} | ||
}, | ||
"bin": "./bin/svgo", | ||
"types": "./lib/svgo.d.ts", | ||
"files": [ | ||
@@ -93,7 +71,6 @@ "bin", | ||
"typecheck": "tsc", | ||
"generate-bundles": "rollup -c", | ||
"test-bundles": "yarn generate-bundles && node ./test/svgo.cjs && node ./test/browser.js", | ||
"test-browser": "rollup -c && node ./test/browser.js", | ||
"test-regression": "node ./test/regression-extract.js && NO_DIFF=1 node ./test/regression.js", | ||
"prepublishOnly": "rm -rf dist && yarn generate-bundles", | ||
"qa": "yarn lint && yarn typecheck && yarn test && yarn test-bundles && yarn test-regression" | ||
"prepublishOnly": "rm -rf dist && rollup -c", | ||
"qa": "yarn lint && yarn typecheck && yarn test && yarn test-browser && yarn test-regression" | ||
}, | ||
@@ -115,6 +92,5 @@ "jest": { | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^25.0.7", | ||
"@rollup/plugin-node-resolve": "^15.2.3", | ||
"@rollup/plugin-terser": "^0.4.4", | ||
"@types/css-tree": "^2.3.5", | ||
"@rollup/plugin-commonjs": "^22.0.2", | ||
"@rollup/plugin-node-resolve": "^14.1.0", | ||
"@types/css-tree": "^2.3.4", | ||
"@types/csso": "^5.0.4", | ||
@@ -130,3 +106,4 @@ "@types/jest": "^29.5.5", | ||
"prettier": "^3.1.1", | ||
"rollup": "^4.9.2", | ||
"rollup": "^2.79.1", | ||
"rollup-plugin-terser": "^7.0.2", | ||
"tar-stream": "^3.1.6", | ||
@@ -133,0 +110,0 @@ "typescript": "^5.3.3" |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
// https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
@@ -6,3 +8,3 @@ | ||
*/ | ||
export const elemsGroups = { | ||
exports.elemsGroups = { | ||
animation: new Set([ | ||
@@ -59,3 +61,2 @@ 'animate', | ||
textContent: new Set([ | ||
'a', | ||
'altGlyph', | ||
@@ -111,5 +112,9 @@ 'altGlyphDef', | ||
*/ | ||
export const textElems = new Set([...elemsGroups.textContent, 'pre', 'title']); | ||
exports.textElems = new Set([ | ||
...exports.elemsGroups.textContent, | ||
'pre', | ||
'title', | ||
]); | ||
export const pathElems = new Set(['glyph', 'missing-glyph', 'path']); | ||
exports.pathElems = new Set(['glyph', 'missing-glyph', 'path']); | ||
@@ -120,3 +125,3 @@ /** | ||
*/ | ||
export const attrsGroups = { | ||
exports.attrsGroups = { | ||
animationAddition: new Set(['additive', 'accumulate']), | ||
@@ -319,3 +324,3 @@ animationAttributeTarget: new Set(['attributeType', 'attributeName']), | ||
*/ | ||
export const attrsGroupsDefaults = { | ||
exports.attrsGroupsDefaults = { | ||
core: { 'xml:space': 'default' }, | ||
@@ -385,22 +390,2 @@ presentation: { | ||
/** | ||
* @type {Record<string, { safe?: Set<string>, unsafe?: Set<string> }>} | ||
* @see https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
*/ | ||
export const attrsGroupsDeprecated = { | ||
animationAttributeTarget: { unsafe: new Set(['attributeType']) }, | ||
conditionalProcessing: { unsafe: new Set(['requiredFeatures']) }, | ||
core: { unsafe: new Set(['xml:base', 'xml:lang', 'xml:space']) }, | ||
presentation: { | ||
unsafe: new Set([ | ||
'clip', | ||
'color-profile', | ||
'enable-background', | ||
'glyph-orientation-horizontal', | ||
'glyph-orientation-vertical', | ||
'kerning', | ||
]), | ||
}, | ||
}; | ||
/** | ||
* @type {Record<string, { | ||
@@ -410,6 +395,2 @@ * attrsGroups: Set<string>, | ||
* defaults?: Record<string, string>, | ||
* deprecated?: { | ||
* safe?: Set<string>, | ||
* unsafe?: Set<string>, | ||
* }, | ||
* contentGroups?: Set<string>, | ||
@@ -420,3 +401,3 @@ * content?: Set<string>, | ||
*/ | ||
export const elems = { | ||
exports.elems = { | ||
a: { | ||
@@ -610,3 +591,2 @@ attrsGroups: new Set([ | ||
}, | ||
deprecated: { unsafe: new Set(['name']) }, | ||
contentGroups: new Set(['descriptive']), | ||
@@ -996,3 +976,2 @@ }, | ||
}, | ||
deprecated: { unsafe: new Set(['filterRes']) }, | ||
contentGroups: new Set(['descriptive', 'filterPrimitive']), | ||
@@ -1018,11 +997,2 @@ content: new Set(['animate', 'set']), | ||
}, | ||
deprecated: { | ||
unsafe: new Set([ | ||
'horiz-origin-x', | ||
'horiz-origin-y', | ||
'vert-adv-y', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
]), | ||
}, | ||
contentGroups: new Set(['descriptive']), | ||
@@ -1078,27 +1048,2 @@ content: new Set(['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern']), | ||
}, | ||
deprecated: { | ||
unsafe: new Set([ | ||
'accent-height', | ||
'alphabetic', | ||
'ascent', | ||
'bbox', | ||
'cap-height', | ||
'descent', | ||
'hanging', | ||
'ideographic', | ||
'mathematical', | ||
'panose-1', | ||
'slope', | ||
'stemh', | ||
'stemv', | ||
'unicode-range', | ||
'units-per-em', | ||
'v-alphabetic', | ||
'v-hanging', | ||
'v-ideographic', | ||
'v-mathematical', | ||
'widths', | ||
'x-height', | ||
]), | ||
}, | ||
contentGroups: new Set(['descriptive']), | ||
@@ -1114,3 +1059,2 @@ content: new Set([ | ||
attrs: new Set(['string']), | ||
deprecated: { unsafe: new Set(['string']) }, | ||
}, | ||
@@ -1120,3 +1064,2 @@ 'font-face-name': { | ||
attrs: new Set(['name']), | ||
deprecated: { unsafe: new Set(['name']) }, | ||
}, | ||
@@ -1214,14 +1157,2 @@ 'font-face-src': { | ||
}, | ||
deprecated: { | ||
unsafe: new Set([ | ||
'arabic-form', | ||
'glyph-name', | ||
'horiz-adv-x', | ||
'orientation', | ||
'unicode', | ||
'vert-adv-y', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
]), | ||
}, | ||
contentGroups: new Set([ | ||
@@ -1266,10 +1197,2 @@ 'animation', | ||
]), | ||
deprecated: { | ||
unsafe: new Set([ | ||
'horiz-adv-x', | ||
'vert-adv-y', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
]), | ||
}, | ||
contentGroups: new Set([ | ||
@@ -1338,3 +1261,2 @@ 'animation', | ||
attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']), | ||
deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) }, | ||
}, | ||
@@ -1534,10 +1456,2 @@ image: { | ||
]), | ||
deprecated: { | ||
unsafe: new Set([ | ||
'horiz-adv-x', | ||
'vert-adv-y', | ||
'vert-origin-x', | ||
'vert-origin-y', | ||
]), | ||
}, | ||
contentGroups: new Set([ | ||
@@ -1823,11 +1737,2 @@ 'animation', | ||
}, | ||
deprecated: { | ||
safe: new Set(['version']), | ||
unsafe: new Set([ | ||
'baseProfile', | ||
'contentScriptType', | ||
'contentStyleType', | ||
'zoomAndPan', | ||
]), | ||
}, | ||
contentGroups: new Set([ | ||
@@ -2079,3 +1984,2 @@ 'animation', | ||
]), | ||
deprecated: { unsafe: new Set(['viewTarget', 'zoomAndPan']) }, | ||
contentGroups: new Set(['descriptive']), | ||
@@ -2086,3 +1990,2 @@ }, | ||
attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']), | ||
deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) }, | ||
}, | ||
@@ -2092,3 +1995,3 @@ }; | ||
// https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes | ||
export const editorNamespaces = new Set([ | ||
exports.editorNamespaces = new Set([ | ||
'http://creativecommons.org/ns#', | ||
@@ -2121,3 +2024,3 @@ 'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd', | ||
*/ | ||
export const referencesProps = new Set([ | ||
exports.referencesProps = new Set([ | ||
'clip-path', | ||
@@ -2138,3 +2041,3 @@ 'color-profile', | ||
*/ | ||
export const inheritableAttrs = new Set([ | ||
exports.inheritableAttrs = new Set([ | ||
'clip-rule', | ||
@@ -2187,3 +2090,3 @@ 'color-interpolation-filters', | ||
export const presentationNonInheritableGroupAttrs = new Set([ | ||
exports.presentationNonInheritableGroupAttrs = new Set([ | ||
'clip-path', | ||
@@ -2204,3 +2107,3 @@ 'display', | ||
*/ | ||
export const colorsNames = { | ||
exports.colorsNames = { | ||
aliceblue: '#f0f8ff', | ||
@@ -2359,3 +2262,3 @@ antiquewhite: '#faebd7', | ||
*/ | ||
export const colorsShortNames = { | ||
exports.colorsShortNames = { | ||
'#f0ffff': 'azure', | ||
@@ -2398,3 +2301,3 @@ '#f5f5dc': 'beige', | ||
*/ | ||
export const colorsProps = new Set([ | ||
exports.colorsProps = new Set([ | ||
'color', | ||
@@ -2409,3 +2312,3 @@ 'fill', | ||
/** @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes */ | ||
export const pseudoClasses = { | ||
exports.pseudoClasses = { | ||
displayState: new Set(['fullscreen', 'modal', 'picture-in-picture']), | ||
@@ -2412,0 +2315,0 @@ input: new Set([ |
@@ -1,8 +0,10 @@ | ||
import { parsePathData, stringifyPathData } from '../lib/path.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').PathDataItem} PathDataItem | ||
*/ | ||
const { parsePathData, stringifyPathData } = require('../lib/path.js'); | ||
/** | ||
@@ -18,3 +20,3 @@ * @type {[number, number]} | ||
*/ | ||
export const path2js = (path) => { | ||
const path2js = (path) => { | ||
// @ts-ignore legacy | ||
@@ -38,2 +40,3 @@ if (path.pathJS) return path.pathJS; | ||
}; | ||
exports.path2js = path2js; | ||
@@ -183,3 +186,3 @@ /** | ||
*/ | ||
export const js2path = function (path, data, params) { | ||
exports.js2path = function (path, data, params) { | ||
// @ts-ignore legacy | ||
@@ -229,3 +232,3 @@ path.pathJS = data; | ||
*/ | ||
export const intersects = function (path1, path2) { | ||
exports.intersects = function (path1, path2) { | ||
// Collect points of every subpath. | ||
@@ -232,0 +235,0 @@ const points1 = gatherPoints(convertRelativeToAbsolute(path1)); |
@@ -1,3 +0,5 @@ | ||
import { cleanupOutData, toFixed } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
const { toFixed } = require('../lib/svgo/tools'); | ||
/** | ||
@@ -7,3 +9,2 @@ * @typedef {{ name: string, data: number[] }} TransformItem | ||
* convertToShorts: boolean, | ||
* degPrecision?: number, | ||
* floatPrecision: number, | ||
@@ -41,3 +42,3 @@ * transformPrecision: number, | ||
*/ | ||
export const transform2js = (transformString) => { | ||
exports.transform2js = (transformString) => { | ||
/** @type {TransformItem[]} */ | ||
@@ -73,2 +74,3 @@ const transforms = []; | ||
}; | ||
/** | ||
@@ -80,3 +82,3 @@ * Multiply transforms into one. | ||
*/ | ||
export const transformsMultiply = (transforms) => { | ||
exports.transformsMultiply = (transforms) => { | ||
const matrixData = transforms.map((transform) => { | ||
@@ -171,321 +173,111 @@ if (transform.name === 'matrix') { | ||
/** | ||
* @param {TransformItem} matrix | ||
* @returns {TransformItem[][]} | ||
* Decompose matrix into simple transforms. | ||
* | ||
* @param {TransformItem} transform | ||
* @param {TransformParams} params | ||
* @returns {TransformItem[]} | ||
* @see https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html | ||
*/ | ||
const getDecompositions = (matrix) => { | ||
const decompositions = []; | ||
const qrab = decomposeQRAB(matrix); | ||
const qrcd = decomposeQRCD(matrix); | ||
exports.matrixToTransform = (transform, params) => { | ||
const floatPrecision = params.floatPrecision; | ||
const data = transform.data; | ||
const transforms = []; | ||
if (qrab) { | ||
decompositions.push(qrab); | ||
} | ||
if (qrcd) { | ||
decompositions.push(qrcd); | ||
} | ||
return decompositions; | ||
}; | ||
/** | ||
* @param {TransformItem} matrix | ||
* @returns {TransformItem[]|undefined} | ||
* @see {@link https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html} Where applicable, variables are named in accordance with this document. | ||
*/ | ||
const decomposeQRAB = (matrix) => { | ||
const data = matrix.data; | ||
const [a, b, c, d, e, f] = data; | ||
const delta = a * d - b * c; | ||
if (delta === 0) { | ||
return; | ||
} | ||
const r = Math.hypot(a, b); | ||
if (r === 0) { | ||
return; | ||
} | ||
const decomposition = []; | ||
const cosOfRotationAngle = a / r; | ||
// [..., ..., ..., ..., tx, ty] → translate(tx, ty) | ||
if (e || f) { | ||
decomposition.push({ | ||
if (data[4] || data[5]) { | ||
transforms.push({ | ||
name: 'translate', | ||
data: [e, f], | ||
data: data.slice(4, data[5] ? 6 : 5), | ||
}); | ||
} | ||
if (cosOfRotationAngle !== 1) { | ||
const rotationAngleRads = Math.acos(cosOfRotationAngle); | ||
decomposition.push({ | ||
name: 'rotate', | ||
data: [mth.deg(b < 0 ? -rotationAngleRads : rotationAngleRads), 0, 0], | ||
}); | ||
} | ||
let sx = toFixed(Math.hypot(data[0], data[1]), params.transformPrecision); | ||
let sy = toFixed( | ||
(data[0] * data[3] - data[1] * data[2]) / sx, | ||
params.transformPrecision, | ||
); | ||
const colsSum = data[0] * data[2] + data[1] * data[3]; | ||
const rowsSum = data[0] * data[1] + data[2] * data[3]; | ||
const scaleBefore = rowsSum !== 0 || sx === sy; | ||
const sx = r; | ||
const sy = delta / sx; | ||
if (sx !== 1 || sy !== 1) { | ||
decomposition.push({ name: 'scale', data: [sx, sy] }); | ||
} | ||
const ac_plus_bd = a * c + b * d; | ||
if (ac_plus_bd) { | ||
decomposition.push({ | ||
// [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy) | ||
if (!data[1] && data[2]) { | ||
transforms.push({ | ||
name: 'skewX', | ||
data: [mth.deg(Math.atan(ac_plus_bd / (a * a + b * b)))], | ||
data: [mth.atan(data[2] / sy, floatPrecision)], | ||
}); | ||
} | ||
return decomposition; | ||
}; | ||
/** | ||
* @param {TransformItem} matrix | ||
* @returns {TransformItem[]|undefined} | ||
* @see {@link https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html} Where applicable, variables are named in accordance with this document. | ||
*/ | ||
const decomposeQRCD = (matrix) => { | ||
const data = matrix.data; | ||
const [a, b, c, d, e, f] = data; | ||
const delta = a * d - b * c; | ||
if (delta === 0) { | ||
return; | ||
} | ||
const s = Math.hypot(c, d); | ||
if (s === 0) { | ||
return; | ||
} | ||
const decomposition = []; | ||
if (e || f) { | ||
decomposition.push({ | ||
name: 'translate', | ||
data: [e, f], | ||
}); | ||
} | ||
const rotationAngleRads = Math.PI / 2 - (d < 0 ? -1 : 1) * Math.acos(-c / s); | ||
decomposition.push({ | ||
name: 'rotate', | ||
data: [mth.deg(rotationAngleRads), 0, 0], | ||
}); | ||
const sx = delta / s; | ||
const sy = s; | ||
if (sx !== 1 || sy !== 1) { | ||
decomposition.push({ name: 'scale', data: [sx, sy] }); | ||
} | ||
const ac_plus_bd = a * c + b * d; | ||
if (ac_plus_bd) { | ||
decomposition.push({ | ||
// [sx, sx·tan(a), 0, sy, 0, 0] → skewY(a)·scale(sx, sy) | ||
} else if (data[1] && !data[2]) { | ||
transforms.push({ | ||
name: 'skewY', | ||
data: [mth.deg(Math.atan(ac_plus_bd / (c * c + d * d)))], | ||
data: [mth.atan(data[1] / data[0], floatPrecision)], | ||
}); | ||
} | ||
sx = data[0]; | ||
sy = data[3]; | ||
return decomposition; | ||
}; | ||
// [sx·cos(a), sx·sin(a), sy·-sin(a), sy·cos(a), x, y] → rotate(a[, cx, cy])·(scale or skewX) or | ||
// [sx·cos(a), sy·sin(a), sx·-sin(a), sy·cos(a), x, y] → scale(sx, sy)·rotate(a[, cx, cy]) (if !scaleBefore) | ||
} else if (!colsSum || (sx === 1 && sy === 1) || !scaleBefore) { | ||
if (!scaleBefore) { | ||
sx = Math.hypot(data[0], data[2]); | ||
sy = Math.hypot(data[1], data[3]); | ||
/** | ||
* Convert translate(tx,ty)rotate(a) to rotate(a,cx,cy). | ||
* @param {number} tx | ||
* @param {number} ty | ||
* @param {number} a | ||
* @returns {TransformItem} | ||
*/ | ||
const mergeTranslateAndRotate = (tx, ty, a) => { | ||
// From https://www.w3.org/TR/SVG11/coords.html#TransformAttribute: | ||
// We have translate(tx,ty) rotate(a). This is equivalent to [cos(a) sin(a) -sin(a) cos(a) tx ty]. | ||
// | ||
// rotate(a,cx,cy) is equivalent to translate(cx, cy) rotate(a) translate(-cx, -cy). | ||
// Multiplying the right side gives the matrix | ||
// [cos(a) sin(a) -sin(a) cos(a) | ||
// -cx * cos(a) + cy * sin(a) + cx | ||
// -cx * sin(a) - cy * cos(a) + cy | ||
// ] | ||
// | ||
// We need cx and cy such that | ||
// tx = -cx * cos(a) + cy * sin(a) + cx | ||
// ty = -cx * sin(a) - cy * cos(a) + cy | ||
// | ||
// Solving these for cx and cy gives | ||
// cy = (d * ty + e * tx)/(d^2 + e^2) | ||
// cx = (tx - e * cy) / d | ||
// where d = 1 - cos(a) and e = sin(a) | ||
if (toFixed(data[0], params.transformPrecision) < 0) { | ||
sx = -sx; | ||
} | ||
const rotationAngleRads = mth.rad(a); | ||
const d = 1 - Math.cos(rotationAngleRads); | ||
const e = Math.sin(rotationAngleRads); | ||
const cy = (d * ty + e * tx) / (d * d + e * e); | ||
const cx = (tx - e * cy) / d; | ||
return { name: 'rotate', data: [a, cx, cy] }; | ||
}; | ||
if ( | ||
data[3] < 0 || | ||
(Math.sign(data[1]) === Math.sign(data[2]) && | ||
toFixed(data[3], params.transformPrecision) === 0) | ||
) { | ||
sy = -sy; | ||
} | ||
/** | ||
* @param {TransformItem} t | ||
* @returns {Boolean} | ||
*/ | ||
const isIdentityTransform = (t) => { | ||
switch (t.name) { | ||
case 'rotate': | ||
case 'skewX': | ||
case 'skewY': | ||
return t.data[0] === 0; | ||
case 'scale': | ||
return t.data[0] === 1 && t.data[1] === 1; | ||
case 'translate': | ||
return t.data[0] === 0 && t.data[1] === 0; | ||
} | ||
return false; | ||
}; | ||
transforms.push({ name: 'scale', data: [sx, sy] }); | ||
} | ||
const angle = Math.min(Math.max(-1, data[0] / sx), 1); | ||
const rotate = [ | ||
mth.acos(angle, floatPrecision) * | ||
((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1), | ||
]; | ||
/** | ||
* Optimize matrix of simple transforms. | ||
* @param {TransformItem[]} roundedTransforms | ||
* @param {TransformItem[]} rawTransforms | ||
* @returns {TransformItem[]} | ||
*/ | ||
const optimize = (roundedTransforms, rawTransforms) => { | ||
const optimizedTransforms = []; | ||
if (rotate[0]) { | ||
transforms.push({ name: 'rotate', data: rotate }); | ||
} | ||
for (let index = 0; index < roundedTransforms.length; index++) { | ||
const roundedTransform = roundedTransforms[index]; | ||
if (rowsSum && colsSum) | ||
transforms.push({ | ||
name: 'skewX', | ||
data: [mth.atan(colsSum / (sx * sx), floatPrecision)], | ||
}); | ||
// Don't include any identity transforms. | ||
if (isIdentityTransform(roundedTransform)) { | ||
continue; | ||
// rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point) | ||
if (rotate[0] && (data[4] || data[5])) { | ||
transforms.shift(); | ||
const oneOverCos = 1 - data[0] / sx; | ||
const sin = data[1] / (scaleBefore ? sx : sy); | ||
const x = data[4] * (scaleBefore ? 1 : sy); | ||
const y = data[5] * (scaleBefore ? 1 : sx); | ||
const denom = (oneOverCos ** 2 + sin ** 2) * (scaleBefore ? 1 : sx * sy); | ||
rotate.push( | ||
(oneOverCos * x - sin * y) / denom, | ||
(oneOverCos * y + sin * x) / denom, | ||
); | ||
} | ||
const data = roundedTransform.data; | ||
switch (roundedTransform.name) { | ||
case 'rotate': | ||
switch (data[0]) { | ||
case 180: | ||
case -180: | ||
{ | ||
// If the next element is a scale, invert it, and don't add the rotate to the optimized array. | ||
const next = roundedTransforms[index + 1]; | ||
if (next && next.name === 'scale') { | ||
optimizedTransforms.push( | ||
createScaleTransform(next.data.map((v) => -v)), | ||
); | ||
index++; | ||
} else { | ||
// Otherwise replace the rotate with a scale(-1). | ||
optimizedTransforms.push({ | ||
name: 'scale', | ||
data: [-1], | ||
}); | ||
} | ||
} | ||
continue; | ||
} | ||
optimizedTransforms.push({ | ||
name: 'rotate', | ||
data: data.slice(0, data[1] || data[2] ? 3 : 1), | ||
}); | ||
break; | ||
case 'scale': | ||
optimizedTransforms.push(createScaleTransform(data)); | ||
break; | ||
case 'skewX': | ||
case 'skewY': | ||
optimizedTransforms.push({ | ||
name: roundedTransform.name, | ||
data: [data[0]], | ||
}); | ||
break; | ||
case 'translate': | ||
{ | ||
// If the next item is a rotate(a,0,0), merge the translate and rotate. | ||
// If the rotation angle is +/-180, assume it will be optimized out, and don't do the merge. | ||
const next = roundedTransforms[index + 1]; | ||
if ( | ||
next && | ||
next.name === 'rotate' && | ||
next.data[0] !== 180 && | ||
next.data[0] !== -180 && | ||
next.data[0] !== 0 && | ||
next.data[1] === 0 && | ||
next.data[2] === 0 | ||
) { | ||
// Use the un-rounded data to do the merge. | ||
const data = rawTransforms[index].data; | ||
optimizedTransforms.push( | ||
mergeTranslateAndRotate( | ||
data[0], | ||
data[1], | ||
rawTransforms[index + 1].data[0], | ||
), | ||
); | ||
// Skip over the rotate. | ||
index++; | ||
continue; | ||
} | ||
} | ||
optimizedTransforms.push({ | ||
name: 'translate', | ||
data: data.slice(0, data[1] ? 2 : 1), | ||
}); | ||
break; | ||
} | ||
// Too many transformations, return original matrix if it isn't just a scale/translate | ||
} else if (data[1] || data[2]) { | ||
return [transform]; | ||
} | ||
// If everything was optimized out, reture identity transform scale(1). | ||
return optimizedTransforms.length | ||
? optimizedTransforms | ||
: [{ name: 'scale', data: [1] }]; | ||
}; | ||
/** | ||
* @param {number[]} data | ||
* @returns {TransformItem} | ||
*/ | ||
const createScaleTransform = (data) => { | ||
const scaleData = data.slice(0, data[0] === data[1] ? 1 : 2); | ||
return { | ||
name: 'scale', | ||
data: scaleData, | ||
}; | ||
}; | ||
/** | ||
* Decompose matrix into simple transforms and optimize. | ||
* @param {TransformItem} origMatrix | ||
* @param {TransformParams} params | ||
* @returns {TransformItem[]} | ||
*/ | ||
export const matrixToTransform = (origMatrix, params) => { | ||
const decomposed = getDecompositions(origMatrix); | ||
let shortest; | ||
let shortestLen = Number.MAX_VALUE; | ||
for (const decomposition of decomposed) { | ||
// Make a copy of the decomposed matrix, and round all data. We need to keep the original decomposition, | ||
// at full precision, to perform some optimizations. | ||
const roundedTransforms = decomposition.map((transformItem) => { | ||
const transformCopy = { | ||
name: transformItem.name, | ||
data: [...transformItem.data], | ||
}; | ||
return roundTransform(transformCopy, params); | ||
if ((scaleBefore && (sx != 1 || sy != 1)) || !transforms.length) { | ||
transforms.push({ | ||
name: 'scale', | ||
data: sx == sy ? [sx] : [sx, sy], | ||
}); | ||
const optimized = optimize(roundedTransforms, decomposition); | ||
const len = js2transform(optimized, params).length; | ||
if (len < shortestLen) { | ||
shortest = optimized; | ||
shortestLen = len; | ||
} | ||
} | ||
return shortest ?? [origMatrix]; | ||
return transforms; | ||
}; | ||
@@ -512,3 +304,3 @@ | ||
0, | ||
transform.data[1] ?? transform.data[0], | ||
transform.data[1] || transform.data[0], | ||
0, | ||
@@ -554,3 +346,3 @@ 0, | ||
*/ | ||
export const transformArc = (cursor, arc, transform) => { | ||
exports.transformArc = (cursor, arc, transform) => { | ||
const x = arc[5] - cursor[0]; | ||
@@ -626,124 +418,1 @@ const y = arc[6] - cursor[1]; | ||
}; | ||
/** | ||
* @type {(transform: TransformItem, params: TransformParams) => TransformItem} | ||
*/ | ||
export const roundTransform = (transform, params) => { | ||
switch (transform.name) { | ||
case 'translate': | ||
transform.data = floatRound(transform.data, params); | ||
break; | ||
case 'rotate': | ||
transform.data = [ | ||
...degRound(transform.data.slice(0, 1), params), | ||
...floatRound(transform.data.slice(1), params), | ||
]; | ||
break; | ||
case 'skewX': | ||
case 'skewY': | ||
transform.data = degRound(transform.data, params); | ||
break; | ||
case 'scale': | ||
transform.data = transformRound(transform.data, params); | ||
break; | ||
case 'matrix': | ||
transform.data = [ | ||
...transformRound(transform.data.slice(0, 4), params), | ||
...floatRound(transform.data.slice(4), params), | ||
]; | ||
break; | ||
} | ||
return transform; | ||
}; | ||
/** | ||
* @type {(data: number[], params: TransformParams) => number[]} | ||
*/ | ||
const degRound = (data, params) => { | ||
if ( | ||
params.degPrecision != null && | ||
params.degPrecision >= 1 && | ||
params.floatPrecision < 20 | ||
) { | ||
return smartRound(params.degPrecision, data); | ||
} else { | ||
return round(data); | ||
} | ||
}; | ||
/** | ||
* @type {(data: number[], params: TransformParams) => number[]} | ||
*/ | ||
const floatRound = (data, params) => { | ||
if (params.floatPrecision >= 1 && params.floatPrecision < 20) { | ||
return smartRound(params.floatPrecision, data); | ||
} else { | ||
return round(data); | ||
} | ||
}; | ||
/** | ||
* @type {(data: number[], params: TransformParams) => number[]} | ||
*/ | ||
const transformRound = (data, params) => { | ||
if (params.transformPrecision >= 1 && params.floatPrecision < 20) { | ||
return smartRound(params.transformPrecision, data); | ||
} else { | ||
return round(data); | ||
} | ||
}; | ||
/** | ||
* Rounds numbers in array. | ||
* | ||
* @type {(data: number[]) => number[]} | ||
*/ | ||
const round = (data) => { | ||
return data.map(Math.round); | ||
}; | ||
/** | ||
* Decrease accuracy of floating-point numbers | ||
* in transforms keeping a specified number of decimals. | ||
* Smart rounds values like 2.349 to 2.35. | ||
* | ||
* @param {number} precision | ||
* @param {number[]} data | ||
* @returns {number[]} | ||
*/ | ||
const smartRound = (precision, data) => { | ||
for ( | ||
var i = data.length, | ||
tolerance = +Math.pow(0.1, precision).toFixed(precision); | ||
i--; | ||
) { | ||
if (toFixed(data[i], precision) !== data[i]) { | ||
var rounded = +data[i].toFixed(precision - 1); | ||
data[i] = | ||
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance | ||
? +data[i].toFixed(precision) | ||
: rounded; | ||
} | ||
} | ||
return data; | ||
}; | ||
/** | ||
* Convert transforms JS representation to string. | ||
* | ||
* @param {TransformItem[]} transformJS | ||
* @param {TransformParams} params | ||
* @returns {string} | ||
*/ | ||
export const js2transform = (transformJS, params) => { | ||
const transformString = transformJS | ||
.map((transform) => { | ||
roundTransform(transform, params); | ||
return `${transform.name}(${cleanupOutData(transform.data, params)})`; | ||
}) | ||
.join(''); | ||
return transformString; | ||
}; |
@@ -1,4 +0,6 @@ | ||
export const name = 'addAttributesToSVGElement'; | ||
export const description = 'adds attributes to an outer <svg> element'; | ||
'use strict'; | ||
exports.name = 'addAttributesToSVGElement'; | ||
exports.description = 'adds attributes to an outer <svg> element'; | ||
var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters. | ||
@@ -48,5 +50,5 @@ It should have a list of "attributes" or one "attribute". | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'addAttributesToSVGElement'>} | ||
* @type {import('./plugins-types').Plugin<'addAttributesToSVGElement'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
if (!Array.isArray(params.attributes) && !params.attribute) { | ||
@@ -53,0 +55,0 @@ console.error(ENOCLS); |
@@ -1,4 +0,6 @@ | ||
export const name = 'addClassesToSVGElement'; | ||
export const description = 'adds classnames to an outer <svg> element'; | ||
'use strict'; | ||
exports.name = 'addClassesToSVGElement'; | ||
exports.description = 'adds classnames to an outer <svg> element'; | ||
var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters. | ||
@@ -50,7 +52,7 @@ It should have a list of classes in "classNames" or one "className". | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'addClassesToSVGElement'>} | ||
* @type {import('./plugins-types').Plugin<'addClassesToSVGElement'>} | ||
*/ | ||
export const fn = (root, params, info) => { | ||
exports.fn = (root, params) => { | ||
if ( | ||
!(Array.isArray(params.classNames) && params.classNames.length !== 0) && | ||
!(Array.isArray(params.classNames) && params.classNames.some(String)) && | ||
!params.className | ||
@@ -73,7 +75,3 @@ ) { | ||
if (className != null) { | ||
const classToAdd = | ||
typeof className === 'string' | ||
? className | ||
: className(node, info); | ||
classList.add(classToAdd); | ||
classList.add(className); | ||
} | ||
@@ -80,0 +78,0 @@ } |
@@ -0,17 +1,21 @@ | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types').PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
*/ | ||
import { path2js } from './_path.js'; | ||
import { | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { | ||
transformsMultiply, | ||
transform2js, | ||
transformArc, | ||
} from './_transforms.js'; | ||
import { referencesProps, attrsGroupsDefaults } from './_collections.js'; | ||
import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
} = require('./_transforms.js'); | ||
const { path2js } = require('./_path.js'); | ||
const { | ||
removeLeadingZero, | ||
includesUrlReference, | ||
} = require('../lib/svgo/tools.js'); | ||
const { referencesProps, attrsGroupsDefaults } = require('./_collections.js'); | ||
import { removeLeadingZero, includesUrlReference } from '../lib/svgo/tools.js'; | ||
/** | ||
@@ -27,3 +31,3 @@ * @typedef {PathDataItem[]} PathData | ||
* | ||
* @type {import('../lib/types.js').Plugin<{ | ||
* @type {import('../lib/types').Plugin<{ | ||
* transformPrecision: number, | ||
@@ -33,3 +37,3 @@ * applyTransformsStroked: boolean, | ||
*/ | ||
export const applyTransforms = (root, params) => { | ||
const applyTransforms = (root, params) => { | ||
const stylesheet = collectStylesheet(root); | ||
@@ -98,5 +102,5 @@ return { | ||
const scale = Number( | ||
Math.hypot(matrix.data[0], matrix.data[1]).toFixed( | ||
transformPrecision, | ||
), | ||
Math.sqrt( | ||
matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1], | ||
).toFixed(transformPrecision), | ||
); | ||
@@ -162,2 +166,3 @@ | ||
}; | ||
exports.applyTransforms = applyTransforms; | ||
@@ -219,3 +224,3 @@ /** | ||
// horizontal lineto (x) | ||
// convert to lineto to handle two-dimensional transforms | ||
// convert to lineto to handle two-dimentional transforms | ||
if (command === 'H') { | ||
@@ -231,3 +236,3 @@ command = 'L'; | ||
// vertical lineto (y) | ||
// convert to lineto to handle two-dimensional transforms | ||
// convert to lineto to handle two-dimentional transforms | ||
if (command === 'V') { | ||
@@ -234,0 +239,0 @@ command = 'L'; |
@@ -1,3 +0,5 @@ | ||
export const name = 'cleanupAttrs'; | ||
export const description = | ||
'use strict'; | ||
exports.name = 'cleanupAttrs'; | ||
exports.description = | ||
'cleanups attributes from newlines, trailing and repeating spaces'; | ||
@@ -13,5 +15,6 @@ | ||
* @author Kir Belevich | ||
* @type {import('./plugins-types.js').Plugin<'cleanupAttrs'>} | ||
* | ||
* @type {import('./plugins-types').Plugin<'cleanupAttrs'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { newlines = true, trim = true, spaces = true } = params; | ||
@@ -18,0 +21,0 @@ return { |
@@ -1,6 +0,8 @@ | ||
import * as csstree from 'css-tree'; | ||
import { visit } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'cleanupEnableBackground'; | ||
export const description = | ||
const csstree = require('css-tree'); | ||
const { visit } = require('../lib/xast.js'); | ||
exports.name = 'cleanupEnableBackground'; | ||
exports.description = | ||
'remove or cleanup enable-background attribute when possible'; | ||
@@ -20,5 +22,5 @@ | ||
* @author Kir Belevich | ||
* @type {import('./plugins-types.js').Plugin<'cleanupEnableBackground'>} | ||
* @type {import('./plugins-types').Plugin<'cleanupEnableBackground'>} | ||
*/ | ||
export const fn = (root) => { | ||
exports.fn = (root) => { | ||
let hasFilter = false; | ||
@@ -25,0 +27,0 @@ |
@@ -1,11 +0,13 @@ | ||
import { visitSkip } from '../lib/xast.js'; | ||
import { hasScripts, findReferences } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
*/ | ||
export const name = 'cleanupIds'; | ||
export const description = 'removes unused IDs and minifies used'; | ||
const { visitSkip } = require('../lib/xast.js'); | ||
const { hasScripts, findReferences } = require('../lib/svgo/tools'); | ||
exports.name = 'cleanupIds'; | ||
exports.description = 'removes unused IDs and minifies used'; | ||
const generateIdChars = [ | ||
@@ -122,5 +124,5 @@ 'a', | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'cleanupIds'>} | ||
* @type {import('./plugins-types').Plugin<'cleanupIds'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { | ||
@@ -236,5 +238,6 @@ remove = true, | ||
// replace id in href and url() | ||
element.attributes[name] = value | ||
.replace(`#${encodeURI(id)}`, `#${currentIdString}`) | ||
.replace(`#${id}`, `#${currentIdString}`); | ||
element.attributes[name] = value.replace( | ||
`#${encodeURI(id)}`, | ||
`#${currentIdString}`, | ||
); | ||
} else { | ||
@@ -241,0 +244,0 @@ // replace id in begin attribute |
@@ -1,6 +0,8 @@ | ||
import { removeLeadingZero } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
export const name = 'cleanupListOfValues'; | ||
export const description = 'rounds list of values to the fixed precision'; | ||
const { removeLeadingZero } = require('../lib/svgo/tools.js'); | ||
exports.name = 'cleanupListOfValues'; | ||
exports.description = 'rounds list of values to the fixed precision'; | ||
const regNumericValues = | ||
@@ -33,5 +35,5 @@ /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/; | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'cleanupListOfValues'>} | ||
* @type {import('./plugins-types').Plugin<'cleanupListOfValues'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { | ||
@@ -38,0 +40,0 @@ floatPrecision = 3, |
@@ -1,5 +0,7 @@ | ||
import { removeLeadingZero } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
export const name = 'cleanupNumericValues'; | ||
export const description = | ||
const { removeLeadingZero } = require('../lib/svgo/tools'); | ||
exports.name = 'cleanupNumericValues'; | ||
exports.description = | ||
'rounds numeric values to the fixed precision, removes default ‘px’ units'; | ||
@@ -26,5 +28,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'cleanupNumericValues'>} | ||
* @type {import('./plugins-types').Plugin<'cleanupNumericValues'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { | ||
@@ -31,0 +33,0 @@ floatPrecision = 3, |
@@ -1,12 +0,12 @@ | ||
import { computeStyle, collectStylesheet } from '../lib/style.js'; | ||
import { inheritableAttrs, elemsGroups } from './_collections.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastNode} XastNode | ||
* @typedef {import('../lib/types').XastNode} XastNode | ||
*/ | ||
export const name = 'collapseGroups'; | ||
export const description = 'collapses useless groups'; | ||
const { inheritableAttrs, elemsGroups } = require('./_collections.js'); | ||
exports.name = 'collapseGroups'; | ||
exports.description = 'collapses useless groups'; | ||
/** | ||
@@ -52,7 +52,5 @@ * @type {(node: XastNode, name: string) => boolean} | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'collapseGroups'>} | ||
* @type {import('./plugins-types').Plugin<'collapseGroups'>} | ||
*/ | ||
export const fn = (root) => { | ||
const stylesheet = collectStylesheet(root); | ||
exports.fn = () => { | ||
return { | ||
@@ -75,5 +73,2 @@ element: { | ||
const firstChild = node.children[0]; | ||
const nodeHasFilter = !!( | ||
node.attributes.filter || computeStyle(stylesheet, node).filter | ||
); | ||
// TODO untangle this mess | ||
@@ -83,3 +78,3 @@ if ( | ||
firstChild.attributes.id == null && | ||
!nodeHasFilter && | ||
node.attributes.filter == null && | ||
(node.attributes.class == null || | ||
@@ -93,4 +88,2 @@ firstChild.attributes.class == null) && | ||
) { | ||
const newChildElemAttrs = { ...firstChild.attributes }; | ||
for (const [name, value] of Object.entries(node.attributes)) { | ||
@@ -101,19 +94,17 @@ // avoid copying to not conflict with animated attribute | ||
} | ||
if (newChildElemAttrs[name] == null) { | ||
newChildElemAttrs[name] = value; | ||
if (firstChild.attributes[name] == null) { | ||
firstChild.attributes[name] = value; | ||
} else if (name === 'transform') { | ||
newChildElemAttrs[name] = value + ' ' + newChildElemAttrs[name]; | ||
} else if (newChildElemAttrs[name] === 'inherit') { | ||
newChildElemAttrs[name] = value; | ||
firstChild.attributes[name] = | ||
value + ' ' + firstChild.attributes[name]; | ||
} else if (firstChild.attributes[name] === 'inherit') { | ||
firstChild.attributes[name] = value; | ||
} else if ( | ||
!inheritableAttrs.has(name) && | ||
newChildElemAttrs[name] !== value | ||
inheritableAttrs.has(name) === false && | ||
firstChild.attributes[name] !== value | ||
) { | ||
return; | ||
} | ||
delete node.attributes[name]; | ||
} | ||
node.attributes = {}; | ||
firstChild.attributes = newChildElemAttrs; | ||
} | ||
@@ -120,0 +111,0 @@ } |
@@ -1,8 +0,8 @@ | ||
import { colorsNames, colorsProps, colorsShortNames } from './_collections.js'; | ||
import { includesUrlReference } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
export const name = 'convertColors'; | ||
export const description = | ||
'converts colors: rgb() to #rrggbb and #rrggbb to #rgb'; | ||
const collections = require('./_collections.js'); | ||
exports.name = 'convertColors'; | ||
exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb'; | ||
const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)'; | ||
@@ -64,5 +64,5 @@ const rComma = '\\s*,\\s*'; | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'convertColors'>} | ||
* @type {import('./plugins-types').Plugin<'convertColors'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { | ||
@@ -72,3 +72,2 @@ currentColor = false, | ||
rgb2hex = true, | ||
convertCase = 'lower', | ||
shorthex = true, | ||
@@ -82,3 +81,3 @@ shortname = true, | ||
for (const [name, value] of Object.entries(node.attributes)) { | ||
if (colorsProps.has(name)) { | ||
if (collections.colorsProps.has(name)) { | ||
let val = value; | ||
@@ -97,3 +96,3 @@ | ||
if (matched) { | ||
val = 'currentcolor'; | ||
val = 'currentColor'; | ||
} | ||
@@ -105,4 +104,4 @@ } | ||
const colorName = val.toLowerCase(); | ||
if (colorsNames[colorName] != null) { | ||
val = colorsNames[colorName]; | ||
if (collections.colorsNames[colorName] != null) { | ||
val = collections.colorsNames[colorName]; | ||
} | ||
@@ -128,13 +127,5 @@ } | ||
if (convertCase && !includesUrlReference(val)) { | ||
if (convertCase === 'lower') { | ||
val = val.toLowerCase(); | ||
} else if (convertCase === 'upper') { | ||
val = val.toUpperCase(); | ||
} | ||
} | ||
// convert long hex to short hex | ||
if (shorthex) { | ||
let match = regHEX.exec(val); | ||
let match = val.match(regHEX); | ||
if (match != null) { | ||
@@ -148,4 +139,4 @@ val = '#' + match[0][1] + match[0][3] + match[0][5]; | ||
const colorName = val.toLowerCase(); | ||
if (colorsShortNames[colorName] != null) { | ||
val = colorsShortNames[colorName]; | ||
if (collections.colorsShortNames[colorName] != null) { | ||
val = collections.colorsShortNames[colorName]; | ||
} | ||
@@ -152,0 +143,0 @@ } |
@@ -1,4 +0,6 @@ | ||
export const name = 'convertEllipseToCircle'; | ||
export const description = 'converts non-eccentric <ellipse>s to <circle>s'; | ||
'use strict'; | ||
exports.name = 'convertEllipseToCircle'; | ||
exports.description = 'converts non-eccentric <ellipse>s to <circle>s'; | ||
/** | ||
@@ -11,5 +13,5 @@ * Converts non-eccentric <ellipse>s to <circle>s. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'convertEllipseToCircle'>} | ||
* @type {import('./plugins-types').Plugin<'convertEllipseToCircle'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -16,0 +18,0 @@ element: { |
@@ -1,16 +0,18 @@ | ||
import { attrsGroupsDefaults, colorsProps } from './_collections.js'; | ||
import { | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastParent} XastParent | ||
*/ | ||
const { attrsGroupsDefaults, colorsProps } = require('./_collections'); | ||
const { | ||
detachNodeFromParent, | ||
querySelectorAll, | ||
querySelector, | ||
} from '../lib/xast.js'; | ||
import { computeStyle, collectStylesheet } from '../lib/style.js'; | ||
} = require('../lib/xast'); | ||
const { computeStyle, collectStylesheet } = require('../lib/style'); | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastParent} XastParent | ||
*/ | ||
export const name = 'convertOneStopGradients'; | ||
export const description = | ||
exports.name = 'convertOneStopGradients'; | ||
exports.description = | ||
'converts one-stop (single color) gradients to a plain color'; | ||
@@ -22,7 +24,7 @@ | ||
* @author Seth Falco <seth@falco.fun> | ||
* @type {import('./plugins-types.js').Plugin<'convertOneStopGradients'>} | ||
* @type {import('./plugins-types').Plugin<'convertOneStopGradients'>} | ||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/linearGradient | ||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/radialGradient | ||
*/ | ||
export const fn = (root) => { | ||
exports.fn = (root) => { | ||
const stylesheet = collectStylesheet(root); | ||
@@ -29,0 +31,0 @@ |
@@ -1,14 +0,16 @@ | ||
import { path2js, js2path } from './_path.js'; | ||
import { pathElems } from './_collections.js'; | ||
import { applyTransforms } from './applyTransforms.js'; | ||
import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
import { visit } from '../lib/xast.js'; | ||
import { cleanupOutData, toFixed } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem | ||
* @typedef {import('../lib//types').PathDataItem} PathDataItem | ||
*/ | ||
export const name = 'convertPathData'; | ||
export const description = | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { visit } = require('../lib/xast.js'); | ||
const { pathElems } = require('./_collections.js'); | ||
const { path2js, js2path } = require('./_path.js'); | ||
const { applyTransforms } = require('./applyTransforms.js'); | ||
const { cleanupOutData, toFixed } = require('../lib/svgo/tools'); | ||
exports.name = 'convertPathData'; | ||
exports.description = | ||
'optimizes path data: writes in shorter form, applies transformations'; | ||
@@ -76,5 +78,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'convertPathData'>} | ||
* @type {import('./plugins-types').Plugin<'convertPathData'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { | ||
@@ -176,9 +178,6 @@ // TODO convert to separate plugin in v3 | ||
let data = path2js(node); | ||
var data = path2js(node); | ||
// TODO: get rid of functions returns | ||
if (data.length) { | ||
const includesVertices = data.some( | ||
(item) => item.command !== 'm' && item.command !== 'M', | ||
); | ||
convertToRelative(data); | ||
@@ -196,19 +195,2 @@ | ||
const hasMarker = | ||
node.attributes['marker-start'] != null || | ||
node.attributes['marker-end'] != null; | ||
const isMarkersOnlyPath = | ||
hasMarker && | ||
includesVertices && | ||
data.every( | ||
(item) => item.command === 'm' || item.command === 'M', | ||
); | ||
if (isMarkersOnlyPath) { | ||
data.push({ | ||
command: 'z', | ||
args: [], | ||
}); | ||
} | ||
// @ts-ignore | ||
@@ -420,2 +402,3 @@ js2path(node, data, newParams); | ||
const qControlPoint = prevQControlPoint; | ||
prevQControlPoint = undefined; | ||
@@ -921,4 +904,2 @@ let command = item.command; | ||
} | ||
} else { | ||
prevQControlPoint = undefined; | ||
} | ||
@@ -1141,3 +1122,3 @@ prev = item; | ||
if (Math.abs(rx - ry) > error) return undefined; | ||
const chord = Math.hypot(data[5], data[6]); | ||
const chord = Math.sqrt(data[5] ** 2 + data[6] ** 2); | ||
if (chord > rx * 2) return undefined; | ||
@@ -1174,3 +1155,3 @@ return rx - Math.sqrt(rx ** 2 - 0.25 * chord ** 2); | ||
function getDistance(point1, point2) { | ||
return Math.hypot(point1[0] - point2[0], point1[1] - point2[1]); | ||
return Math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2); | ||
} | ||
@@ -1177,0 +1158,0 @@ |
@@ -1,11 +0,13 @@ | ||
import { stringifyPathData } from '../lib/path.js'; | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types').PathDataItem} PathDataItem | ||
*/ | ||
export const name = 'convertShapeToPath'; | ||
export const description = 'converts basic shapes to more compact path form'; | ||
const { stringifyPathData } = require('../lib/path.js'); | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'convertShapeToPath'; | ||
exports.description = 'converts basic shapes to more compact path form'; | ||
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; | ||
@@ -22,5 +24,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'convertShapeToPath'>} | ||
* @type {import('./plugins-types').Plugin<'convertShapeToPath'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { convertArcs = false, floatPrecision: precision } = params; | ||
@@ -27,0 +29,0 @@ |
@@ -1,6 +0,8 @@ | ||
import { attrsGroups } from './_collections.js'; | ||
'use strict'; | ||
export const name = 'convertStyleToAttrs'; | ||
export const description = 'converts style to attributes'; | ||
const { attrsGroups } = require('./_collections'); | ||
exports.name = 'convertStyleToAttrs'; | ||
exports.description = 'converts style to attributes'; | ||
/** | ||
@@ -66,5 +68,5 @@ * @type {(...args: string[]) => string} | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'convertStyleToAttrs'>} | ||
* @type {import('./plugins-types').Plugin<'convertStyleToAttrs'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { keepImportant = false } = params; | ||
@@ -71,0 +73,0 @@ return { |
@@ -1,19 +0,19 @@ | ||
import { | ||
js2transform, | ||
matrixToTransform, | ||
roundTransform, | ||
transform2js, | ||
transformsMultiply, | ||
} from './_transforms.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastChild} XastChild | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastParent} XastParent | ||
* @typedef {import('../lib/types').XastChild} XastChild | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastParent} XastParent | ||
*/ | ||
export const name = 'convertTransform'; | ||
export const description = | ||
'collapses multiple transformations and optimizes it'; | ||
const { cleanupOutData, toFixed } = require('../lib/svgo/tools.js'); | ||
const { | ||
transform2js, | ||
transformsMultiply, | ||
matrixToTransform, | ||
} = require('./_transforms.js'); | ||
exports.name = 'convertTransform'; | ||
exports.description = 'collapses multiple transformations and optimizes it'; | ||
/** | ||
@@ -29,5 +29,5 @@ * Convert matrices to the short aliases, | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'convertTransform'>} | ||
* @type {import('./plugins-types').Plugin<'convertTransform'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { | ||
@@ -176,2 +176,38 @@ convertToShorts = true, | ||
/** | ||
* @type {(data: number[], params: TransformParams) => number[]} | ||
*/ | ||
const degRound = (data, params) => { | ||
if ( | ||
params.degPrecision != null && | ||
params.degPrecision >= 1 && | ||
params.floatPrecision < 20 | ||
) { | ||
return smartRound(params.degPrecision, data); | ||
} else { | ||
return round(data); | ||
} | ||
}; | ||
/** | ||
* @type {(data: number[], params: TransformParams) => number[]} | ||
*/ | ||
const floatRound = (data, params) => { | ||
if (params.floatPrecision >= 1 && params.floatPrecision < 20) { | ||
return smartRound(params.floatPrecision, data); | ||
} else { | ||
return round(data); | ||
} | ||
}; | ||
/** | ||
* @type {(data: number[], params: TransformParams) => number[]} | ||
*/ | ||
const transformRound = (data, params) => { | ||
if (params.transformPrecision >= 1 && params.floatPrecision < 20) { | ||
return smartRound(params.transformPrecision, data); | ||
} else { | ||
return round(data); | ||
} | ||
}; | ||
/** | ||
* Returns number of digits after the point. 0.125 → 3 | ||
@@ -299,1 +335,87 @@ * | ||
}; | ||
/** | ||
* Convert transforms JS representation to string. | ||
* | ||
* @param {TransformItem[]} transformJS | ||
* @param {TransformParams} params | ||
* @returns {string} | ||
*/ | ||
const js2transform = (transformJS, params) => { | ||
const transformString = transformJS | ||
.map((transform) => { | ||
roundTransform(transform, params); | ||
return `${transform.name}(${cleanupOutData(transform.data, params)})`; | ||
}) | ||
.join(''); | ||
return transformString; | ||
}; | ||
/** | ||
* @type {(transform: TransformItem, params: TransformParams) => TransformItem} | ||
*/ | ||
const roundTransform = (transform, params) => { | ||
switch (transform.name) { | ||
case 'translate': | ||
transform.data = floatRound(transform.data, params); | ||
break; | ||
case 'rotate': | ||
transform.data = [ | ||
...degRound(transform.data.slice(0, 1), params), | ||
...floatRound(transform.data.slice(1), params), | ||
]; | ||
break; | ||
case 'skewX': | ||
case 'skewY': | ||
transform.data = degRound(transform.data, params); | ||
break; | ||
case 'scale': | ||
transform.data = transformRound(transform.data, params); | ||
break; | ||
case 'matrix': | ||
transform.data = [ | ||
...transformRound(transform.data.slice(0, 4), params), | ||
...floatRound(transform.data.slice(4), params), | ||
]; | ||
break; | ||
} | ||
return transform; | ||
}; | ||
/** | ||
* Rounds numbers in array. | ||
* | ||
* @type {(data: number[]) => number[]} | ||
*/ | ||
const round = (data) => { | ||
return data.map(Math.round); | ||
}; | ||
/** | ||
* Decrease accuracy of floating-point numbers | ||
* in transforms keeping a specified number of decimals. | ||
* Smart rounds values like 2.349 to 2.35. | ||
* | ||
* @param {number} precision | ||
* @param {number[]} data | ||
* @returns {number[]} | ||
*/ | ||
const smartRound = (precision, data) => { | ||
for ( | ||
var i = data.length, | ||
tolerance = +Math.pow(0.1, precision).toFixed(precision); | ||
i--; | ||
) { | ||
if (toFixed(data[i], precision) !== data[i]) { | ||
var rounded = +data[i].toFixed(precision - 1); | ||
data[i] = | ||
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance | ||
? +data[i].toFixed(precision) | ||
: rounded; | ||
} | ||
} | ||
return data; | ||
}; |
@@ -1,19 +0,23 @@ | ||
import * as csstree from 'css-tree'; | ||
import { syntax } from 'csso'; | ||
import { attrsGroups, pseudoClasses } from './_collections.js'; | ||
import { | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastParent} XastParent | ||
*/ | ||
const csstree = require('css-tree'); | ||
const { | ||
syntax: { specificity }, | ||
} = require('csso'); | ||
const { | ||
visitSkip, | ||
querySelectorAll, | ||
detachNodeFromParent, | ||
} from '../lib/xast.js'; | ||
import { compareSpecificity, includesAttrSelector } from '../lib/style.js'; | ||
} = require('../lib/xast.js'); | ||
const { compareSpecificity, includesAttrSelector } = require('../lib/style'); | ||
const { attrsGroups, pseudoClasses } = require('./_collections'); | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastParent} XastParent | ||
*/ | ||
exports.name = 'inlineStyles'; | ||
exports.description = 'inline styles (additional options)'; | ||
export const name = 'inlineStyles'; | ||
export const description = 'inline styles (additional options)'; | ||
/** | ||
@@ -37,6 +41,6 @@ * Some pseudo-classes can only be calculated by clients, like :visited, | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'inlineStyles'>} | ||
* @type {import('./plugins-types').Plugin<'inlineStyles'>} | ||
* @author strarsis <strarsis@gmail.com> | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { | ||
@@ -177,4 +181,4 @@ onlyMatchedOnce = true, | ||
.sort((a, b) => { | ||
const aSpecificity = syntax.specificity(a.item.data); | ||
const bSpecificity = syntax.specificity(b.item.data); | ||
const aSpecificity = specificity(a.item.data); | ||
const bSpecificity = specificity(b.item.data); | ||
return compareSpecificity(aSpecificity, bSpecificity); | ||
@@ -181,0 +185,0 @@ }) |
@@ -1,31 +0,14 @@ | ||
/** | ||
* @typedef {import('../lib/types.js').ComputedStyles} ComputedStyles | ||
* @typedef {import('../lib/types.js').StaticStyle} StaticStyle | ||
* @typedef {import('../lib/types.js').DynamicStyle} DynamicStyle | ||
* @typedef {import("../lib/types.js").PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types.js').XastChild} XastChild | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
*/ | ||
'use strict'; | ||
import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
import { path2js, js2path, intersects } from './_path.js'; | ||
import { includesUrlReference } from '../lib/svgo/tools.js'; | ||
export const name = 'mergePaths'; | ||
export const description = 'merges multiple paths in one if possible'; | ||
/** | ||
* @param {ComputedStyles} computedStyle | ||
* @param {string} attName | ||
* @returns {boolean} | ||
* @typedef {import("../lib/types").PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types').XastChild} XastChild | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
*/ | ||
function elementHasUrl(computedStyle, attName) { | ||
const style = computedStyle[attName]; | ||
if (style?.type === 'static') { | ||
return includesUrlReference(style.value); | ||
} | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { path2js, js2path, intersects } = require('./_path.js'); | ||
return false; | ||
} | ||
exports.name = 'mergePaths'; | ||
exports.description = 'merges multiple paths in one if possible'; | ||
@@ -37,8 +20,8 @@ /** | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'mergePaths'>} | ||
* @type {import('./plugins-types').Plugin<'mergePaths'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { | ||
force = false, | ||
floatPrecision = 3, | ||
floatPrecision, | ||
noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 | ||
@@ -105,9 +88,3 @@ } = params; | ||
computedStyle['marker-mid'] || | ||
computedStyle['marker-end'] || | ||
computedStyle['clip-path'] || | ||
computedStyle['mask'] || | ||
computedStyle['mask-image'] || | ||
['fill', 'filter', 'stroke'].some((attName) => | ||
elementHasUrl(computedStyle, attName), | ||
) | ||
computedStyle['marker-end'] | ||
) { | ||
@@ -114,0 +91,0 @@ if (prevPathData) { |
@@ -1,11 +0,13 @@ | ||
import { visitSkip, detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastChild} XastChild | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastChild} XastChild | ||
*/ | ||
export const name = 'mergeStyles'; | ||
export const description = 'merge multiple style elements into one'; | ||
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'mergeStyles'; | ||
exports.description = 'merge multiple style elements into one'; | ||
/** | ||
@@ -16,5 +18,5 @@ * Merge multiple style elements into one. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'mergeStyles'>} | ||
* @type {import('./plugins-types').Plugin<'mergeStyles'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
/** | ||
@@ -21,0 +23,0 @@ * @type {?XastElement} |
@@ -0,12 +1,14 @@ | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastParent} XastParent | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastParent} XastParent | ||
*/ | ||
import * as csso from 'csso'; | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
import { hasScripts } from '../lib/svgo/tools.js'; | ||
const csso = require('csso'); | ||
const { detachNodeFromParent } = require('../lib/xast'); | ||
const { hasScripts } = require('../lib/svgo/tools'); | ||
export const name = 'minifyStyles'; | ||
export const description = 'minifies styles and removes unused styles'; | ||
exports.name = 'minifyStyles'; | ||
exports.description = 'minifies styles and removes unused styles'; | ||
@@ -17,5 +19,5 @@ /** | ||
* @author strarsis <strarsis@gmail.com> | ||
* @type {import('./plugins-types.js').Plugin<'minifyStyles'>} | ||
* @type {import('./plugins-types').Plugin<'minifyStyles'>} | ||
*/ | ||
export const fn = (_root, { usage, ...params }) => { | ||
exports.fn = (_root, { usage, ...params }) => { | ||
/** @type {Map<XastElement, XastParent>} */ | ||
@@ -22,0 +24,0 @@ const styleElements = new Map(); |
@@ -1,8 +0,9 @@ | ||
import { visit } from '../lib/xast.js'; | ||
import { inheritableAttrs, pathElems } from './_collections.js'; | ||
'use strict'; | ||
export const name = 'moveElemsAttrsToGroup'; | ||
export const description = | ||
'Move common attributes of group children to the group'; | ||
const { visit } = require('../lib/xast.js'); | ||
const { inheritableAttrs, pathElems } = require('./_collections.js'); | ||
exports.name = 'moveElemsAttrsToGroup'; | ||
exports.description = 'Move common attributes of group children to the group'; | ||
/** | ||
@@ -28,5 +29,5 @@ * Move common attributes of group children to the group | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'moveElemsAttrsToGroup'>} | ||
* @type {import('./plugins-types').Plugin<'moveElemsAttrsToGroup'>} | ||
*/ | ||
export const fn = (root) => { | ||
exports.fn = (root) => { | ||
// find if any style element is present | ||
@@ -90,5 +91,4 @@ let deoptimizedWithStyles = false; | ||
// preserve transform on children when group has filter or clip-path or mask | ||
// preserve transform on children when group has clip-path or mask | ||
if ( | ||
node.attributes['filter'] != null || | ||
node.attributes['clip-path'] != null || | ||
@@ -95,0 +95,0 @@ node.attributes.mask != null |
@@ -1,8 +0,9 @@ | ||
import { pathElems, referencesProps } from './_collections.js'; | ||
import { includesUrlReference } from '../lib/svgo/tools.js'; | ||
'use strict'; | ||
export const name = 'moveGroupAttrsToElems'; | ||
export const description = | ||
'moves some group attributes to the content elements'; | ||
const { pathElems, referencesProps } = require('./_collections.js'); | ||
const { includesUrlReference } = require('../lib/svgo/tools.js'); | ||
exports.name = 'moveGroupAttrsToElems'; | ||
exports.description = 'moves some group attributes to the content elements'; | ||
const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text']; | ||
@@ -26,5 +27,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'moveGroupAttrsToElems'>} | ||
* @type {import('./plugins-types').Plugin<'moveGroupAttrsToElems'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -31,0 +32,0 @@ element: { |
@@ -5,3 +5,3 @@ import type { | ||
XastElement, | ||
} from '../lib/types.js'; | ||
} from '../lib/types'; | ||
@@ -33,3 +33,2 @@ type DefaultPlugins = { | ||
rgb2hex?: boolean; | ||
convertCase?: false | 'lower' | 'upper'; | ||
shorthex?: boolean; | ||
@@ -149,5 +148,2 @@ shortname?: boolean; | ||
}; | ||
removeDeprecatedAttrs: { | ||
removeUnsafe?: boolean; | ||
}; | ||
removeDesc: { | ||
@@ -279,6 +275,4 @@ removeAny?: boolean; | ||
addClassesToSVGElement: { | ||
className?: string | ((node: XastElement, info: PluginInfo) => string); | ||
classNames?: Array< | ||
string | ((node: XastElement, info: PluginInfo) => string) | ||
>; | ||
className?: string; | ||
classNames?: string[]; | ||
}; | ||
@@ -285,0 +279,0 @@ removeAttributesBySelector: any; |
@@ -1,12 +0,14 @@ | ||
import * as csstree from 'css-tree'; | ||
import { referencesProps } from './_collections.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').PluginInfo} PluginInfo | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
*/ | ||
export const name = 'prefixIds'; | ||
export const description = 'prefix IDs'; | ||
const csstree = require('css-tree'); | ||
const { referencesProps } = require('./_collections.js'); | ||
exports.name = 'prefixIds'; | ||
exports.description = 'prefix IDs'; | ||
/** | ||
@@ -119,5 +121,5 @@ * extract basename from path | ||
* @author strarsis <strarsis@gmail.com> | ||
* @type {import('./plugins-types.js').Plugin<'prefixIds'>} | ||
* @type {import('./plugins-types').Plugin<'prefixIds'>} | ||
*/ | ||
export const fn = (_root, params, info) => { | ||
exports.fn = (_root, params, info) => { | ||
const { | ||
@@ -187,2 +189,3 @@ delim = '__', | ||
child.value = csstree.generate(cssAst); | ||
return; | ||
} | ||
@@ -189,0 +192,0 @@ } |
@@ -1,39 +0,41 @@ | ||
import { createPreset } from '../lib/svgo/plugins.js'; | ||
import * as removeDoctype from './removeDoctype.js'; | ||
import * as removeXMLProcInst from './removeXMLProcInst.js'; | ||
import * as removeComments from './removeComments.js'; | ||
import * as removeDeprecatedAttrs from './removeDeprecatedAttrs.js'; | ||
import * as removeMetadata from './removeMetadata.js'; | ||
import * as removeEditorsNSData from './removeEditorsNSData.js'; | ||
import * as cleanupAttrs from './cleanupAttrs.js'; | ||
import * as mergeStyles from './mergeStyles.js'; | ||
import * as inlineStyles from './inlineStyles.js'; | ||
import * as minifyStyles from './minifyStyles.js'; | ||
import * as cleanupIds from './cleanupIds.js'; | ||
import * as removeUselessDefs from './removeUselessDefs.js'; | ||
import * as cleanupNumericValues from './cleanupNumericValues.js'; | ||
import * as convertColors from './convertColors.js'; | ||
import * as removeUnknownsAndDefaults from './removeUnknownsAndDefaults.js'; | ||
import * as removeNonInheritableGroupAttrs from './removeNonInheritableGroupAttrs.js'; | ||
import * as removeUselessStrokeAndFill from './removeUselessStrokeAndFill.js'; | ||
import * as removeViewBox from './removeViewBox.js'; | ||
import * as cleanupEnableBackground from './cleanupEnableBackground.js'; | ||
import * as removeHiddenElems from './removeHiddenElems.js'; | ||
import * as removeEmptyText from './removeEmptyText.js'; | ||
import * as convertShapeToPath from './convertShapeToPath.js'; | ||
import * as convertEllipseToCircle from './convertEllipseToCircle.js'; | ||
import * as moveElemsAttrsToGroup from './moveElemsAttrsToGroup.js'; | ||
import * as moveGroupAttrsToElems from './moveGroupAttrsToElems.js'; | ||
import * as collapseGroups from './collapseGroups.js'; | ||
import * as convertPathData from './convertPathData.js'; | ||
import * as convertTransform from './convertTransform.js'; | ||
import * as removeEmptyAttrs from './removeEmptyAttrs.js'; | ||
import * as removeEmptyContainers from './removeEmptyContainers.js'; | ||
import * as mergePaths from './mergePaths.js'; | ||
import * as removeUnusedNS from './removeUnusedNS.js'; | ||
import * as sortAttrs from './sortAttrs.js'; | ||
import * as sortDefsChildren from './sortDefsChildren.js'; | ||
import * as removeTitle from './removeTitle.js'; | ||
import * as removeDesc from './removeDesc.js'; | ||
'use strict'; | ||
const { createPreset } = require('../lib/svgo/plugins.js'); | ||
const removeDoctype = require('./removeDoctype.js'); | ||
const removeXMLProcInst = require('./removeXMLProcInst.js'); | ||
const removeComments = require('./removeComments.js'); | ||
const removeMetadata = require('./removeMetadata.js'); | ||
const removeEditorsNSData = require('./removeEditorsNSData.js'); | ||
const cleanupAttrs = require('./cleanupAttrs.js'); | ||
const mergeStyles = require('./mergeStyles.js'); | ||
const inlineStyles = require('./inlineStyles.js'); | ||
const minifyStyles = require('./minifyStyles.js'); | ||
const cleanupIds = require('./cleanupIds.js'); | ||
const removeUselessDefs = require('./removeUselessDefs.js'); | ||
const cleanupNumericValues = require('./cleanupNumericValues.js'); | ||
const convertColors = require('./convertColors.js'); | ||
const removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js'); | ||
const removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js'); | ||
const removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js'); | ||
const removeViewBox = require('./removeViewBox.js'); | ||
const cleanupEnableBackground = require('./cleanupEnableBackground.js'); | ||
const removeHiddenElems = require('./removeHiddenElems.js'); | ||
const removeEmptyText = require('./removeEmptyText.js'); | ||
const convertShapeToPath = require('./convertShapeToPath.js'); | ||
const convertEllipseToCircle = require('./convertEllipseToCircle.js'); | ||
const moveElemsAttrsToGroup = require('./moveElemsAttrsToGroup.js'); | ||
const moveGroupAttrsToElems = require('./moveGroupAttrsToElems.js'); | ||
const collapseGroups = require('./collapseGroups.js'); | ||
const convertPathData = require('./convertPathData.js'); | ||
const convertTransform = require('./convertTransform.js'); | ||
const removeEmptyAttrs = require('./removeEmptyAttrs.js'); | ||
const removeEmptyContainers = require('./removeEmptyContainers.js'); | ||
const mergePaths = require('./mergePaths.js'); | ||
const removeUnusedNS = require('./removeUnusedNS.js'); | ||
const sortAttrs = require('./sortAttrs.js'); | ||
const sortDefsChildren = require('./sortDefsChildren.js'); | ||
const removeTitle = require('./removeTitle.js'); | ||
const removeDesc = require('./removeDesc.js'); | ||
const presetDefault = createPreset({ | ||
@@ -45,3 +47,2 @@ name: 'preset-default', | ||
removeComments, | ||
removeDeprecatedAttrs, | ||
removeMetadata, | ||
@@ -82,2 +83,2 @@ removeEditorsNSData, | ||
export default presetDefault; | ||
module.exports = presetDefault; |
@@ -1,5 +0,7 @@ | ||
import { querySelectorAll } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeAttributesBySelector'; | ||
export const description = | ||
const { querySelectorAll } = require('../lib/xast.js'); | ||
exports.name = 'removeAttributesBySelector'; | ||
exports.description = | ||
'removes attributes of elements that match a css selector'; | ||
@@ -74,5 +76,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeAttributesBySelector'>} | ||
* @type {import('./plugins-types').Plugin<'removeAttributesBySelector'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const selectors = Array.isArray(params.selectors) | ||
@@ -79,0 +81,0 @@ ? params.selectors |
@@ -1,4 +0,6 @@ | ||
export const name = 'removeAttrs'; | ||
export const description = 'removes specified attributes'; | ||
'use strict'; | ||
exports.name = 'removeAttrs'; | ||
exports.description = 'removes specified attributes'; | ||
const DEFAULT_SEPARATOR = ':'; | ||
@@ -82,5 +84,5 @@ const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeAttrs'>} | ||
* @type {import('./plugins-types').Plugin<'removeAttrs'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
if (typeof params.attrs == 'undefined') { | ||
@@ -126,7 +128,10 @@ console.warn(ENOATTRS); | ||
for (const [name, value] of Object.entries(node.attributes)) { | ||
const isCurrentColor = value.toLowerCase() === 'currentcolor'; | ||
const isFillCurrentColor = | ||
preserveCurrentColor && name == 'fill' && isCurrentColor; | ||
preserveCurrentColor && | ||
name == 'fill' && | ||
value == 'currentColor'; | ||
const isStrokeCurrentColor = | ||
preserveCurrentColor && name == 'stroke' && isCurrentColor; | ||
preserveCurrentColor && | ||
name == 'stroke' && | ||
value == 'currentColor'; | ||
if ( | ||
@@ -133,0 +138,0 @@ !isFillCurrentColor && |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeComments'; | ||
export const description = 'removes comments'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeComments'; | ||
exports.description = 'removes comments'; | ||
/** | ||
@@ -21,5 +23,5 @@ * If a comment matches one of the following patterns, it will be | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeComments'>} | ||
* @type {import('./plugins-types').Plugin<'removeComments'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { preservePatterns = DEFAULT_PRESERVE_PATTERNS } = params; | ||
@@ -26,0 +28,0 @@ |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeDesc'; | ||
export const description = 'removes <desc>'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeDesc'; | ||
exports.description = 'removes <desc>'; | ||
const standardDescs = /^(Created with|Created using)/; | ||
@@ -17,5 +19,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeDesc'>} | ||
* @type {import('./plugins-types').Plugin<'removeDesc'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { removeAny = false } = params; | ||
@@ -22,0 +24,0 @@ return { |
@@ -1,3 +0,5 @@ | ||
export const name = 'removeDimensions'; | ||
export const description = | ||
'use strict'; | ||
exports.name = 'removeDimensions'; | ||
exports.description = | ||
'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)'; | ||
@@ -15,5 +17,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeDimensions'>} | ||
* @type {import('./plugins-types').Plugin<'removeDimensions'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -20,0 +22,0 @@ element: { |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeDoctype'; | ||
export const description = 'removes doctype declaration'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeDoctype'; | ||
exports.description = 'removes doctype declaration'; | ||
/** | ||
@@ -28,5 +30,5 @@ * Remove DOCTYPE declaration. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeDoctype'>} | ||
* @type {import('./plugins-types').Plugin<'removeDoctype'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -33,0 +35,0 @@ doctype: { |
@@ -1,8 +0,9 @@ | ||
import { editorNamespaces } from './_collections.js'; | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeEditorsNSData'; | ||
export const description = | ||
'removes editors namespaces, elements and attributes'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
const { editorNamespaces } = require('./_collections.js'); | ||
exports.name = 'removeEditorsNSData'; | ||
exports.description = 'removes editors namespaces, elements and attributes'; | ||
/** | ||
@@ -18,5 +19,5 @@ * Remove editors namespaces, elements and attributes. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeEditorsNSData'>} | ||
* @type {import('./plugins-types').Plugin<'removeEditorsNSData'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
let namespaces = [...editorNamespaces]; | ||
@@ -23,0 +24,0 @@ if (Array.isArray(params.additionalNamespaces)) { |
@@ -1,5 +0,7 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeElementsByAttr'; | ||
export const description = | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeElementsByAttr'; | ||
exports.description = | ||
'removes arbitrary elements by ID or className (disabled by default)'; | ||
@@ -38,5 +40,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeElementsByAttr'>} | ||
* @type {import('./plugins-types').Plugin<'removeElementsByAttr'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const ids = | ||
@@ -43,0 +45,0 @@ params.id == null ? [] : Array.isArray(params.id) ? params.id : [params.id]; |
@@ -1,6 +0,8 @@ | ||
import { attrsGroups } from './_collections.js'; | ||
'use strict'; | ||
export const name = 'removeEmptyAttrs'; | ||
export const description = 'removes empty attributes'; | ||
const { attrsGroups } = require('./_collections.js'); | ||
exports.name = 'removeEmptyAttrs'; | ||
exports.description = 'removes empty attributes'; | ||
/** | ||
@@ -11,5 +13,5 @@ * Remove attributes with empty values. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeEmptyAttrs'>} | ||
* @type {import('./plugins-types').Plugin<'removeEmptyAttrs'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -16,0 +18,0 @@ element: { |
@@ -1,7 +0,9 @@ | ||
import { elemsGroups } from './_collections.js'; | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeEmptyContainers'; | ||
export const description = 'removes empty container elements'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
const { elemsGroups } = require('./_collections.js'); | ||
exports.name = 'removeEmptyContainers'; | ||
exports.description = 'removes empty container elements'; | ||
/** | ||
@@ -20,5 +22,5 @@ * Remove empty containers. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeEmptyContainers'>} | ||
* @type {import('./plugins-types').Plugin<'removeEmptyContainers'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -25,0 +27,0 @@ element: { |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeEmptyText'; | ||
export const description = 'removes empty <text> elements'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeEmptyText'; | ||
exports.description = 'removes empty <text> elements'; | ||
/** | ||
@@ -23,5 +25,5 @@ * Remove empty Text elements. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeEmptyText'>} | ||
* @type {import('./plugins-types').Plugin<'removeEmptyText'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { text = true, tspan = true, tref = true } = params; | ||
@@ -28,0 +30,0 @@ return { |
@@ -0,9 +1,11 @@ | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastChild} XastChild | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastParent} XastParent | ||
* @typedef {import('../lib/types').XastChild} XastChild | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastParent} XastParent | ||
*/ | ||
import { elemsGroups } from './_collections.js'; | ||
import { | ||
const { elemsGroups } = require('./_collections.js'); | ||
const { | ||
visit, | ||
@@ -13,11 +15,11 @@ visitSkip, | ||
detachNodeFromParent, | ||
} from '../lib/xast.js'; | ||
import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
import { parsePathData } from '../lib/path.js'; | ||
import { hasScripts, findReferences } from '../lib/svgo/tools.js'; | ||
} = require('../lib/xast.js'); | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { parsePathData } = require('../lib/path.js'); | ||
const { hasScripts, findReferences } = require('../lib/svgo/tools.js'); | ||
const nonRendering = elemsGroups.nonRendering; | ||
export const name = 'removeHiddenElems'; | ||
export const description = | ||
exports.name = 'removeHiddenElems'; | ||
exports.description = | ||
'removes hidden elements (zero sized, with absent attributes)'; | ||
@@ -40,5 +42,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeHiddenElems'>} | ||
* @type {import('./plugins-types').Plugin<'removeHiddenElems'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { | ||
@@ -97,19 +99,2 @@ isHidden = true, | ||
/** | ||
* Nodes can't be removed if they or any of their children have an id attribute that is referenced. | ||
* @param {XastElement} node | ||
* @returns boolean | ||
*/ | ||
function canRemoveNonRenderingNode(node) { | ||
if (allReferences.has(node.attributes.id)) { | ||
return false; | ||
} | ||
for (const child of node.children) { | ||
if (child.type === 'element' && !canRemoveNonRenderingNode(child)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
/** | ||
* @param {XastChild} node | ||
@@ -136,2 +121,7 @@ * @param {XastParent} parentNode | ||
if (nonRendering.has(node.name)) { | ||
if (node.attributes.id == null) { | ||
detachNodeFromParent(node, parentNode); | ||
return visitSkip; | ||
} | ||
nonRenderedNodes.set(node, parentNode); | ||
@@ -150,6 +140,2 @@ return visitSkip; | ||
) { | ||
if (node.name === 'path') { | ||
nonRenderedNodes.set(node, parentNode); | ||
return visitSkip; | ||
} | ||
removeElement(node, parentNode); | ||
@@ -443,3 +429,5 @@ } | ||
] of nonRenderedNodes.entries()) { | ||
if (canRemoveNonRenderingNode(nonRenderedNode)) { | ||
const id = nonRenderedNode.attributes.id; | ||
if (!allReferences.has(id)) { | ||
detachNodeFromParent(nonRenderedNode, nonRenderedParent); | ||
@@ -446,0 +434,0 @@ } |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeMetadata'; | ||
export const description = 'removes <metadata>'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeMetadata'; | ||
exports.description = 'removes <metadata>'; | ||
/** | ||
@@ -13,5 +15,5 @@ * Remove <metadata>. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeMetadata'>} | ||
* @type {import('./plugins-types').Plugin<'removeMetadata'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -18,0 +20,0 @@ element: { |
@@ -1,9 +0,11 @@ | ||
import { | ||
'use strict'; | ||
const { | ||
inheritableAttrs, | ||
attrsGroups, | ||
presentationNonInheritableGroupAttrs, | ||
} from './_collections.js'; | ||
} = require('./_collections'); | ||
export const name = 'removeNonInheritableGroupAttrs'; | ||
export const description = | ||
exports.name = 'removeNonInheritableGroupAttrs'; | ||
exports.description = | ||
'removes non-inheritable group’s presentational attributes'; | ||
@@ -16,5 +18,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeNonInheritableGroupAttrs'>} | ||
* @type {import('./plugins-types').Plugin<'removeNonInheritableGroupAttrs'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -21,0 +23,0 @@ element: { |
@@ -0,11 +1,13 @@ | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem | ||
* @typedef {import('../lib/types').PathDataItem} PathDataItem | ||
*/ | ||
import { visitSkip, detachNodeFromParent } from '../lib/xast.js'; | ||
import { parsePathData } from '../lib/path.js'; | ||
import { intersects } from './_path.js'; | ||
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); | ||
const { parsePathData } = require('../lib/path.js'); | ||
const { intersects } = require('./_path.js'); | ||
export const name = 'removeOffCanvasPaths'; | ||
export const description = | ||
exports.name = 'removeOffCanvasPaths'; | ||
exports.description = | ||
'removes elements that are drawn outside of the viewbox (disabled by default)'; | ||
@@ -18,5 +20,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeOffCanvasPaths'>} | ||
* @type {import('./plugins-types').Plugin<'removeOffCanvasPaths'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
/** | ||
@@ -23,0 +25,0 @@ * @type {?{ |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeRasterImages'; | ||
export const description = 'removes raster images (disabled by default)'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeRasterImages'; | ||
exports.description = 'removes raster images (disabled by default)'; | ||
/** | ||
@@ -13,5 +15,5 @@ * Remove raster images references in <image>. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeRasterImages'>} | ||
* @type {import('./plugins-types').Plugin<'removeRasterImages'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -18,0 +20,0 @@ element: { |
@@ -1,7 +0,9 @@ | ||
import { attrsGroups } from './_collections.js'; | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeScriptElement'; | ||
export const description = 'removes scripts (disabled by default)'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
const { attrsGroups } = require('./_collections.js'); | ||
exports.name = 'removeScriptElement'; | ||
exports.description = 'removes scripts (disabled by default)'; | ||
/** Union of all event attributes. */ | ||
@@ -22,5 +24,5 @@ const eventAttrs = [ | ||
* @author Patrick Klingemann | ||
* @type {import('./plugins-types.js').Plugin<'removeScriptElement'>} | ||
* @type {import('./plugins-types').Plugin<'removeScriptElement'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -55,6 +57,3 @@ element: { | ||
const index = parentNode.children.indexOf(node); | ||
const usefulChildren = node.children.filter( | ||
(child) => !(child.type === 'text' && /\s*/.test(child.value)), | ||
); | ||
parentNode.children.splice(index, 1, ...usefulChildren); | ||
parentNode.children.splice(index, 1, ...node.children); | ||
@@ -61,0 +60,0 @@ // TODO remove legacy parentNode in v4 |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeStyleElement'; | ||
export const description = 'removes <style> element (disabled by default)'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeStyleElement'; | ||
exports.description = 'removes <style> element (disabled by default)'; | ||
/** | ||
@@ -13,5 +15,5 @@ * Remove <style>. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeStyleElement'>} | ||
* @type {import('./plugins-types').Plugin<'removeStyleElement'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -18,0 +20,0 @@ element: { |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeTitle'; | ||
export const description = 'removes <title>'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeTitle'; | ||
exports.description = 'removes <title>'; | ||
/** | ||
@@ -13,5 +15,5 @@ * Remove <title>. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeTitle'>} | ||
* @type {import('./plugins-types').Plugin<'removeTitle'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -18,0 +20,0 @@ element: { |
@@ -1,2 +0,6 @@ | ||
import { | ||
'use strict'; | ||
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { | ||
elems, | ||
@@ -7,8 +11,6 @@ attrsGroups, | ||
presentationNonInheritableGroupAttrs, | ||
} from './_collections.js'; | ||
import { visitSkip, detachNodeFromParent } from '../lib/xast.js'; | ||
import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
} = require('./_collections'); | ||
export const name = 'removeUnknownsAndDefaults'; | ||
export const description = | ||
exports.name = 'removeUnknownsAndDefaults'; | ||
exports.description = | ||
'removes unknown elements content and attributes, removes attrs with default values'; | ||
@@ -94,5 +96,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeUnknownsAndDefaults'>} | ||
* @type {import('./plugins-types').Plugin<'removeUnknownsAndDefaults'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { | ||
@@ -99,0 +101,0 @@ unknownContent = true, |
@@ -1,4 +0,6 @@ | ||
export const name = 'removeUnusedNS'; | ||
export const description = 'removes unused namespaces declaration'; | ||
'use strict'; | ||
exports.name = 'removeUnusedNS'; | ||
exports.description = 'removes unused namespaces declaration'; | ||
/** | ||
@@ -10,5 +12,5 @@ * Remove unused namespaces declaration from svg element | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeUnusedNS'>} | ||
* @type {import('./plugins-types').Plugin<'removeUnusedNS'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
/** | ||
@@ -15,0 +17,0 @@ * @type {Set<string>} |
@@ -1,11 +0,13 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
import { elemsGroups } from './_collections.js'; | ||
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
*/ | ||
export const name = 'removeUselessDefs'; | ||
export const description = 'removes elements in <defs> without id'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
const { elemsGroups } = require('./_collections.js'); | ||
exports.name = 'removeUselessDefs'; | ||
exports.description = 'removes elements in <defs> without id'; | ||
/** | ||
@@ -16,13 +18,9 @@ * Removes content of defs and properties that aren't rendered directly without ids. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeUselessDefs'>} | ||
* @type {import('./plugins-types').Plugin<'removeUselessDefs'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
element: { | ||
enter: (node, parentNode) => { | ||
if ( | ||
node.name === 'defs' || | ||
(elemsGroups.nonRendering.has(node.name) && | ||
node.attributes.id == null) | ||
) { | ||
if (node.name === 'defs') { | ||
/** | ||
@@ -44,2 +42,7 @@ * @type {XastElement[]} | ||
node.children = usefulNodes; | ||
} else if ( | ||
elemsGroups.nonRendering.has(node.name) && | ||
node.attributes.id == null | ||
) { | ||
detachNodeFromParent(node, parentNode); | ||
} | ||
@@ -46,0 +49,0 @@ }, |
@@ -1,9 +0,11 @@ | ||
import { visit, visitSkip, detachNodeFromParent } from '../lib/xast.js'; | ||
import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
import { hasScripts } from '../lib/svgo/tools.js'; | ||
import { elemsGroups } from './_collections.js'; | ||
'use strict'; | ||
export const name = 'removeUselessStrokeAndFill'; | ||
export const description = 'removes useless stroke and fill attributes'; | ||
const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js'); | ||
const { collectStylesheet, computeStyle } = require('../lib/style.js'); | ||
const { hasScripts } = require('../lib/svgo/tools.js'); | ||
const { elemsGroups } = require('./_collections.js'); | ||
exports.name = 'removeUselessStrokeAndFill'; | ||
exports.description = 'removes useless stroke and fill attributes'; | ||
/** | ||
@@ -14,5 +16,5 @@ * Remove useless stroke and fill attrs. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeUselessStrokeAndFill'>} | ||
* @type {import('./plugins-types').Plugin<'removeUselessStrokeAndFill'>} | ||
*/ | ||
export const fn = (root, params) => { | ||
exports.fn = (root, params) => { | ||
const { | ||
@@ -19,0 +21,0 @@ stroke: removeStroke = true, |
@@ -1,4 +0,6 @@ | ||
export const name = 'removeViewBox'; | ||
export const description = 'removes viewBox attribute when possible'; | ||
'use strict'; | ||
exports.name = 'removeViewBox'; | ||
exports.description = 'removes viewBox attribute when possible'; | ||
const viewBoxElems = new Set(['pattern', 'svg', 'symbol']); | ||
@@ -18,5 +20,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeViewBox'>} | ||
* @type {import('./plugins-types').Plugin<'removeViewBox'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -23,0 +25,0 @@ element: { |
@@ -1,9 +0,11 @@ | ||
import { elems } from './_collections.js'; | ||
'use strict'; | ||
const { elems } = require('./_collections'); | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
*/ | ||
export const name = 'removeXlink'; | ||
export const description = | ||
exports.name = 'removeXlink'; | ||
exports.description = | ||
'remove xlink namespace and replaces attributes with the SVG 2 equivalent where applicable'; | ||
@@ -59,6 +61,6 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeXlink'>} | ||
* @type {import('./plugins-types').Plugin<'removeXlink'>} | ||
* @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href | ||
*/ | ||
export const fn = (_, params) => { | ||
exports.fn = (_, params) => { | ||
const { includeLegacy } = params; | ||
@@ -65,0 +67,0 @@ |
@@ -1,3 +0,5 @@ | ||
export const name = 'removeXMLNS'; | ||
export const description = | ||
'use strict'; | ||
exports.name = 'removeXMLNS'; | ||
exports.description = | ||
'removes xmlns attribute (for inline svg, disabled by default)'; | ||
@@ -15,5 +17,5 @@ | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeXMLNS'>} | ||
* @type {import('./plugins-types').Plugin<'removeXMLNS'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -20,0 +22,0 @@ element: { |
@@ -1,6 +0,8 @@ | ||
import { detachNodeFromParent } from '../lib/xast.js'; | ||
'use strict'; | ||
export const name = 'removeXMLProcInst'; | ||
export const description = 'removes XML processing instructions'; | ||
const { detachNodeFromParent } = require('../lib/xast.js'); | ||
exports.name = 'removeXMLProcInst'; | ||
exports.description = 'removes XML processing instructions'; | ||
/** | ||
@@ -14,5 +16,5 @@ * Remove XML Processing Instruction. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'removeXMLProcInst'>} | ||
* @type {import('./plugins-types').Plugin<'removeXMLProcInst'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -19,0 +21,0 @@ instruction: { |
@@ -1,12 +0,14 @@ | ||
import { collectStylesheet } from '../lib/style.js'; | ||
import { detachNodeFromParent, querySelectorAll } from '../lib/xast.js'; | ||
'use strict'; | ||
const { collectStylesheet } = require('../lib/style'); | ||
const { detachNodeFromParent, querySelectorAll } = require('../lib/xast'); | ||
/** | ||
* @typedef {import('../lib/types.js').XastElement} XastElement | ||
* @typedef {import('../lib/types.js').XastParent} XastParent | ||
* @typedef {import('../lib/types.js').XastNode} XastNode | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
* @typedef {import('../lib/types').XastParent} XastParent | ||
* @typedef {import('../lib/types').XastNode} XastNode | ||
*/ | ||
export const name = 'reusePaths'; | ||
export const description = | ||
exports.name = 'reusePaths'; | ||
exports.description = | ||
'Finds <path> elements with the same d, fill, and ' + | ||
@@ -22,5 +24,5 @@ 'stroke, and converts them to <use> elements ' + | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'reusePaths'>} | ||
* @type {import('./plugins-types').Plugin<'reusePaths'>} | ||
*/ | ||
export const fn = (root) => { | ||
exports.fn = (root) => { | ||
const stylesheet = collectStylesheet(root); | ||
@@ -27,0 +29,0 @@ |
@@ -1,4 +0,6 @@ | ||
export const name = 'sortAttrs'; | ||
export const description = 'Sort element attributes for better compression'; | ||
'use strict'; | ||
exports.name = 'sortAttrs'; | ||
exports.description = 'Sort element attributes for better compression'; | ||
/** | ||
@@ -9,5 +11,5 @@ * Sort element attributes for better compression | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'sortAttrs'>} | ||
* @type {import('./plugins-types').Plugin<'sortAttrs'>} | ||
*/ | ||
export const fn = (_root, params) => { | ||
exports.fn = (_root, params) => { | ||
const { | ||
@@ -14,0 +16,0 @@ order = [ |
@@ -1,4 +0,6 @@ | ||
export const name = 'sortDefsChildren'; | ||
export const description = 'Sorts children of <defs> to improve compression'; | ||
'use strict'; | ||
exports.name = 'sortDefsChildren'; | ||
exports.description = 'Sorts children of <defs> to improve compression'; | ||
/** | ||
@@ -10,5 +12,5 @@ * Sorts children of defs in order to improve compression. | ||
* | ||
* @type {import('./plugins-types.js').Plugin<'sortDefsChildren'>} | ||
* @type {import('./plugins-types').Plugin<'sortDefsChildren'>} | ||
*/ | ||
export const fn = () => { | ||
exports.fn = () => { | ||
return { | ||
@@ -15,0 +17,0 @@ element: { |
@@ -52,8 +52,8 @@ <div align="center"> | ||
SVGO reads the configuration from `svgo.config.mjs` or the `--config path/to/config.mjs` command-line option. Some other parameters can be configured though command-line options too. | ||
SVGO reads the configuration from `svgo.config.js` or the `--config path/to/config.js` command-line option. Some other parameters can be configured though command-line options too. | ||
**`svgo.config.mjs`** | ||
**`svgo.config.js`** | ||
```js | ||
export default { | ||
module.exports = { | ||
multipass: false, // boolean | ||
@@ -84,6 +84,6 @@ datauri: 'base64', // 'base64'|'enc'|'unenc' | ||
**`svgo.config.mjs`** | ||
**`svgo.config.js`** | ||
```js | ||
export default { | ||
module.exports = { | ||
plugins: [ | ||
@@ -114,8 +114,8 @@ { | ||
**`svgo.config.mjs`** | ||
**`svgo.config.js`** | ||
```js | ||
import importedPlugin from './imported-plugin'; | ||
const importedPlugin = require('./imported-plugin'); | ||
export default { | ||
module.exports = { | ||
plugins: [ | ||
@@ -146,3 +146,3 @@ // plugin imported from another JavaScript file | ||
```js | ||
import { optimize } from 'svgo'; | ||
const { optimize } = require('svgo'); | ||
@@ -159,6 +159,6 @@ const result = optimize(svgString, { | ||
If you write a tool on top of SVGO you may want to resolve the `svgo.config.mjs` file. | ||
If you write a tool on top of SVGO you may want to resolve the `svgo.config.js` file. | ||
```js | ||
import { loadConfig } from 'svgo'; | ||
const { loadConfig } = require('svgo'); | ||
@@ -165,0 +165,0 @@ const config = await loadConfig(); |
Sorry, the diff of this file is too big to display
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
1488123
0
79
19133
4
No