Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production) Add global file drop handler, order column normalization, SPA hash fix Serve CDN assets via /_vendor/ URLs instead of merging into bundles Add production minification with license preservation Improve JSON formatting for debugging and production optimization Add CDN asset caching with CSS URL inlining for production builds Add three-mode system (development, debug, production) Update Manifest CLAUDE.md to reflect helper class architecture Refactor Manifest.php into helper classes for better organization Pre-manifest-refactor checkpoint: Add app_mode documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
263
node_modules/svgo/README.md
generated
vendored
263
node_modules/svgo/README.md
generated
vendored
@@ -1,40 +1,45 @@
|
||||
<div align="center">
|
||||
<img src="./logo/logo-web.svg" width="348.61" height="100" alt="SVGO logo"/>
|
||||
<img src="./logo/logo-web.svg" width="348.61" height="100" alt=""/>
|
||||
</div>
|
||||
|
||||
## SVGO [](https://npmjs.org/package/svgo) [](https://discord.gg/z8jX8NYxrE)
|
||||
# SVGO [](https://npmjs.org/package/svgo) [](https://discord.gg/z8jX8NYxrE) [](https://svgo.dev/)
|
||||
|
||||
**SVG O**ptimizer is a Node.js-based tool for optimizing SVG vector graphics files.
|
||||
SVGO, short for **SVG O**ptimizer, is a Node.js library and command-line application for optimizing SVG files.
|
||||
|
||||
## Why?
|
||||
|
||||
SVG files, especially those exported from various editors, usually contain a lot of redundant and useless information. This can include editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting the SVG rendering result.
|
||||
SVG files, especially those exported from vector editors, usually contain a lot of redundant information. This includes editor metadata, comments, hidden elements, default or suboptimal values, and other stuff that can be safely removed or converted without impacting rendering.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
npm -g install svgo
|
||||
```
|
||||
|
||||
or
|
||||
You can install SVGO globally through npm, yarn, or pnpm. Alternatively, drop the global flag (`global`/`-g`) to use it in your Node.js project.
|
||||
|
||||
```sh
|
||||
# npm
|
||||
npm install -g svgo
|
||||
|
||||
# yarn
|
||||
yarn global add svgo
|
||||
|
||||
# pnpm
|
||||
pnpm add -g svgo
|
||||
```
|
||||
|
||||
## CLI usage
|
||||
## Command-line usage
|
||||
|
||||
Process single files:
|
||||
|
||||
```sh
|
||||
svgo one.svg two.svg -o one.min.svg two.min.svg
|
||||
```
|
||||
|
||||
Or use the `--folder`/`-f` flag to optimize a whole folder of SVG icons
|
||||
Process a directory of files recursively with `-r`/`--recursive` and `-f`/`--folder`:
|
||||
|
||||
```sh
|
||||
svgo -f ./path/to/folder/with/svg/files -o ./path/to/folder/with/svg/output
|
||||
svgo -rf path/to/directory_with_svgs -o path/to/output_directory
|
||||
```
|
||||
|
||||
See help for advanced usage
|
||||
Help for advanced usage:
|
||||
|
||||
```sh
|
||||
svgo --help
|
||||
@@ -42,132 +47,86 @@ svgo --help
|
||||
|
||||
## Configuration
|
||||
|
||||
Some options can be configured with CLI though it may be easier to have the configuration in a separate file.
|
||||
SVGO automatically loads configuration from `svgo.config.js` or module specified with `--config` flag.
|
||||
SVGO has a plugin architecture. You can read more about all plugins in [Plugins | SVGO Documentation](https://svgo.dev/docs/plugins/), and the default plugins in [Preset Default | SVGO Documentation](https://svgo.dev/docs/preset-default/).
|
||||
|
||||
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.config.mjs`**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
multipass: true, // boolean. false by default
|
||||
datauri: 'enc', // 'base64', 'enc' or 'unenc'. 'base64' by default
|
||||
export default {
|
||||
multipass: false, // boolean
|
||||
datauri: 'base64', // 'base64'|'enc'|'unenc'
|
||||
js2svg: {
|
||||
indent: 2, // string with spaces or number of spaces. 4 by default
|
||||
pretty: true, // boolean, false by default
|
||||
indent: 4, // number
|
||||
pretty: false, // boolean
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
SVGO has a plugin-based architecture, so almost every optimization is a separate plugin.
|
||||
There is a set of [built-in plugins](#built-in-plugins). See how to configure them:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// enable a built-in plugin by name
|
||||
'prefixIds',
|
||||
'preset-default', // built-in plugins enabled by default
|
||||
'prefixIds', // enable built-in plugins by name
|
||||
|
||||
// or by expanded version
|
||||
{
|
||||
name: 'prefixIds',
|
||||
},
|
||||
|
||||
// some plugins allow/require to pass options
|
||||
// enable built-in plugins with an object to configure plugins
|
||||
{
|
||||
name: 'prefixIds',
|
||||
params: {
|
||||
prefix: 'my-prefix',
|
||||
prefix: 'uwu',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
The default preset of plugins is fully overridden if the `plugins` field is specified.
|
||||
Use `preset-default` plugin to customize plugins options.
|
||||
### Default preset
|
||||
|
||||
Instead of configuring SVGO from scratch, you can tweak the default preset to suit your needs by configuring or disabling the respective plugin.
|
||||
|
||||
**`svgo.config.mjs`**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
export default {
|
||||
plugins: [
|
||||
{
|
||||
name: 'preset-default',
|
||||
params: {
|
||||
overrides: {
|
||||
// customize options for plugins included in preset
|
||||
// disable a default plugin
|
||||
cleanupIds: false,
|
||||
|
||||
// customize the params of a default plugin
|
||||
inlineStyles: {
|
||||
onlyMatchedOnce: false,
|
||||
},
|
||||
|
||||
// or disable plugins
|
||||
removeDoctype: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// enable builtin plugin not included in default preset
|
||||
'prefixIds',
|
||||
|
||||
// enable and configure builtin plugin not included in preset
|
||||
{
|
||||
name: 'sortAttrs',
|
||||
params: {
|
||||
xmlnsOrder: 'alphabetical',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
Default preset includes the following list of plugins:
|
||||
You can find a list of the default plugins in the order they run in [Preset Default | SVGO Documentation](https://svgo.dev/docs/preset-default/#plugins-list).
|
||||
|
||||
- removeDoctype
|
||||
- removeXMLProcInst
|
||||
- removeComments
|
||||
- removeMetadata
|
||||
- removeEditorsNSData
|
||||
- cleanupAttrs
|
||||
- mergeStyles
|
||||
- inlineStyles
|
||||
- minifyStyles
|
||||
- cleanupIDs
|
||||
- removeUselessDefs
|
||||
- cleanupNumericValues
|
||||
- convertColors
|
||||
- removeUnknownsAndDefaults
|
||||
- removeNonInheritableGroupAttrs
|
||||
- removeUselessStrokeAndFill
|
||||
- removeViewBox
|
||||
- cleanupEnableBackground
|
||||
- removeHiddenElems
|
||||
- removeEmptyText
|
||||
- convertShapeToPath
|
||||
- convertEllipseToCircle
|
||||
- moveElemsAttrsToGroup
|
||||
- moveGroupAttrsToElems
|
||||
- collapseGroups
|
||||
- convertPathData
|
||||
- convertTransform
|
||||
- removeEmptyAttrs
|
||||
- removeEmptyContainers
|
||||
- mergePaths
|
||||
- removeUnusedNS
|
||||
- sortDefsChildren
|
||||
- removeTitle
|
||||
- removeDesc
|
||||
### Custom plugins
|
||||
|
||||
It's also possible to specify a custom plugin:
|
||||
You can also specify custom plugins:
|
||||
|
||||
**`svgo.config.mjs`**
|
||||
|
||||
```js
|
||||
const anotherCustomPlugin = require('./another-custom-plugin.js');
|
||||
module.exports = {
|
||||
import importedPlugin from './imported-plugin';
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
// plugin imported from another JavaScript file
|
||||
importedPlugin,
|
||||
|
||||
// plugin defined inline
|
||||
{
|
||||
name: 'customPluginName',
|
||||
type: 'perItem', // 'perItem', 'perItemReverse' or 'full'
|
||||
name: 'customPlugin',
|
||||
params: {
|
||||
optionName: 'optionValue',
|
||||
paramName: 'paramValue',
|
||||
},
|
||||
fn: (ast, params, info) => {},
|
||||
},
|
||||
anotherCustomPlugin,
|
||||
],
|
||||
};
|
||||
```
|
||||
@@ -181,114 +140,40 @@ SVGO provides a few low level utilities.
|
||||
The core of SVGO is `optimize` function.
|
||||
|
||||
```js
|
||||
const { optimize } = require('svgo');
|
||||
import { optimize } from 'svgo';
|
||||
|
||||
const result = optimize(svgString, {
|
||||
// optional but recommended field
|
||||
path: 'path-to.svg',
|
||||
// all config fields are also available here
|
||||
multipass: true,
|
||||
path: 'path-to.svg', // recommended
|
||||
multipass: true, // all other config fields are available here
|
||||
});
|
||||
|
||||
const optimizedSvgString = result.data;
|
||||
```
|
||||
|
||||
### loadConfig
|
||||
|
||||
If you write a tool on top of SVGO you might need a way to load SVGO config.
|
||||
If you write a tool on top of SVGO you may want to resolve the `svgo.config.mjs` file.
|
||||
|
||||
```js
|
||||
const { loadConfig } = require('svgo');
|
||||
const config = await loadConfig();
|
||||
import { loadConfig } from 'svgo';
|
||||
|
||||
// you can also specify a relative or absolute path and customize the current working directory
|
||||
const config = await loadConfig();
|
||||
```
|
||||
|
||||
You can also specify a path and customize the current working directory.
|
||||
|
||||
```js
|
||||
const config = await loadConfig(configFile, cwd);
|
||||
```
|
||||
|
||||
## Built-in plugins
|
||||
## Donors
|
||||
|
||||
| Plugin | Description | Default |
|
||||
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
||||
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | `enabled` |
|
||||
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js) | merge multiple style elements into one | `enabled` |
|
||||
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` |
|
||||
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` |
|
||||
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` |
|
||||
| [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` |
|
||||
| [removeMetadata](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) | remove `<metadata>` | `enabled` |
|
||||
| [removeTitle](https://github.com/svg/svgo/blob/master/plugins/removeTitle.js) | remove `<title>` | `enabled` |
|
||||
| [removeDesc](https://github.com/svg/svgo/blob/master/plugins/removeDesc.js) | remove `<desc>` | `enabled` |
|
||||
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` |
|
||||
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` |
|
||||
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` |
|
||||
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` |
|
||||
| [removeHiddenElems](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) | remove hidden elements | `enabled` |
|
||||
| [removeEmptyText](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) | remove empty Text elements | `enabled` |
|
||||
| [removeEmptyContainers](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) | remove empty Container elements | `enabled` |
|
||||
| [removeViewBox](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) | remove `viewBox` attribute when possible | `enabled` |
|
||||
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) | remove or cleanup `enable-background` attribute when possible | `enabled` |
|
||||
| [minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js) | minify `<style>` elements content with [CSSO](https://github.com/css/csso) | `enabled` |
|
||||
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) | convert styles into attributes | `disabled` |
|
||||
| [convertColors](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`) | `enabled` |
|
||||
| [convertPathData](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) | convert Path data to relative or absolute (whichever is shorter), convert one segment to another, trim useless delimiters, smart rounding, and much more | `enabled` |
|
||||
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` |
|
||||
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` |
|
||||
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` |
|
||||
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` |
|
||||
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` |
|
||||
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` |
|
||||
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs | `enabled` |
|
||||
| [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | `enabled` |
|
||||
| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) | `disabled` |
|
||||
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group | `enabled` |
|
||||
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements | `enabled` |
|
||||
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | collapse useless groups | `enabled` |
|
||||
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | remove raster images | `disabled` |
|
||||
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js) | merge multiple Paths into one | `enabled` |
|
||||
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | `enabled` |
|
||||
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | `enabled` |
|
||||
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | sort element attributes for epic readability | `disabled` |
|
||||
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | `enabled` |
|
||||
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first) | `disabled` |
|
||||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` |
|
||||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` |
|
||||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` |
|
||||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` |
|
||||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` |
|
||||
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` |
|
||||
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | remove `<style>` elements | `disabled` |
|
||||
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | remove `<script>` elements | `disabled` |
|
||||
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links | `disabled` |
|
||||
|
||||
## Other Ways to Use SVGO
|
||||
|
||||
- as a web app – [SVGOMG](https://jakearchibald.github.io/svgomg/)
|
||||
- as a GitHub Action – [SVGO Action](https://github.com/marketplace/actions/svgo-action)
|
||||
- as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
|
||||
- as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
|
||||
- as a Mimosa module – [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg)
|
||||
- as an OSX Folder Action – [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
|
||||
- as a webpack loader – [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
|
||||
- as a Telegram Bot – [svgo_bot](https://github.com/maksugr/svgo_bot)
|
||||
- as a PostCSS plugin – [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
|
||||
- as an Inkscape plugin – [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
|
||||
- as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
|
||||
- as a macOS app - [Image Shrinker](https://image-shrinker.com)
|
||||
- as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo)
|
||||
- as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo)
|
||||
- as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo)
|
||||
- as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo)
|
||||
- as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export)
|
||||
- as a Linux app - [Oh My SVG](https://github.com/sonnyp/OhMySVG)
|
||||
- as a Browser extension - [SVG Gobbler](https://github.com/rossmoody/svg-gobbler)
|
||||
- as an API - [Vector Express](https://github.com/smidyo/vectorexpress-api#convertor-svgo)
|
||||
|
||||
## Donators
|
||||
|
||||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/master/fontello-image.svg" width="80">](https://fontello.com/) |
|
||||
| :------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) |
|
||||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/8.0.0/fontello-image.svg" width="80">](https://fontello.com/) |
|
||||
| :------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------: |
|
||||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) |
|
||||
|
||||
## License and Copyright
|
||||
|
||||
This software is released under the terms of the [MIT license](https://github.com/svg/svgo/blob/master/LICENSE).
|
||||
This software is released under the terms of the [MIT license](https://github.com/svg/svgo/blob/main/LICENSE).
|
||||
|
||||
Logo by [André Castillo](https://github.com/DerianAndre).
|
||||
|
||||
10
node_modules/svgo/bin/svgo
generated
vendored
10
node_modules/svgo/bin/svgo
generated
vendored
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const colors = require('picocolors');
|
||||
const { program } = require('commander');
|
||||
const makeProgram = require('../lib/svgo/coa');
|
||||
makeProgram(program);
|
||||
program.parseAsync(process.argv).catch(error => {
|
||||
console.error(colors.red(error.stack));
|
||||
process.exit(1);
|
||||
});
|
||||
10
node_modules/svgo/bin/svgo.js
generated
vendored
Executable file
10
node_modules/svgo/bin/svgo.js
generated
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import colors from 'picocolors';
|
||||
import { program } from 'commander';
|
||||
import makeProgram from '../lib/svgo/coa.js';
|
||||
makeProgram(program);
|
||||
program.parseAsync(process.argv).catch((error) => {
|
||||
console.error(colors.red(error.stack));
|
||||
process.exit(1);
|
||||
});
|
||||
2139
node_modules/svgo/dist/svgo-node.cjs
generated
vendored
Normal file
2139
node_modules/svgo/dist/svgo-node.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
8364
node_modules/svgo/dist/svgo.browser.js
generated
vendored
8364
node_modules/svgo/dist/svgo.browser.js
generated
vendored
File diff suppressed because one or more lines are too long
117
node_modules/svgo/lib/builtin.js
generated
vendored
Normal file
117
node_modules/svgo/lib/builtin.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
import presetDefault from '../plugins/preset-default.js';
|
||||
import * as addAttributesToSVGElement from '../plugins/addAttributesToSVGElement.js';
|
||||
import * as addClassesToSVGElement from '../plugins/addClassesToSVGElement.js';
|
||||
import * as cleanupAttrs from '../plugins/cleanupAttrs.js';
|
||||
import * as cleanupEnableBackground from '../plugins/cleanupEnableBackground.js';
|
||||
import * as cleanupIds from '../plugins/cleanupIds.js';
|
||||
import * as cleanupListOfValues from '../plugins/cleanupListOfValues.js';
|
||||
import * as cleanupNumericValues from '../plugins/cleanupNumericValues.js';
|
||||
import * as collapseGroups from '../plugins/collapseGroups.js';
|
||||
import * as convertColors from '../plugins/convertColors.js';
|
||||
import * as convertEllipseToCircle from '../plugins/convertEllipseToCircle.js';
|
||||
import * as convertOneStopGradients from '../plugins/convertOneStopGradients.js';
|
||||
import * as convertPathData from '../plugins/convertPathData.js';
|
||||
import * as convertShapeToPath from '../plugins/convertShapeToPath.js';
|
||||
import * as convertStyleToAttrs from '../plugins/convertStyleToAttrs.js';
|
||||
import * as convertTransform from '../plugins/convertTransform.js';
|
||||
import * as mergeStyles from '../plugins/mergeStyles.js';
|
||||
import * as inlineStyles from '../plugins/inlineStyles.js';
|
||||
import * as mergePaths from '../plugins/mergePaths.js';
|
||||
import * as minifyStyles from '../plugins/minifyStyles.js';
|
||||
import * as moveElemsAttrsToGroup from '../plugins/moveElemsAttrsToGroup.js';
|
||||
import * as moveGroupAttrsToElems from '../plugins/moveGroupAttrsToElems.js';
|
||||
import * as prefixIds from '../plugins/prefixIds.js';
|
||||
import * as removeAttributesBySelector from '../plugins/removeAttributesBySelector.js';
|
||||
import * as removeAttrs from '../plugins/removeAttrs.js';
|
||||
import * as removeComments from '../plugins/removeComments.js';
|
||||
import * as removeDeprecatedAttrs from '../plugins/removeDeprecatedAttrs.js';
|
||||
import * as removeDesc from '../plugins/removeDesc.js';
|
||||
import * as removeDimensions from '../plugins/removeDimensions.js';
|
||||
import * as removeDoctype from '../plugins/removeDoctype.js';
|
||||
import * as removeEditorsNSData from '../plugins/removeEditorsNSData.js';
|
||||
import * as removeElementsByAttr from '../plugins/removeElementsByAttr.js';
|
||||
import * as removeEmptyAttrs from '../plugins/removeEmptyAttrs.js';
|
||||
import * as removeEmptyContainers from '../plugins/removeEmptyContainers.js';
|
||||
import * as removeEmptyText from '../plugins/removeEmptyText.js';
|
||||
import * as removeHiddenElems from '../plugins/removeHiddenElems.js';
|
||||
import * as removeMetadata from '../plugins/removeMetadata.js';
|
||||
import * as removeNonInheritableGroupAttrs from '../plugins/removeNonInheritableGroupAttrs.js';
|
||||
import * as removeOffCanvasPaths from '../plugins/removeOffCanvasPaths.js';
|
||||
import * as removeRasterImages from '../plugins/removeRasterImages.js';
|
||||
import * as removeScripts from '../plugins/removeScripts.js';
|
||||
import * as removeStyleElement from '../plugins/removeStyleElement.js';
|
||||
import * as removeTitle from '../plugins/removeTitle.js';
|
||||
import * as removeUnknownsAndDefaults from '../plugins/removeUnknownsAndDefaults.js';
|
||||
import * as removeUnusedNS from '../plugins/removeUnusedNS.js';
|
||||
import * as removeUselessDefs from '../plugins/removeUselessDefs.js';
|
||||
import * as removeUselessStrokeAndFill from '../plugins/removeUselessStrokeAndFill.js';
|
||||
import * as removeViewBox from '../plugins/removeViewBox.js';
|
||||
import * as removeXlink from '../plugins/removeXlink.js';
|
||||
import * as removeXMLNS from '../plugins/removeXMLNS.js';
|
||||
import * as removeXMLProcInst from '../plugins/removeXMLProcInst.js';
|
||||
import * as reusePaths from '../plugins/reusePaths.js';
|
||||
import * as sortAttrs from '../plugins/sortAttrs.js';
|
||||
import * as sortDefsChildren from '../plugins/sortDefsChildren.js';
|
||||
|
||||
/**
|
||||
* Plugins that are bundled with SVGO. This includes plugin presets, and plugins
|
||||
* that are not enabled by default.
|
||||
*
|
||||
* @type {ReadonlyArray<{[Name in keyof import('./types.js').PluginsParams]: import('./types.js').BuiltinPluginOrPreset<Name, import('./types.js').PluginsParams[Name]>;}[keyof import('./types.js').PluginsParams]>}
|
||||
*/
|
||||
export const builtinPlugins = Object.freeze([
|
||||
presetDefault,
|
||||
addAttributesToSVGElement,
|
||||
addClassesToSVGElement,
|
||||
cleanupAttrs,
|
||||
cleanupEnableBackground,
|
||||
cleanupIds,
|
||||
cleanupListOfValues,
|
||||
cleanupNumericValues,
|
||||
collapseGroups,
|
||||
convertColors,
|
||||
convertEllipseToCircle,
|
||||
convertOneStopGradients,
|
||||
convertPathData,
|
||||
convertShapeToPath,
|
||||
convertStyleToAttrs,
|
||||
convertTransform,
|
||||
inlineStyles,
|
||||
mergePaths,
|
||||
mergeStyles,
|
||||
minifyStyles,
|
||||
moveElemsAttrsToGroup,
|
||||
moveGroupAttrsToElems,
|
||||
prefixIds,
|
||||
removeAttributesBySelector,
|
||||
removeAttrs,
|
||||
removeComments,
|
||||
removeDeprecatedAttrs,
|
||||
removeDesc,
|
||||
removeDimensions,
|
||||
removeDoctype,
|
||||
removeEditorsNSData,
|
||||
removeElementsByAttr,
|
||||
removeEmptyAttrs,
|
||||
removeEmptyContainers,
|
||||
removeEmptyText,
|
||||
removeHiddenElems,
|
||||
removeMetadata,
|
||||
removeNonInheritableGroupAttrs,
|
||||
removeOffCanvasPaths,
|
||||
removeRasterImages,
|
||||
removeScripts,
|
||||
removeStyleElement,
|
||||
removeTitle,
|
||||
removeUnknownsAndDefaults,
|
||||
removeUnusedNS,
|
||||
removeUselessDefs,
|
||||
removeUselessStrokeAndFill,
|
||||
removeViewBox,
|
||||
removeXlink,
|
||||
removeXMLNS,
|
||||
removeXMLProcInst,
|
||||
reusePaths,
|
||||
sortAttrs,
|
||||
sortDefsChildren,
|
||||
]);
|
||||
239
node_modules/svgo/lib/css-tools.js
generated
vendored
239
node_modules/svgo/lib/css-tools.js
generated
vendored
@@ -1,239 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var csstree = require('css-tree'),
|
||||
List = csstree.List,
|
||||
stable = require('stable'),
|
||||
specificity = require('csso/lib/restructure/prepare/specificity');
|
||||
|
||||
/**
|
||||
* Flatten a CSS AST to a selectors list.
|
||||
*
|
||||
* @param {import('css-tree').CssNode} cssAst css-tree AST to flatten
|
||||
* @return {Array} selectors
|
||||
*/
|
||||
function flattenToSelectors(cssAst) {
|
||||
var selectors = [];
|
||||
|
||||
csstree.walk(cssAst, {
|
||||
visit: 'Rule',
|
||||
enter: function (node) {
|
||||
if (node.type !== 'Rule') {
|
||||
return;
|
||||
}
|
||||
|
||||
var atrule = this.atrule;
|
||||
var rule = node;
|
||||
|
||||
node.prelude.children.each(function (selectorNode, selectorItem) {
|
||||
var selector = {
|
||||
item: selectorItem,
|
||||
atrule: atrule,
|
||||
rule: rule,
|
||||
pseudos: /** @type {{item: any; list: any[]}[]} */ ([]),
|
||||
};
|
||||
|
||||
selectorNode.children.each(function (
|
||||
selectorChildNode,
|
||||
selectorChildItem,
|
||||
selectorChildList
|
||||
) {
|
||||
if (
|
||||
selectorChildNode.type === 'PseudoClassSelector' ||
|
||||
selectorChildNode.type === 'PseudoElementSelector'
|
||||
) {
|
||||
selector.pseudos.push({
|
||||
item: selectorChildItem,
|
||||
list: selectorChildList,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
selectors.push(selector);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter selectors by Media Query.
|
||||
*
|
||||
* @param {Array} selectors to filter
|
||||
* @param {Array} useMqs Array with strings of media queries that should pass (<name> <expression>)
|
||||
* @return {Array} Filtered selectors that match the passed media queries
|
||||
*/
|
||||
function filterByMqs(selectors, useMqs) {
|
||||
return selectors.filter(function (selector) {
|
||||
if (selector.atrule === null) {
|
||||
return ~useMqs.indexOf('');
|
||||
}
|
||||
|
||||
var mqName = selector.atrule.name;
|
||||
var mqStr = mqName;
|
||||
if (
|
||||
selector.atrule.expression &&
|
||||
selector.atrule.expression.children.first().type === 'MediaQueryList'
|
||||
) {
|
||||
var mqExpr = csstree.generate(selector.atrule.expression);
|
||||
mqStr = [mqName, mqExpr].join(' ');
|
||||
}
|
||||
|
||||
return ~useMqs.indexOf(mqStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter selectors by the pseudo-elements and/or -classes they contain.
|
||||
*
|
||||
* @param {Array} selectors to filter
|
||||
* @param {Array} usePseudos Array with strings of single or sequence of pseudo-elements and/or -classes that should pass
|
||||
* @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
|
||||
*/
|
||||
function filterByPseudos(selectors, usePseudos) {
|
||||
return selectors.filter(function (selector) {
|
||||
var pseudoSelectorsStr = csstree.generate({
|
||||
type: 'Selector',
|
||||
children: new List().fromArray(
|
||||
selector.pseudos.map(function (pseudo) {
|
||||
return pseudo.item.data;
|
||||
})
|
||||
),
|
||||
});
|
||||
return ~usePseudos.indexOf(pseudoSelectorsStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove pseudo-elements and/or -classes from the selectors for proper matching.
|
||||
*
|
||||
* @param {Array} selectors to clean
|
||||
* @return {void}
|
||||
*/
|
||||
function cleanPseudos(selectors) {
|
||||
selectors.forEach(function (selector) {
|
||||
selector.pseudos.forEach(function (pseudo) {
|
||||
pseudo.list.remove(pseudo.item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two selector specificities.
|
||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||
*
|
||||
* @param {Array} aSpecificity Specificity of selector A
|
||||
* @param {Array} bSpecificity Specificity of selector B
|
||||
* @return {number} Score of selector specificity A compared to selector specificity B
|
||||
*/
|
||||
function compareSpecificity(aSpecificity, bSpecificity) {
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
if (aSpecificity[i] < bSpecificity[i]) {
|
||||
return -1;
|
||||
} else if (aSpecificity[i] > bSpecificity[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two simple selectors.
|
||||
*
|
||||
* @param {Object} aSimpleSelectorNode Simple selector A
|
||||
* @param {Object} bSimpleSelectorNode Simple selector B
|
||||
* @return {number} Score of selector A compared to selector B
|
||||
*/
|
||||
function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
|
||||
var aSpecificity = specificity(aSimpleSelectorNode),
|
||||
bSpecificity = specificity(bSimpleSelectorNode);
|
||||
return compareSpecificity(aSpecificity, bSpecificity);
|
||||
}
|
||||
|
||||
function _bySelectorSpecificity(selectorA, selectorB) {
|
||||
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort selectors stably by their specificity.
|
||||
*
|
||||
* @param {Array} selectors to be sorted
|
||||
* @return {Array} Stable sorted selectors
|
||||
*/
|
||||
function sortSelectors(selectors) {
|
||||
return stable(selectors, _bySelectorSpecificity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a css-tree AST style declaration to CSSStyleDeclaration property.
|
||||
*
|
||||
* @param {import('css-tree').CssNode} declaration css-tree style declaration
|
||||
* @return {Object} CSSStyleDeclaration property
|
||||
*/
|
||||
function csstreeToStyleDeclaration(declaration) {
|
||||
var propertyName = declaration.property,
|
||||
propertyValue = csstree.generate(declaration.value),
|
||||
propertyPriority = declaration.important ? 'important' : '';
|
||||
return {
|
||||
name: propertyName,
|
||||
value: propertyValue,
|
||||
priority: propertyPriority,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSS string of a style element
|
||||
*
|
||||
* @param {Object} elem style element
|
||||
* @return {string} CSS string or empty array if no styles are set
|
||||
*/
|
||||
function getCssStr(elem) {
|
||||
if (
|
||||
elem.children.length > 0 &&
|
||||
(elem.children[0].type === 'text' || elem.children[0].type === 'cdata')
|
||||
) {
|
||||
return elem.children[0].value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CSS string of a style element
|
||||
*
|
||||
* @param {Object} elem style element
|
||||
* @param {string} css string to be set
|
||||
* @return {string} reference to field with CSS
|
||||
*/
|
||||
function setCssStr(elem, css) {
|
||||
if (elem.children.length === 0) {
|
||||
elem.children.push({
|
||||
type: 'text',
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
|
||||
if (elem.children[0].type !== 'text' && elem.children[0].type !== 'cdata') {
|
||||
return css;
|
||||
}
|
||||
|
||||
elem.children[0].value = css;
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
module.exports.flattenToSelectors = flattenToSelectors;
|
||||
|
||||
module.exports.filterByMqs = filterByMqs;
|
||||
module.exports.filterByPseudos = filterByPseudos;
|
||||
module.exports.cleanPseudos = cleanPseudos;
|
||||
|
||||
module.exports.compareSpecificity = compareSpecificity;
|
||||
module.exports.compareSimpleSelectorNode = compareSimpleSelectorNode;
|
||||
|
||||
module.exports.sortSelectors = sortSelectors;
|
||||
|
||||
module.exports.csstreeToStyleDeclaration = csstreeToStyleDeclaration;
|
||||
|
||||
module.exports.getCssStr = getCssStr;
|
||||
module.exports.setCssStr = setCssStr;
|
||||
141
node_modules/svgo/lib/parser.js
generated
vendored
141
node_modules/svgo/lib/parser.js
generated
vendored
@@ -1,29 +1,13 @@
|
||||
'use strict';
|
||||
import SAX from 'sax';
|
||||
import { textElems } from '../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').XastNode} XastNode
|
||||
* @typedef {import('./types').XastInstruction} XastInstruction
|
||||
* @typedef {import('./types').XastDoctype} XastDoctype
|
||||
* @typedef {import('./types').XastComment} XastComment
|
||||
* @typedef {import('./types').XastRoot} XastRoot
|
||||
* @typedef {import('./types').XastElement} XastElement
|
||||
* @typedef {import('./types').XastCdata} XastCdata
|
||||
* @typedef {import('./types').XastText} XastText
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
*/
|
||||
|
||||
// @ts-ignore sax will be replaced with something else later
|
||||
const SAX = require('@trysound/sax');
|
||||
const JSAPI = require('./svgo/jsAPI.js');
|
||||
const { textElems } = require('../plugins/_collections.js');
|
||||
|
||||
class SvgoParserError extends Error {
|
||||
export class SvgoParserError extends Error {
|
||||
/**
|
||||
* @param message {string}
|
||||
* @param line {number}
|
||||
* @param column {number}
|
||||
* @param source {string}
|
||||
* @param file {void | string}
|
||||
* @param {string} message
|
||||
* @param {number} line
|
||||
* @param {number} column
|
||||
* @param {string} source
|
||||
* @param {string=} file
|
||||
*/
|
||||
constructor(message, line, column, source, file) {
|
||||
super(message);
|
||||
@@ -37,6 +21,7 @@ class SvgoParserError extends Error {
|
||||
Error.captureStackTrace(this, SvgoParserError);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const lines = this.source.split(/\r?\n/);
|
||||
const startLine = Math.max(this.line - 3, 0);
|
||||
@@ -82,44 +67,34 @@ const config = {
|
||||
lowercase: true,
|
||||
xmlns: true,
|
||||
position: true,
|
||||
unparsedEntities: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert SVG (XML) string to SVG-as-JS object.
|
||||
*
|
||||
* @type {(data: string, from?: string) => XastRoot}
|
||||
* @param {string} data
|
||||
* @param {string=} from
|
||||
* @returns {import('./types.js').XastRoot}
|
||||
*/
|
||||
const parseSvg = (data, from) => {
|
||||
export const parseSvg = (data, from) => {
|
||||
const sax = SAX.parser(config.strict, config);
|
||||
/**
|
||||
* @type {XastRoot}
|
||||
*/
|
||||
const root = new JSAPI({ type: 'root', children: [] });
|
||||
/**
|
||||
* @type {XastParent}
|
||||
*/
|
||||
/** @type {import('./types.js').XastRoot} */
|
||||
const root = { type: 'root', children: [] };
|
||||
/** @type {import('./types.js').XastParent} */
|
||||
let current = root;
|
||||
/**
|
||||
* @type {Array<XastParent>}
|
||||
*/
|
||||
/** @type {import('./types.js').XastParent[]} */
|
||||
const stack = [root];
|
||||
|
||||
/**
|
||||
* @type {<T extends XastNode>(node: T) => T}
|
||||
* @param {import('./types.js').XastChild} node
|
||||
*/
|
||||
const pushToContent = (node) => {
|
||||
const wrapped = new JSAPI(node, current);
|
||||
current.children.push(wrapped);
|
||||
return wrapped;
|
||||
current.children.push(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(doctype: string) => void}
|
||||
*/
|
||||
sax.ondoctype = (doctype) => {
|
||||
/**
|
||||
* @type {XastDoctype}
|
||||
*/
|
||||
/** @type {import('./types.js').XastDoctype} */
|
||||
const node = {
|
||||
type: 'doctype',
|
||||
// TODO parse doctype for name, public and system to match xast
|
||||
@@ -140,13 +115,8 @@ const parseSvg = (data, from) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(data: { name: string, body: string }) => void}
|
||||
*/
|
||||
sax.onprocessinginstruction = (data) => {
|
||||
/**
|
||||
* @type {XastInstruction}
|
||||
*/
|
||||
/** @type {import('./types.js').XastInstruction} */
|
||||
const node = {
|
||||
type: 'instruction',
|
||||
name: data.name,
|
||||
@@ -155,13 +125,8 @@ const parseSvg = (data, from) => {
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(comment: string) => void}
|
||||
*/
|
||||
sax.oncomment = (comment) => {
|
||||
/**
|
||||
* @type {XastComment}
|
||||
*/
|
||||
/** @type {import('./types.js').XastComment} */
|
||||
const node = {
|
||||
type: 'comment',
|
||||
value: comment.trim(),
|
||||
@@ -169,13 +134,8 @@ const parseSvg = (data, from) => {
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(cdata: string) => void}
|
||||
*/
|
||||
sax.oncdata = (cdata) => {
|
||||
/**
|
||||
* @type {XastCdata}
|
||||
*/
|
||||
/** @type {import('./types.js').XastCdata} */
|
||||
const node = {
|
||||
type: 'cdata',
|
||||
value: cdata,
|
||||
@@ -183,14 +143,9 @@ const parseSvg = (data, from) => {
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(data: { name: string, attributes: Record<string, { value: string }>}) => void}
|
||||
*/
|
||||
sax.onopentag = (data) => {
|
||||
/**
|
||||
* @type {XastElement}
|
||||
*/
|
||||
let element = {
|
||||
/** @type {import('./types.js').XastElement} */
|
||||
const element = {
|
||||
type: 'element',
|
||||
name: data.name,
|
||||
attributes: {},
|
||||
@@ -199,35 +154,32 @@ const parseSvg = (data, from) => {
|
||||
for (const [name, attr] of Object.entries(data.attributes)) {
|
||||
element.attributes[name] = attr.value;
|
||||
}
|
||||
element = pushToContent(element);
|
||||
pushToContent(element);
|
||||
current = element;
|
||||
stack.push(element);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(text: string) => void}
|
||||
*/
|
||||
sax.ontext = (text) => {
|
||||
if (current.type === 'element') {
|
||||
// prevent trimming of meaningful whitespace inside textual tags
|
||||
if (textElems.includes(current.name)) {
|
||||
/**
|
||||
* @type {XastText}
|
||||
*/
|
||||
if (textElems.has(current.name)) {
|
||||
/** @type {import('./types.js').XastText} */
|
||||
const node = {
|
||||
type: 'text',
|
||||
value: text,
|
||||
};
|
||||
pushToContent(node);
|
||||
} else if (/\S/.test(text)) {
|
||||
/**
|
||||
* @type {XastText}
|
||||
*/
|
||||
const node = {
|
||||
type: 'text',
|
||||
value: text.trim(),
|
||||
};
|
||||
pushToContent(node);
|
||||
} else {
|
||||
const value = text.trim();
|
||||
|
||||
if (value !== '') {
|
||||
/** @type {import('./types.js').XastText} */
|
||||
const node = {
|
||||
type: 'text',
|
||||
value,
|
||||
};
|
||||
pushToContent(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -237,16 +189,14 @@ const parseSvg = (data, from) => {
|
||||
current = stack[stack.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(e: any) => void}
|
||||
*/
|
||||
sax.onerror = (e) => {
|
||||
const reason = e.message.split('\n')[0];
|
||||
const error = new SvgoParserError(
|
||||
e.reason,
|
||||
e.line + 1,
|
||||
e.column,
|
||||
reason,
|
||||
sax.line + 1,
|
||||
sax.column,
|
||||
data,
|
||||
from
|
||||
from,
|
||||
);
|
||||
if (e.message.indexOf('Unexpected end') === -1) {
|
||||
throw error;
|
||||
@@ -256,4 +206,3 @@ const parseSvg = (data, from) => {
|
||||
sax.write(data).close();
|
||||
return root;
|
||||
};
|
||||
exports.parseSvg = parseSvg;
|
||||
|
||||
229
node_modules/svgo/lib/path.js
generated
vendored
229
node_modules/svgo/lib/path.js
generated
vendored
@@ -1,11 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').PathDataItem} PathDataItem
|
||||
* @typedef {import('./types').PathDataCommand} PathDataCommand
|
||||
* @fileoverview Based on https://www.w3.org/TR/SVG11/paths.html#PathDataBNF.
|
||||
*/
|
||||
|
||||
// Based on https://www.w3.org/TR/SVG11/paths.html#PathDataBNF
|
||||
import { removeLeadingZero, toFixed } from './svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState
|
||||
*
|
||||
* @typedef StringifyPathDataOptions
|
||||
* @property {ReadonlyArray<import('./types.js').PathDataItem>} pathData
|
||||
* @property {number=} precision
|
||||
* @property {boolean=} disableSpaceAfterFlags
|
||||
*/
|
||||
|
||||
const argsCountPerCommand = {
|
||||
M: 2,
|
||||
@@ -31,27 +37,24 @@ const argsCountPerCommand = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(c: string) => c is PathDataCommand}
|
||||
* @param {string} c
|
||||
* @returns {c is import('./types.js').PathDataCommand}
|
||||
*/
|
||||
const isCommand = (c) => {
|
||||
return c in argsCountPerCommand;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(c: string) => boolean}
|
||||
* @param {string} c
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isWsp = (c) => {
|
||||
const codePoint = c.codePointAt(0);
|
||||
return (
|
||||
codePoint === 0x20 ||
|
||||
codePoint === 0x9 ||
|
||||
codePoint === 0xd ||
|
||||
codePoint === 0xa
|
||||
);
|
||||
const isWhiteSpace = (c) => {
|
||||
return c === ' ' || c === '\t' || c === '\r' || c === '\n';
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(c: string) => boolean}
|
||||
* @param {string} c
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isDigit = (c) => {
|
||||
const codePoint = c.codePointAt(0);
|
||||
@@ -62,16 +65,15 @@ const isDigit = (c) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {'none' | 'sign' | 'whole' | 'decimal_point' | 'decimal' | 'e' | 'exponent_sign' | 'exponent'} ReadNumberState
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(string: string, cursor: number) => [number, number | null]}
|
||||
* @param {string} string
|
||||
* @param {number} cursor
|
||||
* @returns {[number, ?number]}
|
||||
*/
|
||||
const readNumber = (string, cursor) => {
|
||||
let i = cursor;
|
||||
let value = '';
|
||||
let state = /** @type {ReadNumberState} */ ('none');
|
||||
/** @type {ReadNumberState} */
|
||||
let state = 'none';
|
||||
for (; i < string.length; i += 1) {
|
||||
const c = string[i];
|
||||
if (c === '+' || c === '-') {
|
||||
@@ -133,16 +135,13 @@ const readNumber = (string, cursor) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(string: string) => Array<PathDataItem>}
|
||||
* @param {string} string
|
||||
* @returns {import('./types.js').PathDataItem[]}
|
||||
*/
|
||||
const parsePathData = (string) => {
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
export const parsePathData = (string) => {
|
||||
/** @type {import('./types.js').PathDataItem[]} */
|
||||
const pathData = [];
|
||||
/**
|
||||
* @type {null | PathDataCommand}
|
||||
*/
|
||||
/** @type {?import('./types.js').PathDataCommand} */
|
||||
let command = null;
|
||||
let args = /** @type {number[]} */ ([]);
|
||||
let argsCount = 0;
|
||||
@@ -150,7 +149,7 @@ const parsePathData = (string) => {
|
||||
let hadComma = false;
|
||||
for (let i = 0; i < string.length; i += 1) {
|
||||
const c = string.charAt(i);
|
||||
if (isWsp(c)) {
|
||||
if (isWhiteSpace(c)) {
|
||||
continue;
|
||||
}
|
||||
// allow comma only between arguments
|
||||
@@ -170,11 +169,9 @@ const parsePathData = (string) => {
|
||||
if (c !== 'M' && c !== 'm') {
|
||||
return pathData;
|
||||
}
|
||||
} else {
|
||||
} else if (args.length !== 0) {
|
||||
// stop if previous command arguments are not flushed
|
||||
if (args.length !== 0) {
|
||||
return pathData;
|
||||
}
|
||||
return pathData;
|
||||
}
|
||||
command = c;
|
||||
args = [];
|
||||
@@ -226,7 +223,7 @@ const parsePathData = (string) => {
|
||||
// flush arguments when necessary count is reached
|
||||
if (args.length === argsCount) {
|
||||
pathData.push({ command, args });
|
||||
// subsequent moveto coordinates are threated as implicit lineto commands
|
||||
// subsequent moveto coordinates are treated as implicit lineto commands
|
||||
if (command === 'M') {
|
||||
command = 'L';
|
||||
}
|
||||
@@ -238,110 +235,128 @@ const parsePathData = (string) => {
|
||||
}
|
||||
return pathData;
|
||||
};
|
||||
exports.parsePathData = parsePathData;
|
||||
|
||||
/**
|
||||
* @type {(number: number, precision?: number) => string}
|
||||
* @param {number} number
|
||||
* @param {number=} precision
|
||||
* @returns {{ roundedStr: string, rounded: number }}
|
||||
*/
|
||||
const stringifyNumber = (number, precision) => {
|
||||
const roundAndStringify = (number, precision) => {
|
||||
if (precision != null) {
|
||||
const ratio = 10 ** precision;
|
||||
number = Math.round(number * ratio) / ratio;
|
||||
number = toFixed(number, precision);
|
||||
}
|
||||
// remove zero whole from decimal number
|
||||
return number.toString().replace(/^0\./, '.').replace(/^-0\./, '-.');
|
||||
|
||||
return {
|
||||
roundedStr: removeLeadingZero(number),
|
||||
rounded: number,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Elliptical arc large-arc and sweep flags are rendered with spaces
|
||||
* because many non-browser environments are not able to parse such paths
|
||||
*
|
||||
* @type {(
|
||||
* command: string,
|
||||
* args: number[],
|
||||
* precision?: number,
|
||||
* disableSpaceAfterFlags?: boolean
|
||||
* ) => string}
|
||||
* @param {string} command
|
||||
* @param {ReadonlyArray<number>} args
|
||||
* @param {number=} precision
|
||||
* @param {boolean=} disableSpaceAfterFlags
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => {
|
||||
let result = '';
|
||||
let prev = '';
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const number = args[i];
|
||||
const numberString = stringifyNumber(number, precision);
|
||||
let previous;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const { roundedStr, rounded } = roundAndStringify(args[i], precision);
|
||||
if (
|
||||
disableSpaceAfterFlags &&
|
||||
(command === 'A' || command === 'a') &&
|
||||
// consider combined arcs
|
||||
(i % 7 === 4 || i % 7 === 5)
|
||||
) {
|
||||
result += numberString;
|
||||
} else if (i === 0 || numberString.startsWith('-')) {
|
||||
result += roundedStr;
|
||||
} else if (i === 0 || rounded < 0) {
|
||||
// avoid space before first and negative numbers
|
||||
result += numberString;
|
||||
} else if (prev.includes('.') && numberString.startsWith('.')) {
|
||||
result += roundedStr;
|
||||
} else if (!Number.isInteger(previous) && !isDigit(roundedStr[0])) {
|
||||
// remove space before decimal with zero whole
|
||||
// only when previous number is also decimal
|
||||
result += numberString;
|
||||
result += roundedStr;
|
||||
} else {
|
||||
result += ` ${numberString}`;
|
||||
result += ` ${roundedStr}`;
|
||||
}
|
||||
prev = numberString;
|
||||
previous = rounded;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pathData: Array<PathDataItem>;
|
||||
* precision?: number;
|
||||
* disableSpaceAfterFlags?: boolean;
|
||||
* }} StringifyPathDataOptions
|
||||
* @param {StringifyPathDataOptions} options
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(options: StringifyPathDataOptions) => string}
|
||||
*/
|
||||
const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => {
|
||||
// combine sequence of the same commands
|
||||
let combined = [];
|
||||
for (let i = 0; i < pathData.length; i += 1) {
|
||||
const { command, args } = pathData[i];
|
||||
if (i === 0) {
|
||||
combined.push({ command, args });
|
||||
} else {
|
||||
/**
|
||||
* @type {PathDataItem}
|
||||
*/
|
||||
const last = combined[combined.length - 1];
|
||||
// match leading moveto with following lineto
|
||||
if (i === 1) {
|
||||
if (command === 'L') {
|
||||
last.command = 'M';
|
||||
}
|
||||
if (command === 'l') {
|
||||
last.command = 'm';
|
||||
}
|
||||
}
|
||||
if (
|
||||
(last.command === command &&
|
||||
last.command !== 'M' &&
|
||||
last.command !== 'm') ||
|
||||
// combine matching moveto and lineto sequences
|
||||
(last.command === 'M' && command === 'L') ||
|
||||
(last.command === 'm' && command === 'l')
|
||||
) {
|
||||
last.args = [...last.args, ...args];
|
||||
} else {
|
||||
combined.push({ command, args });
|
||||
}
|
||||
}
|
||||
export const stringifyPathData = ({
|
||||
pathData,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
}) => {
|
||||
if (pathData.length === 1) {
|
||||
const { command, args } = pathData[0];
|
||||
return (
|
||||
command + stringifyArgs(command, args, precision, disableSpaceAfterFlags)
|
||||
);
|
||||
}
|
||||
|
||||
let result = '';
|
||||
for (const { command, args } of combined) {
|
||||
result +=
|
||||
command + stringifyArgs(command, args, precision, disableSpaceAfterFlags);
|
||||
let prev = { ...pathData[0] };
|
||||
|
||||
// match leading moveto with following lineto
|
||||
if (pathData[1].command === 'L') {
|
||||
prev.command = 'M';
|
||||
} else if (pathData[1].command === 'l') {
|
||||
prev.command = 'm';
|
||||
}
|
||||
|
||||
for (let i = 1; i < pathData.length; i++) {
|
||||
const { command, args } = pathData[i];
|
||||
if (
|
||||
(prev.command === command &&
|
||||
prev.command !== 'M' &&
|
||||
prev.command !== 'm') ||
|
||||
// combine matching moveto and lineto sequences
|
||||
(prev.command === 'M' && command === 'L') ||
|
||||
(prev.command === 'm' && command === 'l')
|
||||
) {
|
||||
prev.args = [...prev.args, ...args];
|
||||
if (i === pathData.length - 1) {
|
||||
result +=
|
||||
prev.command +
|
||||
stringifyArgs(
|
||||
prev.command,
|
||||
prev.args,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
result +=
|
||||
prev.command +
|
||||
stringifyArgs(
|
||||
prev.command,
|
||||
prev.args,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
);
|
||||
|
||||
if (i === pathData.length - 1) {
|
||||
result +=
|
||||
command +
|
||||
stringifyArgs(command, args, precision, disableSpaceAfterFlags);
|
||||
} else {
|
||||
prev = { command, args };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
exports.stringifyPathData = stringifyPathData;
|
||||
|
||||
288
node_modules/svgo/lib/stringifier.js
generated
vendored
288
node_modules/svgo/lib/stringifier.js
generated
vendored
@@ -1,43 +1,23 @@
|
||||
'use strict';
|
||||
import { textElems } from '../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
* @typedef {import('./types').XastRoot} XastRoot
|
||||
* @typedef {import('./types').XastElement} XastElement
|
||||
* @typedef {import('./types').XastInstruction} XastInstruction
|
||||
* @typedef {import('./types').XastDoctype} XastDoctype
|
||||
* @typedef {import('./types').XastText} XastText
|
||||
* @typedef {import('./types').XastCdata} XastCdata
|
||||
* @typedef {import('./types').XastComment} XastComment
|
||||
* @typedef {import('./types').StringifyOptions} StringifyOptions
|
||||
*/
|
||||
|
||||
const { textElems } = require('../plugins/_collections.js');
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* width: void | string,
|
||||
* height: void | string,
|
||||
* indent: string,
|
||||
* textContext: null | XastElement,
|
||||
* indentLevel: number,
|
||||
* }} State
|
||||
* @typedef {Required<import('./types.js').StringifyOptions>} Options
|
||||
*
|
||||
* @typedef State
|
||||
* @property {string} indent
|
||||
* @property {?import('./types.js').XastElement} textContext
|
||||
* @property {number} indentLevel
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Required<StringifyOptions>} Options
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(char: string) => string}
|
||||
* @param {string} char
|
||||
* @returns {string}
|
||||
*/
|
||||
const encodeEntity = (char) => {
|
||||
return entities[char];
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {Options}
|
||||
*/
|
||||
/** @type {Options} */
|
||||
const defaults = {
|
||||
doctypeStart: '<!DOCTYPE',
|
||||
doctypeEnd: '>',
|
||||
@@ -60,16 +40,14 @@ const defaults = {
|
||||
indent: 4,
|
||||
regEntities: /[&'"<>]/g,
|
||||
regValEntities: /[&"<>]/g,
|
||||
encodeEntity: encodeEntity,
|
||||
encodeEntity,
|
||||
pretty: false,
|
||||
useShortTags: true,
|
||||
eol: 'lf',
|
||||
finalNewline: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {Record<string, string>}
|
||||
*/
|
||||
/** @type {Record<string, string>} */
|
||||
const entities = {
|
||||
'&': '&',
|
||||
"'": ''',
|
||||
@@ -79,20 +57,14 @@ const entities = {
|
||||
};
|
||||
|
||||
/**
|
||||
* convert XAST to SVG string
|
||||
* Converts XAST to SVG string.
|
||||
*
|
||||
* @type {(data: XastRoot, config: StringifyOptions) => {
|
||||
* data: string,
|
||||
* info: {
|
||||
* width: void | string,
|
||||
* height: void | string
|
||||
* }
|
||||
* }}
|
||||
* @param {import('./types.js').XastRoot} data
|
||||
* @param {import('./types.js').StringifyOptions=} userOptions
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifySvg = (data, userOptions = {}) => {
|
||||
/**
|
||||
* @type {Options}
|
||||
*/
|
||||
export const stringifySvg = (data, userOptions = {}) => {
|
||||
/** @type {Options} */
|
||||
const config = { ...defaults, ...userOptions };
|
||||
const indent = config.indent;
|
||||
let newIndent = ' ';
|
||||
@@ -101,13 +73,8 @@ const stringifySvg = (data, userOptions = {}) => {
|
||||
} else if (typeof indent === 'string') {
|
||||
newIndent = indent;
|
||||
}
|
||||
/**
|
||||
* @type {State}
|
||||
*/
|
||||
/** @type {State} */
|
||||
const state = {
|
||||
// TODO remove width and height in v3
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
indent: newIndent,
|
||||
textContext: null,
|
||||
indentLevel: 0,
|
||||
@@ -124,53 +91,52 @@ const stringifySvg = (data, userOptions = {}) => {
|
||||
config.textEnd += eol;
|
||||
}
|
||||
let svg = stringifyNode(data, config, state);
|
||||
if (config.finalNewline && svg.length > 0 && svg[svg.length - 1] !== '\n') {
|
||||
if (config.finalNewline && svg.length > 0 && !svg.endsWith('\n')) {
|
||||
svg += eol;
|
||||
}
|
||||
return {
|
||||
data: svg,
|
||||
info: {
|
||||
width: state.width,
|
||||
height: state.height,
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.stringifySvg = stringifySvg;
|
||||
|
||||
/**
|
||||
* @type {(node: XastParent, config: Options, state: State) => string}
|
||||
*/
|
||||
const stringifyNode = (data, config, state) => {
|
||||
let svg = '';
|
||||
state.indentLevel += 1;
|
||||
for (const item of data.children) {
|
||||
if (item.type === 'element') {
|
||||
svg += stringifyElement(item, config, state);
|
||||
}
|
||||
if (item.type === 'text') {
|
||||
svg += stringifyText(item, config, state);
|
||||
}
|
||||
if (item.type === 'doctype') {
|
||||
svg += stringifyDoctype(item, config);
|
||||
}
|
||||
if (item.type === 'instruction') {
|
||||
svg += stringifyInstruction(item, config);
|
||||
}
|
||||
if (item.type === 'comment') {
|
||||
svg += stringifyComment(item, config);
|
||||
}
|
||||
if (item.type === 'cdata') {
|
||||
svg += stringifyCdata(item, config, state);
|
||||
}
|
||||
}
|
||||
state.indentLevel -= 1;
|
||||
return svg;
|
||||
};
|
||||
|
||||
/**
|
||||
* create indent string in accordance with the current node level.
|
||||
* @param {import('./types.js').XastParent} data
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyNode = (data, config, state) => {
|
||||
let svg = '';
|
||||
state.indentLevel++;
|
||||
for (const item of data.children) {
|
||||
switch (item.type) {
|
||||
case 'element':
|
||||
svg += stringifyElement(item, config, state);
|
||||
break;
|
||||
case 'text':
|
||||
svg += stringifyText(item, config, state);
|
||||
break;
|
||||
case 'doctype':
|
||||
svg += stringifyDoctype(item, config);
|
||||
break;
|
||||
case 'instruction':
|
||||
svg += stringifyInstruction(item, config);
|
||||
break;
|
||||
case 'comment':
|
||||
svg += stringifyComment(item, config);
|
||||
break;
|
||||
case 'cdata':
|
||||
svg += stringifyCdata(item, config, state);
|
||||
}
|
||||
}
|
||||
state.indentLevel--;
|
||||
return svg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create indent string in accordance with the current node level.
|
||||
*
|
||||
* @type {(config: Options, state: State) => string}
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const createIndent = (config, state) => {
|
||||
let indent = '';
|
||||
@@ -181,14 +147,18 @@ const createIndent = (config, state) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastDoctype, config: Options) => string}
|
||||
* @param {import('./types.js').XastDoctype} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyDoctype = (node, config) => {
|
||||
return config.doctypeStart + node.data.doctype + config.doctypeEnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastInstruction, config: Options) => string}
|
||||
* @param {import('./types.js').XastInstruction} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyInstruction = (node, config) => {
|
||||
return (
|
||||
@@ -197,14 +167,19 @@ const stringifyInstruction = (node, config) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastComment, config: Options) => string}
|
||||
* @param {import('./types.js').XastComment} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyComment = (node, config) => {
|
||||
return config.commentStart + node.value + config.commentEnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastCdata, config: Options, state: State) => string}
|
||||
* @param {import('./types.js').XastCdata} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyCdata = (node, config, state) => {
|
||||
return (
|
||||
@@ -216,19 +191,12 @@ const stringifyCdata = (node, config, state) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastElement, config: Options, state: State) => string}
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyElement = (node, config, state) => {
|
||||
// beautiful injection for obtaining SVG information :)
|
||||
if (
|
||||
node.name === 'svg' &&
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null
|
||||
) {
|
||||
state.width = node.attributes.width;
|
||||
state.height = node.attributes.height;
|
||||
}
|
||||
|
||||
// empty element and short tag
|
||||
if (node.children.length === 0) {
|
||||
if (config.useShortTags) {
|
||||
@@ -239,82 +207,86 @@ const stringifyElement = (node, config, state) => {
|
||||
stringifyAttributes(node, config) +
|
||||
config.tagShortEnd
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
createIndent(config, state) +
|
||||
config.tagShortStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
config.tagOpenEnd +
|
||||
config.tagCloseStart +
|
||||
node.name +
|
||||
config.tagCloseEnd
|
||||
);
|
||||
}
|
||||
// non-empty element
|
||||
} else {
|
||||
let tagOpenStart = config.tagOpenStart;
|
||||
let tagOpenEnd = config.tagOpenEnd;
|
||||
let tagCloseStart = config.tagCloseStart;
|
||||
let tagCloseEnd = config.tagCloseEnd;
|
||||
let openIndent = createIndent(config, state);
|
||||
let closeIndent = createIndent(config, state);
|
||||
|
||||
if (state.textContext) {
|
||||
tagOpenStart = defaults.tagOpenStart;
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
tagCloseEnd = defaults.tagCloseEnd;
|
||||
openIndent = '';
|
||||
} else if (textElems.includes(node.name)) {
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
closeIndent = '';
|
||||
state.textContext = node;
|
||||
}
|
||||
|
||||
const children = stringifyNode(node, config, state);
|
||||
|
||||
if (state.textContext === node) {
|
||||
state.textContext = null;
|
||||
}
|
||||
|
||||
return (
|
||||
openIndent +
|
||||
tagOpenStart +
|
||||
createIndent(config, state) +
|
||||
config.tagShortStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
tagOpenEnd +
|
||||
children +
|
||||
closeIndent +
|
||||
tagCloseStart +
|
||||
config.tagOpenEnd +
|
||||
config.tagCloseStart +
|
||||
node.name +
|
||||
tagCloseEnd
|
||||
config.tagCloseEnd
|
||||
);
|
||||
}
|
||||
|
||||
// non-empty element
|
||||
let tagOpenStart = config.tagOpenStart;
|
||||
let tagOpenEnd = config.tagOpenEnd;
|
||||
let tagCloseStart = config.tagCloseStart;
|
||||
let tagCloseEnd = config.tagCloseEnd;
|
||||
let openIndent = createIndent(config, state);
|
||||
let closeIndent = createIndent(config, state);
|
||||
|
||||
if (state.textContext) {
|
||||
tagOpenStart = defaults.tagOpenStart;
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
tagCloseEnd = defaults.tagCloseEnd;
|
||||
openIndent = '';
|
||||
} else if (textElems.has(node.name)) {
|
||||
tagOpenEnd = defaults.tagOpenEnd;
|
||||
tagCloseStart = defaults.tagCloseStart;
|
||||
closeIndent = '';
|
||||
state.textContext = node;
|
||||
}
|
||||
|
||||
const children = stringifyNode(node, config, state);
|
||||
|
||||
if (state.textContext === node) {
|
||||
state.textContext = null;
|
||||
}
|
||||
|
||||
return (
|
||||
openIndent +
|
||||
tagOpenStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
tagOpenEnd +
|
||||
children +
|
||||
closeIndent +
|
||||
tagCloseStart +
|
||||
node.name +
|
||||
tagCloseEnd
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastElement, config: Options) => string}
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyAttributes = (node, config) => {
|
||||
let attrs = '';
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
// TODO remove attributes without values support in v3
|
||||
attrs += ' ' + name;
|
||||
|
||||
if (value !== undefined) {
|
||||
const encodedValue = value
|
||||
.toString()
|
||||
.replace(config.regValEntities, config.encodeEntity);
|
||||
attrs += ' ' + name + config.attrStart + encodedValue + config.attrEnd;
|
||||
} else {
|
||||
attrs += ' ' + name;
|
||||
attrs += config.attrStart + encodedValue + config.attrEnd;
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastText, config: Options, state: State) => string}
|
||||
* @param {import('./types.js').XastText} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyText = (node, config, state) => {
|
||||
return (
|
||||
|
||||
263
node_modules/svgo/lib/style.js
generated
vendored
263
node_modules/svgo/lib/style.js
generated
vendored
@@ -1,83 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('css-tree').Rule} CsstreeRule
|
||||
* @typedef {import('./types').Specificity} Specificity
|
||||
* @typedef {import('./types').Stylesheet} Stylesheet
|
||||
* @typedef {import('./types').StylesheetRule} StylesheetRule
|
||||
* @typedef {import('./types').StylesheetDeclaration} StylesheetDeclaration
|
||||
* @typedef {import('./types').ComputedStyles} ComputedStyles
|
||||
* @typedef {import('./types').XastRoot} XastRoot
|
||||
* @typedef {import('./types').XastElement} XastElement
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
* @typedef {import('./types').XastChild} XastChild
|
||||
*/
|
||||
|
||||
const stable = require('stable');
|
||||
const csstree = require('css-tree');
|
||||
// @ts-ignore not defined in @types/csso
|
||||
const specificity = require('csso/lib/restructure/prepare/specificity');
|
||||
const { visit, matches } = require('./xast.js');
|
||||
const {
|
||||
import * as csstree from 'css-tree';
|
||||
import * as csswhat from 'css-what';
|
||||
import { syntax } from 'csso';
|
||||
import { matches } from './xast.js';
|
||||
import { visit } from './util/visit.js';
|
||||
import {
|
||||
attrsGroups,
|
||||
inheritableAttrs,
|
||||
presentationNonInheritableGroupAttrs,
|
||||
} = require('../plugins/_collections.js');
|
||||
} from '../plugins/_collections.js';
|
||||
|
||||
// @ts-ignore not defined in @types/csstree
|
||||
const csstreeWalkSkip = csstree.walk.skip;
|
||||
|
||||
/**
|
||||
* @type {(ruleNode: CsstreeRule, dynamic: boolean) => StylesheetRule}
|
||||
* @param {import('css-tree').Rule} ruleNode
|
||||
* @param {boolean} dynamic
|
||||
* @returns {import('./types.js').StylesheetRule[]}
|
||||
*/
|
||||
const parseRule = (ruleNode, dynamic) => {
|
||||
let selectors;
|
||||
let selectorsSpecificity;
|
||||
/**
|
||||
* @type {Array<StylesheetDeclaration>}
|
||||
*/
|
||||
/** @type {import('./types.js').StylesheetDeclaration[]} */
|
||||
const declarations = [];
|
||||
csstree.walk(ruleNode, (cssNode) => {
|
||||
if (cssNode.type === 'SelectorList') {
|
||||
// compute specificity from original node to consider pseudo classes
|
||||
selectorsSpecificity = specificity(cssNode);
|
||||
const newSelectorsNode = csstree.clone(cssNode);
|
||||
csstree.walk(newSelectorsNode, (pseudoClassNode, item, list) => {
|
||||
if (pseudoClassNode.type === 'PseudoClassSelector') {
|
||||
dynamic = true;
|
||||
list.remove(item);
|
||||
}
|
||||
});
|
||||
selectors = csstree.generate(newSelectorsNode);
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
// collect declarations
|
||||
ruleNode.block.children.forEach((cssNode) => {
|
||||
if (cssNode.type === 'Declaration') {
|
||||
declarations.push({
|
||||
name: cssNode.property,
|
||||
value: csstree.generate(cssNode.value),
|
||||
important: cssNode.important === true,
|
||||
});
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
});
|
||||
if (selectors == null || selectorsSpecificity == null) {
|
||||
throw Error('assert');
|
||||
}
|
||||
return {
|
||||
dynamic,
|
||||
selectors,
|
||||
specificity: selectorsSpecificity,
|
||||
declarations,
|
||||
};
|
||||
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
csstree.walk(ruleNode.prelude, (node) => {
|
||||
if (node.type === 'Selector') {
|
||||
const newNode = csstree.clone(node);
|
||||
let hasPseudoClasses = false;
|
||||
csstree.walk(newNode, (pseudoClassNode, item, list) => {
|
||||
if (pseudoClassNode.type === 'PseudoClassSelector') {
|
||||
hasPseudoClasses = true;
|
||||
list.remove(item);
|
||||
}
|
||||
});
|
||||
rules.push({
|
||||
specificity: syntax.specificity(node),
|
||||
dynamic: hasPseudoClasses || dynamic,
|
||||
// compute specificity from original node to consider pseudo classes
|
||||
selector: csstree.generate(newNode),
|
||||
declarations,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return rules;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(css: string, dynamic: boolean) => Array<StylesheetRule>}
|
||||
* @param {string} css
|
||||
* @param {boolean} dynamic
|
||||
* @returns {import('./types.js').StylesheetRule[]}
|
||||
*/
|
||||
const parseStylesheet = (css, dynamic) => {
|
||||
/**
|
||||
* @type {Array<StylesheetRule>}
|
||||
*/
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
const ast = csstree.parse(css, {
|
||||
parseValue: false,
|
||||
@@ -85,16 +69,23 @@ const parseStylesheet = (css, dynamic) => {
|
||||
});
|
||||
csstree.walk(ast, (cssNode) => {
|
||||
if (cssNode.type === 'Rule') {
|
||||
rules.push(parseRule(cssNode, dynamic || false));
|
||||
rules.push(...parseRule(cssNode, dynamic || false));
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
if (cssNode.type === 'Atrule') {
|
||||
if (cssNode.name === 'keyframes') {
|
||||
if (
|
||||
[
|
||||
'keyframes',
|
||||
'-webkit-keyframes',
|
||||
'-o-keyframes',
|
||||
'-moz-keyframes',
|
||||
].includes(cssNode.name)
|
||||
) {
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
csstree.walk(cssNode, (ruleNode) => {
|
||||
if (ruleNode.type === 'Rule') {
|
||||
rules.push(parseRule(ruleNode, dynamic || true));
|
||||
rules.push(...parseRule(ruleNode, dynamic || true));
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
});
|
||||
@@ -105,12 +96,11 @@ const parseStylesheet = (css, dynamic) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(css: string) => Array<StylesheetDeclaration>}
|
||||
* @param {string} css
|
||||
* @returns {import('./types.js').StylesheetDeclaration[]}
|
||||
*/
|
||||
const parseStyleDeclarations = (css) => {
|
||||
/**
|
||||
* @type {Array<StylesheetDeclaration>}
|
||||
*/
|
||||
/** @type {import('./types.js').StylesheetDeclaration[]} */
|
||||
const declarations = [];
|
||||
const ast = csstree.parse(css, {
|
||||
context: 'declarationList',
|
||||
@@ -129,26 +119,27 @@ const parseStyleDeclarations = (css) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
|
||||
* @param {import('./types.js').Stylesheet} stylesheet
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {import('./types.js').ComputedStyles}
|
||||
*/
|
||||
const computeOwnStyle = (stylesheet, node) => {
|
||||
/**
|
||||
* @type {ComputedStyles}
|
||||
*/
|
||||
const computeOwnStyle = (stylesheet, node, parents) => {
|
||||
/** @type {import('./types.js').ComputedStyles} */
|
||||
const computedStyle = {};
|
||||
const importantStyles = new Map();
|
||||
|
||||
// collect attributes
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (attrsGroups.presentation.includes(name)) {
|
||||
if (attrsGroups.presentation.has(name)) {
|
||||
computedStyle[name] = { type: 'static', inherited: false, value };
|
||||
importantStyles.set(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
// collect matching rules
|
||||
for (const { selectors, declarations, dynamic } of stylesheet.rules) {
|
||||
if (matches(node, selectors)) {
|
||||
for (const { selector, declarations, dynamic } of stylesheet.rules) {
|
||||
if (matches(node, selector, parents)) {
|
||||
for (const { name, value, important } of declarations) {
|
||||
const computed = computedStyle[name];
|
||||
if (computed && computed.type === 'dynamic') {
|
||||
@@ -194,13 +185,15 @@ const computeOwnStyle = (stylesheet, node) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two selector specificities.
|
||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||
* Compares selector specificities.
|
||||
* Derived from https://github.com/keeganstreet/specificity/blob/8757133ddd2ed0163f120900047ff0f92760b536/specificity.js#L207
|
||||
*
|
||||
* @type {(a: Specificity, b: Specificity) => number}
|
||||
* @param {import('./types.js').Specificity} a
|
||||
* @param {import('./types.js').Specificity} b
|
||||
* @returns {number}
|
||||
*/
|
||||
const compareSpecificity = (a, b) => {
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
export const compareSpecificity = (a, b) => {
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
if (a[i] < b[i]) {
|
||||
return -1;
|
||||
} else if (a[i] > b[i]) {
|
||||
@@ -212,36 +205,35 @@ const compareSpecificity = (a, b) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(root: XastRoot) => Stylesheet}
|
||||
* @param {import('./types.js').XastRoot} root
|
||||
* @returns {import('./types.js').Stylesheet}
|
||||
*/
|
||||
const collectStylesheet = (root) => {
|
||||
/**
|
||||
* @type {Array<StylesheetRule>}
|
||||
*/
|
||||
export const collectStylesheet = (root) => {
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
/**
|
||||
* @type {Map<XastElement, XastParent>}
|
||||
*/
|
||||
/** @type {Map<import('./types.js').XastElement, import('./types.js').XastParent>} */
|
||||
const parents = new Map();
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// store parents
|
||||
parents.set(node, parentNode);
|
||||
// find and parse all styles
|
||||
if (node.name === 'style') {
|
||||
|
||||
if (node.name !== 'style') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
node.attributes.type == null ||
|
||||
node.attributes.type === '' ||
|
||||
node.attributes.type === 'text/css'
|
||||
) {
|
||||
const dynamic =
|
||||
node.attributes.media != null && node.attributes.media !== 'all';
|
||||
if (
|
||||
node.attributes.type == null ||
|
||||
node.attributes.type === '' ||
|
||||
node.attributes.type === 'text/css'
|
||||
) {
|
||||
const children = node.children;
|
||||
for (const child of children) {
|
||||
if (child.type === 'text' || child.type === 'cdata') {
|
||||
rules.push(...parseStylesheet(child.value, dynamic));
|
||||
}
|
||||
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'text' || child.type === 'cdata') {
|
||||
rules.push(...parseStylesheet(child.value, dynamic));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,29 +241,26 @@ const collectStylesheet = (root) => {
|
||||
},
|
||||
});
|
||||
// sort by selectors specificity
|
||||
stable.inplace(rules, (a, b) =>
|
||||
compareSpecificity(a.specificity, b.specificity)
|
||||
);
|
||||
rules.sort((a, b) => compareSpecificity(a.specificity, b.specificity));
|
||||
return { rules, parents };
|
||||
};
|
||||
exports.collectStylesheet = collectStylesheet;
|
||||
|
||||
/**
|
||||
* @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
|
||||
* @param {import('./types.js').Stylesheet} stylesheet
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @returns {import('./types.js').ComputedStyles}
|
||||
*/
|
||||
const computeStyle = (stylesheet, node) => {
|
||||
export const computeStyle = (stylesheet, node) => {
|
||||
const { parents } = stylesheet;
|
||||
// collect inherited styles
|
||||
const computedStyles = computeOwnStyle(stylesheet, node);
|
||||
const computedStyles = computeOwnStyle(stylesheet, node, parents);
|
||||
let parent = parents.get(node);
|
||||
while (parent != null && parent.type !== 'root') {
|
||||
const inheritedStyles = computeOwnStyle(stylesheet, parent);
|
||||
const inheritedStyles = computeOwnStyle(stylesheet, parent, parents);
|
||||
for (const [name, computed] of Object.entries(inheritedStyles)) {
|
||||
if (
|
||||
computedStyles[name] == null &&
|
||||
// ignore not inheritable styles
|
||||
inheritableAttrs.includes(name) === true &&
|
||||
presentationNonInheritableGroupAttrs.includes(name) === false
|
||||
inheritableAttrs.has(name) &&
|
||||
!presentationNonInheritableGroupAttrs.has(name)
|
||||
) {
|
||||
computedStyles[name] = { ...computed, inherited: true };
|
||||
}
|
||||
@@ -280,4 +269,56 @@ const computeStyle = (stylesheet, node) => {
|
||||
}
|
||||
return computedStyles;
|
||||
};
|
||||
exports.computeStyle = computeStyle;
|
||||
|
||||
/**
|
||||
* Determines if the CSS selector includes or traverses the given attribute.
|
||||
*
|
||||
* Classes and IDs are generated as attribute selectors, so you can check for if
|
||||
* a `.class` or `#id` is included by passing `name=class` or `name=id`
|
||||
* respectively.
|
||||
*
|
||||
* @param {csstree.ListItem<csstree.CssNode> | string} selector
|
||||
* @param {string} name
|
||||
* @param {?string} value
|
||||
* @param {boolean} traversed
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const includesAttrSelector = (
|
||||
selector,
|
||||
name,
|
||||
value = null,
|
||||
traversed = false,
|
||||
) => {
|
||||
const selectors =
|
||||
typeof selector === 'string'
|
||||
? csswhat.parse(selector)
|
||||
: csswhat.parse(csstree.generate(selector.data));
|
||||
|
||||
for (const subselector of selectors) {
|
||||
const hasAttrSelector = subselector.some((segment, index) => {
|
||||
if (traversed) {
|
||||
if (index === subselector.length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isNextTraversal = csswhat.isTraversal(subselector[index + 1]);
|
||||
|
||||
if (!isNextTraversal) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (segment.type !== 'attribute' || segment.name !== name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value == null ? true : segment.value === value;
|
||||
});
|
||||
|
||||
if (hasAttrSelector) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
96
node_modules/svgo/lib/svgo-node.js
generated
vendored
96
node_modules/svgo/lib/svgo-node.js
generated
vendored
@@ -1,100 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const { pathToFileURL } = require('url');
|
||||
const path = require('path');
|
||||
const {
|
||||
extendDefaultPlugins,
|
||||
optimize: optimizeAgnostic,
|
||||
createContentItem,
|
||||
} = require('./svgo.js');
|
||||
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
exports.createContentItem = createContentItem;
|
||||
import os from 'os';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import * as svgo from './svgo.js';
|
||||
import url from 'url';
|
||||
|
||||
/**
|
||||
* @param {string} configFile
|
||||
* @returns {Promise<import('./types.js').Config>}
|
||||
*/
|
||||
const importConfig = async (configFile) => {
|
||||
let config;
|
||||
// at the moment dynamic import may randomly fail with segfault
|
||||
// to workaround this for some users .cjs extension is loaded
|
||||
// exclusively with require
|
||||
if (configFile.endsWith('.cjs')) {
|
||||
config = require(configFile);
|
||||
} else {
|
||||
try {
|
||||
// dynamic import expects file url instead of path and may fail
|
||||
// when windows path is provided
|
||||
const { default: imported } = await import(pathToFileURL(configFile));
|
||||
config = imported;
|
||||
} catch (importError) {
|
||||
// TODO remove require in v3
|
||||
try {
|
||||
config = require(configFile);
|
||||
} catch (requireError) {
|
||||
// throw original error if es module is detected
|
||||
if (requireError.code === 'ERR_REQUIRE_ESM') {
|
||||
throw importError;
|
||||
} else {
|
||||
throw requireError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const resolvedPath = path.resolve(configFile);
|
||||
const imported = await import(url.pathToFileURL(resolvedPath).toString());
|
||||
const config = imported.default;
|
||||
|
||||
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
|
||||
throw Error(`Invalid config file "${configFile}"`);
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
const isFile = async (file) => {
|
||||
try {
|
||||
const stats = await fs.promises.stat(file);
|
||||
const stats = await fs.stat(file);
|
||||
return stats.isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadConfig = async (configFile, cwd = process.cwd()) => {
|
||||
export * from './svgo.js';
|
||||
|
||||
/**
|
||||
* If you write a tool on top of svgo you might need a way to load svgo config.
|
||||
* You can also specify relative or absolute path and customize current working
|
||||
* directory.
|
||||
*
|
||||
* @type {<T extends string | null>(configFile?: T, cwd?: string) => Promise<T extends string ? import('./svgo.js').Config : import('./svgo.js').Config | null>}
|
||||
*/
|
||||
export const loadConfig = async (configFile, cwd = process.cwd()) => {
|
||||
if (configFile != null) {
|
||||
if (path.isAbsolute(configFile)) {
|
||||
return await importConfig(configFile);
|
||||
return importConfig(configFile);
|
||||
} else {
|
||||
return await importConfig(path.join(cwd, configFile));
|
||||
return importConfig(path.join(cwd, configFile));
|
||||
}
|
||||
}
|
||||
let dir = cwd;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
|
||||
while (true) {
|
||||
const js = path.join(dir, 'svgo.config.js');
|
||||
if (await isFile(js)) {
|
||||
return await importConfig(js);
|
||||
return importConfig(js);
|
||||
}
|
||||
const mjs = path.join(dir, 'svgo.config.mjs');
|
||||
if (await isFile(mjs)) {
|
||||
return await importConfig(mjs);
|
||||
return importConfig(mjs);
|
||||
}
|
||||
const cjs = path.join(dir, 'svgo.config.cjs');
|
||||
if (await isFile(cjs)) {
|
||||
return await importConfig(cjs);
|
||||
return importConfig(cjs);
|
||||
}
|
||||
const parent = path.dirname(dir);
|
||||
if (dir === parent) {
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/33912
|
||||
return null;
|
||||
}
|
||||
dir = parent;
|
||||
}
|
||||
};
|
||||
exports.loadConfig = loadConfig;
|
||||
|
||||
const optimize = (input, config) => {
|
||||
/**
|
||||
* The core of SVGO.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {import('./svgo.js').Config=} config
|
||||
* @returns {import('./svgo.js').Output}
|
||||
*/
|
||||
export const optimize = (input, config) => {
|
||||
if (config == null) {
|
||||
config = {};
|
||||
}
|
||||
if (typeof config !== 'object') {
|
||||
throw Error('Config should be an object');
|
||||
}
|
||||
return optimizeAgnostic(input, {
|
||||
return svgo.optimize(input, {
|
||||
...config,
|
||||
js2svg: {
|
||||
// platform specific default for end of line
|
||||
@@ -103,4 +96,3 @@ const optimize = (input, config) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
exports.optimize = optimize;
|
||||
|
||||
166
node_modules/svgo/lib/svgo.js
generated
vendored
166
node_modules/svgo/lib/svgo.js
generated
vendored
@@ -1,19 +1,84 @@
|
||||
'use strict';
|
||||
import { builtinPlugins } from './builtin.js';
|
||||
import { encodeSVGDatauri } from './svgo/tools.js';
|
||||
import { invokePlugins } from './svgo/plugins.js';
|
||||
import { querySelector, querySelectorAll } from './xast.js';
|
||||
import { mapNodesToParents } from './util/map-nodes-to-parents.js';
|
||||
import { parseSvg } from './parser.js';
|
||||
import { stringifySvg } from './stringifier.js';
|
||||
import { VERSION } from './version.js';
|
||||
import * as _collections from '../plugins/_collections.js';
|
||||
|
||||
const {
|
||||
defaultPlugins,
|
||||
resolvePluginConfig,
|
||||
extendDefaultPlugins,
|
||||
} = require('./svgo/config.js');
|
||||
const { parseSvg } = require('./parser.js');
|
||||
const { stringifySvg } = require('./stringifier.js');
|
||||
const { invokePlugins } = require('./svgo/plugins.js');
|
||||
const JSAPI = require('./svgo/jsAPI.js');
|
||||
const { encodeSVGDatauri } = require('./svgo/tools.js');
|
||||
const pluginsMap = new Map();
|
||||
for (const plugin of builtinPlugins) {
|
||||
pluginsMap.set(plugin.name, plugin);
|
||||
}
|
||||
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {import('./types.js').BuiltinPluginOrPreset<?, ?>}
|
||||
*/
|
||||
function getPlugin(name) {
|
||||
if (name === 'removeScriptElement') {
|
||||
console.warn(
|
||||
'Warning: removeScriptElement has been renamed to removeScripts, please update your SVGO config',
|
||||
);
|
||||
return pluginsMap.get('removeScripts');
|
||||
}
|
||||
|
||||
const optimize = (input, config) => {
|
||||
return pluginsMap.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | import('./types.js').PluginConfig} plugin
|
||||
* @returns {?import('./types.js').PluginConfig}
|
||||
*/
|
||||
const resolvePluginConfig = (plugin) => {
|
||||
if (typeof plugin === 'string') {
|
||||
// resolve builtin plugin specified as string
|
||||
const builtinPlugin = getPlugin(plugin);
|
||||
if (builtinPlugin == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin}" specified.`);
|
||||
}
|
||||
return {
|
||||
name: plugin,
|
||||
params: {},
|
||||
fn: builtinPlugin.fn,
|
||||
};
|
||||
}
|
||||
if (typeof plugin === 'object' && plugin != null) {
|
||||
if (plugin.name == null) {
|
||||
throw Error(`Plugin name must be specified`);
|
||||
}
|
||||
// use custom plugin implementation
|
||||
// @ts-expect-error Checking for CustomPlugin with the presence of fn
|
||||
let fn = plugin.fn;
|
||||
if (fn == null) {
|
||||
// resolve builtin plugin implementation
|
||||
const builtinPlugin = getPlugin(plugin.name);
|
||||
if (builtinPlugin == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin.name}" specified.`);
|
||||
}
|
||||
fn = builtinPlugin.fn;
|
||||
}
|
||||
return {
|
||||
name: plugin.name,
|
||||
params: plugin.params,
|
||||
fn,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export * from './types.js';
|
||||
|
||||
/**
|
||||
* The core of SVGO.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {import('./types.js').Config=} config
|
||||
* @returns {import('./types.js').Output}
|
||||
*/
|
||||
export const optimize = (input, config) => {
|
||||
if (config == null) {
|
||||
config = {};
|
||||
}
|
||||
@@ -22,62 +87,57 @@ const optimize = (input, config) => {
|
||||
}
|
||||
const maxPassCount = config.multipass ? 10 : 1;
|
||||
let prevResultSize = Number.POSITIVE_INFINITY;
|
||||
let svgjs = null;
|
||||
let output = '';
|
||||
const info = {};
|
||||
if (config.path != null) {
|
||||
info.path = config.path;
|
||||
}
|
||||
for (let i = 0; i < maxPassCount; i += 1) {
|
||||
info.multipassCount = i;
|
||||
// TODO throw this error in v3
|
||||
try {
|
||||
svgjs = parseSvg(input, config.path);
|
||||
} catch (error) {
|
||||
return { error: error.toString(), modernError: error };
|
||||
}
|
||||
if (svgjs.error != null) {
|
||||
if (config.path != null) {
|
||||
svgjs.path = config.path;
|
||||
}
|
||||
return svgjs;
|
||||
}
|
||||
const plugins = config.plugins || defaultPlugins;
|
||||
if (Array.isArray(plugins) === false) {
|
||||
const ast = parseSvg(input, config.path);
|
||||
const plugins = config.plugins || ['preset-default'];
|
||||
if (!Array.isArray(plugins)) {
|
||||
throw Error(
|
||||
"Invalid plugins list. Provided 'plugins' in config should be an array."
|
||||
'malformed config, `plugins` property must be an array.\nSee more info here: https://github.com/svg/svgo#configuration',
|
||||
);
|
||||
}
|
||||
const resolvedPlugins = plugins.map(resolvePluginConfig);
|
||||
const resolvedPlugins = plugins
|
||||
.filter((plugin) => plugin != null)
|
||||
.map(resolvePluginConfig);
|
||||
|
||||
if (resolvedPlugins.length < plugins.length) {
|
||||
console.warn(
|
||||
'Warning: plugins list includes null or undefined elements, these will be ignored.',
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {import('./types.js').Config} */
|
||||
const globalOverrides = {};
|
||||
if (config.floatPrecision != null) {
|
||||
globalOverrides.floatPrecision = config.floatPrecision;
|
||||
}
|
||||
svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides);
|
||||
svgjs = stringifySvg(svgjs, config.js2svg);
|
||||
if (svgjs.data.length < prevResultSize) {
|
||||
input = svgjs.data;
|
||||
prevResultSize = svgjs.data.length;
|
||||
invokePlugins(ast, info, resolvedPlugins, null, globalOverrides);
|
||||
output = stringifySvg(ast, config.js2svg);
|
||||
if (output.length < prevResultSize) {
|
||||
input = output;
|
||||
prevResultSize = output.length;
|
||||
} else {
|
||||
if (config.datauri) {
|
||||
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
|
||||
}
|
||||
if (config.path != null) {
|
||||
svgjs.path = config.path;
|
||||
}
|
||||
return svgjs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return svgjs;
|
||||
if (config.datauri) {
|
||||
output = encodeSVGDatauri(output, config.datauri);
|
||||
}
|
||||
return {
|
||||
data: output,
|
||||
};
|
||||
};
|
||||
exports.optimize = optimize;
|
||||
|
||||
/**
|
||||
* The factory that creates a content item with the helper methods.
|
||||
*
|
||||
* @param {Object} data which is passed to jsAPI constructor
|
||||
* @returns {JSAPI} content item
|
||||
*/
|
||||
const createContentItem = (data) => {
|
||||
return new JSAPI(data);
|
||||
export {
|
||||
VERSION,
|
||||
builtinPlugins,
|
||||
mapNodesToParents,
|
||||
querySelector,
|
||||
querySelectorAll,
|
||||
_collections,
|
||||
};
|
||||
exports.createContentItem = createContentItem;
|
||||
|
||||
330
node_modules/svgo/lib/svgo/coa.js
generated
vendored
330
node_modules/svgo/lib/svgo/coa.js
generated
vendored
@@ -1,99 +1,100 @@
|
||||
'use strict';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import colors from 'picocolors';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { decodeSVGDatauri, encodeSVGDatauri } from './tools.js';
|
||||
import { loadConfig, optimize } from '../svgo-node.js';
|
||||
import { builtinPlugins } from '../builtin.js';
|
||||
import { SvgoParserError } from '../parser.js';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const colors = require('picocolors');
|
||||
const { loadConfig, optimize } = require('../svgo-node.js');
|
||||
const pluginsMap = require('../../plugins/plugins.js');
|
||||
const PKG = require('../../package.json');
|
||||
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
|
||||
|
||||
const regSVGFile = /\.svg$/i;
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const pkgPath = path.join(__dirname, '../../package.json');
|
||||
const PKG = JSON.parse(await fs.promises.readFile(pkgPath, 'utf-8'));
|
||||
|
||||
/**
|
||||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT.
|
||||
* @param {string} path
|
||||
*
|
||||
* @param {string} filePath
|
||||
*/
|
||||
function checkIsDir(path) {
|
||||
export function checkIsDir(filePath) {
|
||||
try {
|
||||
return fs.lstatSync(path).isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
return fs.lstatSync(filePath).isDirectory();
|
||||
} catch {
|
||||
return filePath.endsWith(path.sep);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function makeProgram(program) {
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
*/
|
||||
export default function makeProgram(program) {
|
||||
program
|
||||
.name(PKG.name)
|
||||
.description(PKG.description, {
|
||||
INPUT: 'Alias to --input',
|
||||
})
|
||||
.description(PKG.description)
|
||||
.version(PKG.version, '-v, --version')
|
||||
.arguments('[INPUT...]')
|
||||
.argument('[INPUT...]', 'Alias to --input')
|
||||
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
|
||||
.option('-s, --string <STRING>', 'Input SVG data string')
|
||||
.option(
|
||||
'-f, --folder <FOLDER>',
|
||||
'Input folder, optimize and rewrite all *.svg files'
|
||||
'Input folder, optimize and rewrite all *.svg files',
|
||||
)
|
||||
.option(
|
||||
'-o, --output <OUTPUT...>',
|
||||
'Output file or folder (by default the same as the input), "-" for STDOUT'
|
||||
'Output file or folder (by default the same as the input), "-" for STDOUT',
|
||||
)
|
||||
.option(
|
||||
'-p, --precision <INTEGER>',
|
||||
'Set number of digits in the fractional part, overrides plugins params'
|
||||
'Set number of digits in the fractional part, overrides plugins params',
|
||||
)
|
||||
.option(
|
||||
'--config <CONFIG>',
|
||||
'Custom config file, only .js, .mjs, and .cjs is supported',
|
||||
)
|
||||
.option('--config <CONFIG>', 'Custom config file, only .js is supported')
|
||||
.option(
|
||||
'--datauri <FORMAT>',
|
||||
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)'
|
||||
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)',
|
||||
)
|
||||
.option(
|
||||
'--multipass',
|
||||
'Pass over SVGs multiple times to ensure all optimizations are applied'
|
||||
'Pass over SVGs multiple times to ensure all optimizations are applied',
|
||||
)
|
||||
.option('--pretty', 'Make SVG pretty printed')
|
||||
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
|
||||
.option(
|
||||
'--eol <EOL>',
|
||||
'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.'
|
||||
'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.',
|
||||
)
|
||||
.option('--final-newline', 'Ensure SVG ends with a line break')
|
||||
.option(
|
||||
'-r, --recursive',
|
||||
"Use with '--folder'. Optimizes *.svg files in folders recursively."
|
||||
"Use with '--folder'. Optimizes *.svg files in folders recursively.",
|
||||
)
|
||||
.option(
|
||||
'--exclude <PATTERN...>',
|
||||
"Use with '--folder'. Exclude files matching regular expression pattern."
|
||||
"Use with '--folder'. Exclude files matching regular expression pattern.",
|
||||
)
|
||||
.option(
|
||||
'-q, --quiet',
|
||||
'Only output error messages, not regular status messages'
|
||||
'Only output error messages, not regular status messages',
|
||||
)
|
||||
.option('--show-plugins', 'Show available plugins and exit')
|
||||
// used by picocolors internally
|
||||
.option('--no-color', 'Output plain text without color')
|
||||
.action(action);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<string>} args
|
||||
* @param {any} opts
|
||||
* @param {import('commander').Command} command
|
||||
* @returns
|
||||
*/
|
||||
async function action(args, opts, command) {
|
||||
var input = opts.input || args;
|
||||
var output = opts.output;
|
||||
var config = {};
|
||||
|
||||
if (opts.precision != null) {
|
||||
const number = Number.parseInt(opts.precision, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '-p, --precision' argument must be an integer number"
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
opts.precision = number;
|
||||
}
|
||||
}
|
||||
const input = opts.input || args;
|
||||
let output = opts.output;
|
||||
/** @type {any} */
|
||||
let config = {};
|
||||
|
||||
if (opts.datauri != null) {
|
||||
if (
|
||||
@@ -102,7 +103,7 @@ async function action(args, opts, command) {
|
||||
opts.datauri !== 'unenc'
|
||||
) {
|
||||
console.error(
|
||||
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'"
|
||||
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -112,7 +113,7 @@ async function action(args, opts, command) {
|
||||
const number = Number.parseInt(opts.indent, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '--indent' argument must be an integer number"
|
||||
"error: option '--indent' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
@@ -122,7 +123,7 @@ async function action(args, opts, command) {
|
||||
|
||||
if (opts.eol != null && opts.eol !== 'lf' && opts.eol !== 'crlf') {
|
||||
console.error(
|
||||
"error: option '--eol' must have one of the following values: 'lf' or 'crlf'"
|
||||
"error: option '--eol' must have one of the following values: 'lf' or 'crlf'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -144,17 +145,12 @@ async function action(args, opts, command) {
|
||||
return command.help();
|
||||
}
|
||||
|
||||
if (
|
||||
typeof process == 'object' &&
|
||||
process.versions &&
|
||||
process.versions.node &&
|
||||
PKG &&
|
||||
PKG.engines.node
|
||||
) {
|
||||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
||||
if (process?.versions?.node && PKG.engines.node) {
|
||||
// @ts-expect-error We control this and ensure it is never null.
|
||||
const nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
||||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
|
||||
throw Error(
|
||||
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`
|
||||
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -177,13 +173,20 @@ async function action(args, opts, command) {
|
||||
|
||||
// --exclude
|
||||
config.exclude = opts.exclude
|
||||
? opts.exclude.map((pattern) => RegExp(pattern))
|
||||
? opts.exclude.map((/** @type {string} */ pattern) => RegExp(pattern))
|
||||
: [];
|
||||
|
||||
// --precision
|
||||
if (opts.precision != null) {
|
||||
var precision = Math.min(Math.max(0, opts.precision), 20);
|
||||
config.floatPrecision = precision;
|
||||
const number = Number.parseInt(opts.precision, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '-p, --precision' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
config.floatPrecision = Math.min(Math.max(0, number), 20);
|
||||
}
|
||||
}
|
||||
|
||||
// --multipass
|
||||
@@ -216,8 +219,8 @@ async function action(args, opts, command) {
|
||||
if (output) {
|
||||
if (input.length && input[0] != '-') {
|
||||
if (output.length == 1 && checkIsDir(output[0])) {
|
||||
var dir = output[0];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
const dir = output[0];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output[i] = checkIsDir(input[i])
|
||||
? input[i]
|
||||
: path.resolve(dir, path.basename(input[i]));
|
||||
@@ -238,8 +241,8 @@ async function action(args, opts, command) {
|
||||
|
||||
// --folder
|
||||
if (opts.folder) {
|
||||
var ouputFolder = (output && output[0]) || opts.folder;
|
||||
await optimizeFolder(config, opts.folder, ouputFolder);
|
||||
const outputFolder = (output && output[0]) || opts.folder;
|
||||
await optimizeFolder(config, opts.folder, outputFolder);
|
||||
}
|
||||
|
||||
// --input
|
||||
@@ -247,39 +250,39 @@ async function action(args, opts, command) {
|
||||
// STDIN
|
||||
if (input[0] === '-') {
|
||||
return new Promise((resolve, reject) => {
|
||||
var data = '',
|
||||
file = output[0];
|
||||
let data = '';
|
||||
const file = output[0];
|
||||
|
||||
process.stdin
|
||||
.on('data', (chunk) => (data += chunk))
|
||||
.once('end', () =>
|
||||
processSVGData(config, { input: 'string' }, data, file).then(
|
||||
resolve,
|
||||
reject
|
||||
)
|
||||
processSVGData(config, null, data, file).then(resolve, reject),
|
||||
);
|
||||
});
|
||||
// file
|
||||
} else {
|
||||
await Promise.all(
|
||||
input.map((file, n) => optimizeFile(config, file, output[n]))
|
||||
input.map((/** @type {string} */ file, /** @type {number} */ n) =>
|
||||
optimizeFile(config, file, output[n]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --string
|
||||
} else if (opts.string) {
|
||||
var data = decodeSVGDatauri(opts.string);
|
||||
const data = decodeSVGDatauri(opts.string);
|
||||
|
||||
return processSVGData(config, { input: 'string' }, data, output[0]);
|
||||
return processSVGData(config, null, data, output[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG files in a directory.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function optimizeFolder(config, dir, output) {
|
||||
if (!config.quiet) {
|
||||
@@ -292,15 +295,16 @@ function optimizeFolder(config, dir, output) {
|
||||
|
||||
/**
|
||||
* Process given files, take only SVG.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {ReadonlyArray<string>} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function processDirectory(config, dir, files, output) {
|
||||
// take only *.svg files, recursively if necessary
|
||||
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
const svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
|
||||
return svgFilesDescriptions.length
|
||||
? Promise.all(
|
||||
@@ -308,93 +312,105 @@ function processDirectory(config, dir, files, output) {
|
||||
optimizeFile(
|
||||
config,
|
||||
fileDescription.inputPath,
|
||||
fileDescription.outputPath
|
||||
)
|
||||
)
|
||||
fileDescription.outputPath,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Promise.reject(
|
||||
new Error(`No SVG files have been found in '${dir}' directory.`)
|
||||
new Error(`No SVG files have been found in '${dir}' directory.`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get svg files descriptions
|
||||
* @param {Object} config options
|
||||
* Get SVG files descriptions.
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {ReadonlyArray<string>} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Array}
|
||||
* @return {any[]}
|
||||
*/
|
||||
function getFilesDescriptions(config, dir, files, output) {
|
||||
const filesInThisFolder = files
|
||||
.filter(
|
||||
(name) =>
|
||||
regSVGFile.test(name) &&
|
||||
!config.exclude.some((regExclude) => regExclude.test(name))
|
||||
name.slice(-4).toLowerCase() === '.svg' &&
|
||||
!config.exclude.some((/** @type {RegExp} */ regExclude) =>
|
||||
regExclude.test(name),
|
||||
),
|
||||
)
|
||||
.map((name) => ({
|
||||
inputPath: path.resolve(dir, name),
|
||||
outputPath: path.resolve(output, name),
|
||||
}));
|
||||
|
||||
return config.recursive
|
||||
? [].concat(
|
||||
filesInThisFolder,
|
||||
files
|
||||
.filter((name) => checkIsDir(path.resolve(dir, name)))
|
||||
.map((subFolderName) => {
|
||||
const subFolderPath = path.resolve(dir, subFolderName);
|
||||
const subFolderFiles = fs.readdirSync(subFolderPath);
|
||||
const subFolderOutput = path.resolve(output, subFolderName);
|
||||
return getFilesDescriptions(
|
||||
config,
|
||||
subFolderPath,
|
||||
subFolderFiles,
|
||||
subFolderOutput
|
||||
);
|
||||
})
|
||||
.reduce((a, b) => [].concat(a, b), [])
|
||||
)
|
||||
: filesInThisFolder;
|
||||
if (!config.recursive) {
|
||||
return filesInThisFolder;
|
||||
}
|
||||
|
||||
return filesInThisFolder.concat(
|
||||
files
|
||||
.filter((name) => checkIsDir(path.resolve(dir, name)))
|
||||
.map((subFolderName) => {
|
||||
const subFolderPath = path.resolve(dir, subFolderName);
|
||||
const subFolderFiles = fs.readdirSync(subFolderPath);
|
||||
const subFolderOutput = path.resolve(output, subFolderName);
|
||||
return getFilesDescriptions(
|
||||
config,
|
||||
subFolderPath,
|
||||
subFolderFiles,
|
||||
subFolderOutput,
|
||||
);
|
||||
})
|
||||
.reduce((a, b) => a.concat(b), []),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SVG file and pass to processing.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} file
|
||||
* @param {string} output
|
||||
* @return {Promise}
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function optimizeFile(config, file, output) {
|
||||
return fs.promises.readFile(file, 'utf8').then(
|
||||
(data) =>
|
||||
processSVGData(config, { input: 'file', path: file }, data, output, file),
|
||||
(error) => checkOptimizeFileError(config, file, output, error)
|
||||
(data) => processSVGData(config, { path: file }, data, output, file),
|
||||
(error) => checkOptimizeFileError(config, file, output, error),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG data.
|
||||
* @param {Object} config options
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {?{ path: string }} info
|
||||
* @param {string} data SVG content to optimize
|
||||
* @param {string} output where to write optimized file
|
||||
* @param {string} [input] input file name (being used if output is a directory)
|
||||
* @return {Promise}
|
||||
* @param {any=} input input file name (being used if output is a directory)
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function processSVGData(config, info, data, output, input) {
|
||||
var startTime = Date.now(),
|
||||
prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
const startTime = Date.now();
|
||||
const prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
|
||||
const result = optimize(data, { ...config, ...info });
|
||||
if (result.modernError) {
|
||||
console.error(colors.red(result.modernError.toString()));
|
||||
process.exit(1);
|
||||
let result;
|
||||
try {
|
||||
result = optimize(data, { ...config, ...info });
|
||||
} catch (error) {
|
||||
if (error instanceof SvgoParserError) {
|
||||
console.error(colors.red(error.toString()));
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (config.datauri) {
|
||||
result.data = encodeSVGDatauri(result.data, config.datauri);
|
||||
}
|
||||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
|
||||
processingTime = Date.now() - startTime;
|
||||
const resultFileSize = Buffer.byteLength(result.data, 'utf8');
|
||||
const processingTime = Date.now() - startTime;
|
||||
|
||||
return writeOutput(input, output, result.data).then(
|
||||
function () {
|
||||
@@ -411,26 +427,27 @@ function processSVGData(config, info, data, output, input) {
|
||||
new Error(
|
||||
error.code === 'ENOTDIR'
|
||||
? `Error: output '${output}' is not a directory.`
|
||||
: error
|
||||
)
|
||||
)
|
||||
: error,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write result of an optimization.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output output file name. '-' for stdout
|
||||
* @param {string} data data to write
|
||||
* @return {Promise}
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function writeOutput(input, output, data) {
|
||||
async function writeOutput(input, output, data) {
|
||||
if (output == '-') {
|
||||
console.log(data);
|
||||
process.stdout.write(data);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(output), { recursive: true });
|
||||
await fs.promises.mkdir(path.dirname(output), { recursive: true });
|
||||
|
||||
return fs.promises
|
||||
.writeFile(output, data, 'utf8')
|
||||
@@ -438,7 +455,8 @@ function writeOutput(input, output, data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a time taken by optimization.
|
||||
* Write time taken to optimize.
|
||||
*
|
||||
* @param {number} time time in milliseconds.
|
||||
*/
|
||||
function printTimeInfo(time) {
|
||||
@@ -446,38 +464,40 @@ function printTimeInfo(time) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write optimizing information in human readable format.
|
||||
* Write optimizing stats in a human-readable format.
|
||||
*
|
||||
* @param {number} inBytes size before optimization.
|
||||
* @param {number} outBytes size after optimization.
|
||||
*/
|
||||
function printProfitInfo(inBytes, outBytes) {
|
||||
var profitPercents = 100 - (outBytes * 100) / inBytes;
|
||||
const profitPercent = 100 - (outBytes * 100) / inBytes;
|
||||
/** @type {[string, Function]} */
|
||||
const ui = profitPercent < 0 ? ['+', colors.red] : ['-', colors.green];
|
||||
|
||||
console.log(
|
||||
Math.round((inBytes / 1024) * 1000) / 1000 +
|
||||
' KiB' +
|
||||
(profitPercents < 0 ? ' + ' : ' - ') +
|
||||
colors.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
|
||||
' = ' +
|
||||
Math.round((outBytes / 1024) * 1000) / 1000 +
|
||||
' KiB'
|
||||
Math.round((inBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
ui[0],
|
||||
ui[1](Math.abs(Math.round(profitPercent * 10) / 10) + '%'),
|
||||
'=',
|
||||
Math.round((outBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for errors, if it's a dir optimize the dir.
|
||||
* @param {Object} config
|
||||
*
|
||||
* @param {any} config
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
* @param {Error & { code: string, path: string }} error
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function checkOptimizeFileError(config, input, output, error) {
|
||||
if (error.code == 'EISDIR') {
|
||||
return optimizeFolder(config, input, output);
|
||||
} else if (error.code == 'ENOENT') {
|
||||
return Promise.reject(
|
||||
new Error(`Error: no such file or directory '${error.path}'.`)
|
||||
new Error(`Error: no such file or directory '${error.path}'.`),
|
||||
);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
@@ -485,33 +505,29 @@ function checkOptimizeFileError(config, input, output, error) {
|
||||
|
||||
/**
|
||||
* Check for saving file error. If the output is a dir, then write file there.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {string} data
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
* @param {Error & { code: string }} error
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function checkWriteFileError(input, output, data, error) {
|
||||
if (error.code == 'EISDIR' && input) {
|
||||
return fs.promises.writeFile(
|
||||
path.resolve(output, path.basename(input)),
|
||||
data,
|
||||
'utf8'
|
||||
'utf8',
|
||||
);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show list of available plugins with short description.
|
||||
*/
|
||||
/** Show list of available plugins with short description. */
|
||||
function showAvailablePlugins() {
|
||||
const list = Object.entries(pluginsMap)
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([name, plugin]) => ` [ ${colors.green(name)} ] ${plugin.description}`)
|
||||
const list = builtinPlugins
|
||||
.map((plugin) => ` [ ${colors.green(plugin.name)} ] ${plugin.description}`)
|
||||
.join('\n');
|
||||
console.log('Currently available plugins:\n' + list);
|
||||
}
|
||||
|
||||
module.exports.checkIsDir = checkIsDir;
|
||||
|
||||
138
node_modules/svgo/lib/svgo/config.js
generated
vendored
138
node_modules/svgo/lib/svgo/config.js
generated
vendored
@@ -1,138 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const pluginsMap = require('../../plugins/plugins.js');
|
||||
|
||||
const pluginsOrder = [
|
||||
'removeDoctype',
|
||||
'removeXMLProcInst',
|
||||
'removeComments',
|
||||
'removeMetadata',
|
||||
'removeXMLNS',
|
||||
'removeEditorsNSData',
|
||||
'cleanupAttrs',
|
||||
'mergeStyles',
|
||||
'inlineStyles',
|
||||
'minifyStyles',
|
||||
'convertStyleToAttrs',
|
||||
'cleanupIDs',
|
||||
'prefixIds',
|
||||
'removeRasterImages',
|
||||
'removeUselessDefs',
|
||||
'cleanupNumericValues',
|
||||
'cleanupListOfValues',
|
||||
'convertColors',
|
||||
'removeUnknownsAndDefaults',
|
||||
'removeNonInheritableGroupAttrs',
|
||||
'removeUselessStrokeAndFill',
|
||||
'removeViewBox',
|
||||
'cleanupEnableBackground',
|
||||
'removeHiddenElems',
|
||||
'removeEmptyText',
|
||||
'convertShapeToPath',
|
||||
'convertEllipseToCircle',
|
||||
'moveElemsAttrsToGroup',
|
||||
'moveGroupAttrsToElems',
|
||||
'collapseGroups',
|
||||
'convertPathData',
|
||||
'convertTransform',
|
||||
'removeEmptyAttrs',
|
||||
'removeEmptyContainers',
|
||||
'mergePaths',
|
||||
'removeUnusedNS',
|
||||
'sortAttrs',
|
||||
'sortDefsChildren',
|
||||
'removeTitle',
|
||||
'removeDesc',
|
||||
'removeDimensions',
|
||||
'removeAttrs',
|
||||
'removeAttributesBySelector',
|
||||
'removeElementsByAttr',
|
||||
'addClassesToSVGElement',
|
||||
'removeStyleElement',
|
||||
'removeScriptElement',
|
||||
'addAttributesToSVGElement',
|
||||
'removeOffCanvasPaths',
|
||||
'reusePaths',
|
||||
];
|
||||
const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
|
||||
exports.defaultPlugins = defaultPlugins;
|
||||
|
||||
const extendDefaultPlugins = (plugins) => {
|
||||
console.warn(
|
||||
'\n"extendDefaultPlugins" utility is deprecated.\n' +
|
||||
'Use "preset-default" plugin with overrides instead.\n' +
|
||||
'For example:\n' +
|
||||
`{\n` +
|
||||
` name: 'preset-default',\n` +
|
||||
` params: {\n` +
|
||||
` overrides: {\n` +
|
||||
` // customize plugin options\n` +
|
||||
` convertShapeToPath: {\n` +
|
||||
` convertArcs: true\n` +
|
||||
` },\n` +
|
||||
` // disable plugins\n` +
|
||||
` convertPathData: false\n` +
|
||||
` }\n` +
|
||||
` }\n` +
|
||||
`}\n`
|
||||
);
|
||||
const extendedPlugins = pluginsOrder.map((name) => ({
|
||||
name,
|
||||
active: pluginsMap[name].active,
|
||||
}));
|
||||
for (const plugin of plugins) {
|
||||
const resolvedPlugin = resolvePluginConfig(plugin);
|
||||
const index = pluginsOrder.indexOf(resolvedPlugin.name);
|
||||
if (index === -1) {
|
||||
extendedPlugins.push(plugin);
|
||||
} else {
|
||||
extendedPlugins[index] = plugin;
|
||||
}
|
||||
}
|
||||
return extendedPlugins;
|
||||
};
|
||||
exports.extendDefaultPlugins = extendDefaultPlugins;
|
||||
|
||||
const resolvePluginConfig = (plugin) => {
|
||||
let configParams = {};
|
||||
if (typeof plugin === 'string') {
|
||||
// resolve builtin plugin specified as string
|
||||
const pluginConfig = pluginsMap[plugin];
|
||||
if (pluginConfig == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin}" specified.`);
|
||||
}
|
||||
return {
|
||||
...pluginConfig,
|
||||
name: plugin,
|
||||
active: true,
|
||||
params: { ...pluginConfig.params, ...configParams },
|
||||
};
|
||||
}
|
||||
if (typeof plugin === 'object' && plugin != null) {
|
||||
if (plugin.name == null) {
|
||||
throw Error(`Plugin name should be specified`);
|
||||
}
|
||||
if (plugin.fn) {
|
||||
// resolve custom plugin with implementation
|
||||
return {
|
||||
active: true,
|
||||
...plugin,
|
||||
params: { ...configParams, ...plugin.params },
|
||||
};
|
||||
} else {
|
||||
// resolve builtin plugin specified as object without implementation
|
||||
const pluginConfig = pluginsMap[plugin.name];
|
||||
if (pluginConfig == null) {
|
||||
throw Error(`Unknown builtin plugin "${plugin.name}" specified.`);
|
||||
}
|
||||
return {
|
||||
...pluginConfig,
|
||||
active: true,
|
||||
...plugin,
|
||||
params: { ...pluginConfig.params, ...configParams, ...plugin.params },
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.resolvePluginConfig = resolvePluginConfig;
|
||||
72
node_modules/svgo/lib/svgo/css-class-list.js
generated
vendored
72
node_modules/svgo/lib/svgo/css-class-list.js
generated
vendored
@@ -1,72 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var CSSClassList = function (node) {
|
||||
this.parentNode = node;
|
||||
this.classNames = new Set();
|
||||
const value = node.attributes.class;
|
||||
if (value != null) {
|
||||
this.addClassValueHandler();
|
||||
this.setClassValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
// attr.class.value
|
||||
|
||||
CSSClassList.prototype.addClassValueHandler = function () {
|
||||
Object.defineProperty(this.parentNode.attributes, 'class', {
|
||||
get: this.getClassValue.bind(this),
|
||||
set: this.setClassValue.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
};
|
||||
|
||||
CSSClassList.prototype.getClassValue = function () {
|
||||
var arrClassNames = Array.from(this.classNames);
|
||||
return arrClassNames.join(' ');
|
||||
};
|
||||
|
||||
CSSClassList.prototype.setClassValue = function (newValue) {
|
||||
if (typeof newValue === 'undefined') {
|
||||
this.classNames.clear();
|
||||
return;
|
||||
}
|
||||
var arrClassNames = newValue.split(' ');
|
||||
this.classNames = new Set(arrClassNames);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.add = function (/* variadic */) {
|
||||
this.addClassValueHandler();
|
||||
Object.values(arguments).forEach(this._addSingle.bind(this));
|
||||
};
|
||||
|
||||
CSSClassList.prototype._addSingle = function (className) {
|
||||
this.classNames.add(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.remove = function (/* variadic */) {
|
||||
this.addClassValueHandler();
|
||||
Object.values(arguments).forEach(this._removeSingle.bind(this));
|
||||
};
|
||||
|
||||
CSSClassList.prototype._removeSingle = function (className) {
|
||||
this.classNames.delete(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.item = function (index) {
|
||||
var arrClassNames = Array.from(this.classNames);
|
||||
return arrClassNames[index];
|
||||
};
|
||||
|
||||
CSSClassList.prototype.toggle = function (className, force) {
|
||||
if (this.contains(className) || force === false) {
|
||||
this.classNames.delete(className);
|
||||
}
|
||||
this.classNames.add(className);
|
||||
};
|
||||
|
||||
CSSClassList.prototype.contains = function (className) {
|
||||
return this.classNames.has(className);
|
||||
};
|
||||
|
||||
module.exports = CSSClassList;
|
||||
2
node_modules/svgo/lib/svgo/css-select-adapter.d.ts
generated
vendored
2
node_modules/svgo/lib/svgo/css-select-adapter.d.ts
generated
vendored
@@ -1,2 +0,0 @@
|
||||
declare let obj: any;
|
||||
export = obj;
|
||||
141
node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
141
node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
@@ -1,79 +1,46 @@
|
||||
'use strict';
|
||||
import { mapNodesToParents } from '../util/map-nodes-to-parents.js';
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['isTag']} */
|
||||
const isTag = (node) => {
|
||||
return node.type === 'element';
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['existsOne']} */
|
||||
const existsOne = (test, elems) => {
|
||||
return elems.some((elem) => {
|
||||
if (isTag(elem)) {
|
||||
return test(elem) || existsOne(test, getChildren(elem));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return isTag(elem) && (test(elem) || existsOne(test, getChildren(elem)));
|
||||
});
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getAttributeValue']} */
|
||||
const getAttributeValue = (elem, name) => {
|
||||
return elem.attributes[name];
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getChildren']} */
|
||||
const getChildren = (node) => {
|
||||
return node.children || [];
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getName']} */
|
||||
const getName = (elemAst) => {
|
||||
return elemAst.name;
|
||||
};
|
||||
|
||||
const getParent = (node) => {
|
||||
return node.parentNode || null;
|
||||
};
|
||||
|
||||
const getSiblings = (elem) => {
|
||||
var parent = getParent(elem);
|
||||
return parent ? getChildren(parent) : [];
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getText']} */
|
||||
const getText = (node) => {
|
||||
if (node.children[0].type === 'text' && node.children[0].type === 'cdata') {
|
||||
if (node.children[0].type === 'text' || node.children[0].type === 'cdata') {
|
||||
return node.children[0].value;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['hasAttrib']} */
|
||||
const hasAttrib = (elem, name) => {
|
||||
return elem.attributes[name] !== undefined;
|
||||
};
|
||||
|
||||
const removeSubsets = (nodes) => {
|
||||
let idx = nodes.length;
|
||||
let node;
|
||||
let ancestor;
|
||||
let replace;
|
||||
// Check if each node (or one of its ancestors) is already contained in the
|
||||
// array.
|
||||
while (--idx > -1) {
|
||||
node = ancestor = nodes[idx];
|
||||
// Temporarily remove the node under consideration
|
||||
nodes[idx] = null;
|
||||
replace = true;
|
||||
while (ancestor) {
|
||||
if (nodes.includes(ancestor)) {
|
||||
replace = false;
|
||||
nodes.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
ancestor = getParent(ancestor);
|
||||
}
|
||||
// If the node has been found to be unique, re-insert it.
|
||||
if (replace) {
|
||||
nodes[idx] = node;
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['findAll']} */
|
||||
const findAll = (test, elems) => {
|
||||
const result = [];
|
||||
for (const elem of elems) {
|
||||
@@ -87,6 +54,7 @@ const findAll = (test, elems) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['findOne']} */
|
||||
const findOne = (test, elems) => {
|
||||
for (const elem of elems) {
|
||||
if (isTag(elem)) {
|
||||
@@ -102,19 +70,74 @@ const findOne = (test, elems) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const svgoCssSelectAdapter = {
|
||||
isTag,
|
||||
existsOne,
|
||||
getAttributeValue,
|
||||
getChildren,
|
||||
getName,
|
||||
getParent,
|
||||
getSiblings,
|
||||
getText,
|
||||
hasAttrib,
|
||||
removeSubsets,
|
||||
findAll,
|
||||
findOne,
|
||||
};
|
||||
/**
|
||||
* @param {import('../types.js').XastParent} relativeNode
|
||||
* @param {Map<import('../types.js').XastNode, import('../types.js').XastParent>=} parents
|
||||
* @returns {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']}
|
||||
*/
|
||||
export function createAdapter(relativeNode, parents) {
|
||||
/** @type {Required<import('css-select').Options<import('../types.js').XastNode & { children?: any }, import('../types.js').XastElement>>['adapter']['getParent']} */
|
||||
const getParent = (node) => {
|
||||
if (!parents) {
|
||||
parents = mapNodesToParents(relativeNode);
|
||||
}
|
||||
|
||||
module.exports = svgoCssSelectAdapter;
|
||||
return parents.get(node) || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} elem
|
||||
* @returns {any}
|
||||
*/
|
||||
const getSiblings = (elem) => {
|
||||
const parent = getParent(elem);
|
||||
return parent ? getChildren(parent) : [];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {any} nodes
|
||||
* @returns {any}
|
||||
*/
|
||||
const removeSubsets = (nodes) => {
|
||||
let idx = nodes.length;
|
||||
let node;
|
||||
let ancestor;
|
||||
let replace;
|
||||
// Check if each node (or one of its ancestors) is already contained in the
|
||||
// array.
|
||||
while (--idx > -1) {
|
||||
node = ancestor = nodes[idx];
|
||||
// Temporarily remove the node under consideration
|
||||
nodes[idx] = null;
|
||||
replace = true;
|
||||
while (ancestor) {
|
||||
if (nodes.includes(ancestor)) {
|
||||
replace = false;
|
||||
nodes.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
ancestor = getParent(ancestor);
|
||||
}
|
||||
// If the node has been found to be unique, re-insert it.
|
||||
if (replace) {
|
||||
nodes[idx] = node;
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
return {
|
||||
isTag,
|
||||
existsOne,
|
||||
getAttributeValue,
|
||||
getChildren,
|
||||
getName,
|
||||
getParent,
|
||||
getSiblings,
|
||||
getText,
|
||||
hasAttrib,
|
||||
removeSubsets,
|
||||
findAll,
|
||||
findOne,
|
||||
};
|
||||
}
|
||||
|
||||
232
node_modules/svgo/lib/svgo/css-style-declaration.js
generated
vendored
232
node_modules/svgo/lib/svgo/css-style-declaration.js
generated
vendored
@@ -1,232 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var csstree = require('css-tree'),
|
||||
csstools = require('../css-tools');
|
||||
|
||||
var CSSStyleDeclaration = function (node) {
|
||||
this.parentNode = node;
|
||||
|
||||
this.properties = new Map();
|
||||
this.hasSynced = false;
|
||||
|
||||
this.styleValue = null;
|
||||
|
||||
this.parseError = false;
|
||||
const value = node.attributes.style;
|
||||
if (value != null) {
|
||||
this.addStyleValueHandler();
|
||||
this.setStyleValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
// attr.style.value
|
||||
|
||||
CSSStyleDeclaration.prototype.addStyleValueHandler = function () {
|
||||
Object.defineProperty(this.parentNode.attributes, 'style', {
|
||||
get: this.getStyleValue.bind(this),
|
||||
set: this.setStyleValue.bind(this),
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.getStyleValue = function () {
|
||||
return this.getCssText();
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype.setStyleValue = function (newValue) {
|
||||
this.properties.clear(); // reset all existing properties
|
||||
this.styleValue = newValue;
|
||||
this.hasSynced = false; // raw css changed
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._loadCssText = function () {
|
||||
if (this.hasSynced) {
|
||||
return;
|
||||
}
|
||||
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
|
||||
|
||||
if (!this.styleValue || this.styleValue.length === 0) {
|
||||
return;
|
||||
}
|
||||
var inlineCssStr = this.styleValue;
|
||||
|
||||
var declarations = {};
|
||||
try {
|
||||
declarations = csstree.parse(inlineCssStr, {
|
||||
context: 'declarationList',
|
||||
parseValue: false,
|
||||
});
|
||||
} catch (parseError) {
|
||||
this.parseError = parseError;
|
||||
return;
|
||||
}
|
||||
this.parseError = false;
|
||||
|
||||
var self = this;
|
||||
declarations.children.each(function (declaration) {
|
||||
try {
|
||||
var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
|
||||
self.setProperty(
|
||||
styleDeclaration.name,
|
||||
styleDeclaration.value,
|
||||
styleDeclaration.priority
|
||||
);
|
||||
} catch (styleError) {
|
||||
if (styleError.message !== 'Unknown node type: undefined') {
|
||||
self.parseError = styleError;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// only reads from properties
|
||||
|
||||
/**
|
||||
* Get the textual representation of the declaration block (equivalent to .cssText attribute).
|
||||
*
|
||||
* @return {string} Textual representation of the declaration block (empty string for no properties)
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getCssText = function () {
|
||||
var properties = this.getProperties();
|
||||
|
||||
if (this.parseError) {
|
||||
// in case of a parse error, pass through original styles
|
||||
return this.styleValue;
|
||||
}
|
||||
|
||||
var cssText = [];
|
||||
properties.forEach(function (property, propertyName) {
|
||||
var strImportant = property.priority === 'important' ? '!important' : '';
|
||||
cssText.push(
|
||||
propertyName.trim() + ':' + property.value.trim() + strImportant
|
||||
);
|
||||
});
|
||||
return cssText.join(';');
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._handleParseError = function () {
|
||||
if (this.parseError) {
|
||||
console.warn(
|
||||
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
|
||||
this.parseError
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
CSSStyleDeclaration.prototype._getProperty = function (propertyName) {
|
||||
if (typeof propertyName === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var property = properties.get(propertyName.trim());
|
||||
return property;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the optional priority, "important".
|
||||
*
|
||||
* @param {string} propertyName representing the property name to be checked.
|
||||
* @return {string} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) {
|
||||
var property = this._getProperty(propertyName);
|
||||
return property ? property.priority : '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the property value given a property name.
|
||||
*
|
||||
* @param {string} propertyName representing the property name to be checked.
|
||||
* @return {string} value containing the value of the property. If not set, returns the empty string.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) {
|
||||
var property = this._getProperty(propertyName);
|
||||
return property ? property.value : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a property name.
|
||||
*
|
||||
* @param {number} index of the node to be fetched. The index is zero-based.
|
||||
* @return {string} propertyName that is the name of the CSS property at the specified index.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.item = function (index) {
|
||||
if (typeof index === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
return Array.from(properties.keys())[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all properties of the node.
|
||||
*
|
||||
* @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.getProperties = function () {
|
||||
this._loadCssText();
|
||||
return this.properties;
|
||||
};
|
||||
|
||||
// writes to properties
|
||||
|
||||
/**
|
||||
* Remove a property from the CSS declaration block.
|
||||
*
|
||||
* @param {string} propertyName representing the property name to be removed.
|
||||
* @return {string} oldValue equal to the value of the CSS property before it was removed.
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.removeProperty = function (propertyName) {
|
||||
if (typeof propertyName === 'undefined') {
|
||||
throw Error('1 argument required, but only 0 present.');
|
||||
}
|
||||
|
||||
this.addStyleValueHandler();
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var oldValue = this.getPropertyValue(propertyName);
|
||||
properties.delete(propertyName.trim());
|
||||
return oldValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify an existing CSS property or creates a new CSS property in the declaration block.
|
||||
*
|
||||
* @param {string} propertyName representing the CSS property name to be modified.
|
||||
* @param {string} value containing the new property value. If not specified, treated as the empty string. value must not contain "!important" -- that should be set using the priority parameter.
|
||||
* @param {string} priority allowing the "important" CSS priority to be set. If not specified, treated as the empty string.
|
||||
* @return {{value: string, priority: string}}
|
||||
*/
|
||||
CSSStyleDeclaration.prototype.setProperty = function (
|
||||
propertyName,
|
||||
value,
|
||||
priority
|
||||
) {
|
||||
if (typeof propertyName === 'undefined') {
|
||||
throw Error('propertyName argument required, but only not present.');
|
||||
}
|
||||
|
||||
this.addStyleValueHandler();
|
||||
|
||||
var properties = this.getProperties();
|
||||
this._handleParseError();
|
||||
|
||||
var property = {
|
||||
value: value.trim(),
|
||||
priority: priority.trim(),
|
||||
};
|
||||
properties.set(propertyName.trim(), property);
|
||||
|
||||
return property;
|
||||
};
|
||||
|
||||
module.exports = CSSStyleDeclaration;
|
||||
2
node_modules/svgo/lib/svgo/jsAPI.d.ts
generated
vendored
2
node_modules/svgo/lib/svgo/jsAPI.d.ts
generated
vendored
@@ -1,2 +0,0 @@
|
||||
declare let obj: any;
|
||||
export = obj;
|
||||
443
node_modules/svgo/lib/svgo/jsAPI.js
generated
vendored
443
node_modules/svgo/lib/svgo/jsAPI.js
generated
vendored
@@ -1,443 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { selectAll, selectOne, is } = require('css-select');
|
||||
const svgoCssSelectAdapter = require('./css-select-adapter');
|
||||
const CSSClassList = require('./css-class-list');
|
||||
const CSSStyleDeclaration = require('./css-style-declaration');
|
||||
|
||||
/**
|
||||
* @type {(name: string) => { prefix: string, local: string }}
|
||||
*/
|
||||
const parseName = (name) => {
|
||||
if (name == null) {
|
||||
return {
|
||||
prefix: '',
|
||||
local: '',
|
||||
};
|
||||
}
|
||||
if (name === 'xmlns') {
|
||||
return {
|
||||
prefix: 'xmlns',
|
||||
local: '',
|
||||
};
|
||||
}
|
||||
const chunks = name.split(':');
|
||||
if (chunks.length === 1) {
|
||||
return {
|
||||
prefix: '',
|
||||
local: chunks[0],
|
||||
};
|
||||
}
|
||||
return {
|
||||
prefix: chunks[0],
|
||||
local: chunks[1],
|
||||
};
|
||||
};
|
||||
|
||||
var cssSelectOpts = {
|
||||
xmlMode: true,
|
||||
adapter: svgoCssSelectAdapter,
|
||||
};
|
||||
|
||||
const attrsHandler = {
|
||||
get: (attributes, name) => {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (attributes.hasOwnProperty(name)) {
|
||||
return {
|
||||
name,
|
||||
get value() {
|
||||
return attributes[name];
|
||||
},
|
||||
set value(value) {
|
||||
attributes[name] = value;
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
set: (attributes, name, attr) => {
|
||||
attributes[name] = attr.value;
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
var JSAPI = function (data, parentNode) {
|
||||
Object.assign(this, data);
|
||||
if (this.type === 'element') {
|
||||
if (this.attributes == null) {
|
||||
this.attributes = {};
|
||||
}
|
||||
if (this.children == null) {
|
||||
this.children = [];
|
||||
}
|
||||
Object.defineProperty(this, 'class', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: new CSSClassList(this),
|
||||
});
|
||||
Object.defineProperty(this, 'style', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: new CSSStyleDeclaration(this),
|
||||
});
|
||||
Object.defineProperty(this, 'parentNode', {
|
||||
writable: true,
|
||||
value: parentNode,
|
||||
});
|
||||
|
||||
// temporary attrs polyfill
|
||||
// TODO remove after migration
|
||||
const element = this;
|
||||
Object.defineProperty(this, 'attrs', {
|
||||
configurable: true,
|
||||
get() {
|
||||
return new Proxy(element.attributes, attrsHandler);
|
||||
},
|
||||
set(value) {
|
||||
const newAttributes = {};
|
||||
for (const attr of Object.values(value)) {
|
||||
newAttributes[attr.name] = attr.value;
|
||||
}
|
||||
element.attributes = newAttributes;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
module.exports = JSAPI;
|
||||
|
||||
/**
|
||||
* Perform a deep clone of this node.
|
||||
*
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.clone = function () {
|
||||
const { children, ...nodeData } = this;
|
||||
// Deep-clone node data.
|
||||
const clonedNode = new JSAPI(JSON.parse(JSON.stringify(nodeData)), null);
|
||||
if (children) {
|
||||
clonedNode.children = children.map((child) => {
|
||||
const clonedChild = child.clone();
|
||||
clonedChild.parentNode = clonedNode;
|
||||
return clonedChild;
|
||||
});
|
||||
}
|
||||
return clonedNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if item is an element
|
||||
* (any, with a specific name or in a names array).
|
||||
*
|
||||
* @param {String|Array} [param] element name or names arrays
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.isElem = function (param) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (param == null) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(param)) {
|
||||
return param.includes(this.name);
|
||||
}
|
||||
return this.name === param;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renames an element
|
||||
*
|
||||
* @param {String} name new element name
|
||||
* @return {Object} element
|
||||
*/
|
||||
JSAPI.prototype.renameElem = function (name) {
|
||||
if (name && typeof name === 'string') this.name = name;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element is empty.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.isEmpty = function () {
|
||||
return !this.children || !this.children.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the closest ancestor of the current element.
|
||||
* @param elemName
|
||||
*
|
||||
* @return {?Object}
|
||||
*/
|
||||
JSAPI.prototype.closestElem = function (elemName) {
|
||||
var elem = this;
|
||||
|
||||
while ((elem = elem.parentNode) && !elem.isElem(elemName));
|
||||
|
||||
return elem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes children by removing elements and/or adding new elements.
|
||||
*
|
||||
* @param {Number} start Index at which to start changing the children.
|
||||
* @param {Number} n Number of elements to remove.
|
||||
* @param {Array|Object} [insertion] Elements to add to the children.
|
||||
* @return {Array} Removed elements.
|
||||
*/
|
||||
JSAPI.prototype.spliceContent = function (start, n, insertion) {
|
||||
if (arguments.length < 2) return [];
|
||||
|
||||
if (!Array.isArray(insertion))
|
||||
insertion = Array.apply(null, arguments).slice(2);
|
||||
|
||||
insertion.forEach(function (inner) {
|
||||
inner.parentNode = this;
|
||||
}, this);
|
||||
|
||||
return this.children.splice.apply(
|
||||
this.children,
|
||||
[start, n].concat(insertion)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String} [name] attribute name
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.hasAttr = function (name, val) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (Object.keys(this.attributes).length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
return true;
|
||||
}
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (this.attributes.hasOwnProperty(name) === false) {
|
||||
return false;
|
||||
}
|
||||
if (val !== undefined) {
|
||||
return this.attributes[name] === val.toString();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if element has an attribute by local name
|
||||
* (any, or by name or by name + value).
|
||||
*
|
||||
* @param {String} [localName] local attribute name
|
||||
* @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored)
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.hasAttrLocal = function (localName, val) {
|
||||
if (!this.attrs || !Object.keys(this.attrs).length) return false;
|
||||
|
||||
if (!arguments.length) return !!this.attrs;
|
||||
|
||||
var callback;
|
||||
|
||||
switch (val != null && val.constructor && val.constructor.name) {
|
||||
case 'Number': // same as String
|
||||
case 'String':
|
||||
callback = stringValueTest;
|
||||
break;
|
||||
case 'RegExp':
|
||||
callback = regexpValueTest;
|
||||
break;
|
||||
case 'Function':
|
||||
callback = funcValueTest;
|
||||
break;
|
||||
default:
|
||||
callback = nameTest;
|
||||
}
|
||||
return this.someAttr(callback);
|
||||
|
||||
function nameTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName;
|
||||
}
|
||||
|
||||
function stringValueTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName && val == attr.value;
|
||||
}
|
||||
|
||||
function regexpValueTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName && val.test(attr.value);
|
||||
}
|
||||
|
||||
function funcValueTest(attr) {
|
||||
const { local } = parseName(attr.name);
|
||||
return local === localName && val(attr.value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a specific attribute from an element
|
||||
* (by name or name + value).
|
||||
*
|
||||
* @param {String} name attribute name
|
||||
* @param {String} [val] attribute value (will be toString()'ed)
|
||||
* @return {Object|Undefined}
|
||||
*/
|
||||
JSAPI.prototype.attr = function (name, val) {
|
||||
if (this.hasAttr(name, val)) {
|
||||
return this.attrs[name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get computed attribute value from an element
|
||||
*
|
||||
* @param {String} name attribute name
|
||||
* @return {Object|Undefined}
|
||||
*/
|
||||
JSAPI.prototype.computedAttr = function (name, val) {
|
||||
if (!arguments.length) return;
|
||||
|
||||
for (
|
||||
var elem = this;
|
||||
elem && (!elem.hasAttr(name) || !elem.attributes[name]);
|
||||
elem = elem.parentNode
|
||||
);
|
||||
|
||||
if (val != null) {
|
||||
return elem ? elem.hasAttr(name, val) : false;
|
||||
} else if (elem && elem.hasAttr(name)) {
|
||||
return elem.attributes[name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a specific attribute.
|
||||
*
|
||||
* @param {String|Array} name attribute name
|
||||
* @param {String} [val] attribute value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
JSAPI.prototype.removeAttr = function (name, val) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (arguments.length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(name)) {
|
||||
for (const nameItem of name) {
|
||||
this.removeAttr(nameItem, val);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (this.hasAttr(name, val) === false) {
|
||||
return false;
|
||||
}
|
||||
delete this.attributes[name];
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add attribute.
|
||||
*
|
||||
* @param {Object} [attr={}] attribute object
|
||||
* @return {Object|Boolean} created attribute or false if no attr was passed in
|
||||
*/
|
||||
JSAPI.prototype.addAttr = function (attr) {
|
||||
attr = attr || {};
|
||||
|
||||
if (attr.name === undefined) return false;
|
||||
|
||||
this.attributes[attr.name] = attr.value;
|
||||
|
||||
if (attr.name === 'class') {
|
||||
// newly added class attribute
|
||||
this.class.addClassValueHandler();
|
||||
}
|
||||
|
||||
if (attr.name === 'style') {
|
||||
// newly added style attribute
|
||||
this.style.addStyleValueHandler();
|
||||
}
|
||||
|
||||
return this.attrs[attr.name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates over all attributes.
|
||||
*
|
||||
* @param {Function} callback callback
|
||||
* @param {Object} [context] callback context
|
||||
* @return {Boolean} false if there are no any attributes
|
||||
*/
|
||||
JSAPI.prototype.eachAttr = function (callback, context) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
if (callback == null) {
|
||||
return false;
|
||||
}
|
||||
for (const attr of Object.values(this.attrs)) {
|
||||
callback.call(context, attr);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests whether some attribute passes the test.
|
||||
*
|
||||
* @param {Function} callback callback
|
||||
* @param {Object} [context] callback context
|
||||
* @return {Boolean} false if there are no any attributes
|
||||
*/
|
||||
JSAPI.prototype.someAttr = function (callback, context) {
|
||||
if (this.type !== 'element') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const attr of Object.values(this.attrs)) {
|
||||
if (callback.call(context, attr)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a string of CSS selectors against the element and returns matched elements.
|
||||
*
|
||||
* @param {String} selectors CSS selector(s) string
|
||||
* @return {Array} null if no elements matched
|
||||
*/
|
||||
JSAPI.prototype.querySelectorAll = function (selectors) {
|
||||
var matchedEls = selectAll(selectors, this, cssSelectOpts);
|
||||
|
||||
return matchedEls.length > 0 ? matchedEls : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a string of CSS selectors against the element and returns only the first matched element.
|
||||
*
|
||||
* @param {String} selectors CSS selector(s) string
|
||||
* @return {Array} null if no element matched
|
||||
*/
|
||||
JSAPI.prototype.querySelector = function (selectors) {
|
||||
return selectOne(selectors, this, cssSelectOpts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if a selector matches a given element.
|
||||
*
|
||||
* @param {String} selector CSS selector string
|
||||
* @return {Boolean} true if element would be selected by selector string, false if it does not
|
||||
*/
|
||||
JSAPI.prototype.matches = function (selector) {
|
||||
return is(this, selector, cssSelectOpts);
|
||||
};
|
||||
102
node_modules/svgo/lib/svgo/plugins.js
generated
vendored
102
node_modules/svgo/lib/svgo/plugins.js
generated
vendored
@@ -1,85 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
const { visit } = require('../xast.js');
|
||||
import { visit } from '../util/visit.js';
|
||||
|
||||
/**
|
||||
* Plugins engine.
|
||||
*
|
||||
* @module plugins
|
||||
*
|
||||
* @param {Object} ast input ast
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins object from config
|
||||
* @return {Object} output ast
|
||||
* @param {import('../types.js').XastNode} ast Input AST.
|
||||
* @param {any} info Extra information.
|
||||
* @param {ReadonlyArray<any>} plugins Plugins property from config.
|
||||
* @param {any} overrides
|
||||
* @param {any} globalOverrides
|
||||
*/
|
||||
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => {
|
||||
export const invokePlugins = (
|
||||
ast,
|
||||
info,
|
||||
plugins,
|
||||
overrides,
|
||||
globalOverrides,
|
||||
) => {
|
||||
for (const plugin of plugins) {
|
||||
const override = overrides == null ? null : overrides[plugin.name];
|
||||
const override = overrides?.[plugin.name];
|
||||
if (override === false) {
|
||||
continue;
|
||||
}
|
||||
const params = { ...plugin.params, ...globalOverrides, ...override };
|
||||
|
||||
if (plugin.type === 'perItem') {
|
||||
ast = perItem(ast, info, plugin, params);
|
||||
}
|
||||
if (plugin.type === 'perItemReverse') {
|
||||
ast = perItem(ast, info, plugin, params, true);
|
||||
}
|
||||
if (plugin.type === 'full') {
|
||||
if (plugin.active) {
|
||||
ast = plugin.fn(ast, params, info);
|
||||
}
|
||||
}
|
||||
if (plugin.type === 'visitor') {
|
||||
if (plugin.active) {
|
||||
const visitor = plugin.fn(ast, params, info);
|
||||
if (visitor != null) {
|
||||
visit(ast, visitor);
|
||||
}
|
||||
}
|
||||
const visitor = plugin.fn(ast, params, info);
|
||||
if (visitor != null) {
|
||||
visit(ast, visitor);
|
||||
}
|
||||
}
|
||||
return ast;
|
||||
};
|
||||
exports.invokePlugins = invokePlugins;
|
||||
|
||||
/**
|
||||
* Direct or reverse per-item loop.
|
||||
*
|
||||
* @param {Object} data input data
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins list to process
|
||||
* @param {boolean} [reverse] reverse pass?
|
||||
* @return {Object} output data
|
||||
* @template {string} T
|
||||
* @param {{ name: T, plugins: ReadonlyArray<import('../types.js').BuiltinPlugin<string, any>> }} arg0
|
||||
* @returns {import('../types.js').BuiltinPluginOrPreset<T, any>}
|
||||
*/
|
||||
function perItem(data, info, plugin, params, reverse) {
|
||||
function monkeys(items) {
|
||||
items.children = items.children.filter(function (item) {
|
||||
// reverse pass
|
||||
if (reverse && item.children) {
|
||||
monkeys(item);
|
||||
}
|
||||
// main filter
|
||||
let kept = true;
|
||||
if (plugin.active) {
|
||||
kept = plugin.fn(item, params, info) !== false;
|
||||
}
|
||||
// direct pass
|
||||
if (!reverse && item.children) {
|
||||
monkeys(item);
|
||||
}
|
||||
return kept;
|
||||
});
|
||||
return items;
|
||||
}
|
||||
return monkeys(data);
|
||||
}
|
||||
|
||||
const createPreset = ({ name, plugins }) => {
|
||||
export const createPreset = ({ name, plugins }) => {
|
||||
return {
|
||||
name,
|
||||
type: 'full',
|
||||
isPreset: true,
|
||||
plugins: Object.freeze(plugins),
|
||||
fn: (ast, params, info) => {
|
||||
const { floatPrecision, overrides } = params;
|
||||
const globalOverrides = {};
|
||||
@@ -87,23 +49,23 @@ const createPreset = ({ name, plugins }) => {
|
||||
globalOverrides.floatPrecision = floatPrecision;
|
||||
}
|
||||
if (overrides) {
|
||||
for (const [pluginName, override] of Object.entries(overrides)) {
|
||||
if (override === true) {
|
||||
const pluginNames = plugins.map(({ name }) => name);
|
||||
for (const pluginName of Object.keys(overrides)) {
|
||||
if (!pluginNames.includes(pluginName)) {
|
||||
console.warn(
|
||||
`You are trying to enable ${pluginName} which is not part of preset.\n` +
|
||||
`Try to put it before or after preset, for example\n\n` +
|
||||
`You are trying to configure ${pluginName} which is not part of ${name}.\n` +
|
||||
`Try to put it before or after, for example\n\n` +
|
||||
`plugins: [\n` +
|
||||
` {\n` +
|
||||
` name: 'preset-default',\n` +
|
||||
` name: '${name}',\n` +
|
||||
` },\n` +
|
||||
` 'cleanupListOfValues'\n` +
|
||||
`]\n`
|
||||
` '${pluginName}'\n` +
|
||||
`]\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invokePlugins(ast, info, plugins, overrides, globalOverrides);
|
||||
invokePlugins(ast, info, plugins, overrides, globalOverrides);
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.createPreset = createPreset;
|
||||
|
||||
184
node_modules/svgo/lib/svgo/tools.js
generated
vendored
184
node_modules/svgo/lib/svgo/tools.js
generated
vendored
@@ -1,16 +1,25 @@
|
||||
'use strict';
|
||||
import { attrsGroups, referencesProps } from '../../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../types').PathDataCommand} PathDataCommand
|
||||
* @typedef CleanupOutDataParams
|
||||
* @property {boolean=} noSpaceAfterFlags
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} negativeExtraSpace
|
||||
*/
|
||||
|
||||
const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g;
|
||||
const regReferencesHref = /^#(.+?)$/;
|
||||
const regReferencesBegin = /(\w+)\.[a-zA-Z]/;
|
||||
|
||||
/**
|
||||
* Encode plain SVG data string into Data URI string.
|
||||
*
|
||||
* @type {(str: string, type?: 'base64' | 'enc' | 'unenc') => string}
|
||||
* @param {string} str
|
||||
* @param {import('../types.js').DataUri=} type
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.encodeSVGDatauri = (str, type) => {
|
||||
var prefix = 'data:image/svg+xml';
|
||||
export const encodeSVGDatauri = (str, type) => {
|
||||
let prefix = 'data:image/svg+xml';
|
||||
if (!type || type === 'base64') {
|
||||
// base64
|
||||
prefix += ';base64,';
|
||||
@@ -28,16 +37,19 @@ exports.encodeSVGDatauri = (str, type) => {
|
||||
/**
|
||||
* Decode SVG Data URI string into plain SVG string.
|
||||
*
|
||||
* @type {(str: string) => string}
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.decodeSVGDatauri = (str) => {
|
||||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
var match = regexp.exec(str);
|
||||
export const decodeSVGDatauri = (str) => {
|
||||
const regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
const match = regexp.exec(str);
|
||||
|
||||
// plain string
|
||||
if (!match) return str;
|
||||
if (!match) {
|
||||
return str;
|
||||
}
|
||||
|
||||
var data = match[3];
|
||||
const data = match[3];
|
||||
|
||||
if (match[2]) {
|
||||
// base64
|
||||
@@ -52,28 +64,21 @@ exports.decodeSVGDatauri = (str) => {
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* noSpaceAfterFlags?: boolean,
|
||||
* leadingZero?: boolean,
|
||||
* negativeExtraSpace?: boolean
|
||||
* }} CleanupOutDataParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a row of numbers to an optimized string view.
|
||||
*
|
||||
* @example
|
||||
* [0, -1, .5, .5] → "0-1 .5.5"
|
||||
*
|
||||
* @type {(data: Array<number>, params: CleanupOutDataParams, command?: PathDataCommand) => string}
|
||||
* @param {ReadonlyArray<number>} data
|
||||
* @param {CleanupOutDataParams} params
|
||||
* @param {import('../types.js').PathDataCommand=} command
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.cleanupOutData = (data, params, command) => {
|
||||
export const cleanupOutData = (data, params, command) => {
|
||||
let str = '';
|
||||
let delimiter;
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
/** @type {number} */
|
||||
let prev;
|
||||
|
||||
data.forEach((item, i) => {
|
||||
@@ -81,13 +86,17 @@ exports.cleanupOutData = (data, params, command) => {
|
||||
delimiter = ' ';
|
||||
|
||||
// no extra space in front of first number
|
||||
if (i == 0) delimiter = '';
|
||||
if (i == 0) {
|
||||
delimiter = '';
|
||||
}
|
||||
|
||||
// no extra space after 'arcto' command flags(large-arc and sweep flags)
|
||||
// no extra space after arc command flags (large-arc and sweep flags)
|
||||
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
|
||||
var pos = i % 7;
|
||||
if (pos == 4 || pos == 5) delimiter = '';
|
||||
const pos = i % 7;
|
||||
if (pos == 4 || pos == 5) {
|
||||
delimiter = '';
|
||||
}
|
||||
}
|
||||
|
||||
// remove floating-point numbers leading zeros
|
||||
@@ -116,22 +125,117 @@ exports.cleanupOutData = (data, params, command) => {
|
||||
/**
|
||||
* Remove floating-point numbers leading zero.
|
||||
*
|
||||
* @param {number} value
|
||||
* @returns {string}
|
||||
* @example
|
||||
* 0.5 → .5
|
||||
*
|
||||
* @example
|
||||
* -0.5 → -.5
|
||||
*
|
||||
* @type {(num: number) => string}
|
||||
*/
|
||||
const removeLeadingZero = (num) => {
|
||||
var strNum = num.toString();
|
||||
export const removeLeadingZero = (value) => {
|
||||
const strValue = value.toString();
|
||||
|
||||
if (0 < num && num < 1 && strNum.charAt(0) === '0') {
|
||||
strNum = strNum.slice(1);
|
||||
} else if (-1 < num && num < 0 && strNum.charAt(1) === '0') {
|
||||
strNum = strNum.charAt(0) + strNum.slice(2);
|
||||
if (0 < value && value < 1 && strValue.startsWith('0')) {
|
||||
return strValue.slice(1);
|
||||
}
|
||||
return strNum;
|
||||
|
||||
if (-1 < value && value < 0 && strValue[1] === '0') {
|
||||
return strValue[0] + strValue.slice(2);
|
||||
}
|
||||
|
||||
return strValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* If the current node contains any scripts. This does not check parents or
|
||||
* children of the node, only the properties and attributes of the node itself.
|
||||
*
|
||||
* @param {import('../types.js').XastElement} node Current node to check against.
|
||||
* @returns {boolean} If the current node contains scripts.
|
||||
*/
|
||||
export const hasScripts = (node) => {
|
||||
if (node.name === 'script' && node.children.length !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.name === 'a') {
|
||||
const hasJsLinks = Object.entries(node.attributes).some(
|
||||
([attrKey, attrValue]) =>
|
||||
(attrKey === 'href' || attrKey.endsWith(':href')) &&
|
||||
attrValue != null &&
|
||||
attrValue.trimStart().startsWith('javascript:'),
|
||||
);
|
||||
|
||||
if (hasJsLinks) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const eventAttrs = [
|
||||
...attrsGroups.animationEvent,
|
||||
...attrsGroups.documentEvent,
|
||||
...attrsGroups.documentElementEvent,
|
||||
...attrsGroups.globalEvent,
|
||||
...attrsGroups.graphicalEvent,
|
||||
];
|
||||
|
||||
return eventAttrs.some((attr) => node.attributes[attr] != null);
|
||||
};
|
||||
|
||||
/**
|
||||
* For example, a string that contains one or more of following would match and
|
||||
* return true:
|
||||
*
|
||||
* * `url(#gradient001)`
|
||||
* * `url('#gradient001')`
|
||||
*
|
||||
* @param {string} body
|
||||
* @returns {boolean} If the given string includes a URL reference.
|
||||
*/
|
||||
export const includesUrlReference = (body) => {
|
||||
return new RegExp(regReferencesUrl).test(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} attribute
|
||||
* @param {string} value
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export const findReferences = (attribute, value) => {
|
||||
const results = [];
|
||||
|
||||
if (referencesProps.has(attribute)) {
|
||||
const matches = value.matchAll(regReferencesUrl);
|
||||
for (const match of matches) {
|
||||
results.push(match[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute === 'href' || attribute.endsWith(':href')) {
|
||||
const match = regReferencesHref.exec(value);
|
||||
if (match != null) {
|
||||
results.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute === 'begin') {
|
||||
const match = regReferencesBegin.exec(value);
|
||||
if (match != null) {
|
||||
results.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return results.map((body) => decodeURI(body));
|
||||
};
|
||||
|
||||
/**
|
||||
* Does the same as {@link Number.toFixed} but without casting
|
||||
* the return value to a string.
|
||||
*
|
||||
* @param {number} num
|
||||
* @param {number} precision
|
||||
* @returns {number}
|
||||
*/
|
||||
export const toFixed = (num, precision) => {
|
||||
const pow = 10 ** precision;
|
||||
return Math.round(num * pow) / pow;
|
||||
};
|
||||
exports.removeLeadingZero = removeLeadingZero;
|
||||
|
||||
1
node_modules/svgo/lib/types.js
generated
vendored
Normal file
1
node_modules/svgo/lib/types.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
208
node_modules/svgo/lib/types.ts
generated
vendored
208
node_modules/svgo/lib/types.ts
generated
vendored
@@ -1,3 +1,154 @@
|
||||
import { AddAttributesToSVGElementParams } from '../plugins/addAttributesToSVGElement.js';
|
||||
import { AddClassesToSVGElementParams } from '../plugins/addClassesToSVGElement.js';
|
||||
import { CleanupAttrsParams } from '../plugins/cleanupAttrs.js';
|
||||
import { CleanupIdsParams } from '../plugins/cleanupIds.js';
|
||||
import { CleanupListOfValuesParams } from '../plugins/cleanupListOfValues.js';
|
||||
import { CleanupNumericValuesParams } from '../plugins/cleanupNumericValues.js';
|
||||
import { ConvertColorsParams } from '../plugins/convertColors.js';
|
||||
import { ConvertPathDataParams } from '../plugins/convertPathData.js';
|
||||
import { ConvertShapeToPathParams } from '../plugins/convertShapeToPath.js';
|
||||
import { ConvertStyleToAttrsParams } from '../plugins/convertStyleToAttrs.js';
|
||||
import { ConvertTransformParams } from '../plugins/convertTransform.js';
|
||||
import { InlineStylesParams } from '../plugins/inlineStyles.js';
|
||||
import { MergePathsParams } from '../plugins/mergePaths.js';
|
||||
import { MinifyStylesParams } from '../plugins/minifyStyles.js';
|
||||
import { PrefixIdsParams } from '../plugins/prefixIds.js';
|
||||
import { RemoveAttrsParams } from '../plugins/removeAttrs.js';
|
||||
import { RemoveCommentsParams } from '../plugins/removeComments.js';
|
||||
import { RemoveDeprecatedAttrsParams } from '../plugins/removeDeprecatedAttrs.js';
|
||||
import { RemoveDescParams } from '../plugins/removeDesc.js';
|
||||
import { RemoveEditorsNSDataParams } from '../plugins/removeEditorsNSData.js';
|
||||
import { RemoveElementsByAttrParams } from '../plugins/removeElementsByAttr.js';
|
||||
import { RemoveEmptyTextParams } from '../plugins/removeEmptyText.js';
|
||||
import { RemoveHiddenElemsParams } from '../plugins/removeHiddenElems.js';
|
||||
import { RemoveUnknownsAndDefaultsParams } from '../plugins/removeUnknownsAndDefaults.js';
|
||||
import { RemoveUselessStrokeAndFillParams } from '../plugins/removeUselessStrokeAndFill.js';
|
||||
import { RemoveXlinkParams } from '../plugins/removeXlink.js';
|
||||
import { SortAttrsParams } from '../plugins/sortAttrs.js';
|
||||
|
||||
export type DefaultPlugins = {
|
||||
cleanupAttrs: CleanupAttrsParams;
|
||||
cleanupEnableBackground: null;
|
||||
cleanupIds: CleanupIdsParams;
|
||||
cleanupNumericValues: CleanupNumericValuesParams;
|
||||
collapseGroups: null;
|
||||
convertColors: ConvertColorsParams;
|
||||
convertEllipseToCircle: null;
|
||||
convertPathData: ConvertPathDataParams;
|
||||
convertShapeToPath: ConvertShapeToPathParams;
|
||||
convertTransform: ConvertTransformParams;
|
||||
mergeStyles: null;
|
||||
inlineStyles: InlineStylesParams;
|
||||
mergePaths: MergePathsParams;
|
||||
minifyStyles: MinifyStylesParams;
|
||||
moveElemsAttrsToGroup: null;
|
||||
moveGroupAttrsToElems: null;
|
||||
removeComments: RemoveCommentsParams;
|
||||
removeDeprecatedAttrs: RemoveDeprecatedAttrsParams;
|
||||
removeDesc: RemoveDescParams;
|
||||
removeDoctype: null;
|
||||
removeEditorsNSData: RemoveEditorsNSDataParams;
|
||||
removeEmptyAttrs: null;
|
||||
removeEmptyContainers: null;
|
||||
removeEmptyText: RemoveEmptyTextParams;
|
||||
removeHiddenElems: RemoveHiddenElemsParams;
|
||||
removeMetadata: null;
|
||||
removeNonInheritableGroupAttrs: null;
|
||||
removeUnknownsAndDefaults: RemoveUnknownsAndDefaultsParams;
|
||||
removeUnusedNS: null;
|
||||
removeUselessDefs: null;
|
||||
removeUselessStrokeAndFill: RemoveUselessStrokeAndFillParams;
|
||||
removeXMLProcInst: null;
|
||||
sortAttrs: SortAttrsParams;
|
||||
sortDefsChildren: null;
|
||||
};
|
||||
|
||||
export type PresetDefaultOverrides = {
|
||||
[Name in keyof DefaultPlugins]?: DefaultPlugins[Name] | false;
|
||||
};
|
||||
|
||||
export type BuiltinsWithOptionalParams = DefaultPlugins & {
|
||||
'preset-default': {
|
||||
floatPrecision?: number;
|
||||
/**
|
||||
* All default plugins can be customized or disabled here
|
||||
* for example
|
||||
* {
|
||||
* sortAttrs: { xmlnsOrder: "alphabetical" },
|
||||
* cleanupAttrs: false,
|
||||
* }
|
||||
*/
|
||||
overrides?: PresetDefaultOverrides;
|
||||
};
|
||||
cleanupListOfValues: CleanupListOfValuesParams;
|
||||
convertOneStopGradients: null;
|
||||
convertStyleToAttrs: ConvertStyleToAttrsParams;
|
||||
prefixIds: PrefixIdsParams;
|
||||
removeDimensions: null;
|
||||
removeOffCanvasPaths: null;
|
||||
removeRasterImages: null;
|
||||
removeScripts: null;
|
||||
removeStyleElement: null;
|
||||
removeTitle: null;
|
||||
removeViewBox: null;
|
||||
removeXlink: RemoveXlinkParams;
|
||||
removeXMLNS: null;
|
||||
reusePaths: null;
|
||||
};
|
||||
|
||||
export type BuiltinsWithRequiredParams = {
|
||||
addAttributesToSVGElement: AddAttributesToSVGElementParams;
|
||||
addClassesToSVGElement: AddClassesToSVGElementParams;
|
||||
removeAttributesBySelector: any;
|
||||
removeAttrs: RemoveAttrsParams;
|
||||
removeElementsByAttr: RemoveElementsByAttrParams;
|
||||
};
|
||||
|
||||
export type PluginsParams = BuiltinsWithOptionalParams &
|
||||
BuiltinsWithRequiredParams;
|
||||
|
||||
export type CustomPlugin<T = any> = {
|
||||
name: string;
|
||||
fn: Plugin<T>;
|
||||
params?: T;
|
||||
};
|
||||
|
||||
export type PluginConfig =
|
||||
| keyof BuiltinsWithOptionalParams
|
||||
| {
|
||||
[Name in keyof BuiltinsWithOptionalParams]: {
|
||||
name: Name;
|
||||
params?: BuiltinsWithOptionalParams[Name];
|
||||
};
|
||||
}[keyof BuiltinsWithOptionalParams]
|
||||
| {
|
||||
[Name in keyof BuiltinsWithRequiredParams]: {
|
||||
name: Name;
|
||||
params: BuiltinsWithRequiredParams[Name];
|
||||
};
|
||||
}[keyof BuiltinsWithRequiredParams]
|
||||
| CustomPlugin;
|
||||
|
||||
export type BuiltinPlugin<Name extends string, Params> = {
|
||||
/** Name of the plugin, also known as the plugin ID. */
|
||||
name: Name;
|
||||
description?: string;
|
||||
fn: Plugin<Params>;
|
||||
};
|
||||
|
||||
export type BuiltinPluginOrPreset<Name extends string, Params> = BuiltinPlugin<
|
||||
Name,
|
||||
Params
|
||||
> & {
|
||||
/** If the plugin is itself a preset that invokes other plugins. */
|
||||
isPreset?: true;
|
||||
/**
|
||||
* If the plugin is a preset that invokes other plugins, this returns an
|
||||
* array of the plugins in the preset in the order that they are invoked.
|
||||
*/
|
||||
plugins?: ReadonlyArray<BuiltinPlugin<string, Object>>;
|
||||
};
|
||||
|
||||
export type XastDoctype = {
|
||||
type: 'doctype';
|
||||
name: string;
|
||||
@@ -31,7 +182,7 @@ export type XastElement = {
|
||||
type: 'element';
|
||||
name: string;
|
||||
attributes: Record<string, string>;
|
||||
children: Array<XastChild>;
|
||||
children: XastChild[];
|
||||
};
|
||||
|
||||
export type XastChild =
|
||||
@@ -44,7 +195,7 @@ export type XastChild =
|
||||
|
||||
export type XastRoot = {
|
||||
type: 'root';
|
||||
children: Array<XastChild>;
|
||||
children: XastChild[];
|
||||
};
|
||||
|
||||
export type XastParent = XastRoot | XastElement;
|
||||
@@ -80,12 +231,12 @@ export type StringifyOptions = {
|
||||
finalNewline?: boolean;
|
||||
};
|
||||
|
||||
type VisitorNode<Node> = {
|
||||
export type VisitorNode<Node> = {
|
||||
enter?: (node: Node, parentNode: XastParent) => void | symbol;
|
||||
exit?: (node: Node, parentNode: XastParent) => void;
|
||||
};
|
||||
|
||||
type VisitorRoot = {
|
||||
export type VisitorRoot = {
|
||||
enter?: (node: XastRoot, parentNode: null) => void;
|
||||
exit?: (node: XastRoot, parentNode: null) => void;
|
||||
};
|
||||
@@ -105,13 +256,13 @@ export type PluginInfo = {
|
||||
multipassCount: number;
|
||||
};
|
||||
|
||||
export type Plugin<Params> = (
|
||||
export type Plugin<P = null> = (
|
||||
root: XastRoot,
|
||||
params: Params,
|
||||
info: PluginInfo
|
||||
) => null | Visitor;
|
||||
params: P,
|
||||
info: PluginInfo,
|
||||
) => Visitor | null | void;
|
||||
|
||||
export type Specificity = [number, number, number, number];
|
||||
export type Specificity = [number, number, number];
|
||||
|
||||
export type StylesheetDeclaration = {
|
||||
name: string;
|
||||
@@ -121,23 +272,23 @@ export type StylesheetDeclaration = {
|
||||
|
||||
export type StylesheetRule = {
|
||||
dynamic: boolean;
|
||||
selectors: string;
|
||||
selector: string;
|
||||
specificity: Specificity;
|
||||
declarations: Array<StylesheetDeclaration>;
|
||||
declarations: StylesheetDeclaration[];
|
||||
};
|
||||
|
||||
export type Stylesheet = {
|
||||
rules: Array<StylesheetRule>;
|
||||
rules: StylesheetRule[];
|
||||
parents: Map<XastElement, XastParent>;
|
||||
};
|
||||
|
||||
type StaticStyle = {
|
||||
export type StaticStyle = {
|
||||
type: 'static';
|
||||
inherited: boolean;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type DynamicStyle = {
|
||||
export type DynamicStyle = {
|
||||
type: 'dynamic';
|
||||
inherited: boolean;
|
||||
};
|
||||
@@ -168,5 +319,32 @@ export type PathDataCommand =
|
||||
|
||||
export type PathDataItem = {
|
||||
command: PathDataCommand;
|
||||
args: Array<number>;
|
||||
args: number[];
|
||||
};
|
||||
|
||||
export type DataUri = 'base64' | 'enc' | 'unenc';
|
||||
|
||||
export type Config = {
|
||||
/** Can be used by plugins, for example prefixIds. */
|
||||
path?: string;
|
||||
/** Pass over SVGs multiple times to ensure all optimizations are applied. */
|
||||
multipass?: boolean;
|
||||
/**
|
||||
* Precision of floating point numbers. Will be passed to each plugin that
|
||||
* supports this param.
|
||||
*/
|
||||
floatPrecision?: number;
|
||||
/**
|
||||
* Plugins configuration. By default SVGO uses `preset-default`, but may
|
||||
* contain builtin or custom plugins.
|
||||
*/
|
||||
plugins?: PluginConfig[];
|
||||
/** Options for rendering optimized SVG from AST. */
|
||||
js2svg?: StringifyOptions;
|
||||
/** Output as Data URI string. */
|
||||
datauri?: DataUri;
|
||||
};
|
||||
|
||||
export type Output = {
|
||||
data: string;
|
||||
};
|
||||
|
||||
29
node_modules/svgo/lib/util/map-nodes-to-parents.js
generated
vendored
Normal file
29
node_modules/svgo/lib/util/map-nodes-to-parents.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import { visit } from './visit.js';
|
||||
|
||||
/**
|
||||
* Maps all nodes to their parent node recursively.
|
||||
*
|
||||
* @param {import('../types.js').XastParent} node
|
||||
* @returns {Map<import('../types.js').XastNode, import('../types.js').XastParent>}
|
||||
*/
|
||||
export function mapNodesToParents(node) {
|
||||
/** @type {Map<import('../types.js').XastNode, import('../types.js').XastParent>} */
|
||||
const parents = new Map();
|
||||
|
||||
for (const child of node.children) {
|
||||
parents.set(child, node);
|
||||
visit(
|
||||
child,
|
||||
{
|
||||
element: {
|
||||
enter: (child, parent) => {
|
||||
parents.set(child, parent);
|
||||
},
|
||||
},
|
||||
},
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
return parents;
|
||||
}
|
||||
36
node_modules/svgo/lib/util/visit.js
generated
vendored
Normal file
36
node_modules/svgo/lib/util/visit.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
export const visitSkip = Symbol();
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').XastNode} node
|
||||
* @param {import('../types.js').Visitor} visitor
|
||||
* @param {any=} parentNode
|
||||
*/
|
||||
export const visit = (node, visitor, parentNode) => {
|
||||
const callbacks = visitor[node.type];
|
||||
if (callbacks?.enter) {
|
||||
// @ts-expect-error hard to infer
|
||||
const symbol = callbacks.enter(node, parentNode);
|
||||
if (symbol === visitSkip) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// visit root children
|
||||
if (node.type === 'root') {
|
||||
// copy children array to not lose cursor when children is spliced
|
||||
for (const child of node.children) {
|
||||
visit(child, visitor, node);
|
||||
}
|
||||
}
|
||||
// visit element children if still attached to parent
|
||||
if (node.type === 'element') {
|
||||
if (parentNode.children.includes(node)) {
|
||||
for (const child of node.children) {
|
||||
visit(child, visitor, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callbacks?.exit) {
|
||||
// @ts-expect-error hard to infer
|
||||
callbacks.exit(node, parentNode);
|
||||
}
|
||||
};
|
||||
7
node_modules/svgo/lib/version.js
generated
vendored
Normal file
7
node_modules/svgo/lib/version.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Version of SVGO.
|
||||
*
|
||||
* @type {string}
|
||||
* @since 4.0.0
|
||||
*/
|
||||
export const VERSION = '4.0.0';
|
||||
117
node_modules/svgo/lib/xast.js
generated
vendored
117
node_modules/svgo/lib/xast.js
generated
vendored
@@ -1,102 +1,53 @@
|
||||
'use strict';
|
||||
import { is, selectAll, selectOne } from 'css-select';
|
||||
import { createAdapter } from './svgo/css-select-adapter.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('./types').XastNode} XastNode
|
||||
* @typedef {import('./types').XastChild} XastChild
|
||||
* @typedef {import('./types').XastParent} XastParent
|
||||
* @typedef {import('./types').Visitor} Visitor
|
||||
* @param {import('./types.js').XastParent} relativeNode
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {import('css-select').Options<import('./types.js').XastNode & { children?: any }, import('./types.js').XastElement>}
|
||||
*/
|
||||
function createCssSelectOptions(relativeNode, parents) {
|
||||
return {
|
||||
xmlMode: true,
|
||||
adapter: createAdapter(relativeNode, parents),
|
||||
};
|
||||
}
|
||||
|
||||
const { selectAll, selectOne, is } = require('css-select');
|
||||
const xastAdaptor = require('./svgo/css-select-adapter.js');
|
||||
|
||||
const cssSelectOptions = {
|
||||
xmlMode: true,
|
||||
adapter: xastAdaptor,
|
||||
/**
|
||||
* @param {import('./types.js').XastParent} node Element to query the children of.
|
||||
* @param {string} selector CSS selector string.
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {import('./types.js').XastChild[]} All matching elements.
|
||||
*/
|
||||
export const querySelectorAll = (node, selector, parents) => {
|
||||
return selectAll(selector, node, createCssSelectOptions(node, parents));
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastNode, selector: string) => Array<XastChild>}
|
||||
* @param {import('./types.js').XastParent} node Element to query the children of.
|
||||
* @param {string} selector CSS selector string.
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {?import('./types.js').XastChild} First match, or null if there was no match.
|
||||
*/
|
||||
const querySelectorAll = (node, selector) => {
|
||||
return selectAll(selector, node, cssSelectOptions);
|
||||
export const querySelector = (node, selector, parents) => {
|
||||
return selectOne(selector, node, createCssSelectOptions(node, parents));
|
||||
};
|
||||
exports.querySelectorAll = querySelectorAll;
|
||||
|
||||
/**
|
||||
* @type {(node: XastNode, selector: string) => null | XastChild}
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {string} selector
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const querySelector = (node, selector) => {
|
||||
return selectOne(selector, node, cssSelectOptions);
|
||||
export const matches = (node, selector, parents) => {
|
||||
return is(node, selector, createCssSelectOptions(node, parents));
|
||||
};
|
||||
exports.querySelector = querySelector;
|
||||
|
||||
/**
|
||||
* @type {(node: XastChild, selector: string) => boolean}
|
||||
* @param {import('./types.js').XastChild} node
|
||||
* @param {import('./types.js').XastParent} parentNode
|
||||
*/
|
||||
const matches = (node, selector) => {
|
||||
return is(node, selector, cssSelectOptions);
|
||||
};
|
||||
exports.matches = matches;
|
||||
|
||||
/**
|
||||
* @type {(node: XastChild, name: string) => null | XastChild}
|
||||
*/
|
||||
const closestByName = (node, name) => {
|
||||
let currentNode = node;
|
||||
while (currentNode) {
|
||||
if (currentNode.type === 'element' && currentNode.name === name) {
|
||||
return currentNode;
|
||||
}
|
||||
// @ts-ignore parentNode is hidden from public usage
|
||||
currentNode = currentNode.parentNode;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.closestByName = closestByName;
|
||||
|
||||
const visitSkip = Symbol();
|
||||
exports.visitSkip = visitSkip;
|
||||
|
||||
/**
|
||||
* @type {(node: XastNode, visitor: Visitor, parentNode?: any) => void}
|
||||
*/
|
||||
const visit = (node, visitor, parentNode) => {
|
||||
const callbacks = visitor[node.type];
|
||||
if (callbacks && callbacks.enter) {
|
||||
// @ts-ignore hard to infer
|
||||
const symbol = callbacks.enter(node, parentNode);
|
||||
if (symbol === visitSkip) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// visit root children
|
||||
if (node.type === 'root') {
|
||||
// copy children array to not loose cursor when children is spliced
|
||||
for (const child of node.children) {
|
||||
visit(child, visitor, node);
|
||||
}
|
||||
}
|
||||
// visit element children if still attached to parent
|
||||
if (node.type === 'element') {
|
||||
if (parentNode.children.includes(node)) {
|
||||
for (const child of node.children) {
|
||||
visit(child, visitor, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callbacks && callbacks.exit) {
|
||||
// @ts-ignore hard to infer
|
||||
callbacks.exit(node, parentNode);
|
||||
}
|
||||
};
|
||||
exports.visit = visit;
|
||||
|
||||
/**
|
||||
* @type {(node: XastChild, parentNode: XastParent) => void}
|
||||
*/
|
||||
const detachNodeFromParent = (node, parentNode) => {
|
||||
export const detachNodeFromParent = (node, parentNode) => {
|
||||
// avoid splice to not break for loops
|
||||
parentNode.children = parentNode.children.filter((child) => child !== node);
|
||||
};
|
||||
exports.detachNodeFromParent = detachNodeFromParent;
|
||||
|
||||
22
node_modules/svgo/node_modules/commander/LICENSE
generated
vendored
Executable file
22
node_modules/svgo/node_modules/commander/LICENSE
generated
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
1148
node_modules/svgo/node_modules/commander/Readme.md
generated
vendored
Executable file
1148
node_modules/svgo/node_modules/commander/Readme.md
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
16
node_modules/svgo/node_modules/commander/esm.mjs
generated
vendored
Executable file
16
node_modules/svgo/node_modules/commander/esm.mjs
generated
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
import commander from './index.js';
|
||||
|
||||
// wrapper to provide named exports for ESM.
|
||||
export const {
|
||||
program,
|
||||
createCommand,
|
||||
createArgument,
|
||||
createOption,
|
||||
CommanderError,
|
||||
InvalidArgumentError,
|
||||
InvalidOptionArgumentError, // deprecated old name
|
||||
Command,
|
||||
Argument,
|
||||
Option,
|
||||
Help
|
||||
} = commander;
|
||||
26
node_modules/svgo/node_modules/commander/index.js
generated
vendored
Executable file
26
node_modules/svgo/node_modules/commander/index.js
generated
vendored
Executable file
@@ -0,0 +1,26 @@
|
||||
const { Argument } = require('./lib/argument.js');
|
||||
const { Command } = require('./lib/command.js');
|
||||
const { CommanderError, InvalidArgumentError } = require('./lib/error.js');
|
||||
const { Help } = require('./lib/help.js');
|
||||
const { Option } = require('./lib/option.js');
|
||||
|
||||
/**
|
||||
* Expose the root command.
|
||||
*/
|
||||
|
||||
exports = module.exports = new Command();
|
||||
exports.program = exports; // More explicit access to global command.
|
||||
// createArgument, createCommand, and createOption are implicitly available as they are methods on program.
|
||||
|
||||
/**
|
||||
* Expose classes
|
||||
*/
|
||||
|
||||
exports.Command = Command;
|
||||
exports.Option = Option;
|
||||
exports.Argument = Argument;
|
||||
exports.Help = Help;
|
||||
|
||||
exports.CommanderError = CommanderError;
|
||||
exports.InvalidArgumentError = InvalidArgumentError;
|
||||
exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated
|
||||
145
node_modules/svgo/node_modules/commander/lib/argument.js
generated
vendored
Executable file
145
node_modules/svgo/node_modules/commander/lib/argument.js
generated
vendored
Executable file
@@ -0,0 +1,145 @@
|
||||
const { InvalidArgumentError } = require('./error.js');
|
||||
|
||||
class Argument {
|
||||
/**
|
||||
* Initialize a new command argument with the given name and description.
|
||||
* The default is that the argument is required, and you can explicitly
|
||||
* indicate this with <> around the name. Put [] around the name for an optional argument.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} [description]
|
||||
*/
|
||||
|
||||
constructor(name, description) {
|
||||
this.description = description || '';
|
||||
this.variadic = false;
|
||||
this.parseArg = undefined;
|
||||
this.defaultValue = undefined;
|
||||
this.defaultValueDescription = undefined;
|
||||
this.argChoices = undefined;
|
||||
|
||||
switch (name[0]) {
|
||||
case '<': // e.g. <required>
|
||||
this.required = true;
|
||||
this._name = name.slice(1, -1);
|
||||
break;
|
||||
case '[': // e.g. [optional]
|
||||
this.required = false;
|
||||
this._name = name.slice(1, -1);
|
||||
break;
|
||||
default:
|
||||
this.required = true;
|
||||
this._name = name;
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._name.length > 3 && this._name.slice(-3) === '...') {
|
||||
this.variadic = true;
|
||||
this._name = this._name.slice(0, -3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return argument name.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
name() {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api private
|
||||
*/
|
||||
|
||||
_concatValue(value, previous) {
|
||||
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
||||
return [value];
|
||||
}
|
||||
|
||||
return previous.concat(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value, and optionally supply the description to be displayed in the help.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {string} [description]
|
||||
* @return {Argument}
|
||||
*/
|
||||
|
||||
default(value, description) {
|
||||
this.defaultValue = value;
|
||||
this.defaultValueDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom handler for processing CLI command arguments into argument values.
|
||||
*
|
||||
* @param {Function} [fn]
|
||||
* @return {Argument}
|
||||
*/
|
||||
|
||||
argParser(fn) {
|
||||
this.parseArg = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow argument value to be one of choices.
|
||||
*
|
||||
* @param {string[]} values
|
||||
* @return {Argument}
|
||||
*/
|
||||
|
||||
choices(values) {
|
||||
this.argChoices = values.slice();
|
||||
this.parseArg = (arg, previous) => {
|
||||
if (!this.argChoices.includes(arg)) {
|
||||
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`);
|
||||
}
|
||||
if (this.variadic) {
|
||||
return this._concatValue(arg, previous);
|
||||
}
|
||||
return arg;
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make argument required.
|
||||
*/
|
||||
argRequired() {
|
||||
this.required = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make argument optional.
|
||||
*/
|
||||
argOptional() {
|
||||
this.required = false;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an argument and returns its human readable equivalent for help usage.
|
||||
*
|
||||
* @param {Argument} arg
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function humanReadableArgName(arg) {
|
||||
const nameOutput = arg.name() + (arg.variadic === true ? '...' : '');
|
||||
|
||||
return arg.required
|
||||
? '<' + nameOutput + '>'
|
||||
: '[' + nameOutput + ']';
|
||||
}
|
||||
|
||||
exports.Argument = Argument;
|
||||
exports.humanReadableArgName = humanReadableArgName;
|
||||
2179
node_modules/svgo/node_modules/commander/lib/command.js
generated
vendored
Executable file
2179
node_modules/svgo/node_modules/commander/lib/command.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
43
node_modules/svgo/node_modules/commander/lib/error.js
generated
vendored
Executable file
43
node_modules/svgo/node_modules/commander/lib/error.js
generated
vendored
Executable file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* CommanderError class
|
||||
* @class
|
||||
*/
|
||||
class CommanderError extends Error {
|
||||
/**
|
||||
* Constructs the CommanderError class
|
||||
* @param {number} exitCode suggested exit code which could be used with process.exit
|
||||
* @param {string} code an id string representing the error
|
||||
* @param {string} message human-readable description of the error
|
||||
* @constructor
|
||||
*/
|
||||
constructor(exitCode, code, message) {
|
||||
super(message);
|
||||
// properly capture stack trace in Node.js
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
this.code = code;
|
||||
this.exitCode = exitCode;
|
||||
this.nestedError = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InvalidArgumentError class
|
||||
* @class
|
||||
*/
|
||||
class InvalidArgumentError extends CommanderError {
|
||||
/**
|
||||
* Constructs the InvalidArgumentError class
|
||||
* @param {string} [message] explanation of why argument is invalid
|
||||
* @constructor
|
||||
*/
|
||||
constructor(message) {
|
||||
super(1, 'commander.invalidArgument', message);
|
||||
// properly capture stack trace in Node.js
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
exports.CommanderError = CommanderError;
|
||||
exports.InvalidArgumentError = InvalidArgumentError;
|
||||
462
node_modules/svgo/node_modules/commander/lib/help.js
generated
vendored
Executable file
462
node_modules/svgo/node_modules/commander/lib/help.js
generated
vendored
Executable file
@@ -0,0 +1,462 @@
|
||||
const { humanReadableArgName } = require('./argument.js');
|
||||
|
||||
/**
|
||||
* TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
|
||||
* https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
|
||||
* @typedef { import("./argument.js").Argument } Argument
|
||||
* @typedef { import("./command.js").Command } Command
|
||||
* @typedef { import("./option.js").Option } Option
|
||||
*/
|
||||
|
||||
// Although this is a class, methods are static in style to allow override using subclass or just functions.
|
||||
class Help {
|
||||
constructor() {
|
||||
this.helpWidth = undefined;
|
||||
this.sortSubcommands = false;
|
||||
this.sortOptions = false;
|
||||
this.showGlobalOptions = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Command[]}
|
||||
*/
|
||||
|
||||
visibleCommands(cmd) {
|
||||
const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden);
|
||||
if (cmd._hasImplicitHelpCommand()) {
|
||||
// Create a command matching the implicit help command.
|
||||
const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/);
|
||||
const helpCommand = cmd.createCommand(helpName)
|
||||
.helpOption(false);
|
||||
helpCommand.description(cmd._helpCommandDescription);
|
||||
if (helpArgs) helpCommand.arguments(helpArgs);
|
||||
visibleCommands.push(helpCommand);
|
||||
}
|
||||
if (this.sortSubcommands) {
|
||||
visibleCommands.sort((a, b) => {
|
||||
// @ts-ignore: overloaded return type
|
||||
return a.name().localeCompare(b.name());
|
||||
});
|
||||
}
|
||||
return visibleCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare options for sort.
|
||||
*
|
||||
* @param {Option} a
|
||||
* @param {Option} b
|
||||
* @returns number
|
||||
*/
|
||||
compareOptions(a, b) {
|
||||
const getSortKey = (option) => {
|
||||
// WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
|
||||
return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
|
||||
};
|
||||
return getSortKey(a).localeCompare(getSortKey(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Option[]}
|
||||
*/
|
||||
|
||||
visibleOptions(cmd) {
|
||||
const visibleOptions = cmd.options.filter((option) => !option.hidden);
|
||||
// Implicit help
|
||||
const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag);
|
||||
const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag);
|
||||
if (showShortHelpFlag || showLongHelpFlag) {
|
||||
let helpOption;
|
||||
if (!showShortHelpFlag) {
|
||||
helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription);
|
||||
} else if (!showLongHelpFlag) {
|
||||
helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription);
|
||||
} else {
|
||||
helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription);
|
||||
}
|
||||
visibleOptions.push(helpOption);
|
||||
}
|
||||
if (this.sortOptions) {
|
||||
visibleOptions.sort(this.compareOptions);
|
||||
}
|
||||
return visibleOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the visible global options. (Not including help.)
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Option[]}
|
||||
*/
|
||||
|
||||
visibleGlobalOptions(cmd) {
|
||||
if (!this.showGlobalOptions) return [];
|
||||
|
||||
const globalOptions = [];
|
||||
for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
|
||||
const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
|
||||
globalOptions.push(...visibleOptions);
|
||||
}
|
||||
if (this.sortOptions) {
|
||||
globalOptions.sort(this.compareOptions);
|
||||
}
|
||||
return globalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the arguments if any have a description.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Argument[]}
|
||||
*/
|
||||
|
||||
visibleArguments(cmd) {
|
||||
// Side effect! Apply the legacy descriptions before the arguments are displayed.
|
||||
if (cmd._argsDescription) {
|
||||
cmd.registeredArguments.forEach(argument => {
|
||||
argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
|
||||
});
|
||||
}
|
||||
|
||||
// If there are any arguments with a description then return all the arguments.
|
||||
if (cmd.registeredArguments.find(argument => argument.description)) {
|
||||
return cmd.registeredArguments;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command term to show in the list of subcommands.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
subcommandTerm(cmd) {
|
||||
// Legacy. Ignores custom usage string, and nested commands.
|
||||
const args = cmd.registeredArguments.map(arg => humanReadableArgName(arg)).join(' ');
|
||||
return cmd._name +
|
||||
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
|
||||
(cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
|
||||
(args ? ' ' + args : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option term to show in the list of options.
|
||||
*
|
||||
* @param {Option} option
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
optionTerm(option) {
|
||||
return option.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument term to show in the list of arguments.
|
||||
*
|
||||
* @param {Argument} argument
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
argumentTerm(argument) {
|
||||
return argument.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest command term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestSubcommandTermLength(cmd, helper) {
|
||||
return helper.visibleCommands(cmd).reduce((max, command) => {
|
||||
return Math.max(max, helper.subcommandTerm(command).length);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest option term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestOptionTermLength(cmd, helper) {
|
||||
return helper.visibleOptions(cmd).reduce((max, option) => {
|
||||
return Math.max(max, helper.optionTerm(option).length);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest global option term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestGlobalOptionTermLength(cmd, helper) {
|
||||
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
|
||||
return Math.max(max, helper.optionTerm(option).length);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest argument term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestArgumentTermLength(cmd, helper) {
|
||||
return helper.visibleArguments(cmd).reduce((max, argument) => {
|
||||
return Math.max(max, helper.argumentTerm(argument).length);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command usage to be displayed at the top of the built-in help.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
commandUsage(cmd) {
|
||||
// Usage
|
||||
let cmdName = cmd._name;
|
||||
if (cmd._aliases[0]) {
|
||||
cmdName = cmdName + '|' + cmd._aliases[0];
|
||||
}
|
||||
let ancestorCmdNames = '';
|
||||
for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
|
||||
ancestorCmdNames = ancestorCmd.name() + ' ' + ancestorCmdNames;
|
||||
}
|
||||
return ancestorCmdNames + cmdName + ' ' + cmd.usage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the command.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
commandDescription(cmd) {
|
||||
// @ts-ignore: overloaded return type
|
||||
return cmd.description();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subcommand summary to show in the list of subcommands.
|
||||
* (Fallback to description for backwards compatibility.)
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
subcommandDescription(cmd) {
|
||||
// @ts-ignore: overloaded return type
|
||||
return cmd.summary() || cmd.description();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option description to show in the list of options.
|
||||
*
|
||||
* @param {Option} option
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
optionDescription(option) {
|
||||
const extraInfo = [];
|
||||
|
||||
if (option.argChoices) {
|
||||
extraInfo.push(
|
||||
// use stringify to match the display of the default value
|
||||
`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
|
||||
}
|
||||
if (option.defaultValue !== undefined) {
|
||||
// default for boolean and negated more for programmer than end user,
|
||||
// but show true/false for boolean option as may be for hand-rolled env or config processing.
|
||||
const showDefault = option.required || option.optional ||
|
||||
(option.isBoolean() && typeof option.defaultValue === 'boolean');
|
||||
if (showDefault) {
|
||||
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
|
||||
}
|
||||
}
|
||||
// preset for boolean and negated are more for programmer than end user
|
||||
if (option.presetArg !== undefined && option.optional) {
|
||||
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
|
||||
}
|
||||
if (option.envVar !== undefined) {
|
||||
extraInfo.push(`env: ${option.envVar}`);
|
||||
}
|
||||
if (extraInfo.length > 0) {
|
||||
return `${option.description} (${extraInfo.join(', ')})`;
|
||||
}
|
||||
|
||||
return option.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument description to show in the list of arguments.
|
||||
*
|
||||
* @param {Argument} argument
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
argumentDescription(argument) {
|
||||
const extraInfo = [];
|
||||
if (argument.argChoices) {
|
||||
extraInfo.push(
|
||||
// use stringify to match the display of the default value
|
||||
`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
|
||||
}
|
||||
if (argument.defaultValue !== undefined) {
|
||||
extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
|
||||
}
|
||||
if (extraInfo.length > 0) {
|
||||
const extraDescripton = `(${extraInfo.join(', ')})`;
|
||||
if (argument.description) {
|
||||
return `${argument.description} ${extraDescripton}`;
|
||||
}
|
||||
return extraDescripton;
|
||||
}
|
||||
return argument.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the built-in help text.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
formatHelp(cmd, helper) {
|
||||
const termWidth = helper.padWidth(cmd, helper);
|
||||
const helpWidth = helper.helpWidth || 80;
|
||||
const itemIndentWidth = 2;
|
||||
const itemSeparatorWidth = 2; // between term and description
|
||||
function formatItem(term, description) {
|
||||
if (description) {
|
||||
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
|
||||
return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
|
||||
}
|
||||
return term;
|
||||
}
|
||||
function formatList(textArray) {
|
||||
return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
|
||||
}
|
||||
|
||||
// Usage
|
||||
let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
|
||||
|
||||
// Description
|
||||
const commandDescription = helper.commandDescription(cmd);
|
||||
if (commandDescription.length > 0) {
|
||||
output = output.concat([helper.wrap(commandDescription, helpWidth, 0), '']);
|
||||
}
|
||||
|
||||
// Arguments
|
||||
const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
||||
return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument));
|
||||
});
|
||||
if (argumentList.length > 0) {
|
||||
output = output.concat(['Arguments:', formatList(argumentList), '']);
|
||||
}
|
||||
|
||||
// Options
|
||||
const optionList = helper.visibleOptions(cmd).map((option) => {
|
||||
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
|
||||
});
|
||||
if (optionList.length > 0) {
|
||||
output = output.concat(['Options:', formatList(optionList), '']);
|
||||
}
|
||||
|
||||
if (this.showGlobalOptions) {
|
||||
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
|
||||
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
|
||||
});
|
||||
if (globalOptionList.length > 0) {
|
||||
output = output.concat(['Global Options:', formatList(globalOptionList), '']);
|
||||
}
|
||||
}
|
||||
|
||||
// Commands
|
||||
const commandList = helper.visibleCommands(cmd).map((cmd) => {
|
||||
return formatItem(helper.subcommandTerm(cmd), helper.subcommandDescription(cmd));
|
||||
});
|
||||
if (commandList.length > 0) {
|
||||
output = output.concat(['Commands:', formatList(commandList), '']);
|
||||
}
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the pad width from the maximum term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
padWidth(cmd, helper) {
|
||||
return Math.max(
|
||||
helper.longestOptionTermLength(cmd, helper),
|
||||
helper.longestGlobalOptionTermLength(cmd, helper),
|
||||
helper.longestSubcommandTermLength(cmd, helper),
|
||||
helper.longestArgumentTermLength(cmd, helper)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given string to width characters per line, with lines after the first indented.
|
||||
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {number} width
|
||||
* @param {number} indent
|
||||
* @param {number} [minColumnWidth=40]
|
||||
* @return {string}
|
||||
*
|
||||
*/
|
||||
|
||||
wrap(str, width, indent, minColumnWidth = 40) {
|
||||
// Full \s characters, minus the linefeeds.
|
||||
const indents = ' \\f\\t\\v\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff';
|
||||
// Detect manually wrapped and indented strings by searching for line break followed by spaces.
|
||||
const manualIndent = new RegExp(`[\\n][${indents}]+`);
|
||||
if (str.match(manualIndent)) return str;
|
||||
// Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
|
||||
const columnWidth = width - indent;
|
||||
if (columnWidth < minColumnWidth) return str;
|
||||
|
||||
const leadingStr = str.slice(0, indent);
|
||||
const columnText = str.slice(indent).replace('\r\n', '\n');
|
||||
const indentString = ' '.repeat(indent);
|
||||
const zeroWidthSpace = '\u200B';
|
||||
const breaks = `\\s${zeroWidthSpace}`;
|
||||
// Match line end (so empty lines don't collapse),
|
||||
// or as much text as will fit in column, or excess text up to first break.
|
||||
const regex = new RegExp(`\n|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`, 'g');
|
||||
const lines = columnText.match(regex) || [];
|
||||
return leadingStr + lines.map((line, i) => {
|
||||
if (line === '\n') return ''; // preserve empty lines
|
||||
return ((i > 0) ? indentString : '') + line.trimEnd();
|
||||
}).join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
exports.Help = Help;
|
||||
329
node_modules/svgo/node_modules/commander/lib/option.js
generated
vendored
Executable file
329
node_modules/svgo/node_modules/commander/lib/option.js
generated
vendored
Executable file
@@ -0,0 +1,329 @@
|
||||
const { InvalidArgumentError } = require('./error.js');
|
||||
|
||||
class Option {
|
||||
/**
|
||||
* Initialize a new `Option` with the given `flags` and `description`.
|
||||
*
|
||||
* @param {string} flags
|
||||
* @param {string} [description]
|
||||
*/
|
||||
|
||||
constructor(flags, description) {
|
||||
this.flags = flags;
|
||||
this.description = description || '';
|
||||
|
||||
this.required = flags.includes('<'); // A value must be supplied when the option is specified.
|
||||
this.optional = flags.includes('['); // A value is optional when the option is specified.
|
||||
// variadic test ignores <value,...> et al which might be used to describe custom splitting of single argument
|
||||
this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values.
|
||||
this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line.
|
||||
const optionFlags = splitOptionFlags(flags);
|
||||
this.short = optionFlags.shortFlag;
|
||||
this.long = optionFlags.longFlag;
|
||||
this.negate = false;
|
||||
if (this.long) {
|
||||
this.negate = this.long.startsWith('--no-');
|
||||
}
|
||||
this.defaultValue = undefined;
|
||||
this.defaultValueDescription = undefined;
|
||||
this.presetArg = undefined;
|
||||
this.envVar = undefined;
|
||||
this.parseArg = undefined;
|
||||
this.hidden = false;
|
||||
this.argChoices = undefined;
|
||||
this.conflictsWith = [];
|
||||
this.implied = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value, and optionally supply the description to be displayed in the help.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {string} [description]
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
default(value, description) {
|
||||
this.defaultValue = value;
|
||||
this.defaultValueDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
|
||||
* The custom processing (parseArg) is called.
|
||||
*
|
||||
* @example
|
||||
* new Option('--color').default('GREYSCALE').preset('RGB');
|
||||
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
|
||||
*
|
||||
* @param {*} arg
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
preset(arg) {
|
||||
this.presetArg = arg;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add option name(s) that conflict with this option.
|
||||
* An error will be displayed if conflicting options are found during parsing.
|
||||
*
|
||||
* @example
|
||||
* new Option('--rgb').conflicts('cmyk');
|
||||
* new Option('--js').conflicts(['ts', 'jsx']);
|
||||
*
|
||||
* @param {string | string[]} names
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
conflicts(names) {
|
||||
this.conflictsWith = this.conflictsWith.concat(names);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify implied option values for when this option is set and the implied options are not.
|
||||
*
|
||||
* The custom processing (parseArg) is not called on the implied values.
|
||||
*
|
||||
* @example
|
||||
* program
|
||||
* .addOption(new Option('--log', 'write logging information to file'))
|
||||
* .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
|
||||
*
|
||||
* @param {Object} impliedOptionValues
|
||||
* @return {Option}
|
||||
*/
|
||||
implies(impliedOptionValues) {
|
||||
let newImplied = impliedOptionValues;
|
||||
if (typeof impliedOptionValues === 'string') {
|
||||
// string is not documented, but easy mistake and we can do what user probably intended.
|
||||
newImplied = { [impliedOptionValues]: true };
|
||||
}
|
||||
this.implied = Object.assign(this.implied || {}, newImplied);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set environment variable to check for option value.
|
||||
*
|
||||
* An environment variable is only used if when processed the current option value is
|
||||
* undefined, or the source of the current value is 'default' or 'config' or 'env'.
|
||||
*
|
||||
* @param {string} name
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
env(name) {
|
||||
this.envVar = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom handler for processing CLI option arguments into option values.
|
||||
*
|
||||
* @param {Function} [fn]
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
argParser(fn) {
|
||||
this.parseArg = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the option is mandatory and must have a value after parsing.
|
||||
*
|
||||
* @param {boolean} [mandatory=true]
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
makeOptionMandatory(mandatory = true) {
|
||||
this.mandatory = !!mandatory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide option in help.
|
||||
*
|
||||
* @param {boolean} [hide=true]
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
hideHelp(hide = true) {
|
||||
this.hidden = !!hide;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api private
|
||||
*/
|
||||
|
||||
_concatValue(value, previous) {
|
||||
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
||||
return [value];
|
||||
}
|
||||
|
||||
return previous.concat(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow option value to be one of choices.
|
||||
*
|
||||
* @param {string[]} values
|
||||
* @return {Option}
|
||||
*/
|
||||
|
||||
choices(values) {
|
||||
this.argChoices = values.slice();
|
||||
this.parseArg = (arg, previous) => {
|
||||
if (!this.argChoices.includes(arg)) {
|
||||
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`);
|
||||
}
|
||||
if (this.variadic) {
|
||||
return this._concatValue(arg, previous);
|
||||
}
|
||||
return arg;
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option name.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
name() {
|
||||
if (this.long) {
|
||||
return this.long.replace(/^--/, '');
|
||||
}
|
||||
return this.short.replace(/^-/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option name, in a camelcase format that can be used
|
||||
* as a object attribute key.
|
||||
*
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
attributeName() {
|
||||
return camelcase(this.name().replace(/^no-/, ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if `arg` matches the short or long flag.
|
||||
*
|
||||
* @param {string} arg
|
||||
* @return {boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
is(arg) {
|
||||
return this.short === arg || this.long === arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a boolean option.
|
||||
*
|
||||
* Options are one of boolean, negated, required argument, or optional argument.
|
||||
*
|
||||
* @return {boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
isBoolean() {
|
||||
return !this.required && !this.optional && !this.negate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is to make it easier to work with dual options, without changing the existing
|
||||
* implementation. We support separate dual options for separate positive and negative options,
|
||||
* like `--build` and `--no-build`, which share a single option value. This works nicely for some
|
||||
* use cases, but is tricky for others where we want separate behaviours despite
|
||||
* the single shared option value.
|
||||
*/
|
||||
class DualOptions {
|
||||
/**
|
||||
* @param {Option[]} options
|
||||
*/
|
||||
constructor(options) {
|
||||
this.positiveOptions = new Map();
|
||||
this.negativeOptions = new Map();
|
||||
this.dualOptions = new Set();
|
||||
options.forEach(option => {
|
||||
if (option.negate) {
|
||||
this.negativeOptions.set(option.attributeName(), option);
|
||||
} else {
|
||||
this.positiveOptions.set(option.attributeName(), option);
|
||||
}
|
||||
});
|
||||
this.negativeOptions.forEach((value, key) => {
|
||||
if (this.positiveOptions.has(key)) {
|
||||
this.dualOptions.add(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Did the value come from the option, and not from possible matching dual option?
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {Option} option
|
||||
* @returns {boolean}
|
||||
*/
|
||||
valueFromOption(value, option) {
|
||||
const optionKey = option.attributeName();
|
||||
if (!this.dualOptions.has(optionKey)) return true;
|
||||
|
||||
// Use the value to deduce if (probably) came from the option.
|
||||
const preset = this.negativeOptions.get(optionKey).presetArg;
|
||||
const negativeValue = (preset !== undefined) ? preset : false;
|
||||
return option.negate === (negativeValue === value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string from kebab-case to camelCase.
|
||||
*
|
||||
* @param {string} str
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function camelcase(str) {
|
||||
return str.split('-').reduce((str, word) => {
|
||||
return str + word[0].toUpperCase() + word.slice(1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the short and long flag out of something like '-m,--mixed <value>'
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function splitOptionFlags(flags) {
|
||||
let shortFlag;
|
||||
let longFlag;
|
||||
// Use original very loose parsing to maintain backwards compatibility for now,
|
||||
// which allowed for example unintended `-sw, --short-word` [sic].
|
||||
const flagParts = flags.split(/[ |,]+/);
|
||||
if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1])) shortFlag = flagParts.shift();
|
||||
longFlag = flagParts.shift();
|
||||
// Add support for lone short flag without significantly changing parsing!
|
||||
if (!shortFlag && /^-[^-]$/.test(longFlag)) {
|
||||
shortFlag = longFlag;
|
||||
longFlag = undefined;
|
||||
}
|
||||
return { shortFlag, longFlag };
|
||||
}
|
||||
|
||||
exports.Option = Option;
|
||||
exports.splitOptionFlags = splitOptionFlags;
|
||||
exports.DualOptions = DualOptions;
|
||||
100
node_modules/svgo/node_modules/commander/lib/suggestSimilar.js
generated
vendored
Executable file
100
node_modules/svgo/node_modules/commander/lib/suggestSimilar.js
generated
vendored
Executable file
@@ -0,0 +1,100 @@
|
||||
const maxDistance = 3;
|
||||
|
||||
function editDistance(a, b) {
|
||||
// https://en.wikipedia.org/wiki/Damerau–Levenshtein_distance
|
||||
// Calculating optimal string alignment distance, no substring is edited more than once.
|
||||
// (Simple implementation.)
|
||||
|
||||
// Quick early exit, return worst case.
|
||||
if (Math.abs(a.length - b.length) > maxDistance) return Math.max(a.length, b.length);
|
||||
|
||||
// distance between prefix substrings of a and b
|
||||
const d = [];
|
||||
|
||||
// pure deletions turn a into empty string
|
||||
for (let i = 0; i <= a.length; i++) {
|
||||
d[i] = [i];
|
||||
}
|
||||
// pure insertions turn empty string into b
|
||||
for (let j = 0; j <= b.length; j++) {
|
||||
d[0][j] = j;
|
||||
}
|
||||
|
||||
// fill matrix
|
||||
for (let j = 1; j <= b.length; j++) {
|
||||
for (let i = 1; i <= a.length; i++) {
|
||||
let cost = 1;
|
||||
if (a[i - 1] === b[j - 1]) {
|
||||
cost = 0;
|
||||
} else {
|
||||
cost = 1;
|
||||
}
|
||||
d[i][j] = Math.min(
|
||||
d[i - 1][j] + 1, // deletion
|
||||
d[i][j - 1] + 1, // insertion
|
||||
d[i - 1][j - 1] + cost // substitution
|
||||
);
|
||||
// transposition
|
||||
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
||||
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d[a.length][b.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find close matches, restricted to same number of edits.
|
||||
*
|
||||
* @param {string} word
|
||||
* @param {string[]} candidates
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
function suggestSimilar(word, candidates) {
|
||||
if (!candidates || candidates.length === 0) return '';
|
||||
// remove possible duplicates
|
||||
candidates = Array.from(new Set(candidates));
|
||||
|
||||
const searchingOptions = word.startsWith('--');
|
||||
if (searchingOptions) {
|
||||
word = word.slice(2);
|
||||
candidates = candidates.map(candidate => candidate.slice(2));
|
||||
}
|
||||
|
||||
let similar = [];
|
||||
let bestDistance = maxDistance;
|
||||
const minSimilarity = 0.4;
|
||||
candidates.forEach((candidate) => {
|
||||
if (candidate.length <= 1) return; // no one character guesses
|
||||
|
||||
const distance = editDistance(word, candidate);
|
||||
const length = Math.max(word.length, candidate.length);
|
||||
const similarity = (length - distance) / length;
|
||||
if (similarity > minSimilarity) {
|
||||
if (distance < bestDistance) {
|
||||
// better edit distance, throw away previous worse matches
|
||||
bestDistance = distance;
|
||||
similar = [candidate];
|
||||
} else if (distance === bestDistance) {
|
||||
similar.push(candidate);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
similar.sort((a, b) => a.localeCompare(b));
|
||||
if (searchingOptions) {
|
||||
similar = similar.map(candidate => `--${candidate}`);
|
||||
}
|
||||
|
||||
if (similar.length > 1) {
|
||||
return `\n(Did you mean one of ${similar.join(', ')}?)`;
|
||||
}
|
||||
if (similar.length === 1) {
|
||||
return `\n(Did you mean ${similar[0]}?)`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
exports.suggestSimilar = suggestSimilar;
|
||||
16
node_modules/svgo/node_modules/commander/package-support.json
generated
vendored
Executable file
16
node_modules/svgo/node_modules/commander/package-support.json
generated
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "*",
|
||||
"target": {
|
||||
"node": "supported"
|
||||
},
|
||||
"response": {
|
||||
"type": "time-permitting"
|
||||
},
|
||||
"backing": {
|
||||
"npm-funding": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
80
node_modules/svgo/node_modules/commander/package.json
generated
vendored
Executable file
80
node_modules/svgo/node_modules/commander/package.json
generated
vendored
Executable file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "commander",
|
||||
"version": "11.1.0",
|
||||
"description": "the complete solution for node.js command-line programs",
|
||||
"keywords": [
|
||||
"commander",
|
||||
"command",
|
||||
"option",
|
||||
"parser",
|
||||
"cli",
|
||||
"argument",
|
||||
"args",
|
||||
"argv"
|
||||
],
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tj/commander.js.git"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "npm run lint:javascript && npm run lint:typescript",
|
||||
"lint:javascript": "eslint index.js esm.mjs \"lib/*.js\" \"tests/**/*.js\"",
|
||||
"lint:typescript": "eslint typings/*.ts tests/*.ts",
|
||||
"test": "jest && npm run typecheck-ts",
|
||||
"test-esm": "node ./tests/esm-imports-test.mjs",
|
||||
"typecheck-ts": "tsd && tsc -p tsconfig.ts.json",
|
||||
"typecheck-js": "tsc -p tsconfig.js.json",
|
||||
"test-all": "npm run test && npm run lint && npm run typecheck-js && npm run test-esm"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib/*.js",
|
||||
"esm.mjs",
|
||||
"typings/index.d.ts",
|
||||
"typings/esm.d.mts",
|
||||
"package-support.json"
|
||||
],
|
||||
"type": "commonjs",
|
||||
"main": "./index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": {
|
||||
"types": "./typings/index.d.ts",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"types": "./typings/esm.d.mts",
|
||||
"default": "./esm.mjs"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./esm.mjs": {
|
||||
"types": "./typings/esm.d.mts",
|
||||
"import": "./esm.mjs"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/node": "^20.2.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-config-standard-with-typescript": "^33.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"eslint-plugin-n": "^15.6.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"jest": "^29.3.1",
|
||||
"ts-jest": "^29.0.3",
|
||||
"tsd": "^0.28.1",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"types": "typings/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"support": true
|
||||
}
|
||||
3
node_modules/svgo/node_modules/commander/typings/esm.d.mts
generated
vendored
Normal file
3
node_modules/svgo/node_modules/commander/typings/esm.d.mts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Just reexport the types from cjs
|
||||
// This is a bit indirect. There is not an index.js, but TypeScript will look for index.d.ts for types.
|
||||
export * from './index.js';
|
||||
884
node_modules/svgo/node_modules/commander/typings/index.d.ts
generated
vendored
Normal file
884
node_modules/svgo/node_modules/commander/typings/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,884 @@
|
||||
// Type definitions for commander
|
||||
// Original definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
|
||||
|
||||
// Using method rather than property for method-signature-style, to document method overloads separately. Allow either.
|
||||
/* eslint-disable @typescript-eslint/method-signature-style */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
// This is a trick to encourage editor to suggest the known literals while still
|
||||
// allowing any BaseType value.
|
||||
// References:
|
||||
// - https://github.com/microsoft/TypeScript/issues/29729
|
||||
// - https://github.com/sindresorhus/type-fest/blob/main/source/literal-union.d.ts
|
||||
// - https://github.com/sindresorhus/type-fest/blob/main/source/primitive.d.ts
|
||||
type LiteralUnion<LiteralType, BaseType extends string | number> = LiteralType | (BaseType & Record<never, never>);
|
||||
|
||||
export class CommanderError extends Error {
|
||||
code: string;
|
||||
exitCode: number;
|
||||
message: string;
|
||||
nestedError?: string;
|
||||
|
||||
/**
|
||||
* Constructs the CommanderError class
|
||||
* @param exitCode - suggested exit code which could be used with process.exit
|
||||
* @param code - an id string representing the error
|
||||
* @param message - human-readable description of the error
|
||||
* @constructor
|
||||
*/
|
||||
constructor(exitCode: number, code: string, message: string);
|
||||
}
|
||||
|
||||
export class InvalidArgumentError extends CommanderError {
|
||||
/**
|
||||
* Constructs the InvalidArgumentError class
|
||||
* @param message - explanation of why argument is invalid
|
||||
* @constructor
|
||||
*/
|
||||
constructor(message: string);
|
||||
}
|
||||
export { InvalidArgumentError as InvalidOptionArgumentError }; // deprecated old name
|
||||
|
||||
export interface ErrorOptions { // optional parameter for error()
|
||||
/** an id string representing the error */
|
||||
code?: string;
|
||||
/** suggested exit code which could be used with process.exit */
|
||||
exitCode?: number;
|
||||
}
|
||||
|
||||
export class Argument {
|
||||
description: string;
|
||||
required: boolean;
|
||||
variadic: boolean;
|
||||
defaultValue?: any;
|
||||
defaultValueDescription?: string;
|
||||
argChoices?: string[];
|
||||
|
||||
/**
|
||||
* Initialize a new command argument with the given name and description.
|
||||
* The default is that the argument is required, and you can explicitly
|
||||
* indicate this with <> around the name. Put [] around the name for an optional argument.
|
||||
*/
|
||||
constructor(arg: string, description?: string);
|
||||
|
||||
/**
|
||||
* Return argument name.
|
||||
*/
|
||||
name(): string;
|
||||
|
||||
/**
|
||||
* Set the default value, and optionally supply the description to be displayed in the help.
|
||||
*/
|
||||
default(value: unknown, description?: string): this;
|
||||
|
||||
/**
|
||||
* Set the custom handler for processing CLI command arguments into argument values.
|
||||
*/
|
||||
argParser<T>(fn: (value: string, previous: T) => T): this;
|
||||
|
||||
/**
|
||||
* Only allow argument value to be one of choices.
|
||||
*/
|
||||
choices(values: readonly string[]): this;
|
||||
|
||||
/**
|
||||
* Make argument required.
|
||||
*/
|
||||
argRequired(): this;
|
||||
|
||||
/**
|
||||
* Make argument optional.
|
||||
*/
|
||||
argOptional(): this;
|
||||
}
|
||||
|
||||
export class Option {
|
||||
flags: string;
|
||||
description: string;
|
||||
|
||||
required: boolean; // A value must be supplied when the option is specified.
|
||||
optional: boolean; // A value is optional when the option is specified.
|
||||
variadic: boolean;
|
||||
mandatory: boolean; // The option must have a value after parsing, which usually means it must be specified on command line.
|
||||
short?: string;
|
||||
long?: string;
|
||||
negate: boolean;
|
||||
defaultValue?: any;
|
||||
defaultValueDescription?: string;
|
||||
presetArg?: unknown;
|
||||
envVar?: string;
|
||||
parseArg?: <T>(value: string, previous: T) => T;
|
||||
hidden: boolean;
|
||||
argChoices?: string[];
|
||||
|
||||
constructor(flags: string, description?: string);
|
||||
|
||||
/**
|
||||
* Set the default value, and optionally supply the description to be displayed in the help.
|
||||
*/
|
||||
default(value: unknown, description?: string): this;
|
||||
|
||||
/**
|
||||
* Preset to use when option used without option-argument, especially optional but also boolean and negated.
|
||||
* The custom processing (parseArg) is called.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* new Option('--color').default('GREYSCALE').preset('RGB');
|
||||
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
|
||||
* ```
|
||||
*/
|
||||
preset(arg: unknown): this;
|
||||
|
||||
/**
|
||||
* Add option name(s) that conflict with this option.
|
||||
* An error will be displayed if conflicting options are found during parsing.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* new Option('--rgb').conflicts('cmyk');
|
||||
* new Option('--js').conflicts(['ts', 'jsx']);
|
||||
* ```
|
||||
*/
|
||||
conflicts(names: string | string[]): this;
|
||||
|
||||
/**
|
||||
* Specify implied option values for when this option is set and the implied options are not.
|
||||
*
|
||||
* The custom processing (parseArg) is not called on the implied values.
|
||||
*
|
||||
* @example
|
||||
* program
|
||||
* .addOption(new Option('--log', 'write logging information to file'))
|
||||
* .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
|
||||
*/
|
||||
implies(optionValues: OptionValues): this;
|
||||
|
||||
/**
|
||||
* Set environment variable to check for option value.
|
||||
*
|
||||
* An environment variables is only used if when processed the current option value is
|
||||
* undefined, or the source of the current value is 'default' or 'config' or 'env'.
|
||||
*/
|
||||
env(name: string): this;
|
||||
|
||||
/**
|
||||
* Calculate the full description, including defaultValue etc.
|
||||
*/
|
||||
fullDescription(): string;
|
||||
|
||||
/**
|
||||
* Set the custom handler for processing CLI option arguments into option values.
|
||||
*/
|
||||
argParser<T>(fn: (value: string, previous: T) => T): this;
|
||||
|
||||
/**
|
||||
* Whether the option is mandatory and must have a value after parsing.
|
||||
*/
|
||||
makeOptionMandatory(mandatory?: boolean): this;
|
||||
|
||||
/**
|
||||
* Hide option in help.
|
||||
*/
|
||||
hideHelp(hide?: boolean): this;
|
||||
|
||||
/**
|
||||
* Only allow option value to be one of choices.
|
||||
*/
|
||||
choices(values: readonly string[]): this;
|
||||
|
||||
/**
|
||||
* Return option name.
|
||||
*/
|
||||
name(): string;
|
||||
|
||||
/**
|
||||
* Return option name, in a camelcase format that can be used
|
||||
* as a object attribute key.
|
||||
*/
|
||||
attributeName(): string;
|
||||
|
||||
/**
|
||||
* Return whether a boolean option.
|
||||
*
|
||||
* Options are one of boolean, negated, required argument, or optional argument.
|
||||
*/
|
||||
isBoolean(): boolean;
|
||||
}
|
||||
|
||||
export class Help {
|
||||
/** output helpWidth, long lines are wrapped to fit */
|
||||
helpWidth?: number;
|
||||
sortSubcommands: boolean;
|
||||
sortOptions: boolean;
|
||||
showGlobalOptions: boolean;
|
||||
|
||||
constructor();
|
||||
|
||||
/** Get the command term to show in the list of subcommands. */
|
||||
subcommandTerm(cmd: Command): string;
|
||||
/** Get the command summary to show in the list of subcommands. */
|
||||
subcommandDescription(cmd: Command): string;
|
||||
/** Get the option term to show in the list of options. */
|
||||
optionTerm(option: Option): string;
|
||||
/** Get the option description to show in the list of options. */
|
||||
optionDescription(option: Option): string;
|
||||
/** Get the argument term to show in the list of arguments. */
|
||||
argumentTerm(argument: Argument): string;
|
||||
/** Get the argument description to show in the list of arguments. */
|
||||
argumentDescription(argument: Argument): string;
|
||||
|
||||
/** Get the command usage to be displayed at the top of the built-in help. */
|
||||
commandUsage(cmd: Command): string;
|
||||
/** Get the description for the command. */
|
||||
commandDescription(cmd: Command): string;
|
||||
|
||||
/** Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one. */
|
||||
visibleCommands(cmd: Command): Command[];
|
||||
/** Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one. */
|
||||
visibleOptions(cmd: Command): Option[];
|
||||
/** Get an array of the visible global options. (Not including help.) */
|
||||
visibleGlobalOptions(cmd: Command): Option[];
|
||||
/** Get an array of the arguments which have descriptions. */
|
||||
visibleArguments(cmd: Command): Argument[];
|
||||
|
||||
/** Get the longest command term length. */
|
||||
longestSubcommandTermLength(cmd: Command, helper: Help): number;
|
||||
/** Get the longest option term length. */
|
||||
longestOptionTermLength(cmd: Command, helper: Help): number;
|
||||
/** Get the longest global option term length. */
|
||||
longestGlobalOptionTermLength(cmd: Command, helper: Help): number;
|
||||
/** Get the longest argument term length. */
|
||||
longestArgumentTermLength(cmd: Command, helper: Help): number;
|
||||
/** Calculate the pad width from the maximum term length. */
|
||||
padWidth(cmd: Command, helper: Help): number;
|
||||
|
||||
/**
|
||||
* Wrap the given string to width characters per line, with lines after the first indented.
|
||||
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
|
||||
*/
|
||||
wrap(str: string, width: number, indent: number, minColumnWidth?: number): string;
|
||||
|
||||
/** Generate the built-in help text. */
|
||||
formatHelp(cmd: Command, helper: Help): string;
|
||||
}
|
||||
export type HelpConfiguration = Partial<Help>;
|
||||
|
||||
export interface ParseOptions {
|
||||
from: 'node' | 'electron' | 'user';
|
||||
}
|
||||
export interface HelpContext { // optional parameter for .help() and .outputHelp()
|
||||
error: boolean;
|
||||
}
|
||||
export interface AddHelpTextContext { // passed to text function used with .addHelpText()
|
||||
error: boolean;
|
||||
command: Command;
|
||||
}
|
||||
export interface OutputConfiguration {
|
||||
writeOut?(str: string): void;
|
||||
writeErr?(str: string): void;
|
||||
getOutHelpWidth?(): number;
|
||||
getErrHelpWidth?(): number;
|
||||
outputError?(str: string, write: (str: string) => void): void;
|
||||
|
||||
}
|
||||
|
||||
export type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll';
|
||||
export type HookEvent = 'preSubcommand' | 'preAction' | 'postAction';
|
||||
// The source is a string so author can define their own too.
|
||||
export type OptionValueSource = LiteralUnion<'default' | 'config' | 'env' | 'cli' | 'implied', string> | undefined;
|
||||
|
||||
export type OptionValues = Record<string, any>;
|
||||
|
||||
export class Command {
|
||||
args: string[];
|
||||
processedArgs: any[];
|
||||
readonly commands: readonly Command[];
|
||||
readonly options: readonly Option[];
|
||||
readonly registeredArguments: readonly Argument[];
|
||||
parent: Command | null;
|
||||
|
||||
constructor(name?: string);
|
||||
|
||||
/**
|
||||
* Set the program version to `str`.
|
||||
*
|
||||
* This method auto-registers the "-V, --version" flag
|
||||
* which will print the version number when passed.
|
||||
*
|
||||
* You can optionally supply the flags and description to override the defaults.
|
||||
*/
|
||||
version(str: string, flags?: string, description?: string): this;
|
||||
/**
|
||||
* Get the program version.
|
||||
*/
|
||||
version(): string | undefined;
|
||||
|
||||
/**
|
||||
* Define a command, implemented using an action handler.
|
||||
*
|
||||
* @remarks
|
||||
* The command description is supplied using `.description`, not as a parameter to `.command`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* program
|
||||
* .command('clone <source> [destination]')
|
||||
* .description('clone a repository into a newly created directory')
|
||||
* .action((source, destination) => {
|
||||
* console.log('clone command called');
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
|
||||
* @param opts - configuration options
|
||||
* @returns new command
|
||||
*/
|
||||
command(nameAndArgs: string, opts?: CommandOptions): ReturnType<this['createCommand']>;
|
||||
/**
|
||||
* Define a command, implemented in a separate executable file.
|
||||
*
|
||||
* @remarks
|
||||
* The command description is supplied as the second parameter to `.command`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* program
|
||||
* .command('start <service>', 'start named service')
|
||||
* .command('stop [service]', 'stop named service, or all if no name supplied');
|
||||
* ```
|
||||
*
|
||||
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
|
||||
* @param description - description of executable command
|
||||
* @param opts - configuration options
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this;
|
||||
|
||||
/**
|
||||
* Factory routine to create a new unattached command.
|
||||
*
|
||||
* See .command() for creating an attached subcommand, which uses this routine to
|
||||
* create the command. You can override createCommand to customise subcommands.
|
||||
*/
|
||||
createCommand(name?: string): Command;
|
||||
|
||||
/**
|
||||
* Add a prepared subcommand.
|
||||
*
|
||||
* See .command() for creating an attached subcommand which inherits settings from its parent.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
addCommand(cmd: Command, opts?: CommandOptions): this;
|
||||
|
||||
/**
|
||||
* Factory routine to create a new unattached argument.
|
||||
*
|
||||
* See .argument() for creating an attached argument, which uses this routine to
|
||||
* create the argument. You can override createArgument to return a custom argument.
|
||||
*/
|
||||
createArgument(name: string, description?: string): Argument;
|
||||
|
||||
/**
|
||||
* Define argument syntax for command.
|
||||
*
|
||||
* The default is that the argument is required, and you can explicitly
|
||||
* indicate this with <> around the name. Put [] around the name for an optional argument.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* program.argument('<input-file>');
|
||||
* program.argument('[output-file]');
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
argument<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
|
||||
argument(name: string, description?: string, defaultValue?: unknown): this;
|
||||
|
||||
/**
|
||||
* Define argument syntax for command, adding a prepared argument.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
addArgument(arg: Argument): this;
|
||||
|
||||
/**
|
||||
* Define argument syntax for command, adding multiple at once (without descriptions).
|
||||
*
|
||||
* See also .argument().
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* program.arguments('<cmd> [env]');
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
arguments(names: string): this;
|
||||
|
||||
/**
|
||||
* Override default decision whether to add implicit help command.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* addHelpCommand() // force on
|
||||
* addHelpCommand(false); // force off
|
||||
* addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
addHelpCommand(enableOrNameAndArgs?: string | boolean, description?: string): this;
|
||||
|
||||
/**
|
||||
* Add hook for life cycle event.
|
||||
*/
|
||||
hook(event: HookEvent, listener: (thisCommand: Command, actionCommand: Command) => void | Promise<void>): this;
|
||||
|
||||
/**
|
||||
* Register callback to use as replacement for calling process.exit.
|
||||
*/
|
||||
exitOverride(callback?: (err: CommanderError) => never | void): this;
|
||||
|
||||
/**
|
||||
* Display error message and exit (or call exitOverride).
|
||||
*/
|
||||
error(message: string, errorOptions?: ErrorOptions): never;
|
||||
|
||||
/**
|
||||
* You can customise the help with a subclass of Help by overriding createHelp,
|
||||
* or by overriding Help properties using configureHelp().
|
||||
*/
|
||||
createHelp(): Help;
|
||||
|
||||
/**
|
||||
* You can customise the help by overriding Help properties using configureHelp(),
|
||||
* or with a subclass of Help by overriding createHelp().
|
||||
*/
|
||||
configureHelp(configuration: HelpConfiguration): this;
|
||||
/** Get configuration */
|
||||
configureHelp(): HelpConfiguration;
|
||||
|
||||
/**
|
||||
* The default output goes to stdout and stderr. You can customise this for special
|
||||
* applications. You can also customise the display of errors by overriding outputError.
|
||||
*
|
||||
* The configuration properties are all functions:
|
||||
* ```
|
||||
* // functions to change where being written, stdout and stderr
|
||||
* writeOut(str)
|
||||
* writeErr(str)
|
||||
* // matching functions to specify width for wrapping help
|
||||
* getOutHelpWidth()
|
||||
* getErrHelpWidth()
|
||||
* // functions based on what is being written out
|
||||
* outputError(str, write) // used for displaying errors, and not used for displaying help
|
||||
* ```
|
||||
*/
|
||||
configureOutput(configuration: OutputConfiguration): this;
|
||||
/** Get configuration */
|
||||
configureOutput(): OutputConfiguration;
|
||||
|
||||
/**
|
||||
* Copy settings that are useful to have in common across root command and subcommands.
|
||||
*
|
||||
* (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
|
||||
*/
|
||||
copyInheritedSettings(sourceCommand: Command): this;
|
||||
|
||||
/**
|
||||
* Display the help or a custom message after an error occurs.
|
||||
*/
|
||||
showHelpAfterError(displayHelp?: boolean | string): this;
|
||||
|
||||
/**
|
||||
* Display suggestion of similar commands for unknown commands, or options for unknown options.
|
||||
*/
|
||||
showSuggestionAfterError(displaySuggestion?: boolean): this;
|
||||
|
||||
/**
|
||||
* Register callback `fn` for the command.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* program
|
||||
* .command('serve')
|
||||
* .description('start service')
|
||||
* .action(function() {
|
||||
* // do work here
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
action(fn: (...args: any[]) => void | Promise<void>): this;
|
||||
|
||||
/**
|
||||
* Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both.
|
||||
*
|
||||
* The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required
|
||||
* option-argument is indicated by `<>` and an optional option-argument by `[]`.
|
||||
*
|
||||
* See the README for more details, and see also addOption() and requiredOption().
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* program
|
||||
* .option('-p, --pepper', 'add pepper')
|
||||
* .option('-p, --pizza-type <TYPE>', 'type of pizza') // required option-argument
|
||||
* .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
|
||||
* .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
|
||||
option<T>(flags: string, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: T): this;
|
||||
/** @deprecated since v7, instead use choices or a custom function */
|
||||
option(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
|
||||
|
||||
/**
|
||||
* Define a required option, which must have a value after parsing. This usually means
|
||||
* the option must be specified on the command line. (Otherwise the same as .option().)
|
||||
*
|
||||
* The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
|
||||
*/
|
||||
requiredOption(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
|
||||
requiredOption<T>(flags: string, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: T): this;
|
||||
/** @deprecated since v7, instead use choices or a custom function */
|
||||
requiredOption(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
|
||||
|
||||
/**
|
||||
* Factory routine to create a new unattached option.
|
||||
*
|
||||
* See .option() for creating an attached option, which uses this routine to
|
||||
* create the option. You can override createOption to return a custom option.
|
||||
*/
|
||||
|
||||
createOption(flags: string, description?: string): Option;
|
||||
|
||||
/**
|
||||
* Add a prepared Option.
|
||||
*
|
||||
* See .option() and .requiredOption() for creating and attaching an option in a single call.
|
||||
*/
|
||||
addOption(option: Option): this;
|
||||
|
||||
/**
|
||||
* Whether to store option values as properties on command object,
|
||||
* or store separately (specify false). In both cases the option values can be accessed using .opts().
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
storeOptionsAsProperties<T extends OptionValues>(): this & T;
|
||||
storeOptionsAsProperties<T extends OptionValues>(storeAsProperties: true): this & T;
|
||||
storeOptionsAsProperties(storeAsProperties?: boolean): this;
|
||||
|
||||
/**
|
||||
* Retrieve option value.
|
||||
*/
|
||||
getOptionValue(key: string): any;
|
||||
|
||||
/**
|
||||
* Store option value.
|
||||
*/
|
||||
setOptionValue(key: string, value: unknown): this;
|
||||
|
||||
/**
|
||||
* Store option value and where the value came from.
|
||||
*/
|
||||
setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this;
|
||||
|
||||
/**
|
||||
* Get source of option value.
|
||||
*/
|
||||
getOptionValueSource(key: string): OptionValueSource | undefined;
|
||||
|
||||
/**
|
||||
* Get source of option value. See also .optsWithGlobals().
|
||||
*/
|
||||
getOptionValueSourceWithGlobals(key: string): OptionValueSource | undefined;
|
||||
|
||||
/**
|
||||
* Alter parsing of short flags with optional values.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* // for `.option('-f,--flag [value]'):
|
||||
* .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour
|
||||
* .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
combineFlagAndOptionalValue(combine?: boolean): this;
|
||||
|
||||
/**
|
||||
* Allow unknown options on the command line.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
allowUnknownOption(allowUnknown?: boolean): this;
|
||||
|
||||
/**
|
||||
* Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
allowExcessArguments(allowExcess?: boolean): this;
|
||||
|
||||
/**
|
||||
* Enable positional options. Positional means global options are specified before subcommands which lets
|
||||
* subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions.
|
||||
*
|
||||
* The default behaviour is non-positional and global options may appear anywhere on the command line.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
enablePositionalOptions(positional?: boolean): this;
|
||||
|
||||
/**
|
||||
* Pass through options that come after command-arguments rather than treat them as command-options,
|
||||
* so actual command-options come before command-arguments. Turning this on for a subcommand requires
|
||||
* positional options to have been enabled on the program (parent commands).
|
||||
*
|
||||
* The default behaviour is non-positional and options may appear before or after command-arguments.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
passThroughOptions(passThrough?: boolean): this;
|
||||
|
||||
/**
|
||||
* Parse `argv`, setting options and invoking commands when defined.
|
||||
*
|
||||
* The default expectation is that the arguments are from node and have the application as argv[0]
|
||||
* and the script being run in argv[1], with user parameters after that.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* program.parse(process.argv);
|
||||
* program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
|
||||
* program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
parse(argv?: readonly string[], options?: ParseOptions): this;
|
||||
|
||||
/**
|
||||
* Parse `argv`, setting options and invoking commands when defined.
|
||||
*
|
||||
* Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
|
||||
*
|
||||
* The default expectation is that the arguments are from node and have the application as argv[0]
|
||||
* and the script being run in argv[1], with user parameters after that.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* program.parseAsync(process.argv);
|
||||
* program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
|
||||
* program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
|
||||
* ```
|
||||
*
|
||||
* @returns Promise
|
||||
*/
|
||||
parseAsync(argv?: readonly string[], options?: ParseOptions): Promise<this>;
|
||||
|
||||
/**
|
||||
* Parse options from `argv` removing known options,
|
||||
* and return argv split into operands and unknown arguments.
|
||||
*
|
||||
* argv => operands, unknown
|
||||
* --known kkk op => [op], []
|
||||
* op --known kkk => [op], []
|
||||
* sub --unknown uuu op => [sub], [--unknown uuu op]
|
||||
* sub -- --unknown uuu op => [sub --unknown uuu op], []
|
||||
*/
|
||||
parseOptions(argv: string[]): ParseOptionsResult;
|
||||
|
||||
/**
|
||||
* Return an object containing local option values as key-value pairs
|
||||
*/
|
||||
opts<T extends OptionValues>(): T;
|
||||
|
||||
/**
|
||||
* Return an object containing merged local and global option values as key-value pairs.
|
||||
*/
|
||||
optsWithGlobals<T extends OptionValues>(): T;
|
||||
|
||||
/**
|
||||
* Set the description.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
|
||||
description(str: string): this;
|
||||
/** @deprecated since v8, instead use .argument to add command argument with description */
|
||||
description(str: string, argsDescription: Record<string, string>): this;
|
||||
/**
|
||||
* Get the description.
|
||||
*/
|
||||
description(): string;
|
||||
|
||||
/**
|
||||
* Set the summary. Used when listed as subcommand of parent.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
|
||||
summary(str: string): this;
|
||||
/**
|
||||
* Get the summary.
|
||||
*/
|
||||
summary(): string;
|
||||
|
||||
/**
|
||||
* Set an alias for the command.
|
||||
*
|
||||
* You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
alias(alias: string): this;
|
||||
/**
|
||||
* Get alias for the command.
|
||||
*/
|
||||
alias(): string;
|
||||
|
||||
/**
|
||||
* Set aliases for the command.
|
||||
*
|
||||
* Only the first alias is shown in the auto-generated help.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
aliases(aliases: readonly string[]): this;
|
||||
/**
|
||||
* Get aliases for the command.
|
||||
*/
|
||||
aliases(): string[];
|
||||
|
||||
/**
|
||||
* Set the command usage.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
usage(str: string): this;
|
||||
/**
|
||||
* Get the command usage.
|
||||
*/
|
||||
usage(): string;
|
||||
|
||||
/**
|
||||
* Set the name of the command.
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
name(str: string): this;
|
||||
/**
|
||||
* Get the name of the command.
|
||||
*/
|
||||
name(): string;
|
||||
|
||||
/**
|
||||
* Set the name of the command from script filename, such as process.argv[1],
|
||||
* or require.main.filename, or __filename.
|
||||
*
|
||||
* (Used internally and public although not documented in README.)
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* program.nameFromFilename(require.main.filename);
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
nameFromFilename(filename: string): this;
|
||||
|
||||
/**
|
||||
* Set the directory for searching for executable subcommands of this command.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* program.executableDir(__dirname);
|
||||
* // or
|
||||
* program.executableDir('subcommands');
|
||||
* ```
|
||||
*
|
||||
* @returns `this` command for chaining
|
||||
*/
|
||||
executableDir(path: string): this;
|
||||
/**
|
||||
* Get the executable search directory.
|
||||
*/
|
||||
executableDir(): string | null;
|
||||
|
||||
/**
|
||||
* Output help information for this command.
|
||||
*
|
||||
* Outputs built-in help, and custom text added using `.addHelpText()`.
|
||||
*
|
||||
*/
|
||||
outputHelp(context?: HelpContext): void;
|
||||
/** @deprecated since v7 */
|
||||
outputHelp(cb?: (str: string) => string): void;
|
||||
|
||||
/**
|
||||
* Return command help documentation.
|
||||
*/
|
||||
helpInformation(context?: HelpContext): string;
|
||||
|
||||
/**
|
||||
* You can pass in flags and a description to override the help
|
||||
* flags and help description for your command. Pass in false
|
||||
* to disable the built-in help option.
|
||||
*/
|
||||
helpOption(flags?: string | boolean, description?: string): this;
|
||||
|
||||
/**
|
||||
* Output help information and exit.
|
||||
*
|
||||
* Outputs built-in help, and custom text added using `.addHelpText()`.
|
||||
*/
|
||||
help(context?: HelpContext): never;
|
||||
/** @deprecated since v7 */
|
||||
help(cb?: (str: string) => string): never;
|
||||
|
||||
/**
|
||||
* Add additional text to be displayed with the built-in help.
|
||||
*
|
||||
* Position is 'before' or 'after' to affect just this command,
|
||||
* and 'beforeAll' or 'afterAll' to affect this command and all its subcommands.
|
||||
*/
|
||||
addHelpText(position: AddHelpTextPosition, text: string): this;
|
||||
addHelpText(position: AddHelpTextPosition, text: (context: AddHelpTextContext) => string): this;
|
||||
|
||||
/**
|
||||
* Add a listener (callback) for when events occur. (Implemented using EventEmitter.)
|
||||
*/
|
||||
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
}
|
||||
|
||||
export interface CommandOptions {
|
||||
hidden?: boolean;
|
||||
isDefault?: boolean;
|
||||
/** @deprecated since v7, replaced by hidden */
|
||||
noHelp?: boolean;
|
||||
}
|
||||
export interface ExecutableCommandOptions extends CommandOptions {
|
||||
executableFile?: string;
|
||||
}
|
||||
|
||||
export interface ParseOptionsResult {
|
||||
operands: string[];
|
||||
unknown: string[];
|
||||
}
|
||||
|
||||
export function createCommand(name?: string): Command;
|
||||
export function createOption(flags: string, description?: string): Option;
|
||||
export function createArgument(name: string, description?: string): Argument;
|
||||
|
||||
export const program: Command;
|
||||
158
node_modules/svgo/package.json
generated
vendored
158
node_modules/svgo/package.json
generated
vendored
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"packageManager": "yarn@2.4.3",
|
||||
"packageManager": "yarn@3.8.7",
|
||||
"name": "svgo",
|
||||
"version": "2.8.0",
|
||||
"description": "Nodejs-based tool for optimizing SVG vector graphics files",
|
||||
"version": "4.0.0",
|
||||
"description": "SVGO is a Node.js library and command-line application for optimizing vector images.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"svgo",
|
||||
"svg",
|
||||
"optimize",
|
||||
"minify"
|
||||
],
|
||||
"homepage": "https://github.com/svg/svgo",
|
||||
"homepage": "https://svgo.dev",
|
||||
"bugs": {
|
||||
"url": "https://github.com/svg/svgo/issues"
|
||||
},
|
||||
@@ -34,98 +35,107 @@
|
||||
"name": "Bogdan Chadkin",
|
||||
"email": "trysound@yandex.ru",
|
||||
"url": "https://github.com/TrySound"
|
||||
},
|
||||
{
|
||||
"name": "Seth Falco",
|
||||
"email": "seth@falco.fun",
|
||||
"url": "https://falco.fun/"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/svg/svgo.git"
|
||||
},
|
||||
"main": "./lib/svgo-node.js",
|
||||
"bin": "./bin/svgo",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/svgo"
|
||||
},
|
||||
"bin": "./bin/svgo.js",
|
||||
"types": "./types/lib/svgo-node.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/svgo-node.js",
|
||||
"require": "./dist/svgo-node.cjs",
|
||||
"types": "./types/lib/svgo-node.d.ts"
|
||||
},
|
||||
"./browser": {
|
||||
"import": "./dist/svgo.browser.js",
|
||||
"types": "./types/lib/svgo.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"dist",
|
||||
"lib",
|
||||
"plugins",
|
||||
"dist",
|
||||
"types",
|
||||
"!**/*.test.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
"node": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules jest --maxWorkers=4 --coverage",
|
||||
"lint": "eslint --ignore-path .gitignore . && prettier --check \"**/*.js\" --ignore-path .gitignore",
|
||||
"fix": "eslint --ignore-path .gitignore --fix . && prettier --write \"**/*.js\" --ignore-path .gitignore",
|
||||
"typecheck": "tsc",
|
||||
"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 && rollup -c"
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "2021"
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"rollup.config.js"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/*.test.js"
|
||||
],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
]
|
||||
"build": "node scripts/sync-version.js && yarn build:bundles && yarn build:types",
|
||||
"build:bundles": "yarn clean:build && rollup -c",
|
||||
"build:types": "yarn clean:types && tsc && tsc -p tsconfig.build.json",
|
||||
"lint": "eslint . && prettier --check .",
|
||||
"lint:fix": "eslint --fix . && prettier --write .",
|
||||
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --maxWorkers=4 --coverage",
|
||||
"test:bundles": "yarn build:bundles && node ./test/svgo.cjs && node ./test/browser.js",
|
||||
"test:types": "yarn build:types && tsc && tsd",
|
||||
"test:regression": "node ./test/regression-extract.js && cross-env NO_DIFF=1 node ./test/regression.js",
|
||||
"qa": "yarn lint && yarn test:types && yarn test && yarn test:bundles && yarn test:regression",
|
||||
"clean": "yarn clean:build && yarn clean:types",
|
||||
"clean:build": "rimraf dist",
|
||||
"clean:types": "rimraf types",
|
||||
"prepublishOnly": "yarn clean && yarn build"
|
||||
},
|
||||
"jest": {
|
||||
"coveragePathIgnorePatterns": [
|
||||
"fixtures"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"html",
|
||||
"lcov",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^4.1.3",
|
||||
"css-tree": "^1.1.3",
|
||||
"csso": "^4.2.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"stable": "^0.1.8"
|
||||
"commander": "^11.1.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^3.0.1",
|
||||
"css-what": "^6.1.0",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.1.1",
|
||||
"sax": "^1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@types/css-tree": "^1.0.6",
|
||||
"@types/csso": "^4.2.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
"del": "^6.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"jest": "^27.2.5",
|
||||
"node-fetch": "^2.6.2",
|
||||
"pixelmatch": "^5.2.1",
|
||||
"playwright": "^1.14.1",
|
||||
"pngjs": "^6.0.0",
|
||||
"prettier": "^2.4.0",
|
||||
"rollup": "^2.56.3",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"tar-stream": "^2.2.0",
|
||||
"typescript": "^4.4.3"
|
||||
"@eslint/js": "^9.25.1",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@rollup/plugin-commonjs": "^26.0.3",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/css-tree": "^2.3.10",
|
||||
"@types/csso": "^5.0.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.15.3",
|
||||
"@types/sax": "^1.2.7",
|
||||
"@types/tar-stream": "^3.1.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.25.1",
|
||||
"globals": "^14.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"pixelmatch": "^7.1.0",
|
||||
"playwright": "^1.52.0",
|
||||
"pngjs": "^7.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"rimraf": "^5.0.10",
|
||||
"rollup": "^4.22.4",
|
||||
"tar-stream": "^3.1.7",
|
||||
"tsd": "^0.32.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"sax@^1.4.1": "patch:sax@npm%3A1.4.1#./.yarn/patches/sax-npm-1.4.1-503b1923cb.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
1703
node_modules/svgo/plugins/_collections.js
generated
vendored
1703
node_modules/svgo/plugins/_collections.js
generated
vendored
File diff suppressed because it is too large
Load Diff
386
node_modules/svgo/plugins/_path.js
generated
vendored
386
node_modules/svgo/plugins/_path.js
generated
vendored
@@ -1,28 +1,41 @@
|
||||
'use strict';
|
||||
import { parsePathData, stringifyPathData } from '../lib/path.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
* @typedef {import('../lib/types').PathDataItem} PathDataItem
|
||||
* @typedef Js2PathParams
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} noSpaceAfterFlags
|
||||
*
|
||||
* @typedef Point
|
||||
* @property {number[][]} list
|
||||
* @property {number} minX
|
||||
* @property {number} minY
|
||||
* @property {number} maxX
|
||||
* @property {number} maxY
|
||||
*
|
||||
* @typedef Points
|
||||
* @property {Point[]} list
|
||||
* @property {number} minX
|
||||
* @property {number} minY
|
||||
* @property {number} maxX
|
||||
* @property {number} maxY
|
||||
*/
|
||||
|
||||
const { parsePathData, stringifyPathData } = require('../lib/path.js');
|
||||
|
||||
/**
|
||||
* @type {[number, number]}
|
||||
*/
|
||||
var prevCtrlPoint;
|
||||
/** @type {[number, number]} */
|
||||
let prevCtrlPoint;
|
||||
|
||||
/**
|
||||
* Convert path string to JS representation.
|
||||
*
|
||||
* @type {(path: XastElement) => Array<PathDataItem>}
|
||||
* @param {import('../lib/types.js').XastElement} path
|
||||
* @returns {import('../lib/types.js').PathDataItem[]}
|
||||
*/
|
||||
const path2js = (path) => {
|
||||
// @ts-ignore legacy
|
||||
if (path.pathJS) return path.pathJS;
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
export const path2js = (path) => {
|
||||
// @ts-expect-error legacy
|
||||
if (path.pathJS) {
|
||||
// @ts-expect-error legacy
|
||||
return path.pathJS;
|
||||
}
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = []; // JS representation of the path data
|
||||
const newPathData = parsePathData(path.attributes.d);
|
||||
for (const { command, args } of newPathData) {
|
||||
@@ -32,25 +45,22 @@ const path2js = (path) => {
|
||||
if (pathData.length && pathData[0].command == 'm') {
|
||||
pathData[0].command = 'M';
|
||||
}
|
||||
// @ts-ignore legacy
|
||||
// @ts-expect-error legacy
|
||||
path.pathJS = pathData;
|
||||
return pathData;
|
||||
};
|
||||
exports.path2js = path2js;
|
||||
|
||||
/**
|
||||
* Convert relative Path data to absolute.
|
||||
*
|
||||
* @type {(data: Array<PathDataItem>) => Array<PathDataItem>}
|
||||
*
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} data
|
||||
* @returns {import('../lib/types.js').PathDataItem[]}
|
||||
*/
|
||||
const convertRelativeToAbsolute = (data) => {
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const newData = [];
|
||||
let start = [0, 0];
|
||||
let cursor = [0, 0];
|
||||
const start = [0, 0];
|
||||
const cursor = [0, 0];
|
||||
|
||||
for (let { command, args } of data) {
|
||||
args = args.slice();
|
||||
@@ -172,17 +182,15 @@ const convertRelativeToAbsolute = (data) => {
|
||||
return newData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{ floatPrecision?: number, noSpaceAfterFlags?: boolean }} Js2PathParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert path array to string.
|
||||
*
|
||||
* @type {(path: XastElement, data: Array<PathDataItem>, params: Js2PathParams) => void}
|
||||
* @param {import('../lib/types.js').XastElement} path
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} data
|
||||
* @param {Js2PathParams} params
|
||||
*/
|
||||
exports.js2path = function (path, data, params) {
|
||||
// @ts-ignore legacy
|
||||
export const js2path = function (path, data, params) {
|
||||
// @ts-expect-error legacy
|
||||
path.pathJS = data;
|
||||
|
||||
const pathData = [];
|
||||
@@ -211,7 +219,9 @@ exports.js2path = function (path, data, params) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(dest: Array<number>, source: Array<number>) => Array<number>}
|
||||
* @param {number[]} dest
|
||||
* @param {ReadonlyArray<number>} source
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function set(dest, source) {
|
||||
dest[0] = source[source.length - 2];
|
||||
@@ -224,9 +234,11 @@ function set(dest, source) {
|
||||
* collision using Gilbert-Johnson-Keerthi distance algorithm
|
||||
* https://web.archive.org/web/20180822200027/http://entropyinteractive.com/2011/04/gjk-algorithm/
|
||||
*
|
||||
* @type {(path1: Array<PathDataItem>, path2: Array<PathDataItem>) => boolean}
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} path1
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} path2
|
||||
* @returns {boolean}
|
||||
*/
|
||||
exports.intersects = function (path1, path2) {
|
||||
export const intersects = function (path1, path2) {
|
||||
// Collect points of every subpath.
|
||||
const points1 = gatherPoints(convertRelativeToAbsolute(path1));
|
||||
const points2 = gatherPoints(convertRelativeToAbsolute(path2));
|
||||
@@ -247,8 +259,9 @@ exports.intersects = function (path1, path2) {
|
||||
);
|
||||
});
|
||||
})
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a convex hull from points of each subpath. Has the most complexity O(n·log n).
|
||||
const hullNest1 = points1.list.map(convexHull);
|
||||
@@ -256,58 +269,72 @@ exports.intersects = function (path1, path2) {
|
||||
|
||||
// Check intersection of every subpath of the first path with every subpath of the second.
|
||||
return hullNest1.some(function (hull1) {
|
||||
if (hull1.list.length < 3) return false;
|
||||
if (hull1.list.length < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hullNest2.some(function (hull2) {
|
||||
if (hull2.list.length < 3) return false;
|
||||
if (hull2.list.length < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex
|
||||
direction = minus(simplex[0]); // set the direction to point towards the origin
|
||||
const simplex = [getSupport(hull1, hull2, [1, 0])]; // create the initial simplex
|
||||
const direction = minus(simplex[0]); // set the direction to point towards the origin
|
||||
|
||||
let iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough
|
||||
|
||||
var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (iterations-- == 0) {
|
||||
console.error(
|
||||
'Error: infinite loop while processing mergePaths plugin.'
|
||||
'Error: infinite loop while processing mergePaths plugin.',
|
||||
);
|
||||
return true; // true is the safe value that means “do nothing with paths”
|
||||
}
|
||||
// add a new point
|
||||
simplex.push(getSupport(hull1, hull2, direction));
|
||||
// see if the new point was on the correct side of the origin
|
||||
if (dot(direction, simplex[simplex.length - 1]) <= 0) return false;
|
||||
if (dot(direction, simplex[simplex.length - 1]) <= 0) {
|
||||
return false;
|
||||
}
|
||||
// process the simplex
|
||||
if (processSimplex(simplex, direction)) return true;
|
||||
if (processSimplex(simplex, direction)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @type {(a: Point, b: Point, direction: Array<number>) => Array<number>}
|
||||
* @param {Point} a
|
||||
* @param {Point} b
|
||||
* @param {ReadonlyArray<number>} direction
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function getSupport(a, b, direction) {
|
||||
return sub(supportPoint(a, direction), supportPoint(b, minus(direction)));
|
||||
}
|
||||
|
||||
// Computes farthest polygon point in particular direction.
|
||||
// Thanks to knowledge of min/max x and y coordinates we can choose a quadrant to search in.
|
||||
// Since we're working on convex hull, the dot product is increasing until we find the farthest point.
|
||||
/**
|
||||
* @type {(polygon: Point, direction: Array<number>) => Array<number>}
|
||||
* Computes farthest polygon point in particular direction. Thanks to
|
||||
* knowledge of min/max x and y coordinates we can choose a quadrant to search
|
||||
* in. Since we're working on convex hull, the dot product is increasing until
|
||||
* we find the farthest point.
|
||||
*
|
||||
* @param {Point} polygon
|
||||
* @param {ReadonlyArray<number>} direction
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function supportPoint(polygon, direction) {
|
||||
var index =
|
||||
direction[1] >= 0
|
||||
? direction[0] < 0
|
||||
? polygon.maxY
|
||||
: polygon.maxX
|
||||
: direction[0] < 0
|
||||
let index =
|
||||
direction[1] >= 0
|
||||
? direction[0] < 0
|
||||
? polygon.maxY
|
||||
: polygon.maxX
|
||||
: direction[0] < 0
|
||||
? polygon.minX
|
||||
: polygon.minY,
|
||||
max = -Infinity,
|
||||
value;
|
||||
: polygon.minY;
|
||||
let max = -Infinity;
|
||||
let value;
|
||||
while ((value = dot(polygon.list[index], direction)) > max) {
|
||||
max = value;
|
||||
index = ++index % polygon.list.length;
|
||||
@@ -317,16 +344,18 @@ exports.intersects = function (path1, path2) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(simplex: Array<Array<number>>, direction: Array<number>) => boolean}
|
||||
* @param {number[][]} simplex
|
||||
* @param {number[]} direction
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function processSimplex(simplex, direction) {
|
||||
// we only need to handle to 1-simplex and 2-simplex
|
||||
if (simplex.length == 2) {
|
||||
// 1-simplex
|
||||
let a = simplex[1],
|
||||
b = simplex[0],
|
||||
AO = minus(simplex[1]),
|
||||
AB = sub(b, a);
|
||||
const a = simplex[1];
|
||||
const b = simplex[0];
|
||||
const AO = minus(simplex[1]);
|
||||
const AB = sub(b, a);
|
||||
// AO is in the same direction as AB
|
||||
if (dot(AO, AB) > 0) {
|
||||
// get the vector perpendicular to AB facing O
|
||||
@@ -338,14 +367,14 @@ function processSimplex(simplex, direction) {
|
||||
}
|
||||
} else {
|
||||
// 2-simplex
|
||||
let a = simplex[2], // [a, b, c] = simplex
|
||||
b = simplex[1],
|
||||
c = simplex[0],
|
||||
AB = sub(b, a),
|
||||
AC = sub(c, a),
|
||||
AO = minus(a),
|
||||
ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C
|
||||
ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B
|
||||
const a = simplex[2]; // [a, b, c] = simplex
|
||||
const b = simplex[1];
|
||||
const c = simplex[0];
|
||||
const AB = sub(b, a);
|
||||
const AC = sub(c, a);
|
||||
const AO = minus(a);
|
||||
const ACB = orth(AB, AC); // the vector perpendicular to AB facing away from C
|
||||
const ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B
|
||||
|
||||
if (dot(ACB, AO) > 0) {
|
||||
if (dot(AB, AO) > 0) {
|
||||
@@ -368,72 +397,62 @@ function processSimplex(simplex, direction) {
|
||||
simplex.splice(0, 2); // simplex = [a]
|
||||
}
|
||||
} // region 7
|
||||
else return true;
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(v: Array<number>) => Array<number>}
|
||||
* @param {ReadonlyArray<number>} v
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function minus(v) {
|
||||
return [-v[0], -v[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(v1: Array<number>, v2: Array<number>) => Array<number>}
|
||||
* @param {ReadonlyArray<number>} v1
|
||||
* @param {ReadonlyArray<number>} v2
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function sub(v1, v2) {
|
||||
return [v1[0] - v2[0], v1[1] - v2[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(v1: Array<number>, v2: Array<number>) => number}
|
||||
* @param {ReadonlyArray<number>} v1
|
||||
* @param {ReadonlyArray<number>} v2
|
||||
* @returns {number}
|
||||
*/
|
||||
function dot(v1, v2) {
|
||||
return v1[0] * v2[0] + v1[1] * v2[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(v1: Array<number>, v2: Array<number>) => Array<number>}
|
||||
* @param {ReadonlyArray<number>} v
|
||||
* @param {ReadonlyArray<number>} from
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function orth(v, from) {
|
||||
var o = [-v[1], v[0]];
|
||||
const o = [-v[1], v[0]];
|
||||
return dot(o, minus(from)) < 0 ? minus(o) : o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* list: Array<Array<number>>,
|
||||
* minX: number,
|
||||
* minY: number,
|
||||
* maxX: number,
|
||||
* maxY: number
|
||||
* }} Point
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* list: Array<Point>,
|
||||
* minX: number,
|
||||
* minY: number,
|
||||
* maxX: number,
|
||||
* maxY: number
|
||||
* }} Points
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {(pathData: Array<PathDataItem>) => Points}
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} pathData
|
||||
* @returns {Points}
|
||||
*/
|
||||
function gatherPoints(pathData) {
|
||||
/**
|
||||
* @type {Points}
|
||||
*/
|
||||
/** @type {Points} */
|
||||
const points = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
||||
|
||||
// Writes data about the extreme points on each axle
|
||||
/**
|
||||
* @type {(path: Point, point: Array<number>) => void}
|
||||
* Writes data about the extreme points on each axle.
|
||||
*
|
||||
* @param {Point} path
|
||||
* @param {number[]} point
|
||||
*/
|
||||
const addPoint = (path, point) => {
|
||||
if (!path.list.length || point[1] > path.list[path.maxY][1]) {
|
||||
@@ -469,15 +488,17 @@ function gatherPoints(pathData) {
|
||||
points.list.length === 0
|
||||
? { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 }
|
||||
: points.list[points.list.length - 1];
|
||||
let prev = i === 0 ? null : pathData[i - 1];
|
||||
const prev = i === 0 ? null : pathData[i - 1];
|
||||
let basePoint =
|
||||
subPath.list.length === 0 ? null : subPath.list[subPath.list.length - 1];
|
||||
let data = pathDataItem.args;
|
||||
const data = pathDataItem.args;
|
||||
let ctrlPoint = basePoint;
|
||||
|
||||
// TODO fix null hack
|
||||
/**
|
||||
* @type {(n: number, i: number) => number}
|
||||
* TODO fix null hack
|
||||
* @param {number} n
|
||||
* @param {number} i
|
||||
* @returns {number}
|
||||
*/
|
||||
const toAbsolute = (n, i) => n + (basePoint == null ? 0 : basePoint[i % 2]);
|
||||
|
||||
@@ -521,7 +542,7 @@ function gatherPoints(pathData) {
|
||||
|
||||
case 'C':
|
||||
if (basePoint != null) {
|
||||
// Approximate quibic Bezier curve with middle points between control points
|
||||
// Approximate cubic Bezier curve with middle points between control points
|
||||
addPoint(subPath, [
|
||||
0.5 * (basePoint[0] + data[0]),
|
||||
0.5 * (basePoint[1] + data[1]),
|
||||
@@ -568,9 +589,9 @@ function gatherPoints(pathData) {
|
||||
|
||||
case 'A':
|
||||
if (basePoint != null) {
|
||||
// Convert the arc to bezier curves and use the same approximation
|
||||
// @ts-ignore no idea what's going on here
|
||||
var curves = a2c.apply(0, basePoint.concat(data));
|
||||
// Convert the arc to Bézier curves and use the same approximation
|
||||
// @ts-expect-error no idea what's going on here
|
||||
const curves = a2c.apply(0, basePoint.concat(data));
|
||||
for (
|
||||
var cData;
|
||||
(cData = curves.splice(0, 6).map(toAbsolute)).length;
|
||||
@@ -590,33 +611,39 @@ function gatherPoints(pathData) {
|
||||
0.5 * (cData[2] + cData[4]),
|
||||
0.5 * (cData[3] + cData[5]),
|
||||
]);
|
||||
if (curves.length) addPoint(subPath, (basePoint = cData.slice(-2)));
|
||||
if (curves.length) {
|
||||
addPoint(subPath, (basePoint = cData.slice(-2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Save final command coordinates
|
||||
if (data.length >= 2) addPoint(subPath, data.slice(-2));
|
||||
if (data.length >= 2) {
|
||||
addPoint(subPath, data.slice(-2));
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms a convex hull from set of points of every subpath using monotone chain convex hull algorithm.
|
||||
* https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
|
||||
* Forms a convex hull from set of points of every subpath using monotone chain
|
||||
* convex hull algorithm.
|
||||
*
|
||||
* @type {(points: Point) => Point}
|
||||
* @see https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
|
||||
* @param {Point} points
|
||||
* @returns {Point}
|
||||
*/
|
||||
function convexHull(points) {
|
||||
points.list.sort(function (a, b) {
|
||||
return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0];
|
||||
});
|
||||
|
||||
var lower = [],
|
||||
minY = 0,
|
||||
bottom = 0;
|
||||
const lower = [];
|
||||
let minY = 0;
|
||||
let bottom = 0;
|
||||
for (let i = 0; i < points.list.length; i++) {
|
||||
while (
|
||||
lower.length >= 2 &&
|
||||
@@ -632,9 +659,9 @@ function convexHull(points) {
|
||||
lower.push(points.list[i]);
|
||||
}
|
||||
|
||||
var upper = [],
|
||||
maxY = points.list.length - 1,
|
||||
top = 0;
|
||||
const upper = [];
|
||||
let maxY = points.list.length - 1;
|
||||
let top = 0;
|
||||
for (let i = points.list.length; i--; ) {
|
||||
while (
|
||||
upper.length >= 2 &&
|
||||
@@ -656,9 +683,7 @@ function convexHull(points) {
|
||||
|
||||
const hullList = lower.concat(upper);
|
||||
|
||||
/**
|
||||
* @type {Point}
|
||||
*/
|
||||
/** @type {Point} */
|
||||
const hull = {
|
||||
list: hullList,
|
||||
minX: 0, // by sorting
|
||||
@@ -671,28 +696,30 @@ function convexHull(points) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(o: Array<number>, a: Array<number>, b: Array<number>) => number}
|
||||
* @param {ReadonlyArray<number>} o
|
||||
* @param {ReadonlyArray<number>} a
|
||||
* @param {ReadonlyArray<number>} b
|
||||
* @returns {number}
|
||||
*/
|
||||
function cross(o, a, b) {
|
||||
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/
|
||||
* Thanks to Dmitry Baranovskiy for his great work!
|
||||
* Based on code from [Snap.svg](http://snapsvg.io/) (Apache 2 license). Thanks
|
||||
* to Dmitry Baranovskiy for his great work!
|
||||
*
|
||||
* @type {(
|
||||
* x1: number,
|
||||
* y1: number,
|
||||
* rx: number,
|
||||
* ry: number,
|
||||
* angle: number,
|
||||
* large_arc_flag: number,
|
||||
* sweep_flag: number,
|
||||
* x2: number,
|
||||
* y2: number,
|
||||
* recursive: Array<number>
|
||||
* ) => Array<number>}
|
||||
* @param {number} x1
|
||||
* @param {number} y1
|
||||
* @param {number} rx
|
||||
* @param {number} ry
|
||||
* @param {number} angle
|
||||
* @param {number} large_arc_flag
|
||||
* @param {number} sweep_flag
|
||||
* @param {number} x2
|
||||
* @param {number} y2
|
||||
* @param {ReadonlyArray<number>} recursive
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const a2c = (
|
||||
x1,
|
||||
@@ -704,24 +731,29 @@ const a2c = (
|
||||
sweep_flag,
|
||||
x2,
|
||||
y2,
|
||||
recursive
|
||||
recursive,
|
||||
) => {
|
||||
// for more information of where this Math came from visit:
|
||||
// https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
||||
const _120 = (Math.PI * 120) / 180;
|
||||
const rad = (Math.PI / 180) * (+angle || 0);
|
||||
/**
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
/** @type {number[]} */
|
||||
let res = [];
|
||||
/**
|
||||
* @type {(x: number, y: number, rad: number) => number}
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} rad
|
||||
* @returns {number}
|
||||
*/
|
||||
const rotateX = (x, y, rad) => {
|
||||
return x * Math.cos(rad) - y * Math.sin(rad);
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(x: number, y: number, rad: number) => number}
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} rad
|
||||
* @returns {number}
|
||||
*/
|
||||
const rotateY = (x, y, rad) => {
|
||||
return x * Math.sin(rad) + y * Math.cos(rad);
|
||||
@@ -731,22 +763,22 @@ const a2c = (
|
||||
y1 = rotateY(x1, y1, -rad);
|
||||
x2 = rotateX(x2, y2, -rad);
|
||||
y2 = rotateY(x2, y2, -rad);
|
||||
var x = (x1 - x2) / 2,
|
||||
y = (y1 - y2) / 2;
|
||||
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
|
||||
const x = (x1 - x2) / 2;
|
||||
const y = (y1 - y2) / 2;
|
||||
let h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
|
||||
if (h > 1) {
|
||||
h = Math.sqrt(h);
|
||||
rx = h * rx;
|
||||
ry = h * ry;
|
||||
}
|
||||
var rx2 = rx * rx;
|
||||
var ry2 = ry * ry;
|
||||
var k =
|
||||
const rx2 = rx * rx;
|
||||
const ry2 = ry * ry;
|
||||
const k =
|
||||
(large_arc_flag == sweep_flag ? -1 : 1) *
|
||||
Math.sqrt(
|
||||
Math.abs(
|
||||
(rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)
|
||||
)
|
||||
(rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x),
|
||||
),
|
||||
);
|
||||
var cx = (k * rx * y) / ry + (x1 + x2) / 2;
|
||||
var cy = (k * -ry * x) / rx + (y1 + y2) / 2;
|
||||
@@ -769,11 +801,11 @@ const a2c = (
|
||||
cx = recursive[2];
|
||||
cy = recursive[3];
|
||||
}
|
||||
var df = f2 - f1;
|
||||
let df = f2 - f1;
|
||||
if (Math.abs(df) > _120) {
|
||||
var f2old = f2,
|
||||
x2old = x2,
|
||||
y2old = y2;
|
||||
const f2old = f2;
|
||||
const x2old = x2;
|
||||
const y2old = y2;
|
||||
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
|
||||
x2 = cx + rx * Math.cos(f2);
|
||||
y2 = cy + ry * Math.sin(f2);
|
||||
@@ -785,27 +817,27 @@ const a2c = (
|
||||
]);
|
||||
}
|
||||
df = f2 - f1;
|
||||
var c1 = Math.cos(f1),
|
||||
s1 = Math.sin(f1),
|
||||
c2 = Math.cos(f2),
|
||||
s2 = Math.sin(f2),
|
||||
t = Math.tan(df / 4),
|
||||
hx = (4 / 3) * rx * t,
|
||||
hy = (4 / 3) * ry * t,
|
||||
m = [
|
||||
-hx * s1,
|
||||
hy * c1,
|
||||
x2 + hx * s2 - x1,
|
||||
y2 - hy * c2 - y1,
|
||||
x2 - x1,
|
||||
y2 - y1,
|
||||
];
|
||||
const c1 = Math.cos(f1);
|
||||
const s1 = Math.sin(f1);
|
||||
const c2 = Math.cos(f2);
|
||||
const s2 = Math.sin(f2);
|
||||
const t = Math.tan(df / 4);
|
||||
const hx = (4 / 3) * rx * t;
|
||||
const hy = (4 / 3) * ry * t;
|
||||
const m = [
|
||||
-hx * s1,
|
||||
hy * c1,
|
||||
x2 + hx * s2 - x1,
|
||||
y2 - hy * c2 - y1,
|
||||
x2 - x1,
|
||||
y2 - y1,
|
||||
];
|
||||
if (recursive) {
|
||||
return m.concat(res);
|
||||
} else {
|
||||
res = m.concat(res);
|
||||
var newres = [];
|
||||
for (var i = 0, n = res.length; i < n; i++) {
|
||||
const newres = [];
|
||||
for (let i = 0, n = res.length; i < n; i++) {
|
||||
newres[i] =
|
||||
i % 2
|
||||
? rotateY(res[i - 1], res[i], rad)
|
||||
|
||||
700
node_modules/svgo/plugins/_transforms.js
generated
vendored
700
node_modules/svgo/plugins/_transforms.js
generated
vendored
@@ -1,255 +1,498 @@
|
||||
'use strict';
|
||||
import { cleanupOutData, toFixed } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef TransformItem
|
||||
* @property {string} name
|
||||
* @property {number[]} data
|
||||
*
|
||||
* @typedef TransformParams
|
||||
* @property {boolean} convertToShorts
|
||||
* @property {number=} degPrecision
|
||||
* @property {number} floatPrecision
|
||||
* @property {number} transformPrecision
|
||||
* @property {boolean} matrixToTransform
|
||||
* @property {boolean} shortTranslate
|
||||
* @property {boolean} shortScale
|
||||
* @property {boolean} shortRotate
|
||||
* @property {boolean} removeUseless
|
||||
* @property {boolean} collapseIntoOne
|
||||
* @property {boolean} leadingZero
|
||||
* @property {boolean} negativeExtraSpace
|
||||
*
|
||||
*/
|
||||
|
||||
const transformTypes = new Set([
|
||||
'matrix',
|
||||
'rotate',
|
||||
'scale',
|
||||
'skewX',
|
||||
'skewY',
|
||||
'translate',
|
||||
]);
|
||||
|
||||
const regTransformTypes = /matrix|translate|scale|rotate|skewX|skewY/;
|
||||
const regTransformSplit =
|
||||
/\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
|
||||
const regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
||||
|
||||
/**
|
||||
* @typedef {{ name: string, data: Array<number> }} TransformItem
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert transform string to JS representation.
|
||||
*
|
||||
* @type {(transformString: string) => Array<TransformItem>}
|
||||
* @param {string} transformString
|
||||
* @returns {TransformItem[]} Object representation of transform, or an empty array if it was malformed.
|
||||
*/
|
||||
exports.transform2js = (transformString) => {
|
||||
// JS representation of the transform data
|
||||
/**
|
||||
* @type {Array<TransformItem>}
|
||||
*/
|
||||
export const transform2js = (transformString) => {
|
||||
/** @type {TransformItem[]} */
|
||||
const transforms = [];
|
||||
// current transform context
|
||||
/**
|
||||
* @type {null | TransformItem}
|
||||
*/
|
||||
let current = null;
|
||||
/** @type {?TransformItem} */
|
||||
let currentTransform = null;
|
||||
|
||||
// split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
|
||||
for (const item of transformString.split(regTransformSplit)) {
|
||||
var num;
|
||||
if (item) {
|
||||
// if item is a translate function
|
||||
if (regTransformTypes.test(item)) {
|
||||
// then collect it and change current context
|
||||
current = { name: item, data: [] };
|
||||
transforms.push(current);
|
||||
// else if item is data
|
||||
} else {
|
||||
// then split it into [10, 50] and collect as context.data
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((num = regNumericValues.exec(item))) {
|
||||
num = Number(num);
|
||||
if (current != null) {
|
||||
current.data.push(num);
|
||||
}
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (transformTypes.has(item)) {
|
||||
currentTransform = { name: item, data: [] };
|
||||
transforms.push(currentTransform);
|
||||
} else {
|
||||
let num;
|
||||
// then split it into [10, 50] and collect as context.data
|
||||
while ((num = regNumericValues.exec(item))) {
|
||||
num = Number(num);
|
||||
if (currentTransform != null) {
|
||||
currentTransform.data.push(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// return empty array if broken transform (no data)
|
||||
return current == null || current.data.length == 0 ? [] : transforms;
|
||||
};
|
||||
|
||||
return currentTransform == null || currentTransform.data.length == 0
|
||||
? []
|
||||
: transforms;
|
||||
};
|
||||
/**
|
||||
* Multiply transforms into one.
|
||||
*
|
||||
* @type {(transforms: Array<TransformItem>) => TransformItem}
|
||||
* @param {ReadonlyArray<TransformItem>} transforms
|
||||
* @returns {TransformItem}
|
||||
*/
|
||||
exports.transformsMultiply = (transforms) => {
|
||||
// convert transforms objects to the matrices
|
||||
export const transformsMultiply = (transforms) => {
|
||||
const matrixData = transforms.map((transform) => {
|
||||
if (transform.name === 'matrix') {
|
||||
return transform.data;
|
||||
}
|
||||
return transformToMatrix(transform);
|
||||
});
|
||||
// multiply all matrices into one
|
||||
|
||||
const matrixTransform = {
|
||||
name: 'matrix',
|
||||
data:
|
||||
matrixData.length > 0 ? matrixData.reduce(multiplyTransformMatrices) : [],
|
||||
};
|
||||
|
||||
return matrixTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
* math utilities in radians.
|
||||
* Math utilities in radians.
|
||||
*/
|
||||
const mth = {
|
||||
/**
|
||||
* @type {(deg: number) => number}
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
rad: (deg) => {
|
||||
return (deg * Math.PI) / 180;
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(rad: number) => number}
|
||||
* @param {number} rad
|
||||
* @returns {number}
|
||||
*/
|
||||
deg: (rad) => {
|
||||
return (rad * 180) / Math.PI;
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(deg: number) => number}
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
cos: (deg) => {
|
||||
return Math.cos(mth.rad(deg));
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(val: number, floatPrecision: number) => number}
|
||||
* @param {number} val
|
||||
* @param {number} floatPrecision
|
||||
* @returns {number}
|
||||
*/
|
||||
acos: (val, floatPrecision) => {
|
||||
return Number(mth.deg(Math.acos(val)).toFixed(floatPrecision));
|
||||
return toFixed(mth.deg(Math.acos(val)), floatPrecision);
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(deg: number) => number}
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
sin: (deg) => {
|
||||
return Math.sin(mth.rad(deg));
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(val: number, floatPrecision: number) => number}
|
||||
* @param {number} val
|
||||
* @param {number} floatPrecision
|
||||
* @returns {number}
|
||||
*/
|
||||
asin: (val, floatPrecision) => {
|
||||
return Number(mth.deg(Math.asin(val)).toFixed(floatPrecision));
|
||||
return toFixed(mth.deg(Math.asin(val)), floatPrecision);
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(deg: number) => number}
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
tan: (deg) => {
|
||||
return Math.tan(mth.rad(deg));
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {(val: number, floatPrecision: number) => number}
|
||||
* @param {number} val
|
||||
* @param {number} floatPrecision
|
||||
* @returns {number}
|
||||
*/
|
||||
atan: (val, floatPrecision) => {
|
||||
return Number(mth.deg(Math.atan(val)).toFixed(floatPrecision));
|
||||
return toFixed(mth.deg(Math.atan(val)), floatPrecision);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* convertToShorts: boolean,
|
||||
* floatPrecision: number,
|
||||
* transformPrecision: number,
|
||||
* matrixToTransform: boolean,
|
||||
* shortTranslate: boolean,
|
||||
* shortScale: boolean,
|
||||
* shortRotate: boolean,
|
||||
* removeUseless: boolean,
|
||||
* collapseIntoOne: boolean,
|
||||
* leadingZero: boolean,
|
||||
* negativeExtraSpace: boolean,
|
||||
* }} TransformParams
|
||||
* @param {TransformItem} matrix
|
||||
* @returns {TransformItem[][]}
|
||||
*/
|
||||
const getDecompositions = (matrix) => {
|
||||
const decompositions = [];
|
||||
const qrab = decomposeQRAB(matrix);
|
||||
const qrcd = decomposeQRCD(matrix);
|
||||
|
||||
if (qrab) {
|
||||
decompositions.push(qrab);
|
||||
}
|
||||
if (qrcd) {
|
||||
decompositions.push(qrcd);
|
||||
}
|
||||
return decompositions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decompose matrix into simple transforms. See
|
||||
* https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html
|
||||
*
|
||||
* @type {(transform: TransformItem, params: TransformParams) => Array<TransformItem>}
|
||||
* @param {TransformItem} matrix
|
||||
* @returns {TransformItem[] | undefined}
|
||||
* @see {@link https://frederic-wang.fr/2013/12/01/decomposition-of-2d-transform-matrices/} Where applicable, variables are named in accordance with this document.
|
||||
*/
|
||||
exports.matrixToTransform = (transform, params) => {
|
||||
let floatPrecision = params.floatPrecision;
|
||||
let data = transform.data;
|
||||
let transforms = [];
|
||||
let sx = Number(
|
||||
Math.hypot(data[0], data[1]).toFixed(params.transformPrecision)
|
||||
);
|
||||
let sy = Number(
|
||||
((data[0] * data[3] - data[1] * data[2]) / sx).toFixed(
|
||||
params.transformPrecision
|
||||
)
|
||||
);
|
||||
let colsSum = data[0] * data[2] + data[1] * data[3];
|
||||
let rowsSum = data[0] * data[1] + data[2] * data[3];
|
||||
let scaleBefore = rowsSum != 0 || sx == sy;
|
||||
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 (data[4] || data[5]) {
|
||||
transforms.push({
|
||||
if (e || f) {
|
||||
decomposition.push({
|
||||
name: 'translate',
|
||||
data: data.slice(4, data[5] ? 6 : 5),
|
||||
data: [e, f],
|
||||
});
|
||||
}
|
||||
|
||||
// [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy)
|
||||
if (!data[1] && data[2]) {
|
||||
transforms.push({
|
||||
if (cosOfRotationAngle !== 1) {
|
||||
const rotationAngleRads = Math.acos(cosOfRotationAngle);
|
||||
decomposition.push({
|
||||
name: 'rotate',
|
||||
data: [mth.deg(b < 0 ? -rotationAngleRads : rotationAngleRads), 0, 0],
|
||||
});
|
||||
}
|
||||
|
||||
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({
|
||||
name: 'skewX',
|
||||
data: [mth.atan(data[2] / sy, floatPrecision)],
|
||||
data: [mth.deg(Math.atan(ac_plus_bd / (a * a + b * b)))],
|
||||
});
|
||||
|
||||
// [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.atan(data[1] / data[0], floatPrecision)],
|
||||
});
|
||||
sx = data[0];
|
||||
sy = data[3];
|
||||
|
||||
// [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 = (data[0] < 0 ? -1 : 1) * Math.hypot(data[0], data[2]);
|
||||
sy = (data[3] < 0 ? -1 : 1) * Math.hypot(data[1], data[3]);
|
||||
transforms.push({ name: 'scale', data: [sx, sy] });
|
||||
}
|
||||
var angle = Math.min(Math.max(-1, data[0] / sx), 1),
|
||||
rotate = [
|
||||
mth.acos(angle, floatPrecision) *
|
||||
((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1),
|
||||
];
|
||||
|
||||
if (rotate[0]) transforms.push({ name: 'rotate', data: rotate });
|
||||
|
||||
if (rowsSum && colsSum)
|
||||
transforms.push({
|
||||
name: 'skewX',
|
||||
data: [mth.atan(colsSum / (sx * sx), floatPrecision)],
|
||||
});
|
||||
|
||||
// rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point)
|
||||
if (rotate[0] && (data[4] || data[5])) {
|
||||
transforms.shift();
|
||||
var cos = data[0] / sx,
|
||||
sin = data[1] / (scaleBefore ? sx : sy),
|
||||
x = data[4] * (scaleBefore ? 1 : sy),
|
||||
y = data[5] * (scaleBefore ? 1 : sx),
|
||||
denom =
|
||||
(Math.pow(1 - cos, 2) + Math.pow(sin, 2)) *
|
||||
(scaleBefore ? 1 : sx * sy);
|
||||
rotate.push(((1 - cos) * x - sin * y) / denom);
|
||||
rotate.push(((1 - cos) * y + sin * x) / denom);
|
||||
}
|
||||
|
||||
// Too many transformations, return original matrix if it isn't just a scale/translate
|
||||
} else if (data[1] || data[2]) {
|
||||
return [transform];
|
||||
}
|
||||
|
||||
if ((scaleBefore && (sx != 1 || sy != 1)) || !transforms.length)
|
||||
transforms.push({
|
||||
name: 'scale',
|
||||
data: sx == sy ? [sx] : [sx, sy],
|
||||
return decomposition;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {TransformItem} matrix
|
||||
* @returns {TransformItem[] | undefined}
|
||||
* @see {@link https://frederic-wang.fr/2013/12/01/decomposition-of-2d-transform-matrices/} 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({
|
||||
name: 'skewY',
|
||||
data: [mth.deg(Math.atan(ac_plus_bd / (c * c + d * d)))],
|
||||
});
|
||||
}
|
||||
|
||||
return decomposition;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
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] };
|
||||
};
|
||||
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Optimize matrix of simple transforms.
|
||||
* @param {ReadonlyArray<TransformItem>} roundedTransforms
|
||||
* @param {ReadonlyArray<TransformItem>} rawTransforms
|
||||
* @returns {TransformItem[]}
|
||||
*/
|
||||
const optimize = (roundedTransforms, rawTransforms) => {
|
||||
const optimizedTransforms = [];
|
||||
|
||||
for (let index = 0; index < roundedTransforms.length; index++) {
|
||||
const roundedTransform = roundedTransforms[index];
|
||||
|
||||
// Don't include any identity transforms.
|
||||
if (isIdentityTransform(roundedTransform)) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// If everything was optimized out, return identity transform scale(1).
|
||||
return optimizedTransforms.length
|
||||
? optimizedTransforms
|
||||
: [{ name: 'scale', data: [1] }];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<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);
|
||||
});
|
||||
|
||||
return transforms;
|
||||
const optimized = optimize(roundedTransforms, decomposition);
|
||||
const len = js2transform(optimized, params).length;
|
||||
if (len < shortestLen) {
|
||||
shortest = optimized;
|
||||
shortestLen = len;
|
||||
}
|
||||
}
|
||||
|
||||
return shortest ?? [origMatrix];
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert transform to the matrix data.
|
||||
*
|
||||
* @type {(transform: TransformItem) => Array<number> }
|
||||
* @param {TransformItem} transform
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const transformToMatrix = (transform) => {
|
||||
if (transform.name === 'matrix') {
|
||||
@@ -265,16 +508,16 @@ const transformToMatrix = (transform) => {
|
||||
transform.data[0],
|
||||
0,
|
||||
0,
|
||||
transform.data[1] || transform.data[0],
|
||||
transform.data[1] ?? transform.data[0],
|
||||
0,
|
||||
0,
|
||||
];
|
||||
case 'rotate':
|
||||
// [cos(a), sin(a), -sin(a), cos(a), x, y]
|
||||
var cos = mth.cos(transform.data[0]),
|
||||
sin = mth.sin(transform.data[0]),
|
||||
cx = transform.data[1] || 0,
|
||||
cy = transform.data[2] || 0;
|
||||
var cos = mth.cos(transform.data[0]);
|
||||
var sin = mth.sin(transform.data[0]);
|
||||
var cx = transform.data[1] || 0;
|
||||
var cy = transform.data[2] || 0;
|
||||
return [
|
||||
cos,
|
||||
sin,
|
||||
@@ -295,18 +538,18 @@ const transformToMatrix = (transform) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies transformation to an arc. To do so, we represent ellipse as a matrix, multiply it
|
||||
* by the transformation matrix and use a singular value decomposition to represent in a form
|
||||
* rotate(θ)·scale(a b)·rotate(φ). This gives us new ellipse params a, b and θ.
|
||||
* SVD is being done with the formulae provided by Wolffram|Alpha (svd {{m0, m2}, {m1, m3}})
|
||||
* Applies transformation to an arc. To do so, we represent ellipse as a matrix,
|
||||
* multiply it by the transformation matrix and use a singular value
|
||||
* decomposition to represent in a form rotate(θ)·scale(a b)·rotate(φ). This
|
||||
* gives us new ellipse params a, b and θ. SVD is being done with the formulae
|
||||
* provided by Wolfram|Alpha (svd {{m0, m2}, {m1, m3}})
|
||||
*
|
||||
* @type {(
|
||||
* cursor: [x: number, y: number],
|
||||
* arc: Array<number>,
|
||||
* transform: Array<number>
|
||||
* ) => Array<number>}
|
||||
* @param {[number, number]} cursor
|
||||
* @param {number[]} arc
|
||||
* @param {ReadonlyArray<number>} transform
|
||||
* @returns {number[]}
|
||||
*/
|
||||
exports.transformArc = (cursor, arc, transform) => {
|
||||
export const transformArc = (cursor, arc, transform) => {
|
||||
const x = arc[5] - cursor[0];
|
||||
const y = arc[6] - cursor[1];
|
||||
let a = arc[0];
|
||||
@@ -365,7 +608,9 @@ exports.transformArc = (cursor, arc, transform) => {
|
||||
/**
|
||||
* Multiply transformation matrices.
|
||||
*
|
||||
* @type {(a: Array<number>, b: Array<number>) => Array<number>}
|
||||
* @param {ReadonlyArray<number>} a
|
||||
* @param {ReadonlyArray<number>} b
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const multiplyTransformMatrices = (a, b) => {
|
||||
return [
|
||||
@@ -377,3 +622,134 @@ const multiplyTransformMatrices = (a, b) => {
|
||||
a[1] * b[4] + a[3] * b[5] + a[5],
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {TransformItem} transform
|
||||
* @param {TransformParams} params
|
||||
* @returns {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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number[]} data
|
||||
* @param {TransformParams} params
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const degRound = (data, params) => {
|
||||
if (
|
||||
params.degPrecision != null &&
|
||||
params.degPrecision >= 1 &&
|
||||
params.floatPrecision < 20
|
||||
) {
|
||||
return smartRound(params.degPrecision, data);
|
||||
} else {
|
||||
return round(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number[]} data
|
||||
* @param {TransformParams} params
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const floatRound = (data, params) => {
|
||||
if (params.floatPrecision >= 1 && params.floatPrecision < 20) {
|
||||
return smartRound(params.floatPrecision, data);
|
||||
} else {
|
||||
return round(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number[]} data
|
||||
* @param {TransformParams} params
|
||||
* @returns {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.
|
||||
*
|
||||
* @param {ReadonlyArray<number>} data
|
||||
* @returns {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 (
|
||||
let i = data.length,
|
||||
tolerance = +Math.pow(0.1, precision).toFixed(precision);
|
||||
i--;
|
||||
|
||||
) {
|
||||
if (toFixed(data[i], precision) !== data[i]) {
|
||||
const 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 {ReadonlyArray<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;
|
||||
};
|
||||
|
||||
27
node_modules/svgo/plugins/addAttributesToSVGElement.js
generated
vendored
27
node_modules/svgo/plugins/addAttributesToSVGElement.js
generated
vendored
@@ -1,11 +1,13 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @typedef AddAttributesToSVGElementParams
|
||||
* @property {string | Record<string, null | string>=} attribute
|
||||
* @property {Array<string | Record<string, null | string>>=} attributes
|
||||
*/
|
||||
|
||||
exports.name = 'addAttributesToSVGElement';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'adds attributes to an outer <svg> element';
|
||||
export const name = 'addAttributesToSVGElement';
|
||||
export const description = 'adds attributes to an outer <svg> element';
|
||||
|
||||
var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.
|
||||
const ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.
|
||||
It should have a list of "attributes" or one "attribute".
|
||||
Config example:
|
||||
|
||||
@@ -45,16 +47,13 @@ plugins: [
|
||||
`;
|
||||
|
||||
/**
|
||||
* Add attributes to an outer <svg> element. Example config:
|
||||
* Add attributes to an outer <svg> element.
|
||||
*
|
||||
* @author April Arcus
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* attribute?: string | Record<string, null | string>,
|
||||
* attributes?: Array<string | Record<string, null | string>>
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<AddAttributesToSVGElementParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
if (!Array.isArray(params.attributes) && !params.attribute) {
|
||||
console.error(ENOCLS);
|
||||
return null;
|
||||
@@ -67,14 +66,14 @@ exports.fn = (root, params) => {
|
||||
for (const attribute of attributes) {
|
||||
if (typeof attribute === 'string') {
|
||||
if (node.attributes[attribute] == null) {
|
||||
// @ts-ignore disallow explicit nullable attribute value
|
||||
// @ts-expect-error disallow explicit nullable attribute value
|
||||
node.attributes[attribute] = undefined;
|
||||
}
|
||||
}
|
||||
if (typeof attribute === 'object') {
|
||||
for (const key of Object.keys(attribute)) {
|
||||
if (node.attributes[key] == null) {
|
||||
// @ts-ignore disallow explicit nullable attribute value
|
||||
// @ts-expect-error disallow explicit nullable attribute value
|
||||
node.attributes[key] = attribute[key];
|
||||
}
|
||||
}
|
||||
|
||||
31
node_modules/svgo/plugins/addClassesToSVGElement.js
generated
vendored
31
node_modules/svgo/plugins/addClassesToSVGElement.js
generated
vendored
@@ -1,11 +1,13 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @typedef AddClassesToSVGElementParams
|
||||
* @property {string | ((node: import('../lib/types.js').XastElement, info: import('../lib/types.js').PluginInfo) => string)=} className
|
||||
* @property {Array<string | ((node: import('../lib/types.js').XastElement, info: import('../lib/types.js').PluginInfo) => string)>=} classNames
|
||||
*/
|
||||
|
||||
exports.name = 'addClassesToSVGElement';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'adds classnames to an outer <svg> element';
|
||||
export const name = 'addClassesToSVGElement';
|
||||
export const description = 'adds classnames to an outer <svg> element';
|
||||
|
||||
var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.
|
||||
const ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.
|
||||
It should have a list of classes in "classNames" or one "className".
|
||||
Config example:
|
||||
|
||||
@@ -51,14 +53,11 @@ plugins: [
|
||||
*
|
||||
* @author April Arcus
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* className?: string,
|
||||
* classNames?: Array<string>
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<AddClassesToSVGElementParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params, info) => {
|
||||
if (
|
||||
!(Array.isArray(params.classNames) && params.classNames.some(String)) &&
|
||||
!(Array.isArray(params.classNames) && params.classNames.length !== 0) &&
|
||||
!params.className
|
||||
) {
|
||||
console.error(ENOCLS);
|
||||
@@ -72,11 +71,15 @@ exports.fn = (root, params) => {
|
||||
const classList = new Set(
|
||||
node.attributes.class == null
|
||||
? null
|
||||
: node.attributes.class.split(' ')
|
||||
: node.attributes.class.split(' '),
|
||||
);
|
||||
for (const className of classNames) {
|
||||
if (className != null) {
|
||||
classList.add(className);
|
||||
const classToAdd =
|
||||
typeof className === 'string'
|
||||
? className
|
||||
: className(node, info);
|
||||
classList.add(classToAdd);
|
||||
}
|
||||
}
|
||||
node.attributes.class = Array.from(classList).join(' ');
|
||||
|
||||
259
node_modules/svgo/plugins/_applyTransforms.js → node_modules/svgo/plugins/applyTransforms.js
generated
vendored
Executable file → Normal file
259
node_modules/svgo/plugins/_applyTransforms.js → node_modules/svgo/plugins/applyTransforms.js
generated
vendored
Executable file → Normal file
@@ -1,143 +1,184 @@
|
||||
'use strict';
|
||||
|
||||
// TODO implement as separate plugin
|
||||
|
||||
const {
|
||||
transformsMultiply,
|
||||
import { path2js } from './_path.js';
|
||||
import {
|
||||
transform2js,
|
||||
transformArc,
|
||||
} = require('./_transforms.js');
|
||||
const { removeLeadingZero } = require('../lib/svgo/tools.js');
|
||||
const { referencesProps, attrsGroupsDefaults } = require('./_collections.js');
|
||||
transformsMultiply,
|
||||
} from './_transforms.js';
|
||||
import { attrsGroupsDefaults, referencesProps } from './_collections.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
|
||||
import { includesUrlReference, removeLeadingZero } from '../lib/svgo/tools.js';
|
||||
|
||||
const regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
||||
const defaultStrokeWidth = attrsGroupsDefaults.presentation['stroke-width'];
|
||||
|
||||
/**
|
||||
* Apply transformation(s) to the Path data.
|
||||
*
|
||||
* @param {Object} elem current element
|
||||
* @param {Array} path input path data
|
||||
* @param {Object} params whether to apply transforms to stroked lines and transform precision (used for stroke width)
|
||||
* @return {Array} output path data
|
||||
* @type {import('../lib/types.js').Plugin<{
|
||||
* transformPrecision: number,
|
||||
* applyTransformsStroked: boolean,
|
||||
* }>}
|
||||
*/
|
||||
const applyTransforms = (elem, pathData, params) => {
|
||||
// if there are no 'stroke' attr and references to other objects such as
|
||||
// gradiends or clip-path which are also subjects to transform.
|
||||
if (
|
||||
elem.attributes.transform == null ||
|
||||
elem.attributes.transform === '' ||
|
||||
// styles are not considered when applying transform
|
||||
// can be fixed properly with new style engine
|
||||
elem.attributes.style != null ||
|
||||
Object.entries(elem.attributes).some(
|
||||
([name, value]) =>
|
||||
referencesProps.includes(name) && value.includes('url(')
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matrix = transformsMultiply(transform2js(elem.attributes.transform));
|
||||
const stroke = elem.computedAttr('stroke');
|
||||
const id = elem.computedAttr('id');
|
||||
const transformPrecision = params.transformPrecision;
|
||||
|
||||
if (stroke && stroke != 'none') {
|
||||
if (
|
||||
!params.applyTransformsStroked ||
|
||||
((matrix.data[0] != matrix.data[3] ||
|
||||
matrix.data[1] != -matrix.data[2]) &&
|
||||
(matrix.data[0] != -matrix.data[3] || matrix.data[1] != matrix.data[2]))
|
||||
)
|
||||
return;
|
||||
|
||||
// "stroke-width" should be inside the part with ID, otherwise it can be overrided in <use>
|
||||
if (id) {
|
||||
let idElem = elem;
|
||||
let hasStrokeWidth = false;
|
||||
|
||||
do {
|
||||
if (idElem.attributes['stroke-width']) {
|
||||
hasStrokeWidth = true;
|
||||
}
|
||||
} while (
|
||||
idElem.attributes.id !== id &&
|
||||
!hasStrokeWidth &&
|
||||
(idElem = idElem.parentNode)
|
||||
);
|
||||
|
||||
if (!hasStrokeWidth) return;
|
||||
}
|
||||
|
||||
const scale = +Math.sqrt(
|
||||
matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1]
|
||||
).toFixed(transformPrecision);
|
||||
|
||||
if (scale !== 1) {
|
||||
const strokeWidth =
|
||||
elem.computedAttr('stroke-width') || defaultStrokeWidth;
|
||||
|
||||
if (
|
||||
elem.attributes['vector-effect'] == null ||
|
||||
elem.attributes['vector-effect'] !== 'non-scaling-stroke'
|
||||
) {
|
||||
if (elem.attributes['stroke-width'] != null) {
|
||||
elem.attributes['stroke-width'] = elem.attributes['stroke-width']
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
|
||||
} else {
|
||||
elem.attributes['stroke-width'] = strokeWidth.replace(
|
||||
regNumericValues,
|
||||
(num) => removeLeadingZero(num * scale)
|
||||
);
|
||||
export const applyTransforms = (root, params) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.d == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (elem.attributes['stroke-dashoffset'] != null) {
|
||||
elem.attributes['stroke-dashoffset'] = elem.attributes[
|
||||
'stroke-dashoffset'
|
||||
]
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
|
||||
// stroke and stroke-width can be redefined with <use>
|
||||
if (node.attributes.id != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (elem.attributes['stroke-dasharray'] != null) {
|
||||
elem.attributes['stroke-dasharray'] = elem.attributes[
|
||||
'stroke-dasharray'
|
||||
]
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
|
||||
// if there are no 'stroke' attr and references to other objects such as
|
||||
// gradients or clip-path which are also subjects to transform.
|
||||
if (
|
||||
node.attributes.transform == null ||
|
||||
node.attributes.transform === '' ||
|
||||
// styles are not considered when applying transform
|
||||
// can be fixed properly with new style engine
|
||||
node.attributes.style != null ||
|
||||
Object.entries(node.attributes).some(
|
||||
([name, value]) =>
|
||||
referencesProps.has(name) && includesUrlReference(value),
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id) {
|
||||
// Stroke and stroke-width can be redefined with <use>
|
||||
return;
|
||||
}
|
||||
|
||||
applyMatrixToPathData(pathData, matrix.data);
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
const transformStyle = computedStyle.transform;
|
||||
|
||||
// remove transform attr
|
||||
delete elem.attributes.transform;
|
||||
// Transform overridden in <style> tag which is not considered
|
||||
if (
|
||||
transformStyle.type === 'static' &&
|
||||
transformStyle.value !== node.attributes.transform
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
const matrix = transformsMultiply(
|
||||
transform2js(node.attributes.transform),
|
||||
);
|
||||
|
||||
const stroke =
|
||||
computedStyle.stroke?.type === 'static'
|
||||
? computedStyle.stroke.value
|
||||
: null;
|
||||
|
||||
const strokeWidth =
|
||||
computedStyle['stroke-width']?.type === 'static'
|
||||
? computedStyle['stroke-width'].value
|
||||
: null;
|
||||
const transformPrecision = params.transformPrecision;
|
||||
|
||||
if (
|
||||
computedStyle.stroke?.type === 'dynamic' ||
|
||||
computedStyle['stroke-width']?.type === 'dynamic'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scale = Number(
|
||||
Math.hypot(matrix.data[0], matrix.data[1]).toFixed(
|
||||
transformPrecision,
|
||||
),
|
||||
);
|
||||
|
||||
if (stroke && stroke != 'none') {
|
||||
if (!params.applyTransformsStroked) {
|
||||
return;
|
||||
}
|
||||
|
||||
// stroke cannot be transformed with different vertical and horizontal scale or skew
|
||||
if (
|
||||
(matrix.data[0] !== matrix.data[3] ||
|
||||
matrix.data[1] !== -matrix.data[2]) &&
|
||||
(matrix.data[0] !== -matrix.data[3] ||
|
||||
matrix.data[1] !== matrix.data[2])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// apply transform to stroke-width, stroke-dashoffset and stroke-dasharray
|
||||
if (scale !== 1) {
|
||||
if (node.attributes['vector-effect'] !== 'non-scaling-stroke') {
|
||||
node.attributes['stroke-width'] = (
|
||||
strokeWidth || attrsGroupsDefaults.presentation['stroke-width']
|
||||
)
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) =>
|
||||
removeLeadingZero(Number(num) * scale),
|
||||
);
|
||||
|
||||
if (node.attributes['stroke-dashoffset'] != null) {
|
||||
node.attributes['stroke-dashoffset'] = node.attributes[
|
||||
'stroke-dashoffset'
|
||||
]
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) =>
|
||||
removeLeadingZero(Number(num) * scale),
|
||||
);
|
||||
}
|
||||
|
||||
if (node.attributes['stroke-dasharray'] != null) {
|
||||
node.attributes['stroke-dasharray'] = node.attributes[
|
||||
'stroke-dasharray'
|
||||
]
|
||||
.trim()
|
||||
.replace(regNumericValues, (num) =>
|
||||
removeLeadingZero(Number(num) * scale),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pathData = path2js(node);
|
||||
applyMatrixToPathData(pathData, matrix.data);
|
||||
|
||||
// remove transform attr
|
||||
delete node.attributes.transform;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.applyTransforms = applyTransforms;
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<number>} matrix
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
const transformAbsolutePoint = (matrix, x, y) => {
|
||||
const newX = matrix[0] * x + matrix[2] * y + matrix[4];
|
||||
const newY = matrix[1] * x + matrix[3] * y + matrix[5];
|
||||
return [newX, newY];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<number>} matrix
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
const transformRelativePoint = (matrix, x, y) => {
|
||||
const newX = matrix[0] * x + matrix[2] * y;
|
||||
const newY = matrix[1] * x + matrix[3] * y;
|
||||
return [newX, newY];
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} pathData
|
||||
* @param {ReadonlyArray<number>} matrix
|
||||
*/
|
||||
const applyMatrixToPathData = (pathData, matrix) => {
|
||||
/** @type {[number, number]} */
|
||||
const start = [0, 0];
|
||||
/** @type {[number, number]} */
|
||||
const cursor = [0, 0];
|
||||
|
||||
for (const pathItem of pathData) {
|
||||
@@ -164,7 +205,7 @@ const applyMatrixToPathData = (pathData, matrix) => {
|
||||
}
|
||||
|
||||
// horizontal lineto (x)
|
||||
// convert to lineto to handle two-dimentional transforms
|
||||
// convert to lineto to handle two-dimensional transforms
|
||||
if (command === 'H') {
|
||||
command = 'L';
|
||||
args = [args[0], cursor[1]];
|
||||
@@ -175,7 +216,7 @@ const applyMatrixToPathData = (pathData, matrix) => {
|
||||
}
|
||||
|
||||
// vertical lineto (y)
|
||||
// convert to lineto to handle two-dimentional transforms
|
||||
// convert to lineto to handle two-dimensional transforms
|
||||
if (command === 'V') {
|
||||
command = 'L';
|
||||
args = [cursor[0], args[0]];
|
||||
30
node_modules/svgo/plugins/cleanupAttrs.js
generated
vendored
30
node_modules/svgo/plugins/cleanupAttrs.js
generated
vendored
@@ -1,9 +1,12 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @typedef CleanupAttrsParams
|
||||
* @property {boolean=} newlines
|
||||
* @property {boolean=} trim
|
||||
* @property {boolean=} spaces
|
||||
*/
|
||||
|
||||
exports.name = 'cleanupAttrs';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description =
|
||||
export const name = 'cleanupAttrs';
|
||||
export const description =
|
||||
'cleanups attributes from newlines, trailing and repeating spaces';
|
||||
|
||||
const regNewlinesNeedSpace = /(\S)\r?\n(\S)/g;
|
||||
@@ -14,29 +17,24 @@ const regSpaces = /\s{2,}/g;
|
||||
* Cleanup attributes values from newlines, trailing and repeating spaces.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* newlines?: boolean,
|
||||
* trim?: boolean,
|
||||
* spaces?: boolean
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<CleanupAttrsParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const { newlines = true, trim = true, spaces = true } = params;
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (newlines) {
|
||||
// new line which requires a space instead of themselve
|
||||
// new line which requires a space instead
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
regNewlinesNeedSpace,
|
||||
(match, p1, p2) => p1 + ' ' + p2
|
||||
(match, p1, p2) => p1 + ' ' + p2,
|
||||
);
|
||||
// simple new line
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
regNewlines,
|
||||
''
|
||||
'',
|
||||
);
|
||||
}
|
||||
if (trim) {
|
||||
@@ -45,7 +43,7 @@ exports.fn = (root, params) => {
|
||||
if (spaces) {
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
regSpaces,
|
||||
' '
|
||||
' ',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
171
node_modules/svgo/plugins/cleanupEnableBackground.js
generated
vendored
171
node_modules/svgo/plugins/cleanupEnableBackground.js
generated
vendored
@@ -1,32 +1,28 @@
|
||||
'use strict';
|
||||
import * as csstree from 'css-tree';
|
||||
import { visit } from '../lib/util/visit.js';
|
||||
|
||||
const { visit } = require('../lib/xast.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'cleanupEnableBackground';
|
||||
exports.active = true;
|
||||
exports.description =
|
||||
export const name = 'cleanupEnableBackground';
|
||||
export const description =
|
||||
'remove or cleanup enable-background attribute when possible';
|
||||
|
||||
const regEnableBackground =
|
||||
/^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/;
|
||||
|
||||
/**
|
||||
* Remove or cleanup enable-background attr which coincides with a width/height box.
|
||||
* Remove or cleanup enable-background attr which coincides with a width/height
|
||||
* box.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty
|
||||
*
|
||||
* @example
|
||||
* <svg width="100" height="50" enable-background="new 0 0 100 50">
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <svg width="100" height="50">
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = (root) => {
|
||||
const regEnableBackground =
|
||||
/^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/;
|
||||
|
||||
export const fn = (root) => {
|
||||
let hasFilter = false;
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
@@ -40,36 +36,129 @@ exports.fn = (root) => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes['enable-background'] == null) {
|
||||
return;
|
||||
}
|
||||
if (hasFilter) {
|
||||
if (
|
||||
(node.name === 'svg' ||
|
||||
node.name === 'mask' ||
|
||||
node.name === 'pattern') &&
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null
|
||||
) {
|
||||
const match =
|
||||
node.attributes['enable-background'].match(regEnableBackground);
|
||||
if (
|
||||
match != null &&
|
||||
node.attributes.width === match[1] &&
|
||||
node.attributes.height === match[3]
|
||||
) {
|
||||
if (node.name === 'svg') {
|
||||
delete node.attributes['enable-background'];
|
||||
} else {
|
||||
node.attributes['enable-background'] = 'new';
|
||||
/** @type {?csstree.CssNode} */
|
||||
let newStyle = null;
|
||||
/** @type {?csstree.ListItem<csstree.CssNode>} */
|
||||
let enableBackgroundDeclaration = null;
|
||||
|
||||
if (node.attributes.style != null) {
|
||||
newStyle = csstree.parse(node.attributes.style, {
|
||||
context: 'declarationList',
|
||||
});
|
||||
|
||||
if (newStyle.type === 'DeclarationList') {
|
||||
/** @type {csstree.ListItem<csstree.CssNode>[]} */
|
||||
const enableBackgroundDeclarations = [];
|
||||
|
||||
csstree.walk(newStyle, (node, nodeItem) => {
|
||||
if (
|
||||
node.type === 'Declaration' &&
|
||||
node.property === 'enable-background'
|
||||
) {
|
||||
enableBackgroundDeclarations.push(nodeItem);
|
||||
enableBackgroundDeclaration = nodeItem;
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < enableBackgroundDeclarations.length - 1; i++) {
|
||||
newStyle.children.remove(enableBackgroundDeclarations[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//we don't need 'enable-background' if we have no filters
|
||||
}
|
||||
|
||||
if (!hasFilter) {
|
||||
delete node.attributes['enable-background'];
|
||||
|
||||
if (newStyle?.type === 'DeclarationList') {
|
||||
if (enableBackgroundDeclaration) {
|
||||
newStyle.children.remove(enableBackgroundDeclaration);
|
||||
}
|
||||
|
||||
if (newStyle.children.isEmpty) {
|
||||
delete node.attributes.style;
|
||||
} else {
|
||||
node.attributes.style = csstree.generate(newStyle);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDimensions =
|
||||
node.attributes.width != null && node.attributes.height != null;
|
||||
|
||||
if (
|
||||
(node.name === 'svg' ||
|
||||
node.name === 'mask' ||
|
||||
node.name === 'pattern') &&
|
||||
hasDimensions
|
||||
) {
|
||||
const attrValue = node.attributes['enable-background'];
|
||||
const attrCleaned = cleanupValue(
|
||||
attrValue,
|
||||
node.name,
|
||||
node.attributes.width,
|
||||
node.attributes.height,
|
||||
);
|
||||
|
||||
if (attrCleaned) {
|
||||
node.attributes['enable-background'] = attrCleaned;
|
||||
} else {
|
||||
delete node.attributes['enable-background'];
|
||||
}
|
||||
|
||||
if (
|
||||
newStyle?.type === 'DeclarationList' &&
|
||||
enableBackgroundDeclaration
|
||||
) {
|
||||
const styleValue = csstree.generate(
|
||||
// @ts-expect-error
|
||||
enableBackgroundDeclaration.data.value,
|
||||
);
|
||||
const styleCleaned = cleanupValue(
|
||||
styleValue,
|
||||
node.name,
|
||||
node.attributes.width,
|
||||
node.attributes.height,
|
||||
);
|
||||
|
||||
if (styleCleaned) {
|
||||
// @ts-expect-error
|
||||
enableBackgroundDeclaration.data.value = {
|
||||
type: 'Raw',
|
||||
value: styleCleaned,
|
||||
};
|
||||
} else {
|
||||
newStyle.children.remove(enableBackgroundDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newStyle?.type === 'DeclarationList') {
|
||||
if (newStyle.children.isEmpty) {
|
||||
delete node.attributes.style;
|
||||
} else {
|
||||
node.attributes.style = csstree.generate(newStyle);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} value Value of an enable-background attribute or style declaration.
|
||||
* @param {string} nodeName Name of the node the value was assigned to.
|
||||
* @param {string} width Width of the node the value was assigned to.
|
||||
* @param {string} height Height of the node the value was assigned to.
|
||||
* @returns {string | undefined} Cleaned up value, or undefined if it's redundant.
|
||||
*/
|
||||
const cleanupValue = (value, nodeName, width, height) => {
|
||||
const match = regEnableBackground.exec(value);
|
||||
|
||||
if (match != null && width === match[1] && height === match[3]) {
|
||||
return nodeName === 'svg' ? undefined : 'new';
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
297
node_modules/svgo/plugins/cleanupIDs.js
generated
vendored
297
node_modules/svgo/plugins/cleanupIDs.js
generated
vendored
@@ -1,297 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
*/
|
||||
|
||||
const { visitSkip } = require('../lib/xast.js');
|
||||
const { referencesProps } = require('./_collections.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'cleanupIDs';
|
||||
exports.active = true;
|
||||
exports.description = 'removes unused IDs and minifies used';
|
||||
|
||||
const regReferencesUrl = /\burl\(("|')?#(.+?)\1\)/;
|
||||
const regReferencesHref = /^#(.+?)$/;
|
||||
const regReferencesBegin = /(\w+)\./;
|
||||
const generateIDchars = [
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
];
|
||||
const maxIDindex = generateIDchars.length - 1;
|
||||
|
||||
/**
|
||||
* Check if an ID starts with any one of a list of strings.
|
||||
*
|
||||
* @type {(string: string, prefixes: Array<string>) => boolean}
|
||||
*/
|
||||
const hasStringPrefix = (string, prefixes) => {
|
||||
for (const prefix of prefixes) {
|
||||
if (string.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate unique minimal ID.
|
||||
*
|
||||
* @type {(currentID: null | Array<number>) => Array<number>}
|
||||
*/
|
||||
const generateID = (currentID) => {
|
||||
if (currentID == null) {
|
||||
return [0];
|
||||
}
|
||||
currentID[currentID.length - 1] += 1;
|
||||
for (let i = currentID.length - 1; i > 0; i--) {
|
||||
if (currentID[i] > maxIDindex) {
|
||||
currentID[i] = 0;
|
||||
if (currentID[i - 1] !== undefined) {
|
||||
currentID[i - 1]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentID[0] > maxIDindex) {
|
||||
currentID[0] = 0;
|
||||
currentID.unshift(0);
|
||||
}
|
||||
return currentID;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string from generated ID array.
|
||||
*
|
||||
* @type {(arr: Array<number>, prefix: string) => string}
|
||||
*/
|
||||
const getIDstring = (arr, prefix) => {
|
||||
return prefix + arr.map((i) => generateIDchars[i]).join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove unused and minify used IDs
|
||||
* (only if there are no any <style> or <script>).
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* remove?: boolean,
|
||||
* minify?: boolean,
|
||||
* prefix?: string,
|
||||
* preserve?: Array<string>,
|
||||
* preservePrefixes?: Array<string>,
|
||||
* force?: boolean,
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (_root, params) => {
|
||||
const {
|
||||
remove = true,
|
||||
minify = true,
|
||||
prefix = '',
|
||||
preserve = [],
|
||||
preservePrefixes = [],
|
||||
force = false,
|
||||
} = params;
|
||||
const preserveIDs = new Set(
|
||||
Array.isArray(preserve) ? preserve : preserve ? [preserve] : []
|
||||
);
|
||||
const preserveIDPrefixes = Array.isArray(preservePrefixes)
|
||||
? preservePrefixes
|
||||
: preservePrefixes
|
||||
? [preservePrefixes]
|
||||
: [];
|
||||
/**
|
||||
* @type {Map<string, XastElement>}
|
||||
*/
|
||||
const nodeById = new Map();
|
||||
/**
|
||||
* @type {Map<string, Array<{element: XastElement, name: string, value: string }>>}
|
||||
*/
|
||||
const referencesById = new Map();
|
||||
let deoptimized = false;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (force == false) {
|
||||
// deoptimize if style or script elements are present
|
||||
if (
|
||||
(node.name === 'style' || node.name === 'script') &&
|
||||
node.children.length !== 0
|
||||
) {
|
||||
deoptimized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid removing IDs if the whole SVG consists only of defs
|
||||
if (node.name === 'svg') {
|
||||
let hasDefsOnly = true;
|
||||
for (const child of node.children) {
|
||||
if (child.type !== 'element' || child.name !== 'defs') {
|
||||
hasDefsOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasDefsOnly) {
|
||||
return visitSkip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (name === 'id') {
|
||||
// collect all ids
|
||||
const id = value;
|
||||
if (nodeById.has(id)) {
|
||||
delete node.attributes.id; // remove repeated id
|
||||
} else {
|
||||
nodeById.set(id, node);
|
||||
}
|
||||
} else {
|
||||
// collect all references
|
||||
/**
|
||||
* @type {null | string}
|
||||
*/
|
||||
let id = null;
|
||||
if (referencesProps.includes(name)) {
|
||||
const match = value.match(regReferencesUrl);
|
||||
if (match != null) {
|
||||
id = match[2]; // url() reference
|
||||
}
|
||||
}
|
||||
if (name === 'href' || name.endsWith(':href')) {
|
||||
const match = value.match(regReferencesHref);
|
||||
if (match != null) {
|
||||
id = match[1]; // href reference
|
||||
}
|
||||
}
|
||||
if (name === 'begin') {
|
||||
const match = value.match(regReferencesBegin);
|
||||
if (match != null) {
|
||||
id = match[1]; // href reference
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
let refs = referencesById.get(id);
|
||||
if (refs == null) {
|
||||
refs = [];
|
||||
referencesById.set(id, refs);
|
||||
}
|
||||
refs.push({ element: node, name, value });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
root: {
|
||||
exit: () => {
|
||||
if (deoptimized) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @type {(id: string) => boolean}
|
||||
**/
|
||||
const isIdPreserved = (id) =>
|
||||
preserveIDs.has(id) || hasStringPrefix(id, preserveIDPrefixes);
|
||||
/**
|
||||
* @type {null | Array<number>}
|
||||
*/
|
||||
let currentID = null;
|
||||
for (const [id, refs] of referencesById) {
|
||||
const node = nodeById.get(id);
|
||||
if (node != null) {
|
||||
// replace referenced IDs with the minified ones
|
||||
if (minify && isIdPreserved(id) === false) {
|
||||
/**
|
||||
* @type {null | string}
|
||||
*/
|
||||
let currentIDString = null;
|
||||
do {
|
||||
currentID = generateID(currentID);
|
||||
currentIDString = getIDstring(currentID, prefix);
|
||||
} while (isIdPreserved(currentIDString));
|
||||
node.attributes.id = currentIDString;
|
||||
for (const { element, name, value } of refs) {
|
||||
if (value.includes('#')) {
|
||||
// replace id in href and url()
|
||||
element.attributes[name] = value.replace(
|
||||
`#${id}`,
|
||||
`#${currentIDString}`
|
||||
);
|
||||
} else {
|
||||
// replace id in begin attribute
|
||||
element.attributes[name] = value.replace(
|
||||
`${id}.`,
|
||||
`${currentIDString}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// keep referenced node
|
||||
nodeById.delete(id);
|
||||
}
|
||||
}
|
||||
// remove non-referenced IDs attributes from elements
|
||||
if (remove) {
|
||||
for (const [id, node] of nodeById) {
|
||||
if (isIdPreserved(id) === false) {
|
||||
delete node.attributes.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
266
node_modules/svgo/plugins/cleanupIds.js
generated
vendored
Normal file
266
node_modules/svgo/plugins/cleanupIds.js
generated
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
import { findReferences, hasScripts } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef CleanupIdsParams
|
||||
* @property {boolean=} remove
|
||||
* @property {boolean=} minify
|
||||
* @property {string[]=} preserve
|
||||
* @property {string[]=} preservePrefixes
|
||||
* @property {boolean=} force
|
||||
*/
|
||||
|
||||
export const name = 'cleanupIds';
|
||||
export const description = 'removes unused IDs and minifies used';
|
||||
|
||||
const generateIdChars = [
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
];
|
||||
const maxIdIndex = generateIdChars.length - 1;
|
||||
|
||||
/**
|
||||
* Check if an ID starts with any one of a list of strings.
|
||||
*
|
||||
* @param {string} string
|
||||
* @param {ReadonlyArray<string>} prefixes
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasStringPrefix = (string, prefixes) => {
|
||||
for (const prefix of prefixes) {
|
||||
if (string.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate unique minimal ID.
|
||||
*
|
||||
* @param {?number[]} currentId
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const generateId = (currentId) => {
|
||||
if (currentId == null) {
|
||||
return [0];
|
||||
}
|
||||
currentId[currentId.length - 1] += 1;
|
||||
for (let i = currentId.length - 1; i > 0; i--) {
|
||||
if (currentId[i] > maxIdIndex) {
|
||||
currentId[i] = 0;
|
||||
if (currentId[i - 1] !== undefined) {
|
||||
currentId[i - 1]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentId[0] > maxIdIndex) {
|
||||
currentId[0] = 0;
|
||||
currentId.unshift(0);
|
||||
}
|
||||
return currentId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get string from generated ID array.
|
||||
*
|
||||
* @param {ReadonlyArray<number>} arr
|
||||
* @returns {string}
|
||||
*/
|
||||
const getIdString = (arr) => {
|
||||
return arr.map((i) => generateIdChars[i]).join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove unused and minify used IDs (only if there are no `<style>` or
|
||||
* `<script>` nodes).
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<CleanupIdsParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
remove = true,
|
||||
minify = true,
|
||||
preserve = [],
|
||||
preservePrefixes = [],
|
||||
force = false,
|
||||
} = params;
|
||||
const preserveIds = new Set(
|
||||
Array.isArray(preserve) ? preserve : preserve ? [preserve] : [],
|
||||
);
|
||||
const preserveIdPrefixes = Array.isArray(preservePrefixes)
|
||||
? preservePrefixes
|
||||
: preservePrefixes
|
||||
? [preservePrefixes]
|
||||
: [];
|
||||
/** @type {Map<string, import('../lib/types.js').XastElement>} */
|
||||
const nodeById = new Map();
|
||||
/** @type {Map<string, {element: import('../lib/types.js').XastElement, name: string }[]>} */
|
||||
const referencesById = new Map();
|
||||
let deoptimized = false;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (!force) {
|
||||
// deoptimize if style or scripts are present
|
||||
if (
|
||||
(node.name === 'style' && node.children.length !== 0) ||
|
||||
hasScripts(node)
|
||||
) {
|
||||
deoptimized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid removing IDs if the whole SVG consists only of defs
|
||||
if (node.name === 'svg') {
|
||||
let hasDefsOnly = true;
|
||||
for (const child of node.children) {
|
||||
if (child.type !== 'element' || child.name !== 'defs') {
|
||||
hasDefsOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasDefsOnly) {
|
||||
return visitSkip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (name === 'id') {
|
||||
// collect all ids
|
||||
const id = value;
|
||||
if (nodeById.has(id)) {
|
||||
delete node.attributes.id; // remove repeated id
|
||||
} else {
|
||||
nodeById.set(id, node);
|
||||
}
|
||||
} else {
|
||||
const ids = findReferences(name, value);
|
||||
for (const id of ids) {
|
||||
let refs = referencesById.get(id);
|
||||
if (refs == null) {
|
||||
refs = [];
|
||||
referencesById.set(id, refs);
|
||||
}
|
||||
refs.push({ element: node, name });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
root: {
|
||||
exit: () => {
|
||||
if (deoptimized) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isIdPreserved = (id) =>
|
||||
preserveIds.has(id) || hasStringPrefix(id, preserveIdPrefixes);
|
||||
/** @type {?number[]} */
|
||||
let currentId = null;
|
||||
for (const [id, refs] of referencesById) {
|
||||
const node = nodeById.get(id);
|
||||
if (node != null) {
|
||||
// replace referenced IDs with the minified ones
|
||||
if (minify && isIdPreserved(id) === false) {
|
||||
/** @type {?string} */
|
||||
let currentIdString;
|
||||
do {
|
||||
currentId = generateId(currentId);
|
||||
currentIdString = getIdString(currentId);
|
||||
} while (
|
||||
isIdPreserved(currentIdString) ||
|
||||
(referencesById.has(currentIdString) &&
|
||||
nodeById.get(currentIdString) == null)
|
||||
);
|
||||
node.attributes.id = currentIdString;
|
||||
for (const { element, name } of refs) {
|
||||
const value = element.attributes[name];
|
||||
if (value.includes('#')) {
|
||||
// replace id in href and url()
|
||||
element.attributes[name] = value
|
||||
.replace(`#${encodeURI(id)}`, `#${currentIdString}`)
|
||||
.replace(`#${id}`, `#${currentIdString}`);
|
||||
} else {
|
||||
// replace id in begin attribute
|
||||
element.attributes[name] = value.replace(
|
||||
`${id}.`,
|
||||
`${currentIdString}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// keep referenced node
|
||||
nodeById.delete(id);
|
||||
}
|
||||
}
|
||||
// remove non-referenced IDs attributes from elements
|
||||
if (remove) {
|
||||
for (const [id, node] of nodeById) {
|
||||
if (isIdPreserved(id) === false) {
|
||||
delete node.attributes.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
48
node_modules/svgo/plugins/cleanupListOfValues.js
generated
vendored
48
node_modules/svgo/plugins/cleanupListOfValues.js
generated
vendored
@@ -1,11 +1,15 @@
|
||||
'use strict';
|
||||
import { removeLeadingZero } from '../lib/svgo/tools.js';
|
||||
|
||||
const { removeLeadingZero } = require('../lib/svgo/tools.js');
|
||||
/**
|
||||
* @typedef CleanupListOfValuesParams
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} defaultPx
|
||||
* @property {boolean=} convertToPx
|
||||
*/
|
||||
|
||||
exports.name = 'cleanupListOfValues';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'rounds list of values to the fixed precision';
|
||||
export const name = 'cleanupListOfValues';
|
||||
export const description = 'rounds list of values to the fixed precision';
|
||||
|
||||
const regNumericValues =
|
||||
/^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;
|
||||
@@ -25,23 +29,18 @@ const absoluteLengths = {
|
||||
*
|
||||
* @example
|
||||
* <svg viewBox="0 0 200.28423 200.28423" enable-background="new 0 0 200.28423 200.28423">
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <svg viewBox="0 0 200.284 200.284" enable-background="new 0 0 200.284 200.284">
|
||||
*
|
||||
* <polygon points="208.250977 77.1308594 223.069336 ... "/>
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <polygon points="208.251 77.131 223.069 ... "/>
|
||||
*
|
||||
* @author kiyopikko
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* floatPrecision?: number,
|
||||
* leadingZero?: boolean,
|
||||
* defaultPx?: boolean,
|
||||
* convertToPx?: boolean
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<CleanupListOfValuesParams>}
|
||||
*/
|
||||
exports.fn = (_root, params) => {
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
floatPrecision = 3,
|
||||
leadingZero = true,
|
||||
@@ -50,7 +49,8 @@ exports.fn = (_root, params) => {
|
||||
} = params;
|
||||
|
||||
/**
|
||||
* @type {(lists: string) => string}
|
||||
* @param {string} lists
|
||||
* @returns {string}
|
||||
*/
|
||||
const roundValues = (lists) => {
|
||||
const roundedList = [];
|
||||
@@ -63,19 +63,15 @@ exports.fn = (_root, params) => {
|
||||
if (match) {
|
||||
// round it to the fixed precision
|
||||
let num = Number(Number(match[1]).toFixed(floatPrecision));
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let matchedUnit = match[3] || '';
|
||||
/**
|
||||
* @type{'' | keyof typeof absoluteLengths}
|
||||
*/
|
||||
/** @type {any} */
|
||||
const matchedUnit = match[3] || '';
|
||||
/** @type {'' | keyof typeof absoluteLengths} */
|
||||
let units = matchedUnit;
|
||||
|
||||
// convert absolute values to pixels
|
||||
if (convertToPx && units && units in absoluteLengths) {
|
||||
const pxNum = Number(
|
||||
(absoluteLengths[units] * Number(match[1])).toFixed(floatPrecision)
|
||||
(absoluteLengths[units] * Number(match[1])).toFixed(floatPrecision),
|
||||
);
|
||||
|
||||
if (pxNum.toString().length < match[0].length) {
|
||||
@@ -119,7 +115,7 @@ exports.fn = (_root, params) => {
|
||||
|
||||
if (node.attributes['enable-background'] != null) {
|
||||
node.attributes['enable-background'] = roundValues(
|
||||
node.attributes['enable-background']
|
||||
node.attributes['enable-background'],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -129,7 +125,7 @@ exports.fn = (_root, params) => {
|
||||
|
||||
if (node.attributes['stroke-dasharray'] != null) {
|
||||
node.attributes['stroke-dasharray'] = roundValues(
|
||||
node.attributes['stroke-dasharray']
|
||||
node.attributes['stroke-dasharray'],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
48
node_modules/svgo/plugins/cleanupNumericValues.js
generated
vendored
48
node_modules/svgo/plugins/cleanupNumericValues.js
generated
vendored
@@ -1,12 +1,16 @@
|
||||
'use strict';
|
||||
import { removeLeadingZero } from '../lib/svgo/tools.js';
|
||||
|
||||
const { removeLeadingZero } = require('../lib/svgo/tools');
|
||||
/**
|
||||
* @typedef CleanupNumericValuesParams
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} defaultPx
|
||||
* @property {boolean=} convertToPx
|
||||
*/
|
||||
|
||||
exports.name = 'cleanupNumericValues';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description =
|
||||
'rounds numeric values to the fixed precision, removes default ‘px’ units';
|
||||
export const name = 'cleanupNumericValues';
|
||||
export const description =
|
||||
'rounds numeric values to the fixed precision, removes default "px" units';
|
||||
|
||||
const regNumericValues =
|
||||
/^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;
|
||||
@@ -22,19 +26,13 @@ const absoluteLengths = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Round numeric values to the fixed precision,
|
||||
* remove default 'px' units.
|
||||
* Round numeric values to the fixed precision, remove default 'px' units.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* floatPrecision?: number,
|
||||
* leadingZero?: boolean,
|
||||
* defaultPx?: boolean,
|
||||
* convertToPx?: boolean
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<CleanupNumericValuesParams>}
|
||||
*/
|
||||
exports.fn = (_root, params) => {
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
floatPrecision = 3,
|
||||
leadingZero = true,
|
||||
@@ -46,7 +44,7 @@ exports.fn = (_root, params) => {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.viewBox != null) {
|
||||
const nums = node.attributes.viewBox.split(/\s,?\s*|,\s*/g);
|
||||
const nums = node.attributes.viewBox.trim().split(/(?:\s,?|,)\s*/g);
|
||||
node.attributes.viewBox = nums
|
||||
.map((value) => {
|
||||
const num = Number(value);
|
||||
@@ -63,27 +61,23 @@ exports.fn = (_root, params) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const match = value.match(regNumericValues);
|
||||
const match = regNumericValues.exec(value);
|
||||
|
||||
// if attribute value matches regNumericValues
|
||||
if (match) {
|
||||
// round it to the fixed precision
|
||||
let num = Number(Number(match[1]).toFixed(floatPrecision));
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let matchedUnit = match[3] || '';
|
||||
/**
|
||||
* @type{'' | keyof typeof absoluteLengths}
|
||||
*/
|
||||
/** @type {any} */
|
||||
const matchedUnit = match[3] || '';
|
||||
/** @type {'' | keyof typeof absoluteLengths} */
|
||||
let units = matchedUnit;
|
||||
|
||||
// convert absolute values to pixels
|
||||
if (convertToPx && units !== '' && units in absoluteLengths) {
|
||||
const pxNum = Number(
|
||||
(absoluteLengths[units] * Number(match[1])).toFixed(
|
||||
floatPrecision
|
||||
)
|
||||
floatPrecision,
|
||||
),
|
||||
);
|
||||
if (pxNum.toString().length < match[0].length) {
|
||||
num = pxNum;
|
||||
|
||||
70
node_modules/svgo/plugins/collapseGroups.js
generated
vendored
70
node_modules/svgo/plugins/collapseGroups.js
generated
vendored
@@ -1,23 +1,18 @@
|
||||
'use strict';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { elemsGroups, inheritableAttrs } from './_collections.js';
|
||||
|
||||
export const name = 'collapseGroups';
|
||||
export const description = 'collapses useless groups';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastNode} XastNode
|
||||
*/
|
||||
|
||||
const { inheritableAttrs, elemsGroups } = require('./_collections.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'collapseGroups';
|
||||
exports.active = true;
|
||||
exports.description = 'collapses useless groups';
|
||||
|
||||
/**
|
||||
* @type {(node: XastNode, name: string) => boolean}
|
||||
* @param {import('../lib/types.js').XastNode} node
|
||||
* @param {string} name
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasAnimatedAttr = (node, name) => {
|
||||
if (node.type === 'element') {
|
||||
if (
|
||||
elemsGroups.animation.includes(node.name) &&
|
||||
elemsGroups.animation.has(node.name) &&
|
||||
node.attributes.attributeName === name
|
||||
) {
|
||||
return true;
|
||||
@@ -40,20 +35,22 @@ const hasAnimatedAttr = (node, name) => {
|
||||
* <path d="..."/>
|
||||
* </g>
|
||||
* </g>
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <g>
|
||||
* <g>
|
||||
* <path attr1="val1" d="..."/>
|
||||
* </g>
|
||||
* </g>
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <path attr1="val1" d="..."/>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = (root) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
return {
|
||||
element: {
|
||||
exit: (node, parentNode) => {
|
||||
@@ -65,17 +62,20 @@ exports.fn = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// move group attibutes to the single child element
|
||||
// move group attributes to the single child element
|
||||
if (
|
||||
Object.keys(node.attributes).length !== 0 &&
|
||||
node.children.length === 1
|
||||
) {
|
||||
const firstChild = node.children[0];
|
||||
const nodeHasFilter = !!(
|
||||
node.attributes.filter || computeStyle(stylesheet, node).filter
|
||||
);
|
||||
// TODO untangle this mess
|
||||
if (
|
||||
firstChild.type === 'element' &&
|
||||
firstChild.attributes.id == null &&
|
||||
node.attributes.filter == null &&
|
||||
!nodeHasFilter &&
|
||||
(node.attributes.class == null ||
|
||||
firstChild.attributes.class == null) &&
|
||||
((node.attributes['clip-path'] == null &&
|
||||
@@ -84,26 +84,30 @@ exports.fn = () => {
|
||||
node.attributes.transform == null &&
|
||||
firstChild.attributes.transform == null))
|
||||
) {
|
||||
const newChildElemAttrs = { ...firstChild.attributes };
|
||||
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
// avoid copying to not conflict with animated attribute
|
||||
if (hasAnimatedAttr(firstChild, name)) {
|
||||
return;
|
||||
}
|
||||
if (firstChild.attributes[name] == null) {
|
||||
firstChild.attributes[name] = value;
|
||||
|
||||
if (newChildElemAttrs[name] == null) {
|
||||
newChildElemAttrs[name] = value;
|
||||
} else if (name === 'transform') {
|
||||
firstChild.attributes[name] =
|
||||
value + ' ' + firstChild.attributes[name];
|
||||
} else if (firstChild.attributes[name] === 'inherit') {
|
||||
firstChild.attributes[name] = value;
|
||||
newChildElemAttrs[name] = value + ' ' + newChildElemAttrs[name];
|
||||
} else if (newChildElemAttrs[name] === 'inherit') {
|
||||
newChildElemAttrs[name] = value;
|
||||
} else if (
|
||||
inheritableAttrs.includes(name) === false &&
|
||||
firstChild.attributes[name] !== value
|
||||
!inheritableAttrs.has(name) &&
|
||||
newChildElemAttrs[name] !== value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
delete node.attributes[name];
|
||||
}
|
||||
|
||||
node.attributes = {};
|
||||
firstChild.attributes = newChildElemAttrs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +118,7 @@ exports.fn = () => {
|
||||
for (const child of node.children) {
|
||||
if (
|
||||
child.type === 'element' &&
|
||||
elemsGroups.animation.includes(child.name)
|
||||
elemsGroups.animation.has(child.name)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -122,12 +126,6 @@ exports.fn = () => {
|
||||
// replace current node with all its children
|
||||
const index = parentNode.children.indexOf(node);
|
||||
parentNode.children.splice(index, 1, ...node.children);
|
||||
// TODO remove in v3
|
||||
for (const child of node.children) {
|
||||
// @ts-ignore parentNode is forbidden for public usage
|
||||
// and will be moved in v3
|
||||
child.parentNode = parentNode;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
78
node_modules/svgo/plugins/convertColors.js
generated
vendored
78
node_modules/svgo/plugins/convertColors.js
generated
vendored
@@ -1,16 +1,24 @@
|
||||
'use strict';
|
||||
import { colorsNames, colorsProps, colorsShortNames } from './_collections.js';
|
||||
import { includesUrlReference } from '../lib/svgo/tools.js';
|
||||
|
||||
const collections = require('./_collections.js');
|
||||
/**
|
||||
* @typedef ConvertColorsParams
|
||||
* @property {boolean | string | RegExp=} currentColor
|
||||
* @property {boolean=} names2hex
|
||||
* @property {boolean=} rgb2hex
|
||||
* @property {false | 'lower' | 'upper'=} convertCase
|
||||
* @property {boolean=} shorthex
|
||||
* @property {boolean=} shortname
|
||||
*/
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'convertColors';
|
||||
exports.active = true;
|
||||
exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
|
||||
export const name = 'convertColors';
|
||||
export const description =
|
||||
'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
|
||||
|
||||
const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)';
|
||||
const rComma = '\\s*,\\s*';
|
||||
const rComma = '(?:\\s*,\\s*|\\s+)';
|
||||
const regRGB = new RegExp(
|
||||
'^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$'
|
||||
'^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$',
|
||||
);
|
||||
const regHEX = /^#(([a-fA-F0-9])\2){3}$/;
|
||||
|
||||
@@ -24,7 +32,8 @@ const regHEX = /^#(([a-fA-F0-9])\2){3}$/;
|
||||
*
|
||||
* @author Jed Schmidt
|
||||
*
|
||||
* @type {(rgb: Array<number>) => string}
|
||||
* @param {ReadonlyArray<number>} param0
|
||||
* @returns {string}
|
||||
*/
|
||||
const convertRgbToHex = ([r, g, b]) => {
|
||||
// combine the octets into a 32-bit integer as: [1][r][g][b]
|
||||
@@ -63,32 +72,32 @@ const convertRgbToHex = ([r, g, b]) => {
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* currentColor?: boolean | string | RegExp,
|
||||
* names2hex?: boolean,
|
||||
* rgb2hex?: boolean,
|
||||
* shorthex?: boolean,
|
||||
* shortname?: boolean,
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<ConvertColorsParams>}
|
||||
*/
|
||||
exports.fn = (_root, params) => {
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
currentColor = false,
|
||||
names2hex = true,
|
||||
rgb2hex = true,
|
||||
convertCase = 'lower',
|
||||
shorthex = true,
|
||||
shortname = true,
|
||||
} = params;
|
||||
|
||||
let maskCounter = 0;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'mask') {
|
||||
maskCounter++;
|
||||
}
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (collections.colorsProps.includes(name)) {
|
||||
if (colorsProps.has(name)) {
|
||||
let val = value;
|
||||
|
||||
// convert colors to currentColor
|
||||
if (currentColor) {
|
||||
if (currentColor && maskCounter === 0) {
|
||||
let matched;
|
||||
if (typeof currentColor === 'string') {
|
||||
matched = val === currentColor;
|
||||
@@ -105,16 +114,16 @@ exports.fn = (_root, params) => {
|
||||
// convert color name keyword to long hex
|
||||
if (names2hex) {
|
||||
const colorName = val.toLowerCase();
|
||||
if (collections.colorsNames[colorName] != null) {
|
||||
val = collections.colorsNames[colorName];
|
||||
if (colorsNames[colorName] != null) {
|
||||
val = colorsNames[colorName];
|
||||
}
|
||||
}
|
||||
|
||||
// convert rgb() to long hex
|
||||
if (rgb2hex) {
|
||||
let match = val.match(regRGB);
|
||||
const match = val.match(regRGB);
|
||||
if (match != null) {
|
||||
let nums = match.slice(1, 4).map((m) => {
|
||||
const nums = match.slice(1, 4).map((m) => {
|
||||
let n;
|
||||
if (m.indexOf('%') > -1) {
|
||||
n = Math.round(parseFloat(m) * 2.55);
|
||||
@@ -127,9 +136,21 @@ exports.fn = (_root, params) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
convertCase &&
|
||||
!includesUrlReference(val) &&
|
||||
val !== 'currentColor'
|
||||
) {
|
||||
if (convertCase === 'lower') {
|
||||
val = val.toLowerCase();
|
||||
} else if (convertCase === 'upper') {
|
||||
val = val.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
// convert long hex to short hex
|
||||
if (shorthex) {
|
||||
let match = val.match(regHEX);
|
||||
const match = regHEX.exec(val);
|
||||
if (match != null) {
|
||||
val = '#' + match[0][1] + match[0][3] + match[0][5];
|
||||
}
|
||||
@@ -138,8 +159,8 @@ exports.fn = (_root, params) => {
|
||||
// convert hex to short name
|
||||
if (shortname) {
|
||||
const colorName = val.toLowerCase();
|
||||
if (collections.colorsShortNames[colorName] != null) {
|
||||
val = collections.colorsShortNames[colorName];
|
||||
if (colorsShortNames[colorName] != null) {
|
||||
val = colorsShortNames[colorName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +168,11 @@ exports.fn = (_root, params) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
exit: (node) => {
|
||||
if (node.name === 'mask') {
|
||||
maskCounter--;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
12
node_modules/svgo/plugins/convertEllipseToCircle.js
generated
vendored
12
node_modules/svgo/plugins/convertEllipseToCircle.js
generated
vendored
@@ -1,9 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
exports.name = 'convertEllipseToCircle';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'converts non-eccentric <ellipse>s to <circle>s';
|
||||
export const name = 'convertEllipseToCircle';
|
||||
export const description = 'converts non-eccentric <ellipse>s to <circle>s';
|
||||
|
||||
/**
|
||||
* Converts non-eccentric <ellipse>s to <circle>s.
|
||||
@@ -12,9 +8,9 @@ exports.description = 'converts non-eccentric <ellipse>s to <circle>s';
|
||||
*
|
||||
* @author Taylor Hunt
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
|
||||
157
node_modules/svgo/plugins/convertOneStopGradients.js
generated
vendored
Normal file
157
node_modules/svgo/plugins/convertOneStopGradients.js
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
import { attrsGroupsDefaults, colorsProps } from './_collections.js';
|
||||
import {
|
||||
detachNodeFromParent,
|
||||
querySelector,
|
||||
querySelectorAll,
|
||||
} from '../lib/xast.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
|
||||
export const name = 'convertOneStopGradients';
|
||||
export const description =
|
||||
'converts one-stop (single color) gradients to a plain color';
|
||||
|
||||
/**
|
||||
* Converts one-stop (single color) gradients to a plain color.
|
||||
*
|
||||
* @author Seth Falco <seth@falco.fun>
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/linearGradient
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/radialGradient
|
||||
*/
|
||||
export const fn = (root) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
/**
|
||||
* Parent defs that had gradients elements removed from them.
|
||||
*
|
||||
* @type {Set<import('../lib/types.js').XastElement>}
|
||||
*/
|
||||
const effectedDefs = new Set();
|
||||
|
||||
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>} */
|
||||
const allDefs = new Map();
|
||||
|
||||
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>} */
|
||||
const gradientsToDetach = new Map();
|
||||
|
||||
/** Number of references to the xlink:href attribute. */
|
||||
let xlinkHrefCount = 0;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.attributes['xlink:href'] != null) {
|
||||
xlinkHrefCount++;
|
||||
}
|
||||
|
||||
if (node.name === 'defs') {
|
||||
allDefs.set(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.name !== 'linearGradient' && node.name !== 'radialGradient') {
|
||||
return;
|
||||
}
|
||||
|
||||
const stops = node.children.filter((child) => {
|
||||
return child.type === 'element' && child.name === 'stop';
|
||||
});
|
||||
|
||||
const href = node.attributes['xlink:href'] || node.attributes['href'];
|
||||
const effectiveNode =
|
||||
stops.length === 0 && href != null && href.startsWith('#')
|
||||
? querySelector(root, href)
|
||||
: node;
|
||||
|
||||
if (effectiveNode == null || effectiveNode.type !== 'element') {
|
||||
gradientsToDetach.set(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
const effectiveStops = effectiveNode.children.filter((child) => {
|
||||
return child.type === 'element' && child.name === 'stop';
|
||||
});
|
||||
|
||||
if (
|
||||
effectiveStops.length !== 1 ||
|
||||
effectiveStops[0].type !== 'element'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (parentNode.type === 'element' && parentNode.name === 'defs') {
|
||||
effectedDefs.add(parentNode);
|
||||
}
|
||||
|
||||
gradientsToDetach.set(node, parentNode);
|
||||
|
||||
let color;
|
||||
const style = computeStyle(stylesheet, effectiveStops[0])['stop-color'];
|
||||
if (style != null && style.type === 'static') {
|
||||
color = style.value;
|
||||
}
|
||||
|
||||
const selectorVal = `url(#${node.attributes.id})`;
|
||||
|
||||
const selector = [...colorsProps]
|
||||
.map((attr) => `[${attr}="${selectorVal}"]`)
|
||||
.join(',');
|
||||
const elements = querySelectorAll(root, selector);
|
||||
for (const element of elements) {
|
||||
if (element.type !== 'element') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const attr of colorsProps) {
|
||||
if (element.attributes[attr] !== selectorVal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (color != null) {
|
||||
element.attributes[attr] = color;
|
||||
} else {
|
||||
delete element.attributes[attr];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styledElements = querySelectorAll(
|
||||
root,
|
||||
`[style*=${selectorVal}]`,
|
||||
);
|
||||
for (const element of styledElements) {
|
||||
if (element.type !== 'element') {
|
||||
continue;
|
||||
}
|
||||
|
||||
element.attributes.style = element.attributes.style.replace(
|
||||
selectorVal,
|
||||
color || attrsGroupsDefaults.presentation['stop-color'],
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
exit: (node) => {
|
||||
if (node.name === 'svg') {
|
||||
for (const [gradient, parent] of gradientsToDetach.entries()) {
|
||||
if (gradient.attributes['xlink:href'] != null) {
|
||||
xlinkHrefCount--;
|
||||
}
|
||||
|
||||
detachNodeFromParent(gradient, parent);
|
||||
}
|
||||
|
||||
if (xlinkHrefCount === 0) {
|
||||
delete node.attributes['xmlns:xlink'];
|
||||
}
|
||||
|
||||
for (const [defs, parent] of allDefs.entries()) {
|
||||
if (effectedDefs.has(defs) && defs.children.length === 0) {
|
||||
detachNodeFromParent(defs, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
827
node_modules/svgo/plugins/convertPathData.js
generated
vendored
827
node_modules/svgo/plugins/convertPathData.js
generated
vendored
File diff suppressed because it is too large
Load Diff
60
node_modules/svgo/plugins/convertShapeToPath.js
generated
vendored
60
node_modules/svgo/plugins/convertShapeToPath.js
generated
vendored
@@ -1,34 +1,28 @@
|
||||
'use strict';
|
||||
import { stringifyPathData } from '../lib/path.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').PathDataItem} PathDataItem
|
||||
* @typedef ConvertShapeToPathParams
|
||||
* @property {boolean=} convertArcs
|
||||
* @property {number=} floatPrecision
|
||||
*/
|
||||
|
||||
const { stringifyPathData } = require('../lib/path.js');
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'convertShapeToPath';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'converts basic shapes to more compact path form';
|
||||
export const name = 'convertShapeToPath';
|
||||
export const description = 'converts basic shapes to more compact path form';
|
||||
|
||||
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
||||
|
||||
/**
|
||||
* Converts basic shape to more compact path.
|
||||
* It also allows further optimizations like
|
||||
* combining paths with similar attributes.
|
||||
* Converts basic shape to more compact path. It also allows further
|
||||
* optimizations like combining paths with similar attributes.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/shapes.html
|
||||
*
|
||||
* @author Lev Solntsev
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* convertArcs?: boolean,
|
||||
* floatPrecision?: number
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<ConvertShapeToPathParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const { convertArcs = false, floatPrecision: precision } = params;
|
||||
|
||||
return {
|
||||
@@ -49,10 +43,10 @@ exports.fn = (root, params) => {
|
||||
// Values like '100%' compute to NaN, thus running after
|
||||
// cleanupNumericValues when 'px' units has already been removed.
|
||||
// TODO: Calculate sizes from % and non-px units if possible.
|
||||
if (Number.isNaN(x - y + width - height)) return;
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
if (Number.isNaN(x - y + width - height)) {
|
||||
return;
|
||||
}
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [
|
||||
{ command: 'M', args: [x, y] },
|
||||
{ command: 'H', args: [x + width] },
|
||||
@@ -74,10 +68,10 @@ exports.fn = (root, params) => {
|
||||
const y1 = Number(node.attributes.y1 || '0');
|
||||
const x2 = Number(node.attributes.x2 || '0');
|
||||
const y2 = Number(node.attributes.y2 || '0');
|
||||
if (Number.isNaN(x1 - y1 + x2 - y2)) return;
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
if (Number.isNaN(x1 - y1 + x2 - y2)) {
|
||||
return;
|
||||
}
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [
|
||||
{ command: 'M', args: [x1, y1] },
|
||||
{ command: 'L', args: [x2, y2] },
|
||||
@@ -96,15 +90,13 @@ exports.fn = (root, params) => {
|
||||
node.attributes.points != null
|
||||
) {
|
||||
const coords = (node.attributes.points.match(regNumber) || []).map(
|
||||
Number
|
||||
Number,
|
||||
);
|
||||
if (coords.length < 4) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [];
|
||||
for (let i = 0; i < coords.length; i += 2) {
|
||||
pathData.push({
|
||||
@@ -128,9 +120,7 @@ exports.fn = (root, params) => {
|
||||
if (Number.isNaN(cx - cy + r)) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [
|
||||
{ command: 'M', args: [cx, cy - r] },
|
||||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
|
||||
@@ -144,7 +134,7 @@ exports.fn = (root, params) => {
|
||||
delete node.attributes.r;
|
||||
}
|
||||
|
||||
// optionally covert ellipse
|
||||
// optionally convert ellipse
|
||||
if (node.name === 'ellipse' && convertArcs) {
|
||||
const ecx = Number(node.attributes.cx || '0');
|
||||
const ecy = Number(node.attributes.cy || '0');
|
||||
@@ -153,9 +143,7 @@ exports.fn = (root, params) => {
|
||||
if (Number.isNaN(ecx - ecy + rx - ry)) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [
|
||||
{ command: 'M', args: [ecx, ecy - ry] },
|
||||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
|
||||
|
||||
223
node_modules/svgo/plugins/convertStyleToAttrs.js
generated
vendored
223
node_modules/svgo/plugins/convertStyleToAttrs.js
generated
vendored
@@ -1,132 +1,145 @@
|
||||
'use strict';
|
||||
import { attrsGroups } from './_collections.js';
|
||||
|
||||
exports.name = 'convertStyleToAttrs';
|
||||
/**
|
||||
* @typedef ConvertStyleToAttrsParams
|
||||
* @property {boolean=} keepImportant
|
||||
*/
|
||||
|
||||
exports.type = 'perItem';
|
||||
export const name = 'convertStyleToAttrs';
|
||||
export const description = 'converts style to attributes';
|
||||
|
||||
exports.active = false;
|
||||
|
||||
exports.description = 'converts style to attributes';
|
||||
|
||||
exports.params = {
|
||||
keepImportant: false,
|
||||
/**
|
||||
* @param {...string} args
|
||||
* @returns {string}
|
||||
*/
|
||||
const g = (...args) => {
|
||||
return '(?:' + args.join('|') + ')';
|
||||
};
|
||||
|
||||
var stylingProps = require('./_collections').attrsGroups.presentation,
|
||||
rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
|
||||
rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like ‘fill’
|
||||
rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
|
||||
rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
|
||||
rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
|
||||
// Parentheses, E.g.: url(data:image/png;base64,iVBO...).
|
||||
// ':' and ';' inside of it should be threated as is. (Just like in strings.)
|
||||
rParenthesis =
|
||||
'\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
|
||||
// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
|
||||
rValue =
|
||||
'\\s*(' +
|
||||
g(
|
||||
'[^!\'"();\\\\]+?',
|
||||
rEscape,
|
||||
rSingleQuotes,
|
||||
rQuotes,
|
||||
rParenthesis,
|
||||
'[^;]*?'
|
||||
) +
|
||||
'*?' +
|
||||
')',
|
||||
// End of declaration. Spaces outside of capturing groups help to do natural trimming.
|
||||
rDeclEnd = '\\s*(?:;\\s*|$)',
|
||||
// Important rule
|
||||
rImportant = '(\\s*!important(?![-(\\w]))?',
|
||||
// Final RegExp to parse CSS declarations.
|
||||
regDeclarationBlock = new RegExp(
|
||||
rAttr + ':' + rValue + rImportant + rDeclEnd,
|
||||
'ig'
|
||||
),
|
||||
// Comments expression. Honors escape sequences and strings.
|
||||
regStripComments = new RegExp(
|
||||
g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
|
||||
'ig'
|
||||
);
|
||||
const stylingProps = attrsGroups.presentation;
|
||||
const rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)'; // Like \" or \2051. Code points consume one space.
|
||||
|
||||
/** Pattern to match attribute name like: 'fill' */
|
||||
const rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*';
|
||||
|
||||
/** Pattern to match string in single quotes like: 'foo' */
|
||||
const rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)";
|
||||
|
||||
/** Pattern to match string in double quotes like: "foo" */
|
||||
const rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)';
|
||||
|
||||
const rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$');
|
||||
// Parentheses, E.g.: url(data:image/png;base64,iVBO...).
|
||||
// ':' and ';' inside of it should be treated as is. (Just like in strings.)
|
||||
const rParenthesis =
|
||||
'\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)';
|
||||
// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
|
||||
const rValue =
|
||||
'\\s*(' +
|
||||
g(
|
||||
'[^!\'"();\\\\]+?',
|
||||
rEscape,
|
||||
rSingleQuotes,
|
||||
rQuotes,
|
||||
rParenthesis,
|
||||
'[^;]*?',
|
||||
) +
|
||||
'*?' +
|
||||
')';
|
||||
// End of declaration. Spaces outside of capturing groups help to do natural trimming.
|
||||
const rDeclEnd = '\\s*(?:;\\s*|$)';
|
||||
// Important rule
|
||||
const rImportant = '(\\s*!important(?![-(\\w]))?';
|
||||
// Final RegExp to parse CSS declarations.
|
||||
const regDeclarationBlock = new RegExp(
|
||||
rAttr + ':' + rValue + rImportant + rDeclEnd,
|
||||
'ig',
|
||||
);
|
||||
// Comments expression. Honors escape sequences and strings.
|
||||
const regStripComments = new RegExp(
|
||||
g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
|
||||
'ig',
|
||||
);
|
||||
|
||||
/**
|
||||
* Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
|
||||
*
|
||||
* @example
|
||||
* <g style="fill:#000; color: #fff;">
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <g fill="#000" color="#fff">
|
||||
*
|
||||
* @example
|
||||
* <g style="fill:#000; color: #fff; -webkit-blah: blah">
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <g fill="#000" color="#fff" style="-webkit-blah: blah">
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<ConvertStyleToAttrsParams>}
|
||||
*/
|
||||
exports.fn = function (item, params) {
|
||||
if (item.type === 'element' && item.attributes.style != null) {
|
||||
// ['opacity: 1', 'color: #000']
|
||||
let styles = [];
|
||||
const newAttributes = {};
|
||||
export const fn = (_root, params) => {
|
||||
const { keepImportant = false } = params;
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.style != null) {
|
||||
// ['opacity: 1', 'color: #000']
|
||||
let styles = [];
|
||||
/** @type {Record<string, string>} */
|
||||
const newAttributes = {};
|
||||
|
||||
// Strip CSS comments preserving escape sequences and strings.
|
||||
const styleValue = item.attributes.style.replace(
|
||||
regStripComments,
|
||||
(match) => {
|
||||
return match[0] == '/'
|
||||
? ''
|
||||
: match[0] == '\\' && /[-g-z]/i.test(match[1])
|
||||
? match[1]
|
||||
: match;
|
||||
}
|
||||
);
|
||||
// Strip CSS comments preserving escape sequences and strings.
|
||||
const styleValue = node.attributes.style.replace(
|
||||
regStripComments,
|
||||
(match) => {
|
||||
return match[0] == '/'
|
||||
? ''
|
||||
: match[0] == '\\' && /[-g-z]/i.test(match[1])
|
||||
? match[1]
|
||||
: match;
|
||||
},
|
||||
);
|
||||
|
||||
regDeclarationBlock.lastIndex = 0;
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
|
||||
if (!params.keepImportant || !rule[3]) {
|
||||
styles.push([rule[1], rule[2]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (styles.length) {
|
||||
styles = styles.filter(function (style) {
|
||||
if (style[0]) {
|
||||
var prop = style[0].toLowerCase(),
|
||||
val = style[1];
|
||||
|
||||
if (rQuotedString.test(val)) {
|
||||
val = val.slice(1, -1);
|
||||
regDeclarationBlock.lastIndex = 0;
|
||||
for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
|
||||
if (!keepImportant || !rule[3]) {
|
||||
styles.push([rule[1], rule[2]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (stylingProps.includes(prop)) {
|
||||
newAttributes[prop] = val;
|
||||
if (styles.length) {
|
||||
styles = styles.filter(function (style) {
|
||||
if (style[0]) {
|
||||
const prop = style[0].toLowerCase();
|
||||
let val = style[1];
|
||||
|
||||
return false;
|
||||
if (rQuotedString.test(val)) {
|
||||
val = val.slice(1, -1);
|
||||
}
|
||||
|
||||
if (stylingProps.has(prop)) {
|
||||
newAttributes[prop] = val;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Object.assign(node.attributes, newAttributes);
|
||||
|
||||
if (styles.length) {
|
||||
node.attributes.style = styles
|
||||
.map((declaration) => declaration.join(':'))
|
||||
.join(';');
|
||||
} else {
|
||||
delete node.attributes.style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
Object.assign(item.attributes, newAttributes);
|
||||
|
||||
if (styles.length) {
|
||||
item.attributes.style = styles
|
||||
.map((declaration) => declaration.join(':'))
|
||||
.join(';');
|
||||
} else {
|
||||
delete item.attributes.style;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function g() {
|
||||
return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
|
||||
}
|
||||
|
||||
279
node_modules/svgo/plugins/convertTransform.js
generated
vendored
279
node_modules/svgo/plugins/convertTransform.js
generated
vendored
@@ -1,20 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
*/
|
||||
|
||||
const { cleanupOutData } = require('../lib/svgo/tools.js');
|
||||
const {
|
||||
import {
|
||||
js2transform,
|
||||
matrixToTransform,
|
||||
roundTransform,
|
||||
transform2js,
|
||||
transformsMultiply,
|
||||
matrixToTransform,
|
||||
} = require('./_transforms.js');
|
||||
} from './_transforms.js';
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'convertTransform';
|
||||
exports.active = true;
|
||||
exports.description = 'collapses multiple transformations and optimizes it';
|
||||
/**
|
||||
* @typedef ConvertTransformParams
|
||||
* @property {boolean=} convertToShorts
|
||||
* @property {number=} degPrecision
|
||||
* @property {number=} floatPrecision
|
||||
* @property {number=} transformPrecision
|
||||
* @property {boolean=} matrixToTransform
|
||||
* @property {boolean=} shortTranslate
|
||||
* @property {boolean=} shortScale
|
||||
* @property {boolean=} shortRotate
|
||||
* @property {boolean=} removeUseless
|
||||
* @property {boolean=} collapseIntoOne
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} negativeExtraSpace
|
||||
*
|
||||
* @typedef TransformParams
|
||||
* @property {boolean} convertToShorts
|
||||
* @property {number=} degPrecision
|
||||
* @property {number} floatPrecision
|
||||
* @property {number} transformPrecision
|
||||
* @property {boolean} matrixToTransform
|
||||
* @property {boolean} shortTranslate
|
||||
* @property {boolean} shortScale
|
||||
* @property {boolean} shortRotate
|
||||
* @property {boolean} removeUseless
|
||||
* @property {boolean} collapseIntoOne
|
||||
* @property {boolean} leadingZero
|
||||
* @property {boolean} negativeExtraSpace
|
||||
*
|
||||
* @typedef TransformItem
|
||||
* @property {string} name
|
||||
* @property {number[]} data
|
||||
*/
|
||||
|
||||
export const name = 'convertTransform';
|
||||
export const description =
|
||||
'collapses multiple transformations and optimizes it';
|
||||
|
||||
/**
|
||||
* Convert matrices to the short aliases,
|
||||
@@ -26,22 +54,9 @@ exports.description = 'collapses multiple transformations and optimizes it';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* convertToShorts?: boolean,
|
||||
* degPrecision?: number,
|
||||
* floatPrecision?: number,
|
||||
* transformPrecision?: number,
|
||||
* matrixToTransform?: boolean,
|
||||
* shortTranslate?: boolean,
|
||||
* shortScale?: boolean,
|
||||
* shortRotate?: boolean,
|
||||
* removeUseless?: boolean,
|
||||
* collapseIntoOne?: boolean,
|
||||
* leadingZero?: boolean,
|
||||
* negativeExtraSpace?: boolean,
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<ConvertTransformParams>}
|
||||
*/
|
||||
exports.fn = (_root, params) => {
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
convertToShorts = true,
|
||||
// degPrecision = 3, // transformPrecision (or matrix precision) - 2 by default
|
||||
@@ -74,15 +89,14 @@ exports.fn = (_root, params) => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
// transform
|
||||
if (node.attributes.transform != null) {
|
||||
convertTransform(node, 'transform', newParams);
|
||||
}
|
||||
// gradientTransform
|
||||
|
||||
if (node.attributes.gradientTransform != null) {
|
||||
convertTransform(node, 'gradientTransform', newParams);
|
||||
}
|
||||
// patternTransform
|
||||
|
||||
if (node.attributes.patternTransform != null) {
|
||||
convertTransform(node, 'patternTransform', newParams);
|
||||
}
|
||||
@@ -92,30 +106,9 @@ exports.fn = (_root, params) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* convertToShorts: boolean,
|
||||
* degPrecision?: number,
|
||||
* floatPrecision: number,
|
||||
* transformPrecision: number,
|
||||
* matrixToTransform: boolean,
|
||||
* shortTranslate: boolean,
|
||||
* shortScale: boolean,
|
||||
* shortRotate: boolean,
|
||||
* removeUseless: boolean,
|
||||
* collapseIntoOne: boolean,
|
||||
* leadingZero: boolean,
|
||||
* negativeExtraSpace: boolean,
|
||||
* }} TransformParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{ name: string, data: Array<number> }} TransformItem
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main function.
|
||||
*
|
||||
* @type {(item: XastElement, attrName: string, params: TransformParams) => void}
|
||||
* @param {import('../lib/types.js').XastElement} item
|
||||
* @param {string} attrName
|
||||
* @param {TransformParams} params
|
||||
*/
|
||||
const convertTransform = (item, attrName, params) => {
|
||||
let data = transform2js(item.attributes[attrName]);
|
||||
@@ -144,14 +137,17 @@ const convertTransform = (item, attrName, params) => {
|
||||
|
||||
/**
|
||||
* Defines precision to work with certain parts.
|
||||
* transformPrecision - for scale and four first matrix parameters (needs a better precision due to multiplying),
|
||||
* floatPrecision - for translate including two last matrix and rotate parameters,
|
||||
* degPrecision - for rotate and skew. By default it's equal to (rougly)
|
||||
* transformPrecision - 2 or floatPrecision whichever is lower. Can be set in params.
|
||||
*
|
||||
* @type {(data: Array<TransformItem>, params: TransformParams) => TransformParams}
|
||||
* - `transformPrecision` - for scale and four first matrix parameters (needs a better precision due to multiplying).
|
||||
* - `floatPrecision` - for translate including two last matrix and rotate parameters.
|
||||
* - `degPrecision` - for rotate and skew. By default it's equal to (roughly).
|
||||
* - `transformPrecision` - 2 or floatPrecision whichever is lower. Can be set in params.
|
||||
*
|
||||
* clone params so it don't affect other elements transformations.
|
||||
* Clone parameters so that it doesn't affect other element transformations.
|
||||
*
|
||||
* @param {ReadonlyArray<TransformItem>} data
|
||||
* @param {TransformParams} param1
|
||||
* @returns {TransformParams}
|
||||
*/
|
||||
const definePrecision = (data, { ...newParams }) => {
|
||||
const matrixData = [];
|
||||
@@ -160,71 +156,37 @@ const definePrecision = (data, { ...newParams }) => {
|
||||
matrixData.push(...item.data.slice(0, 4));
|
||||
}
|
||||
}
|
||||
let significantDigits = newParams.transformPrecision;
|
||||
let numberOfDigits = newParams.transformPrecision;
|
||||
// Limit transform precision with matrix one. Calculating with larger precision doesn't add any value.
|
||||
if (matrixData.length) {
|
||||
newParams.transformPrecision = Math.min(
|
||||
newParams.transformPrecision,
|
||||
Math.max.apply(Math, matrixData.map(floatDigits)) ||
|
||||
newParams.transformPrecision
|
||||
newParams.transformPrecision,
|
||||
);
|
||||
significantDigits = Math.max.apply(
|
||||
numberOfDigits = Math.max.apply(
|
||||
Math,
|
||||
matrixData.map(
|
||||
(n) => n.toString().replace(/\D+/g, '').length // Number of digits in a number. 123.45 → 5
|
||||
)
|
||||
(n) => n.toString().replace(/\D+/g, '').length, // Number of digits in a number. 123.45 → 5
|
||||
),
|
||||
);
|
||||
}
|
||||
// No sense in angle precision more then number of significant digits in matrix.
|
||||
// No sense in angle precision more than number of significant digits in matrix.
|
||||
if (newParams.degPrecision == null) {
|
||||
newParams.degPrecision = Math.max(
|
||||
0,
|
||||
Math.min(newParams.floatPrecision, significantDigits - 2)
|
||||
Math.min(newParams.floatPrecision, numberOfDigits - 2),
|
||||
);
|
||||
}
|
||||
return newParams;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(data: Array<number>, params: TransformParams) => Array<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: Array<number>, params: TransformParams) => Array<number>}
|
||||
*/
|
||||
const floatRound = (data, params) => {
|
||||
if (params.floatPrecision >= 1 && params.floatPrecision < 20) {
|
||||
return smartRound(params.floatPrecision, data);
|
||||
} else {
|
||||
return round(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(data: Array<number>, params: TransformParams) => Array<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
|
||||
* Returns number of digits after the point.
|
||||
*
|
||||
* @type {(n: number) => number}
|
||||
* @example 0.125 → 3
|
||||
* @param {number} n
|
||||
* @returns {number}
|
||||
*/
|
||||
const floatDigits = (n) => {
|
||||
const str = n.toString();
|
||||
@@ -234,15 +196,17 @@ const floatDigits = (n) => {
|
||||
/**
|
||||
* Convert transforms to the shorthand alternatives.
|
||||
*
|
||||
* @type {(transforms: Array<TransformItem>, params: TransformParams) => Array<TransformItem>}
|
||||
* @param {TransformItem[]} transforms
|
||||
* @param {TransformParams} params
|
||||
* @returns {TransformItem[]}
|
||||
*/
|
||||
const convertToShorts = (transforms, params) => {
|
||||
for (var i = 0; i < transforms.length; i++) {
|
||||
var transform = transforms[i];
|
||||
for (let i = 0; i < transforms.length; i++) {
|
||||
let transform = transforms[i];
|
||||
|
||||
// convert matrix to the short aliases
|
||||
if (params.matrixToTransform && transform.name === 'matrix') {
|
||||
var decomposed = matrixToTransform(transform, params);
|
||||
const decomposed = matrixToTransform(transform, params);
|
||||
if (
|
||||
js2transform(decomposed, params).length <=
|
||||
js2transform([transform], params).length
|
||||
@@ -282,8 +246,7 @@ const convertToShorts = (transforms, params) => {
|
||||
// translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy)
|
||||
if (
|
||||
params.shortRotate &&
|
||||
transforms[i - 2] &&
|
||||
transforms[i - 2].name === 'translate' &&
|
||||
transforms[i - 2]?.name === 'translate' &&
|
||||
transforms[i - 1].name === 'rotate' &&
|
||||
transforms[i].name === 'translate' &&
|
||||
transforms[i - 2].data[0] === -transforms[i].data[0] &&
|
||||
@@ -309,7 +272,8 @@ const convertToShorts = (transforms, params) => {
|
||||
/**
|
||||
* Remove useless transforms.
|
||||
*
|
||||
* @type {(trasforms: Array<TransformItem>) => Array<TransformItem>}
|
||||
* @param {ReadonlyArray<TransformItem>} transforms
|
||||
* @returns {TransformItem[]}
|
||||
*/
|
||||
const removeUseless = (transforms) => {
|
||||
return transforms.filter((transform) => {
|
||||
@@ -343,90 +307,3 @@ const removeUseless = (transforms) => {
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert transforms JS representation to string.
|
||||
*
|
||||
* @type {(transformJS: Array<TransformItem>, params: TransformParams) => string}
|
||||
*/
|
||||
const js2transform = (transformJS, params) => {
|
||||
var transformString = '';
|
||||
|
||||
// collect output value string
|
||||
transformJS.forEach((transform) => {
|
||||
roundTransform(transform, params);
|
||||
transformString +=
|
||||
(transformString && ' ') +
|
||||
transform.name +
|
||||
'(' +
|
||||
cleanupOutData(transform.data, params) +
|
||||
')';
|
||||
});
|
||||
|
||||
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: Array<number>) => Array<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.
|
||||
*
|
||||
* @type {(precision: number, data: Array<number>) => Array<number>}
|
||||
*/
|
||||
const smartRound = (precision, data) => {
|
||||
for (
|
||||
var i = data.length,
|
||||
tolerance = +Math.pow(0.1, precision).toFixed(precision);
|
||||
i--;
|
||||
|
||||
) {
|
||||
if (Number(data[i].toFixed(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;
|
||||
};
|
||||
|
||||
334
node_modules/svgo/plugins/inlineStyles.js
generated
vendored
334
node_modules/svgo/plugins/inlineStyles.js
generated
vendored
@@ -1,72 +1,48 @@
|
||||
'use strict';
|
||||
import * as csstree from 'css-tree';
|
||||
import { syntax } from 'csso';
|
||||
import { attrsGroups, pseudoClasses } from './_collections.js';
|
||||
import { detachNodeFromParent, querySelectorAll } from '../lib/xast.js';
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
import { compareSpecificity, includesAttrSelector } from '../lib/style.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').Specificity} Specificity
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
* @typedef {import('../lib/types').XastParent} XastParent
|
||||
* @typedef InlineStylesParams
|
||||
* @property {boolean=} onlyMatchedOnce Inlines selectors that match once only.
|
||||
* @property {boolean=} removeMatchedSelectors
|
||||
* Clean up matched selectors. Unused selects are left as-is.
|
||||
* @property {string[]=} useMqs
|
||||
* Media queries to use. An empty string indicates all selectors outside of
|
||||
* media queries.
|
||||
* @property {string[]=} usePseudos
|
||||
* Pseudo-classes and elements to use. An empty string indicates all
|
||||
* non-pseudo-classes and elements.
|
||||
*/
|
||||
|
||||
const csstree = require('css-tree');
|
||||
// @ts-ignore not defined in @types/csso
|
||||
const specificity = require('csso/lib/restructure/prepare/specificity');
|
||||
const stable = require('stable');
|
||||
const {
|
||||
visitSkip,
|
||||
querySelectorAll,
|
||||
detachNodeFromParent,
|
||||
} = require('../lib/xast.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'inlineStyles';
|
||||
exports.active = true;
|
||||
exports.description = 'inline styles (additional options)';
|
||||
export const name = 'inlineStyles';
|
||||
export const description = 'inline styles (additional options)';
|
||||
|
||||
/**
|
||||
* Compares two selector specificities.
|
||||
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
|
||||
* Some pseudo-classes can only be calculated by clients, like :visited,
|
||||
* :future, or :hover, but there are other pseudo-classes that we can evaluate
|
||||
* during optimization.
|
||||
*
|
||||
* @type {(a: Specificity, b: Specificity) => number}
|
||||
* Pseudo-classes that we can evaluate during optimization, and shouldn't be
|
||||
* toggled conditionally through the `usePseudos` parameter.
|
||||
*
|
||||
* @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes
|
||||
*/
|
||||
const compareSpecificity = (a, b) => {
|
||||
for (var i = 0; i < 4; i += 1) {
|
||||
if (a[i] < b[i]) {
|
||||
return -1;
|
||||
} else if (a[i] > b[i]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
const preservedPseudos = [
|
||||
...pseudoClasses.functional,
|
||||
...pseudoClasses.treeStructural,
|
||||
];
|
||||
|
||||
/**
|
||||
* Moves + merges styles from style elements to element styles
|
||||
*
|
||||
* Options
|
||||
* onlyMatchedOnce (default: true)
|
||||
* inline only selectors that match once
|
||||
*
|
||||
* removeMatchedSelectors (default: true)
|
||||
* clean up matched selectors,
|
||||
* leave selectors that hadn't matched
|
||||
*
|
||||
* useMqs (default: ['', 'screen'])
|
||||
* what media queries to be used
|
||||
* empty string element for styles outside media queries
|
||||
*
|
||||
* usePseudos (default: [''])
|
||||
* what pseudo-classes/-elements to be used
|
||||
* empty string element for all non-pseudo-classes and/or -elements
|
||||
* Merges styles from style nodes into inline styles.
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<InlineStylesParams>}
|
||||
* @author strarsis <strarsis@gmail.com>
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* onlyMatchedOnce?: boolean,
|
||||
* removeMatchedSelectors?: boolean,
|
||||
* useMqs?: Array<string>,
|
||||
* usePseudos?: Array<string>
|
||||
* }>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
onlyMatchedOnce = true,
|
||||
removeMatchedSelectors = true,
|
||||
@@ -75,31 +51,32 @@ exports.fn = (root, params) => {
|
||||
} = params;
|
||||
|
||||
/**
|
||||
* @type {Array<{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }>}
|
||||
* @type {{
|
||||
* node: import('../lib/types.js').XastElement,
|
||||
* parentNode: import('../lib/types.js').XastParent,
|
||||
* cssAst: csstree.StyleSheet
|
||||
* }[]}
|
||||
*/
|
||||
const styles = [];
|
||||
/**
|
||||
* @type {Array<{
|
||||
* @type {{
|
||||
* node: csstree.Selector,
|
||||
* item: csstree.ListItem<csstree.CssNode>,
|
||||
* rule: csstree.Rule,
|
||||
* matchedElements?: Array<XastElement>
|
||||
* }>}
|
||||
* matchedElements?: import('../lib/types.js').XastElement[]
|
||||
* }[]}
|
||||
*/
|
||||
let selectors = [];
|
||||
const selectors = [];
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// skip <foreignObject /> content
|
||||
if (node.name === 'foreignObject') {
|
||||
return visitSkip;
|
||||
}
|
||||
// collect only non-empty <style /> elements
|
||||
if (node.name !== 'style' || node.children.length === 0) {
|
||||
return;
|
||||
}
|
||||
// values other than the empty string or text/css are not used
|
||||
if (
|
||||
node.attributes.type != null &&
|
||||
node.attributes.type !== '' &&
|
||||
@@ -107,16 +84,13 @@ exports.fn = (root, params) => {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// parse css in style element
|
||||
let cssText = '';
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'text' || child.type === 'cdata') {
|
||||
cssText += child.value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @type {null | csstree.CssNode}
|
||||
*/
|
||||
|
||||
const cssText = node.children
|
||||
.filter((child) => child.type === 'text' || child.type === 'cdata')
|
||||
.map((child) => child.value)
|
||||
.join('');
|
||||
|
||||
/** @type {?csstree.CssNode} */
|
||||
let cssAst = null;
|
||||
try {
|
||||
cssAst = csstree.parse(cssText, {
|
||||
@@ -132,63 +106,68 @@ exports.fn = (root, params) => {
|
||||
|
||||
// collect selectors
|
||||
csstree.walk(cssAst, {
|
||||
visit: 'Selector',
|
||||
enter(node, item) {
|
||||
visit: 'Rule',
|
||||
enter(node) {
|
||||
const atrule = this.atrule;
|
||||
const rule = this.rule;
|
||||
if (rule == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// skip media queries not included into useMqs param
|
||||
let mq = '';
|
||||
let mediaQuery = '';
|
||||
if (atrule != null) {
|
||||
mq = atrule.name;
|
||||
mediaQuery = atrule.name;
|
||||
if (atrule.prelude != null) {
|
||||
mq += ` ${csstree.generate(atrule.prelude)}`;
|
||||
mediaQuery += ` ${csstree.generate(atrule.prelude)}`;
|
||||
}
|
||||
}
|
||||
if (useMqs.includes(mq) === false) {
|
||||
if (!useMqs.includes(mediaQuery)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Array<{
|
||||
* item: csstree.ListItem<csstree.CssNode>,
|
||||
* list: csstree.List<csstree.CssNode>
|
||||
* }>}
|
||||
*/
|
||||
const pseudos = [];
|
||||
if (node.type === 'Selector') {
|
||||
node.children.each((childNode, childItem, childList) => {
|
||||
if (
|
||||
childNode.type === 'PseudoClassSelector' ||
|
||||
childNode.type === 'PseudoElementSelector'
|
||||
) {
|
||||
pseudos.push({ item: childItem, list: childList });
|
||||
if (node.prelude.type === 'SelectorList') {
|
||||
node.prelude.children.forEach((childNode, item) => {
|
||||
if (childNode.type === 'Selector') {
|
||||
/**
|
||||
* @type {{
|
||||
* item: csstree.ListItem<csstree.CssNode>,
|
||||
* list: csstree.List<csstree.CssNode>
|
||||
* }[]}
|
||||
*/
|
||||
const pseudos = [];
|
||||
|
||||
childNode.children.forEach(
|
||||
(grandchildNode, grandchildItem, grandchildList) => {
|
||||
const isPseudo =
|
||||
grandchildNode.type === 'PseudoClassSelector' ||
|
||||
grandchildNode.type === 'PseudoElementSelector';
|
||||
|
||||
if (
|
||||
isPseudo &&
|
||||
!preservedPseudos.includes(grandchildNode.name)
|
||||
) {
|
||||
pseudos.push({
|
||||
item: grandchildItem,
|
||||
list: grandchildList,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const pseudoSelectors = csstree.generate({
|
||||
type: 'Selector',
|
||||
children: new csstree.List().fromArray(
|
||||
pseudos.map((pseudo) => pseudo.item.data),
|
||||
),
|
||||
});
|
||||
|
||||
if (usePseudos.includes(pseudoSelectors)) {
|
||||
for (const pseudo of pseudos) {
|
||||
pseudo.list.remove(pseudo.item);
|
||||
}
|
||||
}
|
||||
|
||||
selectors.push({ node: childNode, rule: node, item: item });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// skip pseudo classes and pseudo elements not includes into usePseudos param
|
||||
const pseudoSelectors = csstree.generate({
|
||||
type: 'Selector',
|
||||
children: new csstree.List().fromArray(
|
||||
pseudos.map((pseudo) => pseudo.item.data)
|
||||
),
|
||||
});
|
||||
if (usePseudos.includes(pseudoSelectors) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove pseudo classes and elements to allow querySelector match elements
|
||||
// TODO this is not very accurate since some pseudo classes like first-child
|
||||
// are used for selection
|
||||
for (const pseudo of pseudos) {
|
||||
pseudo.list.remove(pseudo.item);
|
||||
}
|
||||
|
||||
selectors.push({ node, item, rule });
|
||||
},
|
||||
});
|
||||
},
|
||||
@@ -199,19 +178,19 @@ exports.fn = (root, params) => {
|
||||
if (styles.length === 0) {
|
||||
return;
|
||||
}
|
||||
// stable sort selectors
|
||||
const sortedSelectors = stable(selectors, (a, b) => {
|
||||
const aSpecificity = specificity(a.item.data);
|
||||
const bSpecificity = specificity(b.item.data);
|
||||
return compareSpecificity(aSpecificity, bSpecificity);
|
||||
}).reverse();
|
||||
const sortedSelectors = selectors
|
||||
.slice()
|
||||
.sort((a, b) => {
|
||||
const aSpecificity = syntax.specificity(a.item.data);
|
||||
const bSpecificity = syntax.specificity(b.item.data);
|
||||
return compareSpecificity(aSpecificity, bSpecificity);
|
||||
})
|
||||
.reverse();
|
||||
|
||||
for (const selector of sortedSelectors) {
|
||||
// match selectors
|
||||
const selectorText = csstree.generate(selector.item.data);
|
||||
/**
|
||||
* @type {Array<XastElement>}
|
||||
*/
|
||||
/** @type {import('../lib/types.js').XastElement[]} */
|
||||
const matchedElements = [];
|
||||
try {
|
||||
for (const node of querySelectorAll(root, selectorText)) {
|
||||
@@ -219,7 +198,7 @@ exports.fn = (root, params) => {
|
||||
matchedElements.push(node);
|
||||
}
|
||||
}
|
||||
} catch (selectError) {
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
// nothing selected
|
||||
@@ -236,22 +215,28 @@ exports.fn = (root, params) => {
|
||||
// apply <style/> to matched elements
|
||||
for (const selectedEl of matchedElements) {
|
||||
const styleDeclarationList = csstree.parse(
|
||||
selectedEl.attributes.style == null
|
||||
? ''
|
||||
: selectedEl.attributes.style,
|
||||
selectedEl.attributes.style ?? '',
|
||||
{
|
||||
context: 'declarationList',
|
||||
parseValue: false,
|
||||
}
|
||||
},
|
||||
);
|
||||
if (styleDeclarationList.type !== 'DeclarationList') {
|
||||
continue;
|
||||
}
|
||||
const styleDeclarationItems = new Map();
|
||||
|
||||
/** @type {csstree.ListItem<csstree.CssNode>} */
|
||||
let firstListItem;
|
||||
|
||||
csstree.walk(styleDeclarationList, {
|
||||
visit: 'Declaration',
|
||||
enter(node, item) {
|
||||
styleDeclarationItems.set(node.property, item);
|
||||
if (firstListItem == null) {
|
||||
firstListItem = item;
|
||||
}
|
||||
|
||||
styleDeclarationItems.set(node.property.toLowerCase(), item);
|
||||
},
|
||||
});
|
||||
// merge declarations
|
||||
@@ -262,30 +247,42 @@ exports.fn = (root, params) => {
|
||||
// no inline styles, external styles, external styles used
|
||||
// inline styles, external styles same priority as inline styles, inline styles used
|
||||
// inline styles, external styles higher priority than inline styles, external styles used
|
||||
const matchedItem = styleDeclarationItems.get(
|
||||
ruleDeclaration.property
|
||||
);
|
||||
const property = ruleDeclaration.property;
|
||||
|
||||
if (
|
||||
attrsGroups.presentation.has(property) &&
|
||||
!selectors.some((selector) =>
|
||||
includesAttrSelector(selector.item, property),
|
||||
)
|
||||
) {
|
||||
delete selectedEl.attributes[property];
|
||||
}
|
||||
|
||||
const matchedItem = styleDeclarationItems.get(property);
|
||||
const ruleDeclarationItem =
|
||||
styleDeclarationList.children.createItem(ruleDeclaration);
|
||||
if (matchedItem == null) {
|
||||
styleDeclarationList.children.append(ruleDeclarationItem);
|
||||
styleDeclarationList.children.insert(
|
||||
ruleDeclarationItem,
|
||||
firstListItem,
|
||||
);
|
||||
} else if (
|
||||
matchedItem.data.important !== true &&
|
||||
ruleDeclaration.important === true
|
||||
) {
|
||||
styleDeclarationList.children.replace(
|
||||
matchedItem,
|
||||
ruleDeclarationItem
|
||||
);
|
||||
styleDeclarationItems.set(
|
||||
ruleDeclaration.property,
|
||||
ruleDeclarationItem
|
||||
ruleDeclarationItem,
|
||||
);
|
||||
styleDeclarationItems.set(property, ruleDeclarationItem);
|
||||
}
|
||||
},
|
||||
});
|
||||
selectedEl.attributes.style =
|
||||
csstree.generate(styleDeclarationList);
|
||||
|
||||
const newStyles = csstree.generate(styleDeclarationList);
|
||||
if (newStyles.length !== 0) {
|
||||
selectedEl.attributes.style = newStyles;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -300,7 +297,7 @@ exports.fn = (root, params) => {
|
||||
}
|
||||
|
||||
// no further processing required
|
||||
if (removeMatchedSelectors === false) {
|
||||
if (!removeMatchedSelectors) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -320,15 +317,25 @@ exports.fn = (root, params) => {
|
||||
const classList = new Set(
|
||||
selectedEl.attributes.class == null
|
||||
? null
|
||||
: selectedEl.attributes.class.split(' ')
|
||||
: selectedEl.attributes.class.split(' '),
|
||||
);
|
||||
const firstSubSelector = selector.node.children.first();
|
||||
if (
|
||||
firstSubSelector != null &&
|
||||
firstSubSelector.type === 'ClassSelector'
|
||||
) {
|
||||
classList.delete(firstSubSelector.name);
|
||||
|
||||
for (const child of selector.node.children) {
|
||||
if (
|
||||
child.type === 'ClassSelector' &&
|
||||
!selectors.some((selector) =>
|
||||
includesAttrSelector(
|
||||
selector.item,
|
||||
'class',
|
||||
child.name,
|
||||
true,
|
||||
),
|
||||
)
|
||||
) {
|
||||
classList.delete(child.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (classList.size === 0) {
|
||||
delete selectedEl.attributes.class;
|
||||
} else {
|
||||
@@ -336,13 +343,20 @@ exports.fn = (root, params) => {
|
||||
}
|
||||
|
||||
// ID
|
||||
const firstSubSelector = selector.node.children.first;
|
||||
if (
|
||||
firstSubSelector != null &&
|
||||
firstSubSelector.type === 'IdSelector'
|
||||
firstSubSelector?.type === 'IdSelector' &&
|
||||
selectedEl.attributes.id === firstSubSelector.name &&
|
||||
!selectors.some((selector) =>
|
||||
includesAttrSelector(
|
||||
selector.item,
|
||||
'id',
|
||||
firstSubSelector.name,
|
||||
true,
|
||||
),
|
||||
)
|
||||
) {
|
||||
if (selectedEl.attributes.id === firstSubSelector.name) {
|
||||
delete selectedEl.attributes.id;
|
||||
}
|
||||
delete selectedEl.attributes.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,15 +369,15 @@ exports.fn = (root, params) => {
|
||||
if (
|
||||
node.type === 'Rule' &&
|
||||
node.prelude.type === 'SelectorList' &&
|
||||
node.prelude.children.isEmpty()
|
||||
node.prelude.children.isEmpty
|
||||
) {
|
||||
list.remove(item);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (style.cssAst.children.isEmpty()) {
|
||||
// remove emtpy style element
|
||||
if (style.cssAst.children.isEmpty) {
|
||||
// remove empty style element
|
||||
detachNodeFromParent(style.node, style.parentNode);
|
||||
} else {
|
||||
// update style element if any styles left
|
||||
|
||||
157
node_modules/svgo/plugins/mergePaths.js
generated
vendored
157
node_modules/svgo/plugins/mergePaths.js
generated
vendored
@@ -1,29 +1,43 @@
|
||||
'use strict';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { intersects, js2path, path2js } from './_path.js';
|
||||
import { includesUrlReference } from '../lib/svgo/tools.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
||||
const { path2js, js2path, intersects } = require('./_path.js');
|
||||
/**
|
||||
* @typedef MergePathsParams
|
||||
* @property {boolean=} force
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} noSpaceAfterFlags
|
||||
*/
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'mergePaths';
|
||||
exports.active = true;
|
||||
exports.description = 'merges multiple paths in one if possible';
|
||||
export const name = 'mergePaths';
|
||||
export const description = 'merges multiple paths in one if possible';
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').ComputedStyles} computedStyle
|
||||
* @param {string} attName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function elementHasUrl(computedStyle, attName) {
|
||||
const style = computedStyle[attName];
|
||||
|
||||
if (style?.type === 'static') {
|
||||
return includesUrlReference(style.value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge multiple Paths into one.
|
||||
*
|
||||
* @author Kir Belevich, Lev Solntsev
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* force?: boolean,
|
||||
* floatPrecision?: number,
|
||||
* noSpaceAfterFlags?: boolean
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<MergePathsParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
force = false,
|
||||
floatPrecision,
|
||||
floatPrecision = 3,
|
||||
noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||||
} = params;
|
||||
const stylesheet = collectStylesheet(root);
|
||||
@@ -31,73 +45,124 @@ exports.fn = (root, params) => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
let prevChild = null;
|
||||
if (node.children.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {import('../lib/types.js').XastChild[]} */
|
||||
const elementsToRemove = [];
|
||||
let prevChild = node.children[0];
|
||||
let prevPathData = null;
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastElement} child
|
||||
* @param {ReadonlyArray<import("../lib/types.js").PathDataItem>} pathData
|
||||
*/
|
||||
const updatePreviousPath = (child, pathData) => {
|
||||
js2path(child, pathData, {
|
||||
floatPrecision,
|
||||
noSpaceAfterFlags,
|
||||
});
|
||||
prevPathData = null;
|
||||
};
|
||||
|
||||
for (let i = 1; i < node.children.length; i++) {
|
||||
const child = node.children[i];
|
||||
|
||||
for (const child of node.children) {
|
||||
// skip if previous element is not path or contains animation elements
|
||||
if (
|
||||
prevChild == null ||
|
||||
prevChild.type !== 'element' ||
|
||||
prevChild.name !== 'path' ||
|
||||
prevChild.children.length !== 0 ||
|
||||
prevChild.attributes.d == null
|
||||
) {
|
||||
if (prevPathData && prevChild.type === 'element') {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
prevChild = child;
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip if element is not path or contains animation elements
|
||||
if (
|
||||
child.type !== 'element' ||
|
||||
child.name !== 'path' ||
|
||||
child.children.length !== 0 ||
|
||||
child.attributes.d == null
|
||||
) {
|
||||
if (prevPathData) {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
prevChild = child;
|
||||
continue;
|
||||
}
|
||||
|
||||
// preserve paths with markers
|
||||
const computedStyle = computeStyle(stylesheet, child);
|
||||
if (
|
||||
computedStyle['marker-start'] ||
|
||||
computedStyle['marker-mid'] ||
|
||||
computedStyle['marker-end']
|
||||
computedStyle['marker-end'] ||
|
||||
computedStyle['clip-path'] ||
|
||||
computedStyle['mask'] ||
|
||||
computedStyle['mask-image'] ||
|
||||
['fill', 'filter', 'stroke'].some((attName) =>
|
||||
elementHasUrl(computedStyle, attName),
|
||||
)
|
||||
) {
|
||||
if (prevPathData) {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
prevChild = child;
|
||||
continue;
|
||||
}
|
||||
const childAttrs = Object.keys(child.attributes);
|
||||
if (childAttrs.length !== Object.keys(prevChild.attributes).length) {
|
||||
if (prevPathData) {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
prevChild = child;
|
||||
continue;
|
||||
}
|
||||
|
||||
const prevChildAttrs = Object.keys(prevChild.attributes);
|
||||
const childAttrs = Object.keys(child.attributes);
|
||||
let attributesAreEqual = prevChildAttrs.length === childAttrs.length;
|
||||
for (const name of childAttrs) {
|
||||
if (name !== 'd') {
|
||||
if (
|
||||
prevChild.attributes[name] == null ||
|
||||
prevChild.attributes[name] !== child.attributes[name]
|
||||
) {
|
||||
attributesAreEqual = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const prevPathJS = path2js(prevChild);
|
||||
const curPathJS = path2js(child);
|
||||
const areAttrsEqual = childAttrs.some((attr) => {
|
||||
return (
|
||||
attr !== 'd' &&
|
||||
prevChild.type === 'element' &&
|
||||
prevChild.attributes[attr] !== child.attributes[attr]
|
||||
);
|
||||
});
|
||||
|
||||
if (
|
||||
attributesAreEqual &&
|
||||
(force || !intersects(prevPathJS, curPathJS))
|
||||
) {
|
||||
js2path(prevChild, prevPathJS.concat(curPathJS), {
|
||||
floatPrecision,
|
||||
noSpaceAfterFlags,
|
||||
});
|
||||
detachNodeFromParent(child, node);
|
||||
if (areAttrsEqual) {
|
||||
if (prevPathData) {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
prevChild = child;
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasPrevPath = prevPathData != null;
|
||||
const currentPathData = path2js(child);
|
||||
prevPathData = prevPathData ?? path2js(prevChild);
|
||||
|
||||
if (force || !intersects(prevPathData, currentPathData)) {
|
||||
prevPathData.push(...currentPathData);
|
||||
elementsToRemove.push(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasPrevPath) {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
|
||||
prevChild = child;
|
||||
prevPathData = null;
|
||||
}
|
||||
|
||||
if (prevPathData && prevChild.type === 'element') {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
|
||||
node.children = node.children.filter(
|
||||
(child) => !elementsToRemove.includes(child),
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
34
node_modules/svgo/plugins/mergeStyles.js
generated
vendored
34
node_modules/svgo/plugins/mergeStyles.js
generated
vendored
@@ -1,30 +1,21 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
*/
|
||||
|
||||
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
||||
const JSAPI = require('../lib/svgo/jsAPI.js');
|
||||
|
||||
exports.name = 'mergeStyles';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'merge multiple style elements into one';
|
||||
export const name = 'mergeStyles';
|
||||
export const description = 'merge multiple style elements into one';
|
||||
|
||||
/**
|
||||
* Merge multiple style elements into one.
|
||||
*
|
||||
* @author strarsis <strarsis@gmail.com>
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
/**
|
||||
* @type {null | XastElement}
|
||||
*/
|
||||
export const fn = () => {
|
||||
/** @type {?import('../lib/types.js').XastElement} */
|
||||
let firstStyleElement = null;
|
||||
let collectedStyles = '';
|
||||
/** @type {'text' | 'cdata'} */
|
||||
let styleContentType = 'text';
|
||||
|
||||
return {
|
||||
@@ -80,12 +71,9 @@ exports.fn = () => {
|
||||
firstStyleElement = node;
|
||||
} else {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
firstStyleElement.children = [
|
||||
new JSAPI(
|
||||
{ type: styleContentType, value: collectedStyles },
|
||||
firstStyleElement
|
||||
),
|
||||
];
|
||||
/** @type {import('../lib/types.js').XastChild} */
|
||||
const child = { type: styleContentType, value: collectedStyles };
|
||||
firstStyleElement.children = [child];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
139
node_modules/svgo/plugins/minifyStyles.js
generated
vendored
139
node_modules/svgo/plugins/minifyStyles.js
generated
vendored
@@ -1,37 +1,63 @@
|
||||
'use strict';
|
||||
import * as csso from 'csso';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { hasScripts } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
* @typedef Usage
|
||||
* @property {boolean=} force
|
||||
* @property {boolean=} ids
|
||||
* @property {boolean=} classes
|
||||
* @property {boolean=} tags
|
||||
*
|
||||
* @typedef MinifyStylesParams
|
||||
* @property {boolean=} restructure Disable or enable a structure optimizations.
|
||||
* @property {boolean=} forceMediaMerge
|
||||
* Enables merging of `@media` rules with the same media query split by other
|
||||
* rules. Unsafe in general, but should work fine in most cases. Use it on
|
||||
* your own risk.
|
||||
* @property {'exclamation' | 'first-exclamation' | boolean=} comments
|
||||
* Specify what comments to leave:
|
||||
* - `'exclamation'` or `true` — leave all exclamation comments
|
||||
* - `'first-exclamation'` — remove every comment except first one
|
||||
* - `false` — remove all comments
|
||||
* @property {boolean | Usage=} usage Advanced optimizations.
|
||||
*/
|
||||
|
||||
const csso = require('csso');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'minifyStyles';
|
||||
exports.active = true;
|
||||
exports.description =
|
||||
'minifies styles and removes unused styles based on usage data';
|
||||
export const name = 'minifyStyles';
|
||||
export const description = 'minifies styles and removes unused styles';
|
||||
|
||||
/**
|
||||
* Minifies styles (<style> element + style attribute) using CSSO
|
||||
* Minifies styles (<style> element + style attribute) using CSSO.
|
||||
*
|
||||
* @author strarsis <strarsis@gmail.com>
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<csso.MinifyOptions & Omit<csso.CompressOptions, 'usage'> & {
|
||||
* usage?: boolean | {
|
||||
* force?: boolean,
|
||||
* ids?: boolean,
|
||||
* classes?: boolean,
|
||||
* tags?: boolean
|
||||
* }
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<MinifyStylesParams>}
|
||||
*/
|
||||
exports.fn = (_root, { usage, ...params }) => {
|
||||
export const fn = (_root, { usage, ...params }) => {
|
||||
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>} */
|
||||
const styleElements = new Map();
|
||||
|
||||
/** @type {import('../lib/types.js').XastElement[]} */
|
||||
const elementsWithStyleAttributes = [];
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const tagsUsage = new Set();
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const idsUsage = new Set();
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const classesUsage = new Set();
|
||||
|
||||
let enableTagsUsage = true;
|
||||
let enableIdsUsage = true;
|
||||
let enableClassesUsage = true;
|
||||
// force to use usage data even if it unsafe (document contains <script> or on* attributes)
|
||||
|
||||
/**
|
||||
* Force to use usage data even if it unsafe. For example, the document
|
||||
* contains scripts or in attributes.
|
||||
*/
|
||||
let forceUsageDeoptimized = false;
|
||||
|
||||
if (typeof usage === 'boolean') {
|
||||
enableTagsUsage = usage;
|
||||
enableIdsUsage = usage;
|
||||
@@ -42,40 +68,17 @@ exports.fn = (_root, { usage, ...params }) => {
|
||||
enableClassesUsage = usage.classes == null ? true : usage.classes;
|
||||
forceUsageDeoptimized = usage.force == null ? false : usage.force;
|
||||
}
|
||||
/**
|
||||
* @type {Array<XastElement>}
|
||||
*/
|
||||
const styleElements = [];
|
||||
/**
|
||||
* @type {Array<XastElement>}
|
||||
*/
|
||||
const elementsWithStyleAttributes = [];
|
||||
|
||||
let deoptimized = false;
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const tagsUsage = new Set();
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const idsUsage = new Set();
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const classesUsage = new Set();
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
// detect deoptimisations
|
||||
if (node.name === 'script') {
|
||||
enter: (node, parentNode) => {
|
||||
// detect deoptimizations
|
||||
if (hasScripts(node)) {
|
||||
deoptimized = true;
|
||||
}
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (name.startsWith('on')) {
|
||||
deoptimized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// collect tags, ids and classes usage
|
||||
tagsUsage.add(node.name);
|
||||
if (node.attributes.id != null) {
|
||||
@@ -88,7 +91,7 @@ exports.fn = (_root, { usage, ...params }) => {
|
||||
}
|
||||
// collect style elements or elements with style attribute
|
||||
if (node.name === 'style' && node.children.length !== 0) {
|
||||
styleElements.push(node);
|
||||
styleElements.set(node, parentNode);
|
||||
} else if (node.attributes.style != null) {
|
||||
elementsWithStyleAttributes.push(node);
|
||||
}
|
||||
@@ -97,40 +100,44 @@ exports.fn = (_root, { usage, ...params }) => {
|
||||
|
||||
root: {
|
||||
exit: () => {
|
||||
/**
|
||||
* @type {csso.Usage}
|
||||
*/
|
||||
/** @type {csso.Usage} */
|
||||
const cssoUsage = {};
|
||||
if (deoptimized === false || forceUsageDeoptimized === true) {
|
||||
if (enableTagsUsage && tagsUsage.size !== 0) {
|
||||
if (!deoptimized || forceUsageDeoptimized) {
|
||||
if (enableTagsUsage) {
|
||||
cssoUsage.tags = Array.from(tagsUsage);
|
||||
}
|
||||
if (enableIdsUsage && idsUsage.size !== 0) {
|
||||
if (enableIdsUsage) {
|
||||
cssoUsage.ids = Array.from(idsUsage);
|
||||
}
|
||||
if (enableClassesUsage && classesUsage.size !== 0) {
|
||||
if (enableClassesUsage) {
|
||||
cssoUsage.classes = Array.from(classesUsage);
|
||||
}
|
||||
}
|
||||
// minify style elements
|
||||
for (const node of styleElements) {
|
||||
for (const [styleNode, styleNodeParent] of styleElements.entries()) {
|
||||
if (
|
||||
node.children[0].type === 'text' ||
|
||||
node.children[0].type === 'cdata'
|
||||
styleNode.children[0].type === 'text' ||
|
||||
styleNode.children[0].type === 'cdata'
|
||||
) {
|
||||
const cssText = node.children[0].value;
|
||||
const cssText = styleNode.children[0].value;
|
||||
const minified = csso.minify(cssText, {
|
||||
...params,
|
||||
usage: cssoUsage,
|
||||
}).css;
|
||||
|
||||
if (minified.length === 0) {
|
||||
detachNodeFromParent(styleNode, styleNodeParent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// preserve cdata if necessary
|
||||
// TODO split cdata -> text optimisation into separate plugin
|
||||
// TODO split cdata -> text optimization into separate plugin
|
||||
if (cssText.indexOf('>') >= 0 || cssText.indexOf('<') >= 0) {
|
||||
node.children[0].type = 'cdata';
|
||||
node.children[0].value = minified;
|
||||
styleNode.children[0].type = 'cdata';
|
||||
styleNode.children[0].value = minified;
|
||||
} else {
|
||||
node.children[0].type = 'text';
|
||||
node.children[0].value = minified;
|
||||
styleNode.children[0].type = 'text';
|
||||
styleNode.children[0].value = minified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
node_modules/svgo/plugins/moveElemsAttrsToGroup.js
generated
vendored
31
node_modules/svgo/plugins/moveElemsAttrsToGroup.js
generated
vendored
@@ -1,12 +1,9 @@
|
||||
'use strict';
|
||||
import { visit } from '../lib/util/visit.js';
|
||||
import { inheritableAttrs, pathElems } from './_collections.js';
|
||||
|
||||
const { visit } = require('../lib/xast.js');
|
||||
const { inheritableAttrs, pathElems } = require('./_collections.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'moveElemsAttrsToGroup';
|
||||
exports.active = true;
|
||||
exports.description = 'Move common attributes of group children to the group';
|
||||
export const name = 'moveElemsAttrsToGroup';
|
||||
export const description =
|
||||
'Move common attributes of group children to the group';
|
||||
|
||||
/**
|
||||
* Move common attributes of group children to the group
|
||||
@@ -18,7 +15,7 @@ exports.description = 'Move common attributes of group children to the group';
|
||||
* </g>
|
||||
* <circle attr2="val2" attr3="val3"/>
|
||||
* </g>
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <g attr1="val1" attr2="val2">
|
||||
* <g>
|
||||
* text
|
||||
@@ -28,9 +25,9 @@ exports.description = 'Move common attributes of group children to the group';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = (root) => {
|
||||
export const fn = (root) => {
|
||||
// find if any style element is present
|
||||
let deoptimizedWithStyles = false;
|
||||
visit(root, {
|
||||
@@ -46,7 +43,7 @@ exports.fn = (root) => {
|
||||
return {
|
||||
element: {
|
||||
exit: (node) => {
|
||||
// process only groups with more than 1 children
|
||||
// process only groups with more than 1 child
|
||||
if (node.name !== 'g' || node.children.length <= 1) {
|
||||
return;
|
||||
}
|
||||
@@ -58,7 +55,8 @@ exports.fn = (root) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* find common attributes in group children
|
||||
* Find common attributes in group children.
|
||||
*
|
||||
* @type {Map<string, string>}
|
||||
*/
|
||||
const commonAttributes = new Map();
|
||||
@@ -66,7 +64,7 @@ exports.fn = (root) => {
|
||||
let everyChildIsPath = true;
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element') {
|
||||
if (pathElems.includes(child.name) === false) {
|
||||
if (!pathElems.has(child.name)) {
|
||||
everyChildIsPath = false;
|
||||
}
|
||||
if (initial) {
|
||||
@@ -74,7 +72,7 @@ exports.fn = (root) => {
|
||||
// collect all inheritable attributes from first child element
|
||||
for (const [name, value] of Object.entries(child.attributes)) {
|
||||
// consider only inheritable attributes
|
||||
if (inheritableAttrs.includes(name)) {
|
||||
if (inheritableAttrs.has(name)) {
|
||||
commonAttributes.set(name, value);
|
||||
}
|
||||
}
|
||||
@@ -89,8 +87,9 @@ exports.fn = (root) => {
|
||||
}
|
||||
}
|
||||
|
||||
// preserve transform on children when group has clip-path or mask
|
||||
// preserve transform on children when group has filter or clip-path or mask
|
||||
if (
|
||||
node.attributes['filter'] != null ||
|
||||
node.attributes['clip-path'] != null ||
|
||||
node.attributes.mask != null
|
||||
) {
|
||||
|
||||
84
node_modules/svgo/plugins/moveGroupAttrsToElems.js
generated
vendored
84
node_modules/svgo/plugins/moveGroupAttrsToElems.js
generated
vendored
@@ -1,14 +1,9 @@
|
||||
'use strict';
|
||||
import { pathElems, referencesProps } from './_collections.js';
|
||||
import { includesUrlReference } from '../lib/svgo/tools.js';
|
||||
|
||||
const { pathElems, referencesProps } = require('./_collections.js');
|
||||
|
||||
exports.name = 'moveGroupAttrsToElems';
|
||||
|
||||
exports.type = 'perItem';
|
||||
|
||||
exports.active = true;
|
||||
|
||||
exports.description = 'moves some group attributes to the content elements';
|
||||
export const name = 'moveGroupAttrsToElems';
|
||||
export const description =
|
||||
'moves some group attributes to the content elements';
|
||||
|
||||
const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text'];
|
||||
|
||||
@@ -20,43 +15,50 @@ const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text'];
|
||||
* <path transform="rotate(45)" d="M0,0 L10,20"/>
|
||||
* <path transform="translate(10, 20)" d="M0,10 L20,30"/>
|
||||
* </g>
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <g>
|
||||
* <path transform="scale(2) rotate(45)" d="M0,0 L10,20"/>
|
||||
* <path transform="scale(2) translate(10, 20)" d="M0,10 L20,30"/>
|
||||
* </g>
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = function (item) {
|
||||
// move group transform attr to content's pathElems
|
||||
if (
|
||||
item.type === 'element' &&
|
||||
item.name === 'g' &&
|
||||
item.children.length !== 0 &&
|
||||
item.attributes.transform != null &&
|
||||
Object.entries(item.attributes).some(
|
||||
([name, value]) =>
|
||||
referencesProps.includes(name) && value.includes('url(')
|
||||
) === false &&
|
||||
item.children.every(
|
||||
(inner) =>
|
||||
pathElemsWithGroupsAndText.includes(inner.name) &&
|
||||
inner.attributes.id == null
|
||||
)
|
||||
) {
|
||||
for (const inner of item.children) {
|
||||
const value = item.attributes.transform;
|
||||
if (inner.attributes.transform != null) {
|
||||
inner.attributes.transform = value + ' ' + inner.attributes.transform;
|
||||
} else {
|
||||
inner.attributes.transform = value;
|
||||
}
|
||||
}
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
// move group transform attr to content's pathElems
|
||||
if (
|
||||
node.name === 'g' &&
|
||||
node.children.length !== 0 &&
|
||||
node.attributes.transform != null &&
|
||||
Object.entries(node.attributes).some(
|
||||
([name, value]) =>
|
||||
referencesProps.has(name) && includesUrlReference(value),
|
||||
) === false &&
|
||||
node.children.every(
|
||||
(child) =>
|
||||
child.type === 'element' &&
|
||||
pathElemsWithGroupsAndText.includes(child.name) &&
|
||||
child.attributes.id == null,
|
||||
)
|
||||
) {
|
||||
for (const child of node.children) {
|
||||
const value = node.attributes.transform;
|
||||
if (child.type === 'element') {
|
||||
if (child.attributes.transform != null) {
|
||||
child.attributes.transform = `${value} ${child.attributes.transform}`;
|
||||
} else {
|
||||
child.attributes.transform = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete item.attributes.transform;
|
||||
}
|
||||
delete node.attributes.transform;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
56
node_modules/svgo/plugins/plugins.js
generated
vendored
56
node_modules/svgo/plugins/plugins.js
generated
vendored
@@ -1,56 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// builtin presets
|
||||
exports['preset-default'] = require('./preset-default.js');
|
||||
|
||||
// builtin plugins
|
||||
exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js');
|
||||
exports.addClassesToSVGElement = require('./addClassesToSVGElement.js');
|
||||
exports.cleanupAttrs = require('./cleanupAttrs.js');
|
||||
exports.cleanupEnableBackground = require('./cleanupEnableBackground.js');
|
||||
exports.cleanupIDs = require('./cleanupIDs.js');
|
||||
exports.cleanupListOfValues = require('./cleanupListOfValues.js');
|
||||
exports.cleanupNumericValues = require('./cleanupNumericValues.js');
|
||||
exports.collapseGroups = require('./collapseGroups.js');
|
||||
exports.convertColors = require('./convertColors.js');
|
||||
exports.convertEllipseToCircle = require('./convertEllipseToCircle.js');
|
||||
exports.convertPathData = require('./convertPathData.js');
|
||||
exports.convertShapeToPath = require('./convertShapeToPath.js');
|
||||
exports.convertStyleToAttrs = require('./convertStyleToAttrs.js');
|
||||
exports.convertTransform = require('./convertTransform.js');
|
||||
exports.mergeStyles = require('./mergeStyles.js');
|
||||
exports.inlineStyles = require('./inlineStyles.js');
|
||||
exports.mergePaths = require('./mergePaths.js');
|
||||
exports.minifyStyles = require('./minifyStyles.js');
|
||||
exports.moveElemsAttrsToGroup = require('./moveElemsAttrsToGroup.js');
|
||||
exports.moveGroupAttrsToElems = require('./moveGroupAttrsToElems.js');
|
||||
exports.prefixIds = require('./prefixIds.js');
|
||||
exports.removeAttributesBySelector = require('./removeAttributesBySelector.js');
|
||||
exports.removeAttrs = require('./removeAttrs.js');
|
||||
exports.removeComments = require('./removeComments.js');
|
||||
exports.removeDesc = require('./removeDesc.js');
|
||||
exports.removeDimensions = require('./removeDimensions.js');
|
||||
exports.removeDoctype = require('./removeDoctype.js');
|
||||
exports.removeEditorsNSData = require('./removeEditorsNSData.js');
|
||||
exports.removeElementsByAttr = require('./removeElementsByAttr.js');
|
||||
exports.removeEmptyAttrs = require('./removeEmptyAttrs.js');
|
||||
exports.removeEmptyContainers = require('./removeEmptyContainers.js');
|
||||
exports.removeEmptyText = require('./removeEmptyText.js');
|
||||
exports.removeHiddenElems = require('./removeHiddenElems.js');
|
||||
exports.removeMetadata = require('./removeMetadata.js');
|
||||
exports.removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js');
|
||||
exports.removeOffCanvasPaths = require('./removeOffCanvasPaths.js');
|
||||
exports.removeRasterImages = require('./removeRasterImages.js');
|
||||
exports.removeScriptElement = require('./removeScriptElement.js');
|
||||
exports.removeStyleElement = require('./removeStyleElement.js');
|
||||
exports.removeTitle = require('./removeTitle.js');
|
||||
exports.removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js');
|
||||
exports.removeUnusedNS = require('./removeUnusedNS.js');
|
||||
exports.removeUselessDefs = require('./removeUselessDefs.js');
|
||||
exports.removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js');
|
||||
exports.removeViewBox = require('./removeViewBox.js');
|
||||
exports.removeXMLNS = require('./removeXMLNS.js');
|
||||
exports.removeXMLProcInst = require('./removeXMLProcInst.js');
|
||||
exports.reusePaths = require('./reusePaths.js');
|
||||
exports.sortAttrs = require('./sortAttrs.js');
|
||||
exports.sortDefsChildren = require('./sortDefsChildren.js');
|
||||
246
node_modules/svgo/plugins/prefixIds.js
generated
vendored
246
node_modules/svgo/plugins/prefixIds.js
generated
vendored
@@ -1,25 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const csstree = require('css-tree');
|
||||
const { referencesProps } = require('./_collections.js');
|
||||
import * as csstree from 'css-tree';
|
||||
import { referencesProps } from './_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
* @typedef {import('../lib/types').PluginInfo} PluginInfo
|
||||
* @typedef PrefixIdsParams
|
||||
* @property {boolean | string | ((node: import('../lib/types.js').XastElement, info: import('../lib/types.js').PluginInfo) => string)=} prefix
|
||||
* @property {string=} delim
|
||||
* @property {boolean=} prefixIds
|
||||
* @property {boolean=} prefixClassNames
|
||||
*/
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'prefixIds';
|
||||
exports.active = false;
|
||||
exports.description = 'prefix IDs';
|
||||
export const name = 'prefixIds';
|
||||
export const description = 'prefix IDs';
|
||||
|
||||
/**
|
||||
* extract basename from path
|
||||
* @type {(path: string) => string}
|
||||
* Extract basename from path.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
const getBasename = (path) => {
|
||||
// extract everything after latest slash or backslash
|
||||
const matched = path.match(/[/\\]?([^/\\]+)$/);
|
||||
const matched = /[/\\]?([^/\\]+)$/.exec(path);
|
||||
if (matched) {
|
||||
return matched[1];
|
||||
}
|
||||
@@ -27,15 +28,18 @@ const getBasename = (path) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* escapes a string for being used as ID
|
||||
* @type {(string: string) => string}
|
||||
* Escapes a string for being used as ID.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
const escapeIdentifierName = (str) => {
|
||||
return str.replace(/[. ]/g, '_');
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(string: string) => string}
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
*/
|
||||
const unquote = (string) => {
|
||||
if (
|
||||
@@ -48,59 +52,100 @@ const unquote = (string) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* prefix an ID
|
||||
* @type {(prefix: string, name: string) => string}
|
||||
* Prefix the given string, unless it already starts with the generated prefix.
|
||||
*
|
||||
* @param {(id: string) => string} prefixGenerator Function to generate a prefix.
|
||||
* @param {string} body An arbitrary string.
|
||||
* @returns {string} The given string with a prefix prepended to it.
|
||||
*/
|
||||
const prefixId = (prefix, value) => {
|
||||
if (value.startsWith(prefix)) {
|
||||
return value;
|
||||
const prefixId = (prefixGenerator, body) => {
|
||||
const prefix = prefixGenerator(body);
|
||||
if (body.startsWith(prefix)) {
|
||||
return body;
|
||||
}
|
||||
return prefix + value;
|
||||
return prefix + body;
|
||||
};
|
||||
|
||||
/**
|
||||
* prefix an #ID
|
||||
* @type {(prefix: string, name: string) => string | null}
|
||||
* Insert the prefix in a reference string. A reference string is already
|
||||
* prefixed with #, so the prefix is inserted after the first character.
|
||||
*
|
||||
* @param {(id: string) => string} prefixGenerator Function to generate a prefix.
|
||||
* @param {string} reference An arbitrary string, should start with "#".
|
||||
* @returns {?string} The given string with a prefix inserted, or null if the string did not start with "#".
|
||||
*/
|
||||
const prefixReference = (prefix, value) => {
|
||||
if (value.startsWith('#')) {
|
||||
return '#' + prefixId(prefix, value.slice(1));
|
||||
const prefixReference = (prefixGenerator, reference) => {
|
||||
if (reference.startsWith('#')) {
|
||||
return '#' + prefixId(prefixGenerator, reference.slice(1));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a prefix for the given string.
|
||||
*
|
||||
* @param {string} body An arbitrary string.
|
||||
* @param {import('../lib/types.js').XastElement} node XML node that the identifier belongs to.
|
||||
* @param {import('../lib/types.js').PluginInfo} info
|
||||
* @param {((node: import('../lib/types.js').XastElement, info: import('../lib/types.js').PluginInfo) => string) | string | boolean | undefined} prefixGenerator Some way of obtaining a prefix.
|
||||
* @param {string} delim Content to insert between the prefix and original value.
|
||||
* @param {Map<string, string>} history Map of previously generated prefixes to IDs.
|
||||
* @returns {string} A generated prefix.
|
||||
*/
|
||||
const generatePrefix = (body, node, info, prefixGenerator, delim, history) => {
|
||||
if (typeof prefixGenerator === 'function') {
|
||||
let prefix = history.get(body);
|
||||
|
||||
if (prefix != null) {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
prefix = prefixGenerator(node, info) + delim;
|
||||
history.set(body, prefix);
|
||||
return prefix;
|
||||
}
|
||||
|
||||
if (typeof prefixGenerator === 'string') {
|
||||
return prefixGenerator + delim;
|
||||
}
|
||||
|
||||
if (prefixGenerator === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (info.path != null && info.path.length > 0) {
|
||||
return escapeIdentifierName(getBasename(info.path)) + delim;
|
||||
}
|
||||
|
||||
return 'prefix' + delim;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prefixes identifiers
|
||||
*
|
||||
* @author strarsis <strarsis@gmail.com>
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* prefix?: boolean | string | ((node: XastElement, info: PluginInfo) => string),
|
||||
* delim?: string,
|
||||
* prefixIds?: boolean,
|
||||
* prefixClassNames?: boolean,
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<PrefixIdsParams>}
|
||||
*/
|
||||
exports.fn = (_root, params, info) => {
|
||||
const { delim = '__', prefixIds = true, prefixClassNames = true } = params;
|
||||
export const fn = (_root, params, info) => {
|
||||
const {
|
||||
delim = '__',
|
||||
prefix,
|
||||
prefixIds = true,
|
||||
prefixClassNames = true,
|
||||
} = params;
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
const prefixMap = new Map();
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
/**
|
||||
* prefix, from file name or option
|
||||
* @type {string}
|
||||
* @param {string} id A node identifier or class.
|
||||
* @returns {string} Given string with a prefix inserted, or null if the string did not start with "#".
|
||||
*/
|
||||
let prefix = 'prefix' + delim;
|
||||
if (typeof params.prefix === 'function') {
|
||||
prefix = params.prefix(node, info) + delim;
|
||||
} else if (typeof params.prefix === 'string') {
|
||||
prefix = params.prefix + delim;
|
||||
} else if (params.prefix === false) {
|
||||
prefix = '';
|
||||
} else if (info.path != null && info.path.length > 0) {
|
||||
prefix = escapeIdentifierName(getBasename(info.path)) + delim;
|
||||
}
|
||||
const prefixGenerator = (id) =>
|
||||
generatePrefix(id, node, info, prefix, delim, prefixMap);
|
||||
|
||||
// prefix id/class selectors and url() references in styles
|
||||
if (node.name === 'style') {
|
||||
@@ -109,60 +154,44 @@ exports.fn = (_root, params, info) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// parse styles
|
||||
let cssText = '';
|
||||
if (
|
||||
node.children[0].type === 'text' ||
|
||||
node.children[0].type === 'cdata'
|
||||
) {
|
||||
cssText = node.children[0].value;
|
||||
}
|
||||
/**
|
||||
* @type {null | csstree.CssNode}
|
||||
*/
|
||||
let cssAst = null;
|
||||
try {
|
||||
cssAst = csstree.parse(cssText, {
|
||||
parseValue: true,
|
||||
parseCustomProperty: false,
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
for (const child of node.children) {
|
||||
if (child.type !== 'text' && child.type !== 'cdata') {
|
||||
continue;
|
||||
}
|
||||
|
||||
csstree.walk(cssAst, (node) => {
|
||||
// #ID, .class selectors
|
||||
if (
|
||||
(prefixIds && node.type === 'IdSelector') ||
|
||||
(prefixClassNames && node.type === 'ClassSelector')
|
||||
) {
|
||||
node.name = prefixId(prefix, node.name);
|
||||
const cssText = child.value;
|
||||
/** @type {?csstree.CssNode} */
|
||||
let cssAst;
|
||||
try {
|
||||
cssAst = csstree.parse(cssText, {
|
||||
parseValue: true,
|
||||
parseCustomProperty: false,
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
// url(...) references
|
||||
if (
|
||||
node.type === 'Url' &&
|
||||
node.value.value &&
|
||||
node.value.value.length > 0
|
||||
) {
|
||||
const prefixed = prefixReference(
|
||||
prefix,
|
||||
unquote(node.value.value)
|
||||
);
|
||||
if (prefixed != null) {
|
||||
node.value.value = prefixed;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// update styles
|
||||
if (
|
||||
node.children[0].type === 'text' ||
|
||||
node.children[0].type === 'cdata'
|
||||
) {
|
||||
node.children[0].value = csstree.generate(cssAst);
|
||||
csstree.walk(cssAst, (node) => {
|
||||
if (
|
||||
(prefixIds && node.type === 'IdSelector') ||
|
||||
(prefixClassNames && node.type === 'ClassSelector')
|
||||
) {
|
||||
node.name = prefixId(prefixGenerator, node.name);
|
||||
return;
|
||||
}
|
||||
if (node.type === 'Url' && node.value.length > 0) {
|
||||
const prefixed = prefixReference(
|
||||
prefixGenerator,
|
||||
unquote(node.value),
|
||||
);
|
||||
if (prefixed != null) {
|
||||
node.value = prefixed;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
child.value = csstree.generate(cssAst);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// prefix an ID attribute value
|
||||
@@ -171,7 +200,7 @@ exports.fn = (_root, params, info) => {
|
||||
node.attributes.id != null &&
|
||||
node.attributes.id.length !== 0
|
||||
) {
|
||||
node.attributes.id = prefixId(prefix, node.attributes.id);
|
||||
node.attributes.id = prefixId(prefixGenerator, node.attributes.id);
|
||||
}
|
||||
|
||||
// prefix a class attribute value
|
||||
@@ -182,39 +211,42 @@ exports.fn = (_root, params, info) => {
|
||||
) {
|
||||
node.attributes.class = node.attributes.class
|
||||
.split(/\s+/)
|
||||
.map((name) => prefixId(prefix, name))
|
||||
.map((name) => prefixId(prefixGenerator, name))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
// prefix a href attribute value
|
||||
// prefix an href attribute value
|
||||
// xlink:href is deprecated, must be still supported
|
||||
for (const name of ['href', 'xlink:href']) {
|
||||
if (
|
||||
node.attributes[name] != null &&
|
||||
node.attributes[name].length !== 0
|
||||
) {
|
||||
const prefixed = prefixReference(prefix, node.attributes[name]);
|
||||
const prefixed = prefixReference(
|
||||
prefixGenerator,
|
||||
node.attributes[name],
|
||||
);
|
||||
if (prefixed != null) {
|
||||
node.attributes[name] = prefixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prefix an URL attribute value
|
||||
// prefix a URL attribute value
|
||||
for (const name of referencesProps) {
|
||||
if (
|
||||
node.attributes[name] != null &&
|
||||
node.attributes[name].length !== 0
|
||||
) {
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
/url\((.*?)\)/gi,
|
||||
(match, url) => {
|
||||
const prefixed = prefixReference(prefix, url);
|
||||
/\burl\((["'])?(#.+?)\1\)/gi,
|
||||
(match, _, url) => {
|
||||
const prefixed = prefixReference(prefixGenerator, url);
|
||||
if (prefixed == null) {
|
||||
return match;
|
||||
}
|
||||
return `url(${prefixed})`;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -228,7 +260,7 @@ exports.fn = (_root, params, info) => {
|
||||
const parts = node.attributes[name].split(/\s*;\s+/).map((val) => {
|
||||
if (val.endsWith('.end') || val.endsWith('.start')) {
|
||||
const [id, postfix] = val.split('.');
|
||||
return `${prefixId(prefix, id)}.${postfix}`;
|
||||
return `${prefixId(prefixGenerator, id)}.${postfix}`;
|
||||
}
|
||||
return val;
|
||||
});
|
||||
|
||||
83
node_modules/svgo/plugins/preset-default.js
generated
vendored
83
node_modules/svgo/plugins/preset-default.js
generated
vendored
@@ -1,62 +1,59 @@
|
||||
'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 sortDefsChildren = require('./sortDefsChildren.js');
|
||||
const removeTitle = require('./removeTitle.js');
|
||||
const removeDesc = require('./removeDesc.js');
|
||||
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 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 removeDesc from './removeDesc.js';
|
||||
|
||||
const presetDefault = createPreset({
|
||||
name: 'presetDefault',
|
||||
name: 'preset-default',
|
||||
plugins: [
|
||||
removeDoctype,
|
||||
removeXMLProcInst,
|
||||
removeComments,
|
||||
removeDeprecatedAttrs,
|
||||
removeMetadata,
|
||||
removeEditorsNSData,
|
||||
cleanupAttrs,
|
||||
mergeStyles,
|
||||
inlineStyles,
|
||||
minifyStyles,
|
||||
cleanupIDs,
|
||||
cleanupIds,
|
||||
removeUselessDefs,
|
||||
cleanupNumericValues,
|
||||
convertColors,
|
||||
removeUnknownsAndDefaults,
|
||||
removeNonInheritableGroupAttrs,
|
||||
removeUselessStrokeAndFill,
|
||||
removeViewBox,
|
||||
cleanupEnableBackground,
|
||||
removeHiddenElems,
|
||||
removeEmptyText,
|
||||
@@ -71,10 +68,10 @@ const presetDefault = createPreset({
|
||||
removeEmptyContainers,
|
||||
mergePaths,
|
||||
removeUnusedNS,
|
||||
sortAttrs,
|
||||
sortDefsChildren,
|
||||
removeTitle,
|
||||
removeDesc,
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = presetDefault;
|
||||
export default presetDefault;
|
||||
|
||||
16
node_modules/svgo/plugins/removeAttributesBySelector.js
generated
vendored
16
node_modules/svgo/plugins/removeAttributesBySelector.js
generated
vendored
@@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
import { querySelectorAll } from '../lib/xast.js';
|
||||
|
||||
const { querySelectorAll } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeAttributesBySelector';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description =
|
||||
export const name = 'removeAttributesBySelector';
|
||||
export const description =
|
||||
'removes attributes of elements that match a css selector';
|
||||
|
||||
/**
|
||||
@@ -71,13 +67,13 @@ exports.description =
|
||||
* ↓
|
||||
* <rect x="0" y="0" width="100" height="100"/>
|
||||
*
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors
|
||||
* @link https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors
|
||||
*
|
||||
* @author Bradley Mease
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<any>}
|
||||
* @type {import('../lib/types.js').Plugin<any>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const selectors = Array.isArray(params.selectors)
|
||||
? params.selectors
|
||||
: [params];
|
||||
|
||||
38
node_modules/svgo/plugins/removeAttrs.js
generated
vendored
38
node_modules/svgo/plugins/removeAttrs.js
generated
vendored
@@ -1,9 +1,12 @@
|
||||
'use strict';
|
||||
/**
|
||||
* @typedef RemoveAttrsParams
|
||||
* @property {string=} elemSeparator
|
||||
* @property {boolean=} preserveCurrentColor
|
||||
* @property {string | string[]} attrs
|
||||
*/
|
||||
|
||||
exports.name = 'removeAttrs';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'removes specified attributes';
|
||||
export const name = 'removeAttrs';
|
||||
export const description = 'removes specified attributes';
|
||||
|
||||
const DEFAULT_SEPARATOR = ':';
|
||||
const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter.
|
||||
@@ -83,13 +86,9 @@ plugins: [
|
||||
*
|
||||
* @author Benny Schudel
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* elemSeparator?: string,
|
||||
* preserveCurrentColor?: boolean,
|
||||
* attrs: string | Array<string>
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveAttrsParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
if (typeof params.attrs == 'undefined') {
|
||||
console.warn(ENOATTRS);
|
||||
return null;
|
||||
@@ -110,13 +109,11 @@ exports.fn = (root, params) => {
|
||||
enter: (node) => {
|
||||
for (let pattern of attrs) {
|
||||
// if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value*
|
||||
if (pattern.includes(elemSeparator) === false) {
|
||||
pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join(
|
||||
''
|
||||
);
|
||||
if (!pattern.includes(elemSeparator)) {
|
||||
pattern = ['.*', pattern, '.*'].join(elemSeparator);
|
||||
// if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value
|
||||
} else if (pattern.split(elemSeparator).length < 3) {
|
||||
pattern = [pattern, elemSeparator, '.*'].join('');
|
||||
pattern = [pattern, '.*'].join(elemSeparator);
|
||||
}
|
||||
|
||||
// create regexps for element, attribute name, and attribute value
|
||||
@@ -132,14 +129,11 @@ exports.fn = (root, params) => {
|
||||
if (list[0].test(node.name)) {
|
||||
// loop attributes
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
const isCurrentColor = value.toLowerCase() === 'currentcolor';
|
||||
const isFillCurrentColor =
|
||||
preserveCurrentColor &&
|
||||
name == 'fill' &&
|
||||
value == 'currentColor';
|
||||
preserveCurrentColor && name == 'fill' && isCurrentColor;
|
||||
const isStrokeCurrentColor =
|
||||
preserveCurrentColor &&
|
||||
name == 'stroke' &&
|
||||
value == 'currentColor';
|
||||
preserveCurrentColor && name == 'stroke' && isCurrentColor;
|
||||
if (
|
||||
!isFillCurrentColor &&
|
||||
!isStrokeCurrentColor &&
|
||||
|
||||
43
node_modules/svgo/plugins/removeComments.js
generated
vendored
43
node_modules/svgo/plugins/removeComments.js
generated
vendored
@@ -1,11 +1,18 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
/**
|
||||
* @typedef RemoveCommentsParams
|
||||
* @property {ReadonlyArray<RegExp | string> | false=} preservePatterns
|
||||
*/
|
||||
|
||||
exports.name = 'removeComments';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'removes comments';
|
||||
export const name = 'removeComments';
|
||||
export const description = 'removes comments';
|
||||
|
||||
/**
|
||||
* If a comment matches one of the following patterns, it will be
|
||||
* preserved by default. Particularly for copyright/license information.
|
||||
*/
|
||||
const DEFAULT_PRESERVE_PATTERNS = [/^!/];
|
||||
|
||||
/**
|
||||
* Remove comments.
|
||||
@@ -16,15 +23,31 @@ exports.description = 'removes comments';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveCommentsParams>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = (_root, params) => {
|
||||
const { preservePatterns = DEFAULT_PRESERVE_PATTERNS } = params;
|
||||
|
||||
return {
|
||||
comment: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.value.charAt(0) !== '!') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
if (preservePatterns) {
|
||||
if (!Array.isArray(preservePatterns)) {
|
||||
throw Error(
|
||||
`Expected array in removeComments preservePatterns parameter but received ${preservePatterns}`,
|
||||
);
|
||||
}
|
||||
|
||||
const matches = preservePatterns.some((pattern) => {
|
||||
return new RegExp(pattern).test(node.value);
|
||||
});
|
||||
|
||||
if (matches) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
detachNodeFromParent(node, parentNode);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
120
node_modules/svgo/plugins/removeDeprecatedAttrs.js
generated
vendored
Normal file
120
node_modules/svgo/plugins/removeDeprecatedAttrs.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import * as csswhat from 'css-what';
|
||||
import { attrsGroupsDeprecated, elems } from './_collections.js';
|
||||
import { collectStylesheet } from '../lib/style.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveDeprecatedAttrsParams
|
||||
* @property {boolean=} removeUnsafe
|
||||
*/
|
||||
|
||||
export const name = 'removeDeprecatedAttrs';
|
||||
export const description = 'removes deprecated attributes';
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').Stylesheet} stylesheet
|
||||
* @returns {Set<string>}
|
||||
*/
|
||||
function extractAttributesInStylesheet(stylesheet) {
|
||||
const attributesInStylesheet = new Set();
|
||||
|
||||
stylesheet.rules.forEach((rule) => {
|
||||
const selectors = csswhat.parse(rule.selector);
|
||||
selectors.forEach((subselector) => {
|
||||
subselector.forEach((segment) => {
|
||||
if (segment.type !== 'attribute') {
|
||||
return;
|
||||
}
|
||||
|
||||
attributesInStylesheet.add(segment.name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return attributesInStylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastElement} node
|
||||
* @param {{ safe?: Set<string>; unsafe?: Set<string> }|undefined} deprecatedAttrs
|
||||
* @param {import('../lib/types.js').DefaultPlugins['removeDeprecatedAttrs']} params
|
||||
* @param {Set<string>} attributesInStylesheet
|
||||
*/
|
||||
function processAttributes(
|
||||
node,
|
||||
deprecatedAttrs,
|
||||
params,
|
||||
attributesInStylesheet,
|
||||
) {
|
||||
if (!deprecatedAttrs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deprecatedAttrs.safe) {
|
||||
deprecatedAttrs.safe.forEach((name) => {
|
||||
if (attributesInStylesheet.has(name)) {
|
||||
return;
|
||||
}
|
||||
delete node.attributes[name];
|
||||
});
|
||||
}
|
||||
|
||||
if (params.removeUnsafe && deprecatedAttrs.unsafe) {
|
||||
deprecatedAttrs.unsafe.forEach((name) => {
|
||||
if (attributesInStylesheet.has(name)) {
|
||||
return;
|
||||
}
|
||||
delete node.attributes[name];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove deprecated attributes.
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveDeprecatedAttrsParams>}
|
||||
*/
|
||||
export function fn(root, params) {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
const attributesInStylesheet = extractAttributesInStylesheet(stylesheet);
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
const elemConfig = elems[node.name];
|
||||
if (!elemConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Special cases
|
||||
|
||||
// Removing deprecated xml:lang is safe when the lang attribute exists.
|
||||
if (
|
||||
elemConfig.attrsGroups.has('core') &&
|
||||
node.attributes['xml:lang'] &&
|
||||
!attributesInStylesheet.has('xml:lang') &&
|
||||
node.attributes['lang']
|
||||
) {
|
||||
delete node.attributes['xml:lang'];
|
||||
}
|
||||
|
||||
// General cases
|
||||
|
||||
elemConfig.attrsGroups.forEach((attrsGroup) => {
|
||||
processAttributes(
|
||||
node,
|
||||
attrsGroupsDeprecated[attrsGroup],
|
||||
params,
|
||||
attributesInStylesheet,
|
||||
);
|
||||
});
|
||||
|
||||
processAttributes(
|
||||
node,
|
||||
elemConfig.deprecated,
|
||||
params,
|
||||
attributesInStylesheet,
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
27
node_modules/svgo/plugins/removeDesc.js
generated
vendored
27
node_modules/svgo/plugins/removeDesc.js
generated
vendored
@@ -1,27 +1,28 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
/**
|
||||
* @typedef RemoveDescParams
|
||||
* @property {boolean=} removeAny
|
||||
*/
|
||||
|
||||
exports.name = 'removeDesc';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'removes <desc>';
|
||||
export const name = 'removeDesc';
|
||||
export const description = 'removes <desc>';
|
||||
|
||||
const standardDescs = /^(Created with|Created using)/;
|
||||
|
||||
/**
|
||||
* Removes <desc>.
|
||||
* Removes only standard editors content or empty elements 'cause it can be used for accessibility.
|
||||
* Enable parameter 'removeAny' to remove any description.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc
|
||||
* Removes only standard editors content or empty elements because it can be
|
||||
* used for accessibility. Enable parameter 'removeAny' to remove any
|
||||
* description.
|
||||
*
|
||||
* @author Daniel Wabyick
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/desc
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{ removeAny?: boolean }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveDescParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
const { removeAny = true } = params;
|
||||
export const fn = (root, params) => {
|
||||
const { removeAny = false } = params;
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
|
||||
60
node_modules/svgo/plugins/removeDimensions.js
generated
vendored
60
node_modules/svgo/plugins/removeDimensions.js
generated
vendored
@@ -1,13 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
exports.name = 'removeDimensions';
|
||||
|
||||
exports.type = 'perItem';
|
||||
|
||||
exports.active = false;
|
||||
|
||||
exports.description =
|
||||
'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)';
|
||||
export const name = 'removeDimensions';
|
||||
export const description =
|
||||
'removes width and height in presence of viewBox (opposite to removeViewBox)';
|
||||
|
||||
/**
|
||||
* Remove width/height attributes and add the viewBox attribute if it's missing
|
||||
@@ -17,27 +10,32 @@ exports.description =
|
||||
* ↓
|
||||
* <svg viewBox="0 0 100 50" />
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @return {Boolean} if true, with and height will be filtered out
|
||||
*
|
||||
* @author Benny Schudel
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = function (item) {
|
||||
if (item.type === 'element' && item.name === 'svg') {
|
||||
if (item.attributes.viewBox != null) {
|
||||
delete item.attributes.width;
|
||||
delete item.attributes.height;
|
||||
} else if (
|
||||
item.attributes.width != null &&
|
||||
item.attributes.height != null &&
|
||||
Number.isNaN(Number(item.attributes.width)) === false &&
|
||||
Number.isNaN(Number(item.attributes.height)) === false
|
||||
) {
|
||||
const width = Number(item.attributes.width);
|
||||
const height = Number(item.attributes.height);
|
||||
item.attributes.viewBox = `0 0 ${width} ${height}`;
|
||||
delete item.attributes.width;
|
||||
delete item.attributes.height;
|
||||
}
|
||||
}
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'svg') {
|
||||
if (node.attributes.viewBox != null) {
|
||||
delete node.attributes.width;
|
||||
delete node.attributes.height;
|
||||
} else if (
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null &&
|
||||
Number.isNaN(Number(node.attributes.width)) === false &&
|
||||
Number.isNaN(Number(node.attributes.height)) === false
|
||||
) {
|
||||
const width = Number(node.attributes.width);
|
||||
const height = Number(node.attributes.height);
|
||||
node.attributes.viewBox = `0 0 ${width} ${height}`;
|
||||
delete node.attributes.width;
|
||||
delete node.attributes.height;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
14
node_modules/svgo/plugins/removeDoctype.js
generated
vendored
14
node_modules/svgo/plugins/removeDoctype.js
generated
vendored
@@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeDoctype';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'removes doctype declaration';
|
||||
export const name = 'removeDoctype';
|
||||
export const description = 'removes doctype declaration';
|
||||
|
||||
/**
|
||||
* Remove DOCTYPE declaration.
|
||||
@@ -29,9 +25,9 @@ exports.description = 'removes doctype declaration';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
doctype: {
|
||||
enter: (node, parentNode) => {
|
||||
|
||||
30
node_modules/svgo/plugins/removeEditorsNSData.js
generated
vendored
30
node_modules/svgo/plugins/removeEditorsNSData.js
generated
vendored
@@ -1,12 +1,14 @@
|
||||
'use strict';
|
||||
import { editorNamespaces } from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { editorNamespaces } = require('./_collections.js');
|
||||
/**
|
||||
* @typedef RemoveEditorsNSDataParams
|
||||
* @property {string[]=} additionalNamespaces
|
||||
*/
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeEditorsNSData';
|
||||
exports.active = true;
|
||||
exports.description = 'removes editors namespaces, elements and attributes';
|
||||
export const name = 'removeEditorsNSData';
|
||||
export const description =
|
||||
'removes editors namespaces, elements and attributes';
|
||||
|
||||
/**
|
||||
* Remove editors namespaces, elements and attributes.
|
||||
@@ -18,23 +20,19 @@ exports.description = 'removes editors namespaces, elements and attributes';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* additionalNamespaces?: Array<string>
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveEditorsNSDataParams>}
|
||||
*/
|
||||
exports.fn = (_root, params) => {
|
||||
let namespaces = editorNamespaces;
|
||||
export const fn = (_root, params) => {
|
||||
let namespaces = [...editorNamespaces];
|
||||
if (Array.isArray(params.additionalNamespaces)) {
|
||||
namespaces = [...editorNamespaces, ...params.additionalNamespaces];
|
||||
}
|
||||
/**
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
/** @type {string[]} */
|
||||
const prefixes = [];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// collect namespace aliases from svg element
|
||||
// collect namespace prefixes from svg element
|
||||
if (node.name === 'svg') {
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (name.startsWith('xmlns:') && namespaces.includes(value)) {
|
||||
|
||||
25
node_modules/svgo/plugins/removeElementsByAttr.js
generated
vendored
25
node_modules/svgo/plugins/removeElementsByAttr.js
generated
vendored
@@ -1,11 +1,13 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
/**
|
||||
* @typedef RemoveElementsByAttrParams
|
||||
* @property {string | string[]=} id
|
||||
* @property {string | string[]=} class
|
||||
*/
|
||||
|
||||
exports.name = 'removeElementsByAttr';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description =
|
||||
export const name = 'removeElementsByAttr';
|
||||
export const description =
|
||||
'removes arbitrary elements by ID or className (disabled by default)';
|
||||
|
||||
/**
|
||||
@@ -39,20 +41,17 @@ exports.description =
|
||||
*
|
||||
* @author Eli Dupuis (@elidupuis)
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* id?: string | Array<string>,
|
||||
* class?: string | Array<string>
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveElementsByAttrParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const ids =
|
||||
params.id == null ? [] : Array.isArray(params.id) ? params.id : [params.id];
|
||||
const classes =
|
||||
params.class == null
|
||||
? []
|
||||
: Array.isArray(params.class)
|
||||
? params.class
|
||||
: [params.class];
|
||||
? params.class
|
||||
: [params.class];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
|
||||
16
node_modules/svgo/plugins/removeEmptyAttrs.js
generated
vendored
16
node_modules/svgo/plugins/removeEmptyAttrs.js
generated
vendored
@@ -1,20 +1,16 @@
|
||||
'use strict';
|
||||
import { attrsGroups } from './_collections.js';
|
||||
|
||||
const { attrsGroups } = require('./_collections.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeEmptyAttrs';
|
||||
exports.active = true;
|
||||
exports.description = 'removes empty attributes';
|
||||
export const name = 'removeEmptyAttrs';
|
||||
export const description = 'removes empty attributes';
|
||||
|
||||
/**
|
||||
* Remove attributes with empty values.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
@@ -22,7 +18,7 @@ exports.fn = () => {
|
||||
if (
|
||||
value === '' &&
|
||||
// empty conditional processing attributes prevents elements from rendering
|
||||
attrsGroups.conditionalProcessing.includes(name) === false
|
||||
!attrsGroups.conditionalProcessing.has(name)
|
||||
) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
|
||||
41
node_modules/svgo/plugins/removeEmptyContainers.js
generated
vendored
41
node_modules/svgo/plugins/removeEmptyContainers.js
generated
vendored
@@ -1,12 +1,9 @@
|
||||
'use strict';
|
||||
import { elemsGroups } from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { elemsGroups } = require('./_collections.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeEmptyContainers';
|
||||
exports.active = true;
|
||||
exports.description = 'removes empty container elements';
|
||||
export const name = 'removeEmptyContainers';
|
||||
export const description = 'removes empty container elements';
|
||||
|
||||
/**
|
||||
* Remove empty containers.
|
||||
@@ -21,16 +18,18 @@ exports.description = 'removes empty container elements';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = (root) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
return {
|
||||
element: {
|
||||
exit: (node, parentNode) => {
|
||||
// remove only empty non-svg containers
|
||||
if (
|
||||
node.name === 'svg' ||
|
||||
elemsGroups.container.includes(node.name) === false ||
|
||||
!elemsGroups.container.has(node.name) ||
|
||||
node.children.length !== 0
|
||||
) {
|
||||
return;
|
||||
@@ -42,15 +41,25 @@ exports.fn = () => {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// The <g> may not have content, but the filter may cause a rectangle
|
||||
// to be created and filled with pattern.
|
||||
if (node.name === 'g' && node.attributes.filter != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// empty <mask> hides masked element
|
||||
if (node.name === 'mask' && node.attributes.id != null) {
|
||||
return;
|
||||
}
|
||||
if (parentNode.type === 'element' && parentNode.name === 'switch') {
|
||||
return;
|
||||
}
|
||||
|
||||
// The <g> may not have content, but the filter may cause a rectangle
|
||||
// to be created and filled with pattern.
|
||||
if (
|
||||
node.name === 'g' &&
|
||||
(node.attributes.filter != null ||
|
||||
computeStyle(stylesheet, node).filter)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
detachNodeFromParent(node, parentNode);
|
||||
},
|
||||
},
|
||||
|
||||
23
node_modules/svgo/plugins/removeEmptyText.js
generated
vendored
23
node_modules/svgo/plugins/removeEmptyText.js
generated
vendored
@@ -1,11 +1,14 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
/**
|
||||
* @typedef RemoveEmptyTextParams
|
||||
* @property {boolean=} text
|
||||
* @property {boolean=} tspan
|
||||
* @property {boolean=} tref
|
||||
*/
|
||||
|
||||
exports.name = 'removeEmptyText';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'removes empty <text> elements';
|
||||
export const name = 'removeEmptyText';
|
||||
export const description = 'removes empty <text> elements';
|
||||
|
||||
/**
|
||||
* Remove empty Text elements.
|
||||
@@ -24,13 +27,9 @@ exports.description = 'removes empty <text> elements';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* text?: boolean,
|
||||
* tspan?: boolean,
|
||||
* tref?: boolean
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveEmptyTextParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const { text = true, tspan = true, tref = true } = params;
|
||||
return {
|
||||
element: {
|
||||
|
||||
265
node_modules/svgo/plugins/removeHiddenElems.js
generated
vendored
265
node_modules/svgo/plugins/removeHiddenElems.js
generated
vendored
@@ -1,17 +1,33 @@
|
||||
'use strict';
|
||||
import { elemsGroups } from './_collections.js';
|
||||
import { detachNodeFromParent, querySelector } from '../lib/xast.js';
|
||||
import { visit, visitSkip } from '../lib/util/visit.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { parsePathData } from '../lib/path.js';
|
||||
import { findReferences, hasScripts } from '../lib/svgo/tools.js';
|
||||
|
||||
const {
|
||||
querySelector,
|
||||
closestByName,
|
||||
detachNodeFromParent,
|
||||
} = require('../lib/xast.js');
|
||||
const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
||||
const { parsePathData } = require('../lib/path.js');
|
||||
/**
|
||||
* @typedef RemoveHiddenElemsParams
|
||||
* @property {boolean=} isHidden
|
||||
* @property {boolean=} displayNone
|
||||
* @property {boolean=} opacity0
|
||||
* @property {boolean=} circleR0
|
||||
* @property {boolean=} ellipseRX0
|
||||
* @property {boolean=} ellipseRY0
|
||||
* @property {boolean=} rectWidth0
|
||||
* @property {boolean=} rectHeight0
|
||||
* @property {boolean=} patternWidth0
|
||||
* @property {boolean=} patternHeight0
|
||||
* @property {boolean=} imageWidth0
|
||||
* @property {boolean=} imageHeight0
|
||||
* @property {boolean=} pathEmptyD
|
||||
* @property {boolean=} polylineEmptyPoints
|
||||
* @property {boolean=} polygonEmptyPoints
|
||||
*/
|
||||
|
||||
exports.name = 'removeHiddenElems';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description =
|
||||
const nonRendering = elemsGroups.nonRendering;
|
||||
|
||||
export const name = 'removeHiddenElems';
|
||||
export const description =
|
||||
'removes hidden elements (zero sized, with absent attributes)';
|
||||
|
||||
/**
|
||||
@@ -29,25 +45,9 @@ exports.description =
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* isHidden: boolean,
|
||||
* displayNone: boolean,
|
||||
* opacity0: boolean,
|
||||
* circleR0: boolean,
|
||||
* ellipseRX0: boolean,
|
||||
* ellipseRY0: boolean,
|
||||
* rectWidth0: boolean,
|
||||
* rectHeight0: boolean,
|
||||
* patternWidth0: boolean,
|
||||
* patternHeight0: boolean,
|
||||
* imageWidth0: boolean,
|
||||
* imageHeight0: boolean,
|
||||
* pathEmptyD: boolean,
|
||||
* polylineEmptyPoints: boolean,
|
||||
* polygonEmptyPoints: boolean,
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveHiddenElemsParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
isHidden = true,
|
||||
displayNone = true,
|
||||
@@ -67,9 +67,129 @@ exports.fn = (root, params) => {
|
||||
} = params;
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
/**
|
||||
* Skip non-rendered nodes initially, and only detach if they have no ID, or
|
||||
* their ID is not referenced by another node.
|
||||
*
|
||||
* @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>}
|
||||
*/
|
||||
const nonRenderedNodes = new Map();
|
||||
|
||||
/**
|
||||
* IDs for removed hidden definitions.
|
||||
*
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const removedDefIds = new Set();
|
||||
|
||||
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>} */
|
||||
const allDefs = new Map();
|
||||
|
||||
/** @type {Set<string>} */
|
||||
const allReferences = new Set();
|
||||
|
||||
/** @type {Map<string, Array<{ node: import('../lib/types.js').XastElement, parentNode: import('../lib/types.js').XastParent }>>} */
|
||||
const referencesById = new Map();
|
||||
|
||||
/**
|
||||
* If styles are present, we can't be sure if a definition is unused or not
|
||||
*/
|
||||
let deoptimized = false;
|
||||
|
||||
/**
|
||||
* Nodes can't be removed if they or any of their children have an id attribute that is referenced.
|
||||
* @param {import('../lib/types.js').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 {import('../lib/types.js').XastChild} node
|
||||
* @param {import('../lib/types.js').XastParent} parentNode
|
||||
*/
|
||||
function removeElement(node, parentNode) {
|
||||
if (
|
||||
node.type === 'element' &&
|
||||
node.attributes.id != null &&
|
||||
parentNode.type === 'element' &&
|
||||
parentNode.name === 'defs'
|
||||
) {
|
||||
removedDefIds.add(node.attributes.id);
|
||||
}
|
||||
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// transparent non-rendering elements still apply where referenced
|
||||
if (nonRendering.has(node.name)) {
|
||||
nonRenderedNodes.set(node, parentNode);
|
||||
return visitSkip;
|
||||
}
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
// opacity="0"
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
|
||||
if (
|
||||
opacity0 &&
|
||||
computedStyle.opacity &&
|
||||
computedStyle.opacity.type === 'static' &&
|
||||
computedStyle.opacity.value === '0'
|
||||
) {
|
||||
if (node.name === 'path') {
|
||||
nonRenderedNodes.set(node, parentNode);
|
||||
return visitSkip;
|
||||
}
|
||||
removeElement(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (
|
||||
(node.name === 'style' && node.children.length !== 0) ||
|
||||
hasScripts(node)
|
||||
) {
|
||||
deoptimized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.name === 'defs') {
|
||||
allDefs.set(node, parentNode);
|
||||
}
|
||||
|
||||
if (node.name === 'use') {
|
||||
for (const attr of Object.keys(node.attributes)) {
|
||||
if (attr !== 'href' && !attr.endsWith(':href')) {
|
||||
continue;
|
||||
}
|
||||
const value = node.attributes[attr];
|
||||
const id = value.slice(1);
|
||||
|
||||
let refs = referencesById.get(id);
|
||||
if (!refs) {
|
||||
refs = [];
|
||||
referencesById.set(id, refs);
|
||||
}
|
||||
refs.push({ node, parentNode });
|
||||
}
|
||||
}
|
||||
|
||||
// Removes hidden elements
|
||||
// https://www.w3schools.com/cssref/pr_class_visibility.asp
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
@@ -81,7 +201,7 @@ exports.fn = (root, params) => {
|
||||
// keep if any descendant enables visibility
|
||||
querySelector(node, '[visibility=visible]') == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,22 +218,7 @@ exports.fn = (root, params) => {
|
||||
// markers with display: none still rendered
|
||||
node.name !== 'marker'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// opacity="0"
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
|
||||
if (
|
||||
opacity0 &&
|
||||
computedStyle.opacity &&
|
||||
computedStyle.opacity.type === 'static' &&
|
||||
computedStyle.opacity.value === '0' &&
|
||||
// transparent element inside clipPath still affect clipped elements
|
||||
closestByName(node, 'clipPath') == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,7 +234,7 @@ exports.fn = (root, params) => {
|
||||
node.children.length === 0 &&
|
||||
node.attributes.r === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,7 +250,7 @@ exports.fn = (root, params) => {
|
||||
node.children.length === 0 &&
|
||||
node.attributes.rx === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,7 +266,7 @@ exports.fn = (root, params) => {
|
||||
node.children.length === 0 &&
|
||||
node.attributes.ry === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -177,7 +282,7 @@ exports.fn = (root, params) => {
|
||||
node.children.length === 0 &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,7 +299,7 @@ exports.fn = (root, params) => {
|
||||
node.children.length === 0 &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,7 +314,7 @@ exports.fn = (root, params) => {
|
||||
node.name === 'pattern' &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,7 +329,7 @@ exports.fn = (root, params) => {
|
||||
node.name === 'pattern' &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -239,7 +344,7 @@ exports.fn = (root, params) => {
|
||||
node.name === 'image' &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -254,7 +359,7 @@ exports.fn = (root, params) => {
|
||||
node.name === 'image' &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,12 +370,12 @@ exports.fn = (root, params) => {
|
||||
// <path d=""/>
|
||||
if (pathEmptyD && node.name === 'path') {
|
||||
if (node.attributes.d == null) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
const pathData = parsePathData(node.attributes.d);
|
||||
if (pathData.length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
// keep single point paths for markers
|
||||
@@ -279,10 +384,9 @@ exports.fn = (root, params) => {
|
||||
computedStyle['marker-start'] == null &&
|
||||
computedStyle['marker-end'] == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Polyline with empty points
|
||||
@@ -295,7 +399,7 @@ exports.fn = (root, params) => {
|
||||
node.name === 'polyline' &&
|
||||
node.attributes.points == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,9 +413,46 @@ exports.fn = (root, params) => {
|
||||
node.name === 'polygon' &&
|
||||
node.attributes.points == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
const ids = findReferences(name, value);
|
||||
|
||||
for (const id of ids) {
|
||||
allReferences.add(id);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
root: {
|
||||
exit: () => {
|
||||
for (const id of removedDefIds) {
|
||||
const refs = referencesById.get(id);
|
||||
if (refs) {
|
||||
for (const { node, parentNode } of refs) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!deoptimized) {
|
||||
for (const [
|
||||
nonRenderedNode,
|
||||
nonRenderedParent,
|
||||
] of nonRenderedNodes.entries()) {
|
||||
if (canRemoveNonRenderingNode(nonRenderedNode)) {
|
||||
detachNodeFromParent(nonRenderedNode, nonRenderedParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [node, parentNode] of allDefs.entries()) {
|
||||
if (node.children.length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
14
node_modules/svgo/plugins/removeMetadata.js
generated
vendored
14
node_modules/svgo/plugins/removeMetadata.js
generated
vendored
@@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeMetadata';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'removes <metadata>';
|
||||
export const name = 'removeMetadata';
|
||||
export const description = 'removes <metadata>';
|
||||
|
||||
/**
|
||||
* Remove <metadata>.
|
||||
@@ -14,9 +10,9 @@ exports.description = 'removes <metadata>';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
|
||||
56
node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
generated
vendored
56
node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
generated
vendored
@@ -1,38 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
exports.name = 'removeNonInheritableGroupAttrs';
|
||||
|
||||
exports.type = 'perItem';
|
||||
|
||||
exports.active = true;
|
||||
|
||||
exports.description =
|
||||
'removes non-inheritable group’s presentational attributes';
|
||||
|
||||
const {
|
||||
inheritableAttrs,
|
||||
import {
|
||||
attrsGroups,
|
||||
inheritableAttrs,
|
||||
presentationNonInheritableGroupAttrs,
|
||||
} = require('./_collections');
|
||||
} from './_collections.js';
|
||||
|
||||
export const name = 'removeNonInheritableGroupAttrs';
|
||||
export const description =
|
||||
"removes non-inheritable group's presentational attributes";
|
||||
|
||||
/**
|
||||
* Remove non-inheritable group's "presentation" attributes.
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @return {Boolean} if false, item will be filtered out
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = function (item) {
|
||||
if (item.type === 'element' && item.name === 'g') {
|
||||
for (const name of Object.keys(item.attributes)) {
|
||||
if (
|
||||
attrsGroups.presentation.includes(name) === true &&
|
||||
inheritableAttrs.includes(name) === false &&
|
||||
presentationNonInheritableGroupAttrs.includes(name) === false
|
||||
) {
|
||||
delete item.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'g') {
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (
|
||||
attrsGroups.presentation.has(name) &&
|
||||
!inheritableAttrs.has(name) &&
|
||||
!presentationNonInheritableGroupAttrs.has(name)
|
||||
) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
43
node_modules/svgo/plugins/removeOffCanvasPaths.js
generated
vendored
43
node_modules/svgo/plugins/removeOffCanvasPaths.js
generated
vendored
@@ -1,29 +1,22 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
import { parsePathData } from '../lib/path.js';
|
||||
import { intersects } from './_path.js';
|
||||
|
||||
export const name = 'removeOffCanvasPaths';
|
||||
export const description =
|
||||
'removes elements that are drawn outside of the viewBox (disabled by default)';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').PathDataItem} PathDataItem
|
||||
*/
|
||||
|
||||
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { parsePathData } = require('../lib/path.js');
|
||||
const { intersects } = require('./_path.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeOffCanvasPaths';
|
||||
exports.active = false;
|
||||
exports.description =
|
||||
'removes elements that are drawn outside of the viewbox (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove elements that are drawn outside of the viewbox.
|
||||
* Remove elements that are drawn outside of the viewBox.
|
||||
*
|
||||
* @author JoshyPHP
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
/**
|
||||
* @type {null | {
|
||||
* @type {?{
|
||||
* top: number,
|
||||
* right: number,
|
||||
* bottom: number,
|
||||
@@ -39,7 +32,7 @@ exports.fn = () => {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
let viewBox = '';
|
||||
// find viewbox
|
||||
// find viewBox
|
||||
if (node.attributes.viewBox != null) {
|
||||
// remove commas and plus signs, normalize and trim whitespace
|
||||
viewBox = node.attributes.viewBox;
|
||||
@@ -50,7 +43,7 @@ exports.fn = () => {
|
||||
viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
|
||||
}
|
||||
|
||||
// parse viewbox
|
||||
// parse viewBox
|
||||
// remove commas and plus signs, normalize and trim whitespace
|
||||
viewBox = viewBox
|
||||
.replace(/[,+]|px/g, ' ')
|
||||
@@ -59,7 +52,7 @@ exports.fn = () => {
|
||||
// ensure that the dimensions are 4 values separated by space
|
||||
const m =
|
||||
/^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
|
||||
viewBox
|
||||
viewBox,
|
||||
);
|
||||
if (m == null) {
|
||||
return;
|
||||
@@ -92,7 +85,7 @@ exports.fn = () => {
|
||||
) {
|
||||
const pathData = parsePathData(node.attributes.d);
|
||||
|
||||
// consider that a M command within the viewBox is visible
|
||||
// consider that an M command within the viewBox is visible
|
||||
let visible = false;
|
||||
for (const pathDataItem of pathData) {
|
||||
if (pathDataItem.command === 'M') {
|
||||
@@ -117,9 +110,7 @@ exports.fn = () => {
|
||||
}
|
||||
|
||||
const { left, top, width, height } = viewBoxData;
|
||||
/**
|
||||
* @type {Array<PathDataItem>}
|
||||
*/
|
||||
/** @type {ReadonlyArray<import('../lib/types.js').PathDataItem>} */
|
||||
const viewBoxPathData = [
|
||||
{ command: 'M', args: [left, top] },
|
||||
{ command: 'h', args: [width] },
|
||||
|
||||
16
node_modules/svgo/plugins/removeRasterImages.js
generated
vendored
16
node_modules/svgo/plugins/removeRasterImages.js
generated
vendored
@@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeRasterImages';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'removes raster images (disabled by default)';
|
||||
export const name = 'removeRasterImages';
|
||||
export const description = 'removes raster images (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove raster images references in <image>.
|
||||
@@ -14,16 +10,16 @@ exports.description = 'removes raster images (disabled by default)';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (
|
||||
node.name === 'image' &&
|
||||
node.attributes['xlink:href'] != null &&
|
||||
/(\.|image\/)(jpg|png|gif)/.test(node.attributes['xlink:href'])
|
||||
/(\.|image\/)(jpe?g|png|gif)/.test(node.attributes['xlink:href'])
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
|
||||
29
node_modules/svgo/plugins/removeScriptElement.js
generated
vendored
29
node_modules/svgo/plugins/removeScriptElement.js
generated
vendored
@@ -1,29 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeScriptElement';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'removes <script> elements (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove <script>.
|
||||
*
|
||||
* https://www.w3.org/TR/SVG11/script.html
|
||||
*
|
||||
* @author Patrick Klingemann
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'script') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
63
node_modules/svgo/plugins/removeScripts.js
generated
vendored
Normal file
63
node_modules/svgo/plugins/removeScripts.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import { attrsGroups } from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeScripts';
|
||||
export const description = 'removes scripts (disabled by default)';
|
||||
|
||||
/** Union of all event attributes. */
|
||||
const eventAttrs = [
|
||||
...attrsGroups.animationEvent,
|
||||
...attrsGroups.documentEvent,
|
||||
...attrsGroups.documentElementEvent,
|
||||
...attrsGroups.globalEvent,
|
||||
...attrsGroups.graphicalEvent,
|
||||
];
|
||||
|
||||
/**
|
||||
* Remove scripts.
|
||||
*
|
||||
* https://www.w3.org/TR/SVG11/script.html
|
||||
*
|
||||
* @author Patrick Klingemann
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'script') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attr of eventAttrs) {
|
||||
if (node.attributes[attr] != null) {
|
||||
delete node.attributes[attr];
|
||||
}
|
||||
}
|
||||
},
|
||||
exit: (node, parentNode) => {
|
||||
if (node.name !== 'a') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attr of Object.keys(node.attributes)) {
|
||||
if (attr === 'href' || attr.endsWith(':href')) {
|
||||
if (
|
||||
node.attributes[attr] == null ||
|
||||
!node.attributes[attr].trimStart().startsWith('javascript:')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const index = parentNode.children.indexOf(node);
|
||||
const usefulChildren = node.children.filter(
|
||||
(child) => child.type !== 'text',
|
||||
);
|
||||
parentNode.children.splice(index, 1, ...usefulChildren);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
14
node_modules/svgo/plugins/removeStyleElement.js
generated
vendored
14
node_modules/svgo/plugins/removeStyleElement.js
generated
vendored
@@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeStyleElement';
|
||||
exports.type = 'visitor';
|
||||
exports.active = false;
|
||||
exports.description = 'removes <style> element (disabled by default)';
|
||||
export const name = 'removeStyleElement';
|
||||
export const description = 'removes <style> element (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove <style>.
|
||||
@@ -14,9 +10,9 @@ exports.description = 'removes <style> element (disabled by default)';
|
||||
*
|
||||
* @author Betsy Dupuis
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
|
||||
16
node_modules/svgo/plugins/removeTitle.js
generated
vendored
16
node_modules/svgo/plugins/removeTitle.js
generated
vendored
@@ -1,22 +1,18 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
|
||||
exports.name = 'removeTitle';
|
||||
exports.type = 'visitor';
|
||||
exports.active = true;
|
||||
exports.description = 'removes <title>';
|
||||
export const name = 'removeTitle';
|
||||
export const description = 'removes <title>';
|
||||
|
||||
/**
|
||||
* Remove <title>.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
|
||||
* https://developer.mozilla.org/docs/Web/SVG/Element/title
|
||||
*
|
||||
* @author Igor Kalashnikov
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
|
||||
92
node_modules/svgo/plugins/removeUnknownsAndDefaults.js
generated
vendored
92
node_modules/svgo/plugins/removeUnknownsAndDefaults.js
generated
vendored
@@ -1,40 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
||||
const {
|
||||
elems,
|
||||
import {
|
||||
attrsGroups,
|
||||
elemsGroups,
|
||||
attrsGroupsDefaults,
|
||||
elems,
|
||||
elemsGroups,
|
||||
presentationNonInheritableGroupAttrs,
|
||||
} = require('./_collections');
|
||||
} from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeUnknownsAndDefaults';
|
||||
exports.active = true;
|
||||
exports.description =
|
||||
/**
|
||||
* @typedef RemoveUnknownsAndDefaultsParams
|
||||
* @property {boolean=} unknownContent
|
||||
* @property {boolean=} unknownAttrs
|
||||
* @property {boolean=} defaultAttrs
|
||||
* @property {boolean=} defaultMarkupDeclarations
|
||||
* If to remove XML declarations that are assigned their default value. XML
|
||||
* declarations are the properties in the `<?xml … ?>` block at the top of the
|
||||
* document.
|
||||
* @property {boolean=} uselessOverrides
|
||||
* @property {boolean=} keepDataAttrs
|
||||
* @property {boolean=} keepAriaAttrs
|
||||
* @property {boolean=} keepRoleAttr
|
||||
*/
|
||||
|
||||
export const name = 'removeUnknownsAndDefaults';
|
||||
export const description =
|
||||
'removes unknown elements content and attributes, removes attrs with default values';
|
||||
|
||||
// resolve all groups references
|
||||
|
||||
/**
|
||||
* @type {Map<string, Set<string>>}
|
||||
*/
|
||||
/** @type {Map<string, Set<string>>} */
|
||||
const allowedChildrenPerElement = new Map();
|
||||
/**
|
||||
* @type {Map<string, Set<string>>}
|
||||
*/
|
||||
/** @type {Map<string, Set<string>>} */
|
||||
const allowedAttributesPerElement = new Map();
|
||||
/**
|
||||
* @type {Map<string, Map<string, string>>}
|
||||
*/
|
||||
/** @type {Map<string, Map<string, string>>} */
|
||||
const attributesDefaultsPerElement = new Map();
|
||||
|
||||
for (const [name, config] of Object.entries(elems)) {
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
/** @type {Set<string>} */
|
||||
const allowedChildren = new Set();
|
||||
if (config.content) {
|
||||
for (const elementName of config.content) {
|
||||
@@ -51,18 +55,14 @@ for (const [name, config] of Object.entries(elems)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
/** @type {Set<string>} */
|
||||
const allowedAttributes = new Set();
|
||||
if (config.attrs) {
|
||||
for (const attrName of config.attrs) {
|
||||
allowedAttributes.add(attrName);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @type {Map<string, string>}
|
||||
*/
|
||||
/** @type {Map<string, string>} */
|
||||
const attributesDefaults = new Map();
|
||||
if (config.defaults) {
|
||||
for (const [attrName, defaultValue] of Object.entries(config.defaults)) {
|
||||
@@ -94,21 +94,14 @@ for (const [name, config] of Object.entries(elems)) {
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* unknownContent?: boolean,
|
||||
* unknownAttrs?: boolean,
|
||||
* defaultAttrs?: boolean,
|
||||
* uselessOverrides?: boolean,
|
||||
* keepDataAttrs?: boolean,
|
||||
* keepAriaAttrs?: boolean,
|
||||
* keepRoleAttr?: boolean,
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveUnknownsAndDefaultsParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
unknownContent = true,
|
||||
unknownAttrs = true,
|
||||
defaultAttrs = true,
|
||||
defaultMarkupDeclarations = true,
|
||||
uselessOverrides = true,
|
||||
keepDataAttrs = true,
|
||||
keepAriaAttrs = true,
|
||||
@@ -117,6 +110,13 @@ exports.fn = (root, params) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
return {
|
||||
instruction: {
|
||||
enter: (node) => {
|
||||
if (defaultMarkupDeclarations) {
|
||||
node.value = node.value.replace(/\s*standalone\s*=\s*(["'])no\1/, '');
|
||||
}
|
||||
},
|
||||
},
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// skip namespaced elements
|
||||
@@ -131,7 +131,7 @@ exports.fn = (root, params) => {
|
||||
// remove unknown element's content
|
||||
if (unknownContent && parentNode.type === 'element') {
|
||||
const allowedChildren = allowedChildrenPerElement.get(
|
||||
parentNode.name
|
||||
parentNode.name,
|
||||
);
|
||||
if (allowedChildren == null || allowedChildren.size === 0) {
|
||||
// remove unknown elements
|
||||
@@ -192,18 +192,14 @@ exports.fn = (root, params) => {
|
||||
attributesDefaults.get(name) === value
|
||||
) {
|
||||
// keep defaults if parent has own or inherited style
|
||||
if (
|
||||
computedParentStyle == null ||
|
||||
computedParentStyle[name] == null
|
||||
) {
|
||||
if (computedParentStyle?.[name] == null) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
if (uselessOverrides && node.attributes.id == null) {
|
||||
const style =
|
||||
computedParentStyle == null ? null : computedParentStyle[name];
|
||||
const style = computedParentStyle?.[name];
|
||||
if (
|
||||
presentationNonInheritableGroupAttrs.includes(name) === false &&
|
||||
presentationNonInheritableGroupAttrs.has(name) === false &&
|
||||
style != null &&
|
||||
style.type === 'static' &&
|
||||
style.value === value
|
||||
|
||||
16
node_modules/svgo/plugins/removeUnusedNS.js
generated
vendored
16
node_modules/svgo/plugins/removeUnusedNS.js
generated
vendored
@@ -1,9 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeUnusedNS';
|
||||
exports.active = true;
|
||||
exports.description = 'removes unused namespaces declaration';
|
||||
export const name = 'removeUnusedNS';
|
||||
export const description = 'removes unused namespaces declaration';
|
||||
|
||||
/**
|
||||
* Remove unused namespaces declaration from svg element
|
||||
@@ -11,12 +7,10 @@ exports.description = 'removes unused namespaces declaration';
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
export const fn = () => {
|
||||
/** @type {Set<string>} */
|
||||
const unusedNamespaces = new Set();
|
||||
return {
|
||||
element: {
|
||||
|
||||
43
node_modules/svgo/plugins/removeUselessDefs.js
generated
vendored
43
node_modules/svgo/plugins/removeUselessDefs.js
generated
vendored
@@ -1,48 +1,32 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { elemsGroups } from './_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {import('../lib/types').XastElement} XastElement
|
||||
*/
|
||||
|
||||
const { detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { elemsGroups } = require('./_collections.js');
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeUselessDefs';
|
||||
exports.active = true;
|
||||
exports.description = 'removes elements in <defs> without id';
|
||||
export const name = 'removeUselessDefs';
|
||||
export const description = 'removes elements in <defs> without id';
|
||||
|
||||
/**
|
||||
* Removes content of defs and properties that aren't rendered directly without ids.
|
||||
*
|
||||
* @author Lev Solntsev
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'defs') {
|
||||
/**
|
||||
* @type {Array<XastElement>}
|
||||
*/
|
||||
if (
|
||||
node.name === 'defs' ||
|
||||
(elemsGroups.nonRendering.has(node.name) &&
|
||||
node.attributes.id == null)
|
||||
) {
|
||||
/** @type {import('../lib/types.js').XastElement[]} */
|
||||
const usefulNodes = [];
|
||||
collectUsefulNodes(node, usefulNodes);
|
||||
if (usefulNodes.length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
// TODO remove in SVGO 3
|
||||
for (const usefulNode of usefulNodes) {
|
||||
// @ts-ignore parentNode is legacy
|
||||
usefulNode.parentNode = node;
|
||||
}
|
||||
node.children = usefulNodes;
|
||||
} else if (
|
||||
elemsGroups.nonRendering.includes(node.name) &&
|
||||
node.attributes.id == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -50,7 +34,8 @@ exports.fn = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {(node: XastElement, usefulNodes: Array<XastElement>) => void}
|
||||
* @param {import('../lib/types.js').XastElement} node
|
||||
* @param {import('../lib/types.js').XastElement[]} usefulNodes
|
||||
*/
|
||||
const collectUsefulNodes = (node, usefulNodes) => {
|
||||
for (const child of node.children) {
|
||||
|
||||
37
node_modules/svgo/plugins/removeUselessStrokeAndFill.js
generated
vendored
37
node_modules/svgo/plugins/removeUselessStrokeAndFill.js
generated
vendored
@@ -1,38 +1,39 @@
|
||||
'use strict';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { visit, visitSkip } from '../lib/util/visit.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { hasScripts } from '../lib/svgo/tools.js';
|
||||
import { elemsGroups } from './_collections.js';
|
||||
|
||||
const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js');
|
||||
const { collectStylesheet, computeStyle } = require('../lib/style.js');
|
||||
const { elemsGroups } = require('./_collections.js');
|
||||
/**
|
||||
* @typedef RemoveUselessStrokeAndFillParams
|
||||
* @property {boolean=} stroke
|
||||
* @property {boolean=} fill
|
||||
* @property {boolean=} removeNone
|
||||
*/
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeUselessStrokeAndFill';
|
||||
exports.active = true;
|
||||
exports.description = 'removes useless stroke and fill attributes';
|
||||
export const name = 'removeUselessStrokeAndFill';
|
||||
export const description = 'removes useless stroke and fill attributes';
|
||||
|
||||
/**
|
||||
* Remove useless stroke and fill attrs.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<{
|
||||
* stroke?: boolean,
|
||||
* fill?: boolean,
|
||||
* removeNone?: boolean
|
||||
* }>}
|
||||
* @type {import('../lib/types.js').Plugin<RemoveUselessStrokeAndFillParams>}
|
||||
*/
|
||||
exports.fn = (root, params) => {
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
stroke: removeStroke = true,
|
||||
fill: removeFill = true,
|
||||
removeNone = false,
|
||||
} = params;
|
||||
|
||||
// style and script elements deoptimise this plugin
|
||||
// style and script elements deoptimize this plugin
|
||||
let hasStyleOrScript = false;
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'style' || node.name === 'script') {
|
||||
if (node.name === 'style' || hasScripts(node)) {
|
||||
hasStyleOrScript = true;
|
||||
}
|
||||
},
|
||||
@@ -47,11 +48,11 @@ exports.fn = (root, params) => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// id attribute deoptimise the whole subtree
|
||||
// id attribute deoptimize the whole subtree
|
||||
if (node.attributes.id != null) {
|
||||
return visitSkip;
|
||||
}
|
||||
if (elemsGroups.shape.includes(node.name) == false) {
|
||||
if (!elemsGroups.shape.has(node.name)) {
|
||||
return;
|
||||
}
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
|
||||
18
node_modules/svgo/plugins/removeViewBox.js
generated
vendored
18
node_modules/svgo/plugins/removeViewBox.js
generated
vendored
@@ -1,11 +1,7 @@
|
||||
'use strict';
|
||||
export const name = 'removeViewBox';
|
||||
export const description = 'removes viewBox attribute when possible';
|
||||
|
||||
exports.type = 'visitor';
|
||||
exports.name = 'removeViewBox';
|
||||
exports.active = true;
|
||||
exports.description = 'removes viewBox attribute when possible';
|
||||
|
||||
const viewBoxElems = ['svg', 'pattern', 'symbol'];
|
||||
const viewBoxElems = new Set(['pattern', 'svg', 'symbol']);
|
||||
|
||||
/**
|
||||
* Remove viewBox attr which coincides with a width/height box.
|
||||
@@ -14,19 +10,19 @@ const viewBoxElems = ['svg', 'pattern', 'symbol'];
|
||||
*
|
||||
* @example
|
||||
* <svg width="100" height="50" viewBox="0 0 100 50">
|
||||
* ⬇
|
||||
* ⬇
|
||||
* <svg width="100" height="50">
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types').Plugin<void>}
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = () => {
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (
|
||||
viewBoxElems.includes(node.name) &&
|
||||
viewBoxElems.has(node.name) &&
|
||||
node.attributes.viewBox != null &&
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null
|
||||
|
||||
31
node_modules/svgo/plugins/removeXMLNS.js
generated
vendored
31
node_modules/svgo/plugins/removeXMLNS.js
generated
vendored
@@ -1,12 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
exports.name = 'removeXMLNS';
|
||||
|
||||
exports.type = 'perItem';
|
||||
|
||||
exports.active = false;
|
||||
|
||||
exports.description =
|
||||
export const name = 'removeXMLNS';
|
||||
export const description =
|
||||
'removes xmlns attribute (for inline svg, disabled by default)';
|
||||
|
||||
/**
|
||||
@@ -17,14 +10,18 @@ exports.description =
|
||||
* ↓
|
||||
* <svg viewBox="0 0 100 50">
|
||||
*
|
||||
* @param {Object} item current iteration item
|
||||
* @return {Boolean} if true, xmlns will be filtered out
|
||||
*
|
||||
* @author Ricardo Tomasi
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
exports.fn = function (item) {
|
||||
if (item.type === 'element' && item.name === 'svg') {
|
||||
delete item.attributes.xmlns;
|
||||
delete item.attributes['xmlns:xlink'];
|
||||
}
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'svg') {
|
||||
delete node.attributes.xmlns;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user