Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
21
Frontend-Learner/node_modules/svgo/LICENSE
generated
vendored
Normal file
21
Frontend-Learner/node_modules/svgo/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Kir Belevich
|
||||
|
||||
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.
|
||||
179
Frontend-Learner/node_modules/svgo/README.md
generated
vendored
Normal file
179
Frontend-Learner/node_modules/svgo/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<div align="center">
|
||||
<img src="./logo/logo-web.svg" width="348.61" height="100" alt=""/>
|
||||
</div>
|
||||
|
||||
# SVGO [](https://npmjs.org/package/svgo) [](https://discord.gg/z8jX8NYxrE) [](https://svgo.dev/)
|
||||
|
||||
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 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
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## Command-line usage
|
||||
|
||||
Process single files:
|
||||
|
||||
```sh
|
||||
svgo one.svg two.svg -o one.min.svg two.min.svg
|
||||
```
|
||||
|
||||
Process a directory of files recursively with `-r`/`--recursive` and `-f`/`--folder`:
|
||||
|
||||
```sh
|
||||
svgo -rf path/to/directory_with_svgs -o path/to/output_directory
|
||||
```
|
||||
|
||||
Help for advanced usage:
|
||||
|
||||
```sh
|
||||
svgo --help
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
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
|
||||
export default {
|
||||
multipass: false, // boolean
|
||||
datauri: 'base64', // 'base64'|'enc'|'unenc'
|
||||
js2svg: {
|
||||
indent: 4, // number
|
||||
pretty: false, // boolean
|
||||
},
|
||||
plugins: [
|
||||
'preset-default', // built-in plugins enabled by default
|
||||
'prefixIds', // enable built-in plugins by name
|
||||
|
||||
// enable built-in plugins with an object to configure plugins
|
||||
{
|
||||
name: 'prefixIds',
|
||||
params: {
|
||||
prefix: 'uwu',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
### 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
|
||||
export default {
|
||||
plugins: [
|
||||
{
|
||||
name: 'preset-default',
|
||||
params: {
|
||||
overrides: {
|
||||
// disable a default plugin
|
||||
cleanupIds: false,
|
||||
|
||||
// customize the params of a default plugin
|
||||
inlineStyles: {
|
||||
onlyMatchedOnce: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
### Custom plugins
|
||||
|
||||
You can also specify custom plugins:
|
||||
|
||||
**`svgo.config.mjs`**
|
||||
|
||||
```js
|
||||
import importedPlugin from './imported-plugin';
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
// plugin imported from another JavaScript file
|
||||
importedPlugin,
|
||||
|
||||
// plugin defined inline
|
||||
{
|
||||
name: 'customPlugin',
|
||||
params: {
|
||||
paramName: 'paramValue',
|
||||
},
|
||||
fn: (ast, params, info) => {},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
## API usage
|
||||
|
||||
SVGO provides a few low level utilities.
|
||||
|
||||
### optimize
|
||||
|
||||
The core of SVGO is `optimize` function.
|
||||
|
||||
```js
|
||||
import { optimize } from 'svgo';
|
||||
|
||||
const result = optimize(svgString, {
|
||||
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 may want to resolve the `svgo.config.mjs` file.
|
||||
|
||||
```js
|
||||
import { loadConfig } from 'svgo';
|
||||
|
||||
const config = await loadConfig();
|
||||
```
|
||||
|
||||
You can also specify a path and customize the current working directory.
|
||||
|
||||
```js
|
||||
const config = await loadConfig(configFile, cwd);
|
||||
```
|
||||
|
||||
## Donors
|
||||
|
||||
| [<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/main/LICENSE).
|
||||
|
||||
Logo by [André Castillo](https://github.com/DerianAndre).
|
||||
10
Frontend-Learner/node_modules/svgo/bin/svgo.js
generated
vendored
Normal file
10
Frontend-Learner/node_modules/svgo/bin/svgo.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/dist/svgo-node.cjs
generated
vendored
Normal file
2139
Frontend-Learner/node_modules/svgo/dist/svgo-node.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
8363
Frontend-Learner/node_modules/svgo/dist/svgo.browser.js
generated
vendored
Normal file
8363
Frontend-Learner/node_modules/svgo/dist/svgo.browser.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
117
Frontend-Learner/node_modules/svgo/lib/builtin.js
generated
vendored
Normal file
117
Frontend-Learner/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,
|
||||
]);
|
||||
208
Frontend-Learner/node_modules/svgo/lib/parser.js
generated
vendored
Normal file
208
Frontend-Learner/node_modules/svgo/lib/parser.js
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import SAX from 'sax';
|
||||
import { textElems } from '../plugins/_collections.js';
|
||||
|
||||
export class SvgoParserError extends Error {
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {number} line
|
||||
* @param {number} column
|
||||
* @param {string} source
|
||||
* @param {string=} file
|
||||
*/
|
||||
constructor(message, line, column, source, file) {
|
||||
super(message);
|
||||
this.name = 'SvgoParserError';
|
||||
this.message = `${file || '<input>'}:${line}:${column}: ${message}`;
|
||||
this.reason = message;
|
||||
this.line = line;
|
||||
this.column = column;
|
||||
this.source = source;
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, SvgoParserError);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const lines = this.source.split(/\r?\n/);
|
||||
const startLine = Math.max(this.line - 3, 0);
|
||||
const endLine = Math.min(this.line + 2, lines.length);
|
||||
const lineNumberWidth = String(endLine).length;
|
||||
const startColumn = Math.max(this.column - 54, 0);
|
||||
const endColumn = Math.max(this.column + 20, 80);
|
||||
const code = lines
|
||||
.slice(startLine, endLine)
|
||||
.map((line, index) => {
|
||||
const lineSlice = line.slice(startColumn, endColumn);
|
||||
let ellipsisPrefix = '';
|
||||
let ellipsisSuffix = '';
|
||||
if (startColumn !== 0) {
|
||||
ellipsisPrefix = startColumn > line.length - 1 ? ' ' : '…';
|
||||
}
|
||||
if (endColumn < line.length - 1) {
|
||||
ellipsisSuffix = '…';
|
||||
}
|
||||
const number = startLine + 1 + index;
|
||||
const gutter = ` ${number.toString().padStart(lineNumberWidth)} | `;
|
||||
if (number === this.line) {
|
||||
const gutterSpacing = gutter.replace(/[^|]/g, ' ');
|
||||
const lineSpacing = (
|
||||
ellipsisPrefix + line.slice(startColumn, this.column - 1)
|
||||
).replace(/[^\t]/g, ' ');
|
||||
const spacing = gutterSpacing + lineSpacing;
|
||||
return `>${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}\n ${spacing}^`;
|
||||
}
|
||||
return ` ${gutter}${ellipsisPrefix}${lineSlice}${ellipsisSuffix}`;
|
||||
})
|
||||
.join('\n');
|
||||
return `${this.name}: ${this.message}\n\n${code}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
const entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
|
||||
|
||||
const config = {
|
||||
strict: true,
|
||||
trim: false,
|
||||
normalize: false,
|
||||
lowercase: true,
|
||||
xmlns: true,
|
||||
position: true,
|
||||
unparsedEntities: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert SVG (XML) string to SVG-as-JS object.
|
||||
*
|
||||
* @param {string} data
|
||||
* @param {string=} from
|
||||
* @returns {import('./types.js').XastRoot}
|
||||
*/
|
||||
export const parseSvg = (data, from) => {
|
||||
const sax = SAX.parser(config.strict, config);
|
||||
/** @type {import('./types.js').XastRoot} */
|
||||
const root = { type: 'root', children: [] };
|
||||
/** @type {import('./types.js').XastParent} */
|
||||
let current = root;
|
||||
/** @type {import('./types.js').XastParent[]} */
|
||||
const stack = [root];
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastChild} node
|
||||
*/
|
||||
const pushToContent = (node) => {
|
||||
current.children.push(node);
|
||||
};
|
||||
|
||||
sax.ondoctype = (doctype) => {
|
||||
/** @type {import('./types.js').XastDoctype} */
|
||||
const node = {
|
||||
type: 'doctype',
|
||||
// TODO parse doctype for name, public and system to match xast
|
||||
name: 'svg',
|
||||
data: {
|
||||
doctype,
|
||||
},
|
||||
};
|
||||
pushToContent(node);
|
||||
const subsetStart = doctype.indexOf('[');
|
||||
if (subsetStart >= 0) {
|
||||
entityDeclaration.lastIndex = subsetStart;
|
||||
let entityMatch = entityDeclaration.exec(data);
|
||||
while (entityMatch != null) {
|
||||
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
|
||||
entityMatch = entityDeclaration.exec(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sax.onprocessinginstruction = (data) => {
|
||||
/** @type {import('./types.js').XastInstruction} */
|
||||
const node = {
|
||||
type: 'instruction',
|
||||
name: data.name,
|
||||
value: data.body,
|
||||
};
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
sax.oncomment = (comment) => {
|
||||
/** @type {import('./types.js').XastComment} */
|
||||
const node = {
|
||||
type: 'comment',
|
||||
value: comment.trim(),
|
||||
};
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
sax.oncdata = (cdata) => {
|
||||
/** @type {import('./types.js').XastCdata} */
|
||||
const node = {
|
||||
type: 'cdata',
|
||||
value: cdata,
|
||||
};
|
||||
pushToContent(node);
|
||||
};
|
||||
|
||||
sax.onopentag = (data) => {
|
||||
/** @type {import('./types.js').XastElement} */
|
||||
const element = {
|
||||
type: 'element',
|
||||
name: data.name,
|
||||
attributes: {},
|
||||
children: [],
|
||||
};
|
||||
for (const [name, attr] of Object.entries(data.attributes)) {
|
||||
element.attributes[name] = attr.value;
|
||||
}
|
||||
pushToContent(element);
|
||||
current = element;
|
||||
stack.push(element);
|
||||
};
|
||||
|
||||
sax.ontext = (text) => {
|
||||
if (current.type === 'element') {
|
||||
// prevent trimming of meaningful whitespace inside textual tags
|
||||
if (textElems.has(current.name)) {
|
||||
/** @type {import('./types.js').XastText} */
|
||||
const node = {
|
||||
type: 'text',
|
||||
value: text,
|
||||
};
|
||||
pushToContent(node);
|
||||
} else {
|
||||
const value = text.trim();
|
||||
|
||||
if (value !== '') {
|
||||
/** @type {import('./types.js').XastText} */
|
||||
const node = {
|
||||
type: 'text',
|
||||
value,
|
||||
};
|
||||
pushToContent(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sax.onclosetag = () => {
|
||||
stack.pop();
|
||||
current = stack[stack.length - 1];
|
||||
};
|
||||
|
||||
sax.onerror = (e) => {
|
||||
const reason = e.message.split('\n')[0];
|
||||
const error = new SvgoParserError(
|
||||
reason,
|
||||
sax.line + 1,
|
||||
sax.column,
|
||||
data,
|
||||
from,
|
||||
);
|
||||
if (e.message.indexOf('Unexpected end') === -1) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
sax.write(data).close();
|
||||
return root;
|
||||
};
|
||||
362
Frontend-Learner/node_modules/svgo/lib/path.js
generated
vendored
Normal file
362
Frontend-Learner/node_modules/svgo/lib/path.js
generated
vendored
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/**
|
||||
* @fileoverview 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,
|
||||
m: 2,
|
||||
Z: 0,
|
||||
z: 0,
|
||||
L: 2,
|
||||
l: 2,
|
||||
H: 1,
|
||||
h: 1,
|
||||
V: 1,
|
||||
v: 1,
|
||||
C: 6,
|
||||
c: 6,
|
||||
S: 4,
|
||||
s: 4,
|
||||
Q: 4,
|
||||
q: 4,
|
||||
T: 2,
|
||||
t: 2,
|
||||
A: 7,
|
||||
a: 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} c
|
||||
* @returns {c is import('./types.js').PathDataCommand}
|
||||
*/
|
||||
const isCommand = (c) => {
|
||||
return c in argsCountPerCommand;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} c
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isWhiteSpace = (c) => {
|
||||
return c === ' ' || c === '\t' || c === '\r' || c === '\n';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} c
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isDigit = (c) => {
|
||||
const codePoint = c.codePointAt(0);
|
||||
if (codePoint == null) {
|
||||
return false;
|
||||
}
|
||||
return 48 <= codePoint && codePoint <= 57;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {number} cursor
|
||||
* @returns {[number, ?number]}
|
||||
*/
|
||||
const readNumber = (string, cursor) => {
|
||||
let i = cursor;
|
||||
let value = '';
|
||||
/** @type {ReadNumberState} */
|
||||
let state = 'none';
|
||||
for (; i < string.length; i += 1) {
|
||||
const c = string[i];
|
||||
if (c === '+' || c === '-') {
|
||||
if (state === 'none') {
|
||||
state = 'sign';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
if (state === 'e') {
|
||||
state = 'exponent_sign';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isDigit(c)) {
|
||||
if (state === 'none' || state === 'sign' || state === 'whole') {
|
||||
state = 'whole';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
if (state === 'decimal_point' || state === 'decimal') {
|
||||
state = 'decimal';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
if (state === 'e' || state === 'exponent_sign' || state === 'exponent') {
|
||||
state = 'exponent';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c === '.') {
|
||||
if (state === 'none' || state === 'sign' || state === 'whole') {
|
||||
state = 'decimal_point';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c === 'E' || c == 'e') {
|
||||
if (
|
||||
state === 'whole' ||
|
||||
state === 'decimal_point' ||
|
||||
state === 'decimal'
|
||||
) {
|
||||
state = 'e';
|
||||
value += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
const number = Number.parseFloat(value);
|
||||
if (Number.isNaN(number)) {
|
||||
return [cursor, null];
|
||||
} else {
|
||||
// step back to delegate iteration to parent loop
|
||||
return [i - 1, number];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {import('./types.js').PathDataItem[]}
|
||||
*/
|
||||
export const parsePathData = (string) => {
|
||||
/** @type {import('./types.js').PathDataItem[]} */
|
||||
const pathData = [];
|
||||
/** @type {?import('./types.js').PathDataCommand} */
|
||||
let command = null;
|
||||
let args = /** @type {number[]} */ ([]);
|
||||
let argsCount = 0;
|
||||
let canHaveComma = false;
|
||||
let hadComma = false;
|
||||
for (let i = 0; i < string.length; i += 1) {
|
||||
const c = string.charAt(i);
|
||||
if (isWhiteSpace(c)) {
|
||||
continue;
|
||||
}
|
||||
// allow comma only between arguments
|
||||
if (canHaveComma && c === ',') {
|
||||
if (hadComma) {
|
||||
break;
|
||||
}
|
||||
hadComma = true;
|
||||
continue;
|
||||
}
|
||||
if (isCommand(c)) {
|
||||
if (hadComma) {
|
||||
return pathData;
|
||||
}
|
||||
if (command == null) {
|
||||
// moveto should be leading command
|
||||
if (c !== 'M' && c !== 'm') {
|
||||
return pathData;
|
||||
}
|
||||
} else if (args.length !== 0) {
|
||||
// stop if previous command arguments are not flushed
|
||||
return pathData;
|
||||
}
|
||||
command = c;
|
||||
args = [];
|
||||
argsCount = argsCountPerCommand[command];
|
||||
canHaveComma = false;
|
||||
// flush command without arguments
|
||||
if (argsCount === 0) {
|
||||
pathData.push({ command, args });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// avoid parsing arguments if no command detected
|
||||
if (command == null) {
|
||||
return pathData;
|
||||
}
|
||||
// read next argument
|
||||
let newCursor = i;
|
||||
let number = null;
|
||||
if (command === 'A' || command === 'a') {
|
||||
const position = args.length;
|
||||
if (position === 0 || position === 1) {
|
||||
// allow only positive number without sign as first two arguments
|
||||
if (c !== '+' && c !== '-') {
|
||||
[newCursor, number] = readNumber(string, i);
|
||||
}
|
||||
}
|
||||
if (position === 2 || position === 5 || position === 6) {
|
||||
[newCursor, number] = readNumber(string, i);
|
||||
}
|
||||
if (position === 3 || position === 4) {
|
||||
// read flags
|
||||
if (c === '0') {
|
||||
number = 0;
|
||||
}
|
||||
if (c === '1') {
|
||||
number = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[newCursor, number] = readNumber(string, i);
|
||||
}
|
||||
if (number == null) {
|
||||
return pathData;
|
||||
}
|
||||
args.push(number);
|
||||
canHaveComma = true;
|
||||
hadComma = false;
|
||||
i = newCursor;
|
||||
// flush arguments when necessary count is reached
|
||||
if (args.length === argsCount) {
|
||||
pathData.push({ command, args });
|
||||
// subsequent moveto coordinates are treated as implicit lineto commands
|
||||
if (command === 'M') {
|
||||
command = 'L';
|
||||
}
|
||||
if (command === 'm') {
|
||||
command = 'l';
|
||||
}
|
||||
args = [];
|
||||
}
|
||||
}
|
||||
return pathData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} number
|
||||
* @param {number=} precision
|
||||
* @returns {{ roundedStr: string, rounded: number }}
|
||||
*/
|
||||
const roundAndStringify = (number, precision) => {
|
||||
if (precision != null) {
|
||||
number = toFixed(number, precision);
|
||||
}
|
||||
|
||||
return {
|
||||
roundedStr: removeLeadingZero(number),
|
||||
rounded: number,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Elliptical arc large-arc and sweep flags are rendered with spaces
|
||||
* because many non-browser environments are not able to parse such paths
|
||||
*
|
||||
* @param {string} command
|
||||
* @param {ReadonlyArray<number>} args
|
||||
* @param {number=} precision
|
||||
* @param {boolean=} disableSpaceAfterFlags
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyArgs = (command, args, precision, disableSpaceAfterFlags) => {
|
||||
let result = '';
|
||||
let previous;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const { roundedStr, rounded } = roundAndStringify(args[i], precision);
|
||||
if (
|
||||
disableSpaceAfterFlags &&
|
||||
(command === 'A' || command === 'a') &&
|
||||
// consider combined arcs
|
||||
(i % 7 === 4 || i % 7 === 5)
|
||||
) {
|
||||
result += roundedStr;
|
||||
} else if (i === 0 || rounded < 0) {
|
||||
// avoid space before first and negative numbers
|
||||
result += roundedStr;
|
||||
} else if (!Number.isInteger(previous) && !isDigit(roundedStr[0])) {
|
||||
// remove space before decimal with zero whole
|
||||
// only when previous number is also decimal
|
||||
result += roundedStr;
|
||||
} else {
|
||||
result += ` ${roundedStr}`;
|
||||
}
|
||||
previous = rounded;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {StringifyPathDataOptions} options
|
||||
* @returns {string}
|
||||
*/
|
||||
export const stringifyPathData = ({
|
||||
pathData,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
}) => {
|
||||
if (pathData.length === 1) {
|
||||
const { command, args } = pathData[0];
|
||||
return (
|
||||
command + stringifyArgs(command, args, precision, disableSpaceAfterFlags)
|
||||
);
|
||||
}
|
||||
|
||||
let result = '';
|
||||
let prev = { ...pathData[0] };
|
||||
|
||||
// match leading moveto with following lineto
|
||||
if (pathData[1].command === 'L') {
|
||||
prev.command = 'M';
|
||||
} else if (pathData[1].command === 'l') {
|
||||
prev.command = 'm';
|
||||
}
|
||||
|
||||
for (let i = 1; i < pathData.length; i++) {
|
||||
const { command, args } = pathData[i];
|
||||
if (
|
||||
(prev.command === command &&
|
||||
prev.command !== 'M' &&
|
||||
prev.command !== 'm') ||
|
||||
// combine matching moveto and lineto sequences
|
||||
(prev.command === 'M' && command === 'L') ||
|
||||
(prev.command === 'm' && command === 'l')
|
||||
) {
|
||||
prev.args = [...prev.args, ...args];
|
||||
if (i === pathData.length - 1) {
|
||||
result +=
|
||||
prev.command +
|
||||
stringifyArgs(
|
||||
prev.command,
|
||||
prev.args,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
result +=
|
||||
prev.command +
|
||||
stringifyArgs(
|
||||
prev.command,
|
||||
prev.args,
|
||||
precision,
|
||||
disableSpaceAfterFlags,
|
||||
);
|
||||
|
||||
if (i === pathData.length - 1) {
|
||||
result +=
|
||||
command +
|
||||
stringifyArgs(command, args, precision, disableSpaceAfterFlags);
|
||||
} else {
|
||||
prev = { command, args };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
298
Frontend-Learner/node_modules/svgo/lib/stringifier.js
generated
vendored
Normal file
298
Frontend-Learner/node_modules/svgo/lib/stringifier.js
generated
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
import { textElems } from '../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef {Required<import('./types.js').StringifyOptions>} Options
|
||||
*
|
||||
* @typedef State
|
||||
* @property {string} indent
|
||||
* @property {?import('./types.js').XastElement} textContext
|
||||
* @property {number} indentLevel
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} char
|
||||
* @returns {string}
|
||||
*/
|
||||
const encodeEntity = (char) => {
|
||||
return entities[char];
|
||||
};
|
||||
|
||||
/** @type {Options} */
|
||||
const defaults = {
|
||||
doctypeStart: '<!DOCTYPE',
|
||||
doctypeEnd: '>',
|
||||
procInstStart: '<?',
|
||||
procInstEnd: '?>',
|
||||
tagOpenStart: '<',
|
||||
tagOpenEnd: '>',
|
||||
tagCloseStart: '</',
|
||||
tagCloseEnd: '>',
|
||||
tagShortStart: '<',
|
||||
tagShortEnd: '/>',
|
||||
attrStart: '="',
|
||||
attrEnd: '"',
|
||||
commentStart: '<!--',
|
||||
commentEnd: '-->',
|
||||
cdataStart: '<![CDATA[',
|
||||
cdataEnd: ']]>',
|
||||
textStart: '',
|
||||
textEnd: '',
|
||||
indent: 4,
|
||||
regEntities: /[&'"<>]/g,
|
||||
regValEntities: /[&"<>]/g,
|
||||
encodeEntity,
|
||||
pretty: false,
|
||||
useShortTags: true,
|
||||
eol: 'lf',
|
||||
finalNewline: false,
|
||||
};
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const entities = {
|
||||
'&': '&',
|
||||
"'": ''',
|
||||
'"': '"',
|
||||
'>': '>',
|
||||
'<': '<',
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts XAST to SVG string.
|
||||
*
|
||||
* @param {import('./types.js').XastRoot} data
|
||||
* @param {import('./types.js').StringifyOptions=} userOptions
|
||||
* @returns {string}
|
||||
*/
|
||||
export const stringifySvg = (data, userOptions = {}) => {
|
||||
/** @type {Options} */
|
||||
const config = { ...defaults, ...userOptions };
|
||||
const indent = config.indent;
|
||||
let newIndent = ' ';
|
||||
if (typeof indent === 'number' && Number.isNaN(indent) === false) {
|
||||
newIndent = indent < 0 ? '\t' : ' '.repeat(indent);
|
||||
} else if (typeof indent === 'string') {
|
||||
newIndent = indent;
|
||||
}
|
||||
/** @type {State} */
|
||||
const state = {
|
||||
indent: newIndent,
|
||||
textContext: null,
|
||||
indentLevel: 0,
|
||||
};
|
||||
const eol = config.eol === 'crlf' ? '\r\n' : '\n';
|
||||
if (config.pretty) {
|
||||
config.doctypeEnd += eol;
|
||||
config.procInstEnd += eol;
|
||||
config.commentEnd += eol;
|
||||
config.cdataEnd += eol;
|
||||
config.tagShortEnd += eol;
|
||||
config.tagOpenEnd += eol;
|
||||
config.tagCloseEnd += eol;
|
||||
config.textEnd += eol;
|
||||
}
|
||||
let svg = stringifyNode(data, config, state);
|
||||
if (config.finalNewline && svg.length > 0 && !svg.endsWith('\n')) {
|
||||
svg += eol;
|
||||
}
|
||||
return svg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const createIndent = (config, state) => {
|
||||
let indent = '';
|
||||
if (config.pretty && state.textContext == null) {
|
||||
indent = state.indent.repeat(state.indentLevel - 1);
|
||||
}
|
||||
return indent;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastDoctype} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyDoctype = (node, config) => {
|
||||
return config.doctypeStart + node.data.doctype + config.doctypeEnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastInstruction} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyInstruction = (node, config) => {
|
||||
return (
|
||||
config.procInstStart + node.name + ' ' + node.value + config.procInstEnd
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastComment} node
|
||||
* @param {Options} config
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyComment = (node, config) => {
|
||||
return config.commentStart + node.value + config.commentEnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastCdata} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyCdata = (node, config, state) => {
|
||||
return (
|
||||
createIndent(config, state) +
|
||||
config.cdataStart +
|
||||
node.value +
|
||||
config.cdataEnd
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyElement = (node, config, state) => {
|
||||
// empty element and short tag
|
||||
if (node.children.length === 0) {
|
||||
if (config.useShortTags) {
|
||||
return (
|
||||
createIndent(config, state) +
|
||||
config.tagShortStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
config.tagShortEnd
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
createIndent(config, state) +
|
||||
config.tagShortStart +
|
||||
node.name +
|
||||
stringifyAttributes(node, config) +
|
||||
config.tagOpenEnd +
|
||||
config.tagCloseStart +
|
||||
node.name +
|
||||
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
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @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)) {
|
||||
attrs += ' ' + name;
|
||||
|
||||
if (value !== undefined) {
|
||||
const encodedValue = value
|
||||
.toString()
|
||||
.replace(config.regValEntities, config.encodeEntity);
|
||||
attrs += config.attrStart + encodedValue + config.attrEnd;
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastText} node
|
||||
* @param {Options} config
|
||||
* @param {State} state
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringifyText = (node, config, state) => {
|
||||
return (
|
||||
createIndent(config, state) +
|
||||
config.textStart +
|
||||
node.value.replace(config.regEntities, config.encodeEntity) +
|
||||
(state.textContext ? '' : config.textEnd)
|
||||
);
|
||||
};
|
||||
324
Frontend-Learner/node_modules/svgo/lib/style.js
generated
vendored
Normal file
324
Frontend-Learner/node_modules/svgo/lib/style.js
generated
vendored
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
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,
|
||||
} from '../plugins/_collections.js';
|
||||
|
||||
const csstreeWalkSkip = csstree.walk.skip;
|
||||
|
||||
/**
|
||||
* @param {import('css-tree').Rule} ruleNode
|
||||
* @param {boolean} dynamic
|
||||
* @returns {import('./types.js').StylesheetRule[]}
|
||||
*/
|
||||
const parseRule = (ruleNode, dynamic) => {
|
||||
/** @type {import('./types.js').StylesheetDeclaration[]} */
|
||||
const declarations = [];
|
||||
// collect declarations
|
||||
ruleNode.block.children.forEach((cssNode) => {
|
||||
if (cssNode.type === 'Declaration') {
|
||||
declarations.push({
|
||||
name: cssNode.property,
|
||||
value: csstree.generate(cssNode.value),
|
||||
important: cssNode.important === true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** @type {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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} css
|
||||
* @param {boolean} dynamic
|
||||
* @returns {import('./types.js').StylesheetRule[]}
|
||||
*/
|
||||
const parseStylesheet = (css, dynamic) => {
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
const ast = csstree.parse(css, {
|
||||
parseValue: false,
|
||||
parseAtrulePrelude: false,
|
||||
});
|
||||
csstree.walk(ast, (cssNode) => {
|
||||
if (cssNode.type === 'Rule') {
|
||||
rules.push(...parseRule(cssNode, dynamic || false));
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
if (cssNode.type === 'Atrule') {
|
||||
if (
|
||||
[
|
||||
'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));
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
});
|
||||
return csstreeWalkSkip;
|
||||
}
|
||||
});
|
||||
return rules;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} css
|
||||
* @returns {import('./types.js').StylesheetDeclaration[]}
|
||||
*/
|
||||
const parseStyleDeclarations = (css) => {
|
||||
/** @type {import('./types.js').StylesheetDeclaration[]} */
|
||||
const declarations = [];
|
||||
const ast = csstree.parse(css, {
|
||||
context: 'declarationList',
|
||||
parseValue: false,
|
||||
});
|
||||
csstree.walk(ast, (cssNode) => {
|
||||
if (cssNode.type === 'Declaration') {
|
||||
declarations.push({
|
||||
name: cssNode.property,
|
||||
value: csstree.generate(cssNode.value),
|
||||
important: cssNode.important === true,
|
||||
});
|
||||
}
|
||||
});
|
||||
return declarations;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {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, 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.has(name)) {
|
||||
computedStyle[name] = { type: 'static', inherited: false, value };
|
||||
importantStyles.set(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
// collect matching rules
|
||||
for (const { selector, declarations, dynamic } of stylesheet.rules) {
|
||||
if (matches(node, selector, parents)) {
|
||||
for (const { name, value, important } of declarations) {
|
||||
const computed = computedStyle[name];
|
||||
if (computed && computed.type === 'dynamic') {
|
||||
continue;
|
||||
}
|
||||
if (dynamic) {
|
||||
computedStyle[name] = { type: 'dynamic', inherited: false };
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
computed == null ||
|
||||
important === true ||
|
||||
importantStyles.get(name) === false
|
||||
) {
|
||||
computedStyle[name] = { type: 'static', inherited: false, value };
|
||||
importantStyles.set(name, important);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collect inline styles
|
||||
const styleDeclarations =
|
||||
node.attributes.style == null
|
||||
? []
|
||||
: parseStyleDeclarations(node.attributes.style);
|
||||
for (const { name, value, important } of styleDeclarations) {
|
||||
const computed = computedStyle[name];
|
||||
if (computed && computed.type === 'dynamic') {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
computed == null ||
|
||||
important === true ||
|
||||
importantStyles.get(name) === false
|
||||
) {
|
||||
computedStyle[name] = { type: 'static', inherited: false, value };
|
||||
importantStyles.set(name, important);
|
||||
}
|
||||
}
|
||||
|
||||
return computedStyle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares selector specificities.
|
||||
* Derived from https://github.com/keeganstreet/specificity/blob/8757133ddd2ed0163f120900047ff0f92760b536/specificity.js#L207
|
||||
*
|
||||
* @param {import('./types.js').Specificity} a
|
||||
* @param {import('./types.js').Specificity} b
|
||||
* @returns {number}
|
||||
*/
|
||||
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]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastRoot} root
|
||||
* @returns {import('./types.js').Stylesheet}
|
||||
*/
|
||||
export const collectStylesheet = (root) => {
|
||||
/** @type {import('./types.js').StylesheetRule[]} */
|
||||
const rules = [];
|
||||
/** @type {Map<import('./types.js').XastElement, import('./types.js').XastParent>} */
|
||||
const parents = new Map();
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
parents.set(node, parentNode);
|
||||
|
||||
if (node.name !== 'style') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
node.attributes.type == null ||
|
||||
node.attributes.type === '' ||
|
||||
node.attributes.type === 'text/css'
|
||||
) {
|
||||
const dynamic =
|
||||
node.attributes.media != null && node.attributes.media !== 'all';
|
||||
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'text' || child.type === 'cdata') {
|
||||
rules.push(...parseStylesheet(child.value, dynamic));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
// sort by selectors specificity
|
||||
rules.sort((a, b) => compareSpecificity(a.specificity, b.specificity));
|
||||
return { rules, parents };
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').Stylesheet} stylesheet
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @returns {import('./types.js').ComputedStyles}
|
||||
*/
|
||||
export const computeStyle = (stylesheet, node) => {
|
||||
const { parents } = stylesheet;
|
||||
const computedStyles = computeOwnStyle(stylesheet, node, parents);
|
||||
let parent = parents.get(node);
|
||||
while (parent != null && parent.type !== 'root') {
|
||||
const inheritedStyles = computeOwnStyle(stylesheet, parent, parents);
|
||||
for (const [name, computed] of Object.entries(inheritedStyles)) {
|
||||
if (
|
||||
computedStyles[name] == null &&
|
||||
inheritableAttrs.has(name) &&
|
||||
!presentationNonInheritableGroupAttrs.has(name)
|
||||
) {
|
||||
computedStyles[name] = { ...computed, inherited: true };
|
||||
}
|
||||
}
|
||||
parent = parents.get(parent);
|
||||
}
|
||||
return computedStyles;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
98
Frontend-Learner/node_modules/svgo/lib/svgo-node.js
generated
vendored
Normal file
98
Frontend-Learner/node_modules/svgo/lib/svgo-node.js
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
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) => {
|
||||
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.stat(file);
|
||||
return stats.isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
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 importConfig(configFile);
|
||||
} else {
|
||||
return importConfig(path.join(cwd, configFile));
|
||||
}
|
||||
}
|
||||
let dir = cwd;
|
||||
|
||||
while (true) {
|
||||
const js = path.join(dir, 'svgo.config.js');
|
||||
if (await isFile(js)) {
|
||||
return importConfig(js);
|
||||
}
|
||||
const mjs = path.join(dir, 'svgo.config.mjs');
|
||||
if (await isFile(mjs)) {
|
||||
return importConfig(mjs);
|
||||
}
|
||||
const cjs = path.join(dir, 'svgo.config.cjs');
|
||||
if (await isFile(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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 svgo.optimize(input, {
|
||||
...config,
|
||||
js2svg: {
|
||||
// platform specific default for end of line
|
||||
eol: os.EOL === '\r\n' ? 'crlf' : 'lf',
|
||||
...config.js2svg,
|
||||
},
|
||||
});
|
||||
};
|
||||
143
Frontend-Learner/node_modules/svgo/lib/svgo.js
generated
vendored
Normal file
143
Frontend-Learner/node_modules/svgo/lib/svgo.js
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
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 pluginsMap = new Map();
|
||||
for (const plugin of builtinPlugins) {
|
||||
pluginsMap.set(plugin.name, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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');
|
||||
}
|
||||
|
||||
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 = {};
|
||||
}
|
||||
if (typeof config !== 'object') {
|
||||
throw Error('Config should be an object');
|
||||
}
|
||||
const maxPassCount = config.multipass ? 10 : 1;
|
||||
let prevResultSize = Number.POSITIVE_INFINITY;
|
||||
let output = '';
|
||||
const info = {};
|
||||
if (config.path != null) {
|
||||
info.path = config.path;
|
||||
}
|
||||
for (let i = 0; i < maxPassCount; i += 1) {
|
||||
info.multipassCount = i;
|
||||
const ast = parseSvg(input, config.path);
|
||||
const plugins = config.plugins || ['preset-default'];
|
||||
if (!Array.isArray(plugins)) {
|
||||
throw Error(
|
||||
'malformed config, `plugins` property must be an array.\nSee more info here: https://github.com/svg/svgo#configuration',
|
||||
);
|
||||
}
|
||||
const resolvedPlugins = plugins
|
||||
.filter((plugin) => plugin != null)
|
||||
.map(resolvePluginConfig);
|
||||
|
||||
if (resolvedPlugins.length < plugins.length) {
|
||||
console.warn(
|
||||
'Warning: plugins list includes null or undefined elements, these will be ignored.',
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {import('./types.js').Config} */
|
||||
const globalOverrides = {};
|
||||
if (config.floatPrecision != null) {
|
||||
globalOverrides.floatPrecision = config.floatPrecision;
|
||||
}
|
||||
invokePlugins(ast, info, resolvedPlugins, null, globalOverrides);
|
||||
output = stringifySvg(ast, config.js2svg);
|
||||
if (output.length < prevResultSize) {
|
||||
input = output;
|
||||
prevResultSize = output.length;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (config.datauri) {
|
||||
output = encodeSVGDatauri(output, config.datauri);
|
||||
}
|
||||
return {
|
||||
data: output,
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
VERSION,
|
||||
builtinPlugins,
|
||||
mapNodesToParents,
|
||||
querySelector,
|
||||
querySelectorAll,
|
||||
_collections,
|
||||
};
|
||||
533
Frontend-Learner/node_modules/svgo/lib/svgo/coa.js
generated
vendored
Normal file
533
Frontend-Learner/node_modules/svgo/lib/svgo/coa.js
generated
vendored
Normal file
|
|
@ -0,0 +1,533 @@
|
|||
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 __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} filePath
|
||||
*/
|
||||
export function checkIsDir(filePath) {
|
||||
try {
|
||||
return fs.lstatSync(filePath).isDirectory();
|
||||
} catch {
|
||||
return filePath.endsWith(path.sep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('commander').Command} program
|
||||
*/
|
||||
export default function makeProgram(program) {
|
||||
program
|
||||
.name(PKG.name)
|
||||
.description(PKG.description)
|
||||
.version(PKG.version, '-v, --version')
|
||||
.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',
|
||||
)
|
||||
.option(
|
||||
'-o, --output <OUTPUT...>',
|
||||
'Output file or folder (by default the same as the input), "-" for STDOUT',
|
||||
)
|
||||
.option(
|
||||
'-p, --precision <INTEGER>',
|
||||
'Set number of digits in the fractional part, overrides plugins params',
|
||||
)
|
||||
.option(
|
||||
'--config <CONFIG>',
|
||||
'Custom config file, only .js, .mjs, and .cjs is supported',
|
||||
)
|
||||
.option(
|
||||
'--datauri <FORMAT>',
|
||||
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)',
|
||||
)
|
||||
.option(
|
||||
'--multipass',
|
||||
'Pass over SVGs multiple times to ensure all optimizations are applied',
|
||||
)
|
||||
.option('--pretty', 'Make SVG pretty printed')
|
||||
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
|
||||
.option(
|
||||
'--eol <EOL>',
|
||||
'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.',
|
||||
)
|
||||
.option('--final-newline', 'Ensure SVG ends with a line break')
|
||||
.option(
|
||||
'-r, --recursive',
|
||||
"Use with '--folder'. Optimizes *.svg files in folders recursively.",
|
||||
)
|
||||
.option(
|
||||
'--exclude <PATTERN...>',
|
||||
"Use with '--folder'. Exclude files matching regular expression pattern.",
|
||||
)
|
||||
.option(
|
||||
'-q, --quiet',
|
||||
'Only output error messages, not regular status messages',
|
||||
)
|
||||
.option('--show-plugins', 'Show available plugins and exit')
|
||||
// used by picocolors internally
|
||||
.option('--no-color', 'Output plain text without color')
|
||||
.action(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<string>} args
|
||||
* @param {any} opts
|
||||
* @param {import('commander').Command} command
|
||||
* @returns
|
||||
*/
|
||||
async function action(args, opts, command) {
|
||||
const input = opts.input || args;
|
||||
let output = opts.output;
|
||||
/** @type {any} */
|
||||
let config = {};
|
||||
|
||||
if (opts.datauri != null) {
|
||||
if (
|
||||
opts.datauri !== 'base64' &&
|
||||
opts.datauri !== 'enc' &&
|
||||
opts.datauri !== 'unenc'
|
||||
) {
|
||||
console.error(
|
||||
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.indent != null) {
|
||||
const number = Number.parseInt(opts.indent, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '--indent' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
opts.indent = number;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.eol != null && opts.eol !== 'lf' && opts.eol !== 'crlf') {
|
||||
console.error(
|
||||
"error: option '--eol' must have one of the following values: 'lf' or 'crlf'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// --show-plugins
|
||||
if (opts.showPlugins) {
|
||||
showAvailablePlugins();
|
||||
return;
|
||||
}
|
||||
|
||||
// w/o anything
|
||||
if (
|
||||
(input.length === 0 || input[0] === '-') &&
|
||||
!opts.string &&
|
||||
!opts.stdin &&
|
||||
!opts.folder &&
|
||||
process.stdin.isTTY === true
|
||||
) {
|
||||
return command.help();
|
||||
}
|
||||
|
||||
if (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.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --config
|
||||
const loadedConfig = await loadConfig(opts.config);
|
||||
if (loadedConfig != null) {
|
||||
config = loadedConfig;
|
||||
}
|
||||
|
||||
// --quiet
|
||||
if (opts.quiet) {
|
||||
config.quiet = opts.quiet;
|
||||
}
|
||||
|
||||
// --recursive
|
||||
if (opts.recursive) {
|
||||
config.recursive = opts.recursive;
|
||||
}
|
||||
|
||||
// --exclude
|
||||
config.exclude = opts.exclude
|
||||
? opts.exclude.map((/** @type {string} */ pattern) => RegExp(pattern))
|
||||
: [];
|
||||
|
||||
// --precision
|
||||
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 {
|
||||
config.floatPrecision = Math.min(Math.max(0, number), 20);
|
||||
}
|
||||
}
|
||||
|
||||
// --multipass
|
||||
if (opts.multipass) {
|
||||
config.multipass = true;
|
||||
}
|
||||
|
||||
// --pretty
|
||||
if (opts.pretty) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.pretty = true;
|
||||
if (opts.indent != null) {
|
||||
config.js2svg.indent = opts.indent;
|
||||
}
|
||||
}
|
||||
|
||||
// --eol
|
||||
if (opts.eol) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.eol = opts.eol;
|
||||
}
|
||||
|
||||
// --final-newline
|
||||
if (opts.finalNewline) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.finalNewline = true;
|
||||
}
|
||||
|
||||
// --output
|
||||
if (output) {
|
||||
if (input.length && input[0] != '-') {
|
||||
if (output.length == 1 && checkIsDir(output[0])) {
|
||||
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]));
|
||||
}
|
||||
} else if (output.length < input.length) {
|
||||
output = output.concat(input.slice(output.length));
|
||||
}
|
||||
}
|
||||
} else if (input.length) {
|
||||
output = input;
|
||||
} else if (opts.string) {
|
||||
output = '-';
|
||||
}
|
||||
|
||||
if (opts.datauri) {
|
||||
config.datauri = opts.datauri;
|
||||
}
|
||||
|
||||
// --folder
|
||||
if (opts.folder) {
|
||||
const outputFolder = (output && output[0]) || opts.folder;
|
||||
await optimizeFolder(config, opts.folder, outputFolder);
|
||||
}
|
||||
|
||||
// --input
|
||||
if (input.length !== 0) {
|
||||
// STDIN
|
||||
if (input[0] === '-') {
|
||||
return new Promise((resolve, reject) => {
|
||||
let data = '';
|
||||
const file = output[0];
|
||||
|
||||
process.stdin
|
||||
.on('data', (chunk) => (data += chunk))
|
||||
.once('end', () =>
|
||||
processSVGData(config, null, data, file).then(resolve, reject),
|
||||
);
|
||||
});
|
||||
// file
|
||||
} else {
|
||||
await Promise.all(
|
||||
input.map((/** @type {string} */ file, /** @type {number} */ n) =>
|
||||
optimizeFile(config, file, output[n]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// --string
|
||||
} else if (opts.string) {
|
||||
const data = decodeSVGDatauri(opts.string);
|
||||
|
||||
return processSVGData(config, null, data, output[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG files in a directory.
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function optimizeFolder(config, dir, output) {
|
||||
if (!config.quiet) {
|
||||
console.log(`Processing directory '${dir}':\n`);
|
||||
}
|
||||
return fs.promises
|
||||
.readdir(dir)
|
||||
.then((files) => processDirectory(config, dir, files, output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process given files, take only SVG.
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {ReadonlyArray<string>} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function processDirectory(config, dir, files, output) {
|
||||
// take only *.svg files, recursively if necessary
|
||||
const svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
|
||||
return svgFilesDescriptions.length
|
||||
? Promise.all(
|
||||
svgFilesDescriptions.map((fileDescription) =>
|
||||
optimizeFile(
|
||||
config,
|
||||
fileDescription.inputPath,
|
||||
fileDescription.outputPath,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Promise.reject(
|
||||
new Error(`No SVG files have been found in '${dir}' directory.`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SVG files descriptions.
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {ReadonlyArray<string>} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {any[]}
|
||||
*/
|
||||
function getFilesDescriptions(config, dir, files, output) {
|
||||
const filesInThisFolder = files
|
||||
.filter(
|
||||
(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),
|
||||
}));
|
||||
|
||||
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 {any} config options
|
||||
* @param {string} file
|
||||
* @param {string} output
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function optimizeFile(config, file, output) {
|
||||
return fs.promises.readFile(file, 'utf8').then(
|
||||
(data) => processSVGData(config, { path: file }, data, output, file),
|
||||
(error) => checkOptimizeFileError(config, file, output, error),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG data.
|
||||
*
|
||||
* @param {any} config options
|
||||
* @param {?{ path: string }} info
|
||||
* @param {string} data SVG content to optimize
|
||||
* @param {string} output where to write optimized file
|
||||
* @param {any=} input input file name (being used if output is a directory)
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
function processSVGData(config, info, data, output, input) {
|
||||
const startTime = Date.now();
|
||||
const prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
|
||||
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);
|
||||
}
|
||||
const resultFileSize = Buffer.byteLength(result.data, 'utf8');
|
||||
const processingTime = Date.now() - startTime;
|
||||
|
||||
return writeOutput(input, output, result.data).then(
|
||||
function () {
|
||||
if (!config.quiet && output != '-') {
|
||||
if (input) {
|
||||
console.log(`\n${path.basename(input)}:`);
|
||||
}
|
||||
printTimeInfo(processingTime);
|
||||
printProfitInfo(prevFileSize, resultFileSize);
|
||||
}
|
||||
},
|
||||
(error) =>
|
||||
Promise.reject(
|
||||
new Error(
|
||||
error.code === 'ENOTDIR'
|
||||
? `Error: output '${output}' is not a directory.`
|
||||
: error,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write result of an optimization.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output output file name. '-' for stdout
|
||||
* @param {string} data data to write
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function writeOutput(input, output, data) {
|
||||
if (output == '-') {
|
||||
process.stdout.write(data);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
await fs.promises.mkdir(path.dirname(output), { recursive: true });
|
||||
|
||||
return fs.promises
|
||||
.writeFile(output, data, 'utf8')
|
||||
.catch((error) => checkWriteFileError(input, output, data, error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write time taken to optimize.
|
||||
*
|
||||
* @param {number} time time in milliseconds.
|
||||
*/
|
||||
function printTimeInfo(time) {
|
||||
console.log(`Done in ${time} ms!`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write optimizing stats in a human-readable format.
|
||||
*
|
||||
* @param {number} inBytes size before optimization.
|
||||
* @param {number} outBytes size after optimization.
|
||||
*/
|
||||
function printProfitInfo(inBytes, outBytes) {
|
||||
const profitPercent = 100 - (outBytes * 100) / inBytes;
|
||||
/** @type {[string, Function]} */
|
||||
const ui = profitPercent < 0 ? ['+', colors.red] : ['-', colors.green];
|
||||
|
||||
console.log(
|
||||
Math.round((inBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
ui[0],
|
||||
ui[1](Math.abs(Math.round(profitPercent * 10) / 10) + '%'),
|
||||
'=',
|
||||
Math.round((outBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for errors, if it's a dir optimize the dir.
|
||||
*
|
||||
* @param {any} config
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @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}'.`),
|
||||
);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for saving file error. If the output is a dir, then write file there.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {string} data
|
||||
* @param {Error & { 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',
|
||||
);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Show list of available plugins with short description. */
|
||||
function showAvailablePlugins() {
|
||||
const list = builtinPlugins
|
||||
.map((plugin) => ` [ ${colors.green(plugin.name)} ] ${plugin.description}`)
|
||||
.join('\n');
|
||||
console.log('Currently available plugins:\n' + list);
|
||||
}
|
||||
143
Frontend-Learner/node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
Normal file
143
Frontend-Learner/node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
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) => {
|
||||
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;
|
||||
};
|
||||
|
||||
/** @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') {
|
||||
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;
|
||||
};
|
||||
|
||||
/** @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) {
|
||||
if (isTag(elem)) {
|
||||
if (test(elem)) {
|
||||
result.push(elem);
|
||||
}
|
||||
result.push(...findAll(test, getChildren(elem)));
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
if (test(elem)) {
|
||||
return elem;
|
||||
}
|
||||
const result = findOne(test, getChildren(elem));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
71
Frontend-Learner/node_modules/svgo/lib/svgo/plugins.js
generated
vendored
Normal file
71
Frontend-Learner/node_modules/svgo/lib/svgo/plugins.js
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { visit } from '../util/visit.js';
|
||||
|
||||
/**
|
||||
* Plugins engine.
|
||||
*
|
||||
* @module plugins
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
export const invokePlugins = (
|
||||
ast,
|
||||
info,
|
||||
plugins,
|
||||
overrides,
|
||||
globalOverrides,
|
||||
) => {
|
||||
for (const plugin of plugins) {
|
||||
const override = overrides?.[plugin.name];
|
||||
if (override === false) {
|
||||
continue;
|
||||
}
|
||||
const params = { ...plugin.params, ...globalOverrides, ...override };
|
||||
|
||||
const visitor = plugin.fn(ast, params, info);
|
||||
if (visitor != null) {
|
||||
visit(ast, visitor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @template {string} T
|
||||
* @param {{ name: T, plugins: ReadonlyArray<import('../types.js').BuiltinPlugin<string, any>> }} arg0
|
||||
* @returns {import('../types.js').BuiltinPluginOrPreset<T, any>}
|
||||
*/
|
||||
export const createPreset = ({ name, plugins }) => {
|
||||
return {
|
||||
name,
|
||||
isPreset: true,
|
||||
plugins: Object.freeze(plugins),
|
||||
fn: (ast, params, info) => {
|
||||
const { floatPrecision, overrides } = params;
|
||||
const globalOverrides = {};
|
||||
if (floatPrecision != null) {
|
||||
globalOverrides.floatPrecision = floatPrecision;
|
||||
}
|
||||
if (overrides) {
|
||||
const pluginNames = plugins.map(({ name }) => name);
|
||||
for (const pluginName of Object.keys(overrides)) {
|
||||
if (!pluginNames.includes(pluginName)) {
|
||||
console.warn(
|
||||
`You are trying to configure ${pluginName} which is not part of ${name}.\n` +
|
||||
`Try to put it before or after, for example\n\n` +
|
||||
`plugins: [\n` +
|
||||
` {\n` +
|
||||
` name: '${name}',\n` +
|
||||
` },\n` +
|
||||
` '${pluginName}'\n` +
|
||||
`]\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
invokePlugins(ast, info, plugins, overrides, globalOverrides);
|
||||
},
|
||||
};
|
||||
};
|
||||
241
Frontend-Learner/node_modules/svgo/lib/svgo/tools.js
generated
vendored
Normal file
241
Frontend-Learner/node_modules/svgo/lib/svgo/tools.js
generated
vendored
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
import { attrsGroups, referencesProps } from '../../plugins/_collections.js';
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {import('../types.js').DataUri=} type
|
||||
* @returns {string}
|
||||
*/
|
||||
export const encodeSVGDatauri = (str, type) => {
|
||||
let prefix = 'data:image/svg+xml';
|
||||
if (!type || type === 'base64') {
|
||||
// base64
|
||||
prefix += ';base64,';
|
||||
str = prefix + Buffer.from(str).toString('base64');
|
||||
} else if (type === 'enc') {
|
||||
// URI encoded
|
||||
str = prefix + ',' + encodeURIComponent(str);
|
||||
} else if (type === 'unenc') {
|
||||
// unencoded
|
||||
str = prefix + ',' + str;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode SVG Data URI string into plain SVG string.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export const decodeSVGDatauri = (str) => {
|
||||
const regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
const match = regexp.exec(str);
|
||||
|
||||
// plain string
|
||||
if (!match) {
|
||||
return str;
|
||||
}
|
||||
|
||||
const data = match[3];
|
||||
|
||||
if (match[2]) {
|
||||
// base64
|
||||
str = Buffer.from(data, 'base64').toString('utf8');
|
||||
} else if (data.charAt(0) === '%') {
|
||||
// URI encoded
|
||||
str = decodeURIComponent(data);
|
||||
} else if (data.charAt(0) === '<') {
|
||||
// unencoded
|
||||
str = data;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a row of numbers to an optimized string view.
|
||||
*
|
||||
* @example
|
||||
* [0, -1, .5, .5] → "0-1 .5.5"
|
||||
*
|
||||
* @param {ReadonlyArray<number>} data
|
||||
* @param {CleanupOutDataParams} params
|
||||
* @param {import('../types.js').PathDataCommand=} command
|
||||
* @returns {string}
|
||||
*/
|
||||
export const cleanupOutData = (data, params, command) => {
|
||||
let str = '';
|
||||
let delimiter;
|
||||
/** @type {number} */
|
||||
let prev;
|
||||
|
||||
data.forEach((item, i) => {
|
||||
// space delimiter by default
|
||||
delimiter = ' ';
|
||||
|
||||
// no extra space in front of first number
|
||||
if (i == 0) {
|
||||
delimiter = '';
|
||||
}
|
||||
|
||||
// no extra space after 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')) {
|
||||
const pos = i % 7;
|
||||
if (pos == 4 || pos == 5) {
|
||||
delimiter = '';
|
||||
}
|
||||
}
|
||||
|
||||
// remove floating-point numbers leading zeros
|
||||
// 0.5 → .5
|
||||
// -0.5 → -.5
|
||||
const itemStr = params.leadingZero
|
||||
? removeLeadingZero(item)
|
||||
: item.toString();
|
||||
|
||||
// no extra space in front of negative number or
|
||||
// in front of a floating number if a previous number is floating too
|
||||
if (
|
||||
params.negativeExtraSpace &&
|
||||
delimiter != '' &&
|
||||
(item < 0 || (itemStr.charAt(0) === '.' && prev % 1 !== 0))
|
||||
) {
|
||||
delimiter = '';
|
||||
}
|
||||
// save prev item value
|
||||
prev = item;
|
||||
str += delimiter + itemStr;
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove floating-point numbers leading zero.
|
||||
*
|
||||
* @param {number} value
|
||||
* @returns {string}
|
||||
* @example
|
||||
* 0.5 → .5
|
||||
* -0.5 → -.5
|
||||
*/
|
||||
export const removeLeadingZero = (value) => {
|
||||
const strValue = value.toString();
|
||||
|
||||
if (0 < value && value < 1 && strValue.startsWith('0')) {
|
||||
return strValue.slice(1);
|
||||
}
|
||||
|
||||
if (-1 < value && value < 0 && strValue[1] === '0') {
|
||||
return strValue[0] + strValue.slice(2);
|
||||
}
|
||||
|
||||
return strValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
1
Frontend-Learner/node_modules/svgo/lib/types.js
generated
vendored
Normal file
1
Frontend-Learner/node_modules/svgo/lib/types.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export default {};
|
||||
350
Frontend-Learner/node_modules/svgo/lib/types.ts
generated
vendored
Normal file
350
Frontend-Learner/node_modules/svgo/lib/types.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
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;
|
||||
data: {
|
||||
doctype: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type XastInstruction = {
|
||||
type: 'instruction';
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type XastComment = {
|
||||
type: 'comment';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type XastCdata = {
|
||||
type: 'cdata';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type XastText = {
|
||||
type: 'text';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type XastElement = {
|
||||
type: 'element';
|
||||
name: string;
|
||||
attributes: Record<string, string>;
|
||||
children: XastChild[];
|
||||
};
|
||||
|
||||
export type XastChild =
|
||||
| XastDoctype
|
||||
| XastInstruction
|
||||
| XastComment
|
||||
| XastCdata
|
||||
| XastText
|
||||
| XastElement;
|
||||
|
||||
export type XastRoot = {
|
||||
type: 'root';
|
||||
children: XastChild[];
|
||||
};
|
||||
|
||||
export type XastParent = XastRoot | XastElement;
|
||||
|
||||
export type XastNode = XastRoot | XastChild;
|
||||
|
||||
export type StringifyOptions = {
|
||||
doctypeStart?: string;
|
||||
doctypeEnd?: string;
|
||||
procInstStart?: string;
|
||||
procInstEnd?: string;
|
||||
tagOpenStart?: string;
|
||||
tagOpenEnd?: string;
|
||||
tagCloseStart?: string;
|
||||
tagCloseEnd?: string;
|
||||
tagShortStart?: string;
|
||||
tagShortEnd?: string;
|
||||
attrStart?: string;
|
||||
attrEnd?: string;
|
||||
commentStart?: string;
|
||||
commentEnd?: string;
|
||||
cdataStart?: string;
|
||||
cdataEnd?: string;
|
||||
textStart?: string;
|
||||
textEnd?: string;
|
||||
indent?: number | string;
|
||||
regEntities?: RegExp;
|
||||
regValEntities?: RegExp;
|
||||
encodeEntity?: (char: string) => string;
|
||||
pretty?: boolean;
|
||||
useShortTags?: boolean;
|
||||
eol?: 'lf' | 'crlf';
|
||||
finalNewline?: boolean;
|
||||
};
|
||||
|
||||
export type VisitorNode<Node> = {
|
||||
enter?: (node: Node, parentNode: XastParent) => void | symbol;
|
||||
exit?: (node: Node, parentNode: XastParent) => void;
|
||||
};
|
||||
|
||||
export type VisitorRoot = {
|
||||
enter?: (node: XastRoot, parentNode: null) => void;
|
||||
exit?: (node: XastRoot, parentNode: null) => void;
|
||||
};
|
||||
|
||||
export type Visitor = {
|
||||
doctype?: VisitorNode<XastDoctype>;
|
||||
instruction?: VisitorNode<XastInstruction>;
|
||||
comment?: VisitorNode<XastComment>;
|
||||
cdata?: VisitorNode<XastCdata>;
|
||||
text?: VisitorNode<XastText>;
|
||||
element?: VisitorNode<XastElement>;
|
||||
root?: VisitorRoot;
|
||||
};
|
||||
|
||||
export type PluginInfo = {
|
||||
path?: string;
|
||||
multipassCount: number;
|
||||
};
|
||||
|
||||
export type Plugin<P = null> = (
|
||||
root: XastRoot,
|
||||
params: P,
|
||||
info: PluginInfo,
|
||||
) => Visitor | null | void;
|
||||
|
||||
export type Specificity = [number, number, number];
|
||||
|
||||
export type StylesheetDeclaration = {
|
||||
name: string;
|
||||
value: string;
|
||||
important: boolean;
|
||||
};
|
||||
|
||||
export type StylesheetRule = {
|
||||
dynamic: boolean;
|
||||
selector: string;
|
||||
specificity: Specificity;
|
||||
declarations: StylesheetDeclaration[];
|
||||
};
|
||||
|
||||
export type Stylesheet = {
|
||||
rules: StylesheetRule[];
|
||||
parents: Map<XastElement, XastParent>;
|
||||
};
|
||||
|
||||
export type StaticStyle = {
|
||||
type: 'static';
|
||||
inherited: boolean;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type DynamicStyle = {
|
||||
type: 'dynamic';
|
||||
inherited: boolean;
|
||||
};
|
||||
|
||||
export type ComputedStyles = Record<string, StaticStyle | DynamicStyle>;
|
||||
|
||||
export type PathDataCommand =
|
||||
| 'M'
|
||||
| 'm'
|
||||
| 'Z'
|
||||
| 'z'
|
||||
| 'L'
|
||||
| 'l'
|
||||
| 'H'
|
||||
| 'h'
|
||||
| 'V'
|
||||
| 'v'
|
||||
| 'C'
|
||||
| 'c'
|
||||
| 'S'
|
||||
| 's'
|
||||
| 'Q'
|
||||
| 'q'
|
||||
| 'T'
|
||||
| 't'
|
||||
| 'A'
|
||||
| 'a';
|
||||
|
||||
export type PathDataItem = {
|
||||
command: PathDataCommand;
|
||||
args: number[];
|
||||
};
|
||||
|
||||
export type DataUri = 'base64' | 'enc' | 'unenc';
|
||||
|
||||
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
Frontend-Learner/node_modules/svgo/lib/util/map-nodes-to-parents.js
generated
vendored
Normal file
29
Frontend-Learner/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
Frontend-Learner/node_modules/svgo/lib/util/visit.js
generated
vendored
Normal file
36
Frontend-Learner/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
Frontend-Learner/node_modules/svgo/lib/version.js
generated
vendored
Normal file
7
Frontend-Learner/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';
|
||||
53
Frontend-Learner/node_modules/svgo/lib/xast.js
generated
vendored
Normal file
53
Frontend-Learner/node_modules/svgo/lib/xast.js
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { is, selectAll, selectOne } from 'css-select';
|
||||
import { createAdapter } from './svgo/css-select-adapter.js';
|
||||
|
||||
/**
|
||||
* @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),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
export const querySelector = (node, selector, parents) => {
|
||||
return selectOne(selector, node, createCssSelectOptions(node, parents));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastElement} node
|
||||
* @param {string} selector
|
||||
* @param {Map<import('./types.js').XastNode, import('./types.js').XastParent>=} parents
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const matches = (node, selector, parents) => {
|
||||
return is(node, selector, createCssSelectOptions(node, parents));
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('./types.js').XastChild} node
|
||||
* @param {import('./types.js').XastParent} parentNode
|
||||
*/
|
||||
export const detachNodeFromParent = (node, parentNode) => {
|
||||
// avoid splice to not break for loops
|
||||
parentNode.children = parentNode.children.filter((child) => child !== node);
|
||||
};
|
||||
22
Frontend-Learner/node_modules/svgo/node_modules/commander/LICENSE
generated
vendored
Normal file
22
Frontend-Learner/node_modules/svgo/node_modules/commander/LICENSE
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/Readme.md
generated
vendored
Normal file
1148
Frontend-Learner/node_modules/svgo/node_modules/commander/Readme.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
16
Frontend-Learner/node_modules/svgo/node_modules/commander/esm.mjs
generated
vendored
Normal file
16
Frontend-Learner/node_modules/svgo/node_modules/commander/esm.mjs
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/index.js
generated
vendored
Normal file
26
Frontend-Learner/node_modules/svgo/node_modules/commander/index.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/argument.js
generated
vendored
Normal file
145
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/argument.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/command.js
generated
vendored
Normal file
2179
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/command.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
43
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/error.js
generated
vendored
Normal file
43
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/error.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/help.js
generated
vendored
Normal file
462
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/help.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/option.js
generated
vendored
Normal file
329
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/option.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/suggestSimilar.js
generated
vendored
Normal file
100
Frontend-Learner/node_modules/svgo/node_modules/commander/lib/suggestSimilar.js
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/package-support.json
generated
vendored
Normal file
16
Frontend-Learner/node_modules/svgo/node_modules/commander/package-support.json
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "*",
|
||||
"target": {
|
||||
"node": "supported"
|
||||
},
|
||||
"response": {
|
||||
"type": "time-permitting"
|
||||
},
|
||||
"backing": {
|
||||
"npm-funding": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
80
Frontend-Learner/node_modules/svgo/node_modules/commander/package.json
generated
vendored
Normal file
80
Frontend-Learner/node_modules/svgo/node_modules/commander/package.json
generated
vendored
Normal 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
Frontend-Learner/node_modules/svgo/node_modules/commander/typings/esm.d.mts
generated
vendored
Normal file
3
Frontend-Learner/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
Frontend-Learner/node_modules/svgo/node_modules/commander/typings/index.d.ts
generated
vendored
Normal file
884
Frontend-Learner/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;
|
||||
141
Frontend-Learner/node_modules/svgo/package.json
generated
vendored
Normal file
141
Frontend-Learner/node_modules/svgo/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"packageManager": "yarn@3.8.7",
|
||||
"name": "svgo",
|
||||
"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://svgo.dev",
|
||||
"bugs": {
|
||||
"url": "https://github.com/svg/svgo/issues"
|
||||
},
|
||||
"author": {
|
||||
"name": "Kir Belevich",
|
||||
"email": "kir@belevi.ch",
|
||||
"url": "https://github.com/deepsweet"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Sergey Belov",
|
||||
"email": "peimei@ya.ru",
|
||||
"url": "https://github.com/arikon"
|
||||
},
|
||||
{
|
||||
"name": "Lev Solntsev",
|
||||
"email": "lev.sun@ya.ru",
|
||||
"url": "https://github.com/GreLI"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"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",
|
||||
"types",
|
||||
"!**/*.test.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"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": {
|
||||
"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": {
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
2455
Frontend-Learner/node_modules/svgo/plugins/_collections.js
generated
vendored
Normal file
2455
Frontend-Learner/node_modules/svgo/plugins/_collections.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
848
Frontend-Learner/node_modules/svgo/plugins/_path.js
generated
vendored
Normal file
848
Frontend-Learner/node_modules/svgo/plugins/_path.js
generated
vendored
Normal file
|
|
@ -0,0 +1,848 @@
|
|||
import { parsePathData, stringifyPathData } from '../lib/path.js';
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/** @type {[number, number]} */
|
||||
let prevCtrlPoint;
|
||||
|
||||
/**
|
||||
* Convert path string to JS representation.
|
||||
*
|
||||
* @param {import('../lib/types.js').XastElement} path
|
||||
* @returns {import('../lib/types.js').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) {
|
||||
pathData.push({ command, args });
|
||||
}
|
||||
// First moveto is actually absolute. Subsequent coordinates were separated above.
|
||||
if (pathData.length && pathData[0].command == 'm') {
|
||||
pathData[0].command = 'M';
|
||||
}
|
||||
// @ts-expect-error legacy
|
||||
path.pathJS = pathData;
|
||||
return pathData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert relative Path data to absolute.
|
||||
*
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} data
|
||||
* @returns {import('../lib/types.js').PathDataItem[]}
|
||||
*/
|
||||
const convertRelativeToAbsolute = (data) => {
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const newData = [];
|
||||
const start = [0, 0];
|
||||
const cursor = [0, 0];
|
||||
|
||||
for (let { command, args } of data) {
|
||||
args = args.slice();
|
||||
|
||||
// moveto (x y)
|
||||
if (command === 'm') {
|
||||
args[0] += cursor[0];
|
||||
args[1] += cursor[1];
|
||||
command = 'M';
|
||||
}
|
||||
if (command === 'M') {
|
||||
cursor[0] = args[0];
|
||||
cursor[1] = args[1];
|
||||
start[0] = cursor[0];
|
||||
start[1] = cursor[1];
|
||||
}
|
||||
|
||||
// horizontal lineto (x)
|
||||
if (command === 'h') {
|
||||
args[0] += cursor[0];
|
||||
command = 'H';
|
||||
}
|
||||
if (command === 'H') {
|
||||
cursor[0] = args[0];
|
||||
}
|
||||
|
||||
// vertical lineto (y)
|
||||
if (command === 'v') {
|
||||
args[0] += cursor[1];
|
||||
command = 'V';
|
||||
}
|
||||
if (command === 'V') {
|
||||
cursor[1] = args[0];
|
||||
}
|
||||
|
||||
// lineto (x y)
|
||||
if (command === 'l') {
|
||||
args[0] += cursor[0];
|
||||
args[1] += cursor[1];
|
||||
command = 'L';
|
||||
}
|
||||
if (command === 'L') {
|
||||
cursor[0] = args[0];
|
||||
cursor[1] = args[1];
|
||||
}
|
||||
|
||||
// curveto (x1 y1 x2 y2 x y)
|
||||
if (command === 'c') {
|
||||
args[0] += cursor[0];
|
||||
args[1] += cursor[1];
|
||||
args[2] += cursor[0];
|
||||
args[3] += cursor[1];
|
||||
args[4] += cursor[0];
|
||||
args[5] += cursor[1];
|
||||
command = 'C';
|
||||
}
|
||||
if (command === 'C') {
|
||||
cursor[0] = args[4];
|
||||
cursor[1] = args[5];
|
||||
}
|
||||
|
||||
// smooth curveto (x2 y2 x y)
|
||||
if (command === 's') {
|
||||
args[0] += cursor[0];
|
||||
args[1] += cursor[1];
|
||||
args[2] += cursor[0];
|
||||
args[3] += cursor[1];
|
||||
command = 'S';
|
||||
}
|
||||
if (command === 'S') {
|
||||
cursor[0] = args[2];
|
||||
cursor[1] = args[3];
|
||||
}
|
||||
|
||||
// quadratic Bézier curveto (x1 y1 x y)
|
||||
if (command === 'q') {
|
||||
args[0] += cursor[0];
|
||||
args[1] += cursor[1];
|
||||
args[2] += cursor[0];
|
||||
args[3] += cursor[1];
|
||||
command = 'Q';
|
||||
}
|
||||
if (command === 'Q') {
|
||||
cursor[0] = args[2];
|
||||
cursor[1] = args[3];
|
||||
}
|
||||
|
||||
// smooth quadratic Bézier curveto (x y)
|
||||
if (command === 't') {
|
||||
args[0] += cursor[0];
|
||||
args[1] += cursor[1];
|
||||
command = 'T';
|
||||
}
|
||||
if (command === 'T') {
|
||||
cursor[0] = args[0];
|
||||
cursor[1] = args[1];
|
||||
}
|
||||
|
||||
// elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
|
||||
if (command === 'a') {
|
||||
args[5] += cursor[0];
|
||||
args[6] += cursor[1];
|
||||
command = 'A';
|
||||
}
|
||||
if (command === 'A') {
|
||||
cursor[0] = args[5];
|
||||
cursor[1] = args[6];
|
||||
}
|
||||
|
||||
// closepath
|
||||
if (command === 'z' || command === 'Z') {
|
||||
cursor[0] = start[0];
|
||||
cursor[1] = start[1];
|
||||
command = 'z';
|
||||
}
|
||||
|
||||
newData.push({ command, args });
|
||||
}
|
||||
return newData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert path array to string.
|
||||
*
|
||||
* @param {import('../lib/types.js').XastElement} path
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} data
|
||||
* @param {Js2PathParams} params
|
||||
*/
|
||||
export const js2path = function (path, data, params) {
|
||||
// @ts-expect-error legacy
|
||||
path.pathJS = data;
|
||||
|
||||
const pathData = [];
|
||||
for (const item of data) {
|
||||
// remove moveto commands which are followed by moveto commands
|
||||
if (
|
||||
pathData.length !== 0 &&
|
||||
(item.command === 'M' || item.command === 'm')
|
||||
) {
|
||||
const last = pathData[pathData.length - 1];
|
||||
if (last.command === 'M' || last.command === 'm') {
|
||||
pathData.pop();
|
||||
}
|
||||
}
|
||||
pathData.push({
|
||||
command: item.command,
|
||||
args: item.args,
|
||||
});
|
||||
}
|
||||
|
||||
path.attributes.d = stringifyPathData({
|
||||
pathData,
|
||||
precision: params.floatPrecision,
|
||||
disableSpaceAfterFlags: params.noSpaceAfterFlags,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number[]} dest
|
||||
* @param {ReadonlyArray<number>} source
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function set(dest, source) {
|
||||
dest[0] = source[source.length - 2];
|
||||
dest[1] = source[source.length - 1];
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two paths have an intersection by checking convex hulls
|
||||
* collision using Gilbert-Johnson-Keerthi distance algorithm
|
||||
* https://web.archive.org/web/20180822200027/http://entropyinteractive.com/2011/04/gjk-algorithm/
|
||||
*
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} path1
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} path2
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const intersects = function (path1, path2) {
|
||||
// Collect points of every subpath.
|
||||
const points1 = gatherPoints(convertRelativeToAbsolute(path1));
|
||||
const points2 = gatherPoints(convertRelativeToAbsolute(path2));
|
||||
|
||||
// Axis-aligned bounding box check.
|
||||
if (
|
||||
points1.maxX <= points2.minX ||
|
||||
points2.maxX <= points1.minX ||
|
||||
points1.maxY <= points2.minY ||
|
||||
points2.maxY <= points1.minY ||
|
||||
points1.list.every((set1) => {
|
||||
return points2.list.every((set2) => {
|
||||
return (
|
||||
set1.list[set1.maxX][0] <= set2.list[set2.minX][0] ||
|
||||
set2.list[set2.maxX][0] <= set1.list[set1.minX][0] ||
|
||||
set1.list[set1.maxY][1] <= set2.list[set2.minY][1] ||
|
||||
set2.list[set2.maxY][1] <= set1.list[set1.minY][1]
|
||||
);
|
||||
});
|
||||
})
|
||||
) {
|
||||
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);
|
||||
const hullNest2 = points2.list.map(convexHull);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
return hullNest2.some(function (hull2) {
|
||||
if (hull2.list.length < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
while (true) {
|
||||
if (iterations-- == 0) {
|
||||
console.error(
|
||||
'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;
|
||||
}
|
||||
// process the simplex
|
||||
if (processSimplex(simplex, direction)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @param {Point} polygon
|
||||
* @param {ReadonlyArray<number>} direction
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function supportPoint(polygon, direction) {
|
||||
let index =
|
||||
direction[1] >= 0
|
||||
? direction[0] < 0
|
||||
? polygon.maxY
|
||||
: polygon.maxX
|
||||
: direction[0] < 0
|
||||
? polygon.minX
|
||||
: polygon.minY;
|
||||
let max = -Infinity;
|
||||
let value;
|
||||
while ((value = dot(polygon.list[index], direction)) > max) {
|
||||
max = value;
|
||||
index = ++index % polygon.list.length;
|
||||
}
|
||||
return polygon.list[(index || polygon.list.length) - 1];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
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
|
||||
set(direction, orth(AB, a));
|
||||
} else {
|
||||
set(direction, AO);
|
||||
// only A remains in the simplex
|
||||
simplex.shift();
|
||||
}
|
||||
} else {
|
||||
// 2-simplex
|
||||
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) {
|
||||
// region 4
|
||||
set(direction, ACB);
|
||||
simplex.shift(); // simplex = [b, a]
|
||||
} else {
|
||||
// region 5
|
||||
set(direction, AO);
|
||||
simplex.splice(0, 2); // simplex = [a]
|
||||
}
|
||||
} else if (dot(ABC, AO) > 0) {
|
||||
if (dot(AC, AO) > 0) {
|
||||
// region 6
|
||||
set(direction, ABC);
|
||||
simplex.splice(1, 1); // simplex = [c, a]
|
||||
} else {
|
||||
// region 5 (again)
|
||||
set(direction, AO);
|
||||
simplex.splice(0, 2); // simplex = [a]
|
||||
}
|
||||
} // region 7
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<number>} v
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function minus(v) {
|
||||
return [-v[0], -v[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<number>} v1
|
||||
* @param {ReadonlyArray<number>} v2
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function sub(v1, v2) {
|
||||
return [v1[0] - v2[0], v1[1] - v2[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<number>} v1
|
||||
* @param {ReadonlyArray<number>} v2
|
||||
* @returns {number}
|
||||
*/
|
||||
function dot(v1, v2) {
|
||||
return v1[0] * v2[0] + v1[1] * v2[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<number>} v
|
||||
* @param {ReadonlyArray<number>} from
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function orth(v, from) {
|
||||
const o = [-v[1], v[0]];
|
||||
return dot(o, minus(from)) < 0 ? minus(o) : o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReadonlyArray<import('../lib/types.js').PathDataItem>} pathData
|
||||
* @returns {Points}
|
||||
*/
|
||||
function gatherPoints(pathData) {
|
||||
/** @type {Points} */
|
||||
const points = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
||||
|
||||
/**
|
||||
* 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]) {
|
||||
path.maxY = path.list.length;
|
||||
points.maxY = points.list.length
|
||||
? Math.max(point[1], points.maxY)
|
||||
: point[1];
|
||||
}
|
||||
if (!path.list.length || point[0] > path.list[path.maxX][0]) {
|
||||
path.maxX = path.list.length;
|
||||
points.maxX = points.list.length
|
||||
? Math.max(point[0], points.maxX)
|
||||
: point[0];
|
||||
}
|
||||
if (!path.list.length || point[1] < path.list[path.minY][1]) {
|
||||
path.minY = path.list.length;
|
||||
points.minY = points.list.length
|
||||
? Math.min(point[1], points.minY)
|
||||
: point[1];
|
||||
}
|
||||
if (!path.list.length || point[0] < path.list[path.minX][0]) {
|
||||
path.minX = path.list.length;
|
||||
points.minX = points.list.length
|
||||
? Math.min(point[0], points.minX)
|
||||
: point[0];
|
||||
}
|
||||
path.list.push(point);
|
||||
};
|
||||
|
||||
for (let i = 0; i < pathData.length; i += 1) {
|
||||
const pathDataItem = pathData[i];
|
||||
let subPath =
|
||||
points.list.length === 0
|
||||
? { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 }
|
||||
: points.list[points.list.length - 1];
|
||||
const prev = i === 0 ? null : pathData[i - 1];
|
||||
let basePoint =
|
||||
subPath.list.length === 0 ? null : subPath.list[subPath.list.length - 1];
|
||||
const data = pathDataItem.args;
|
||||
let ctrlPoint = basePoint;
|
||||
|
||||
// TODO fix null hack
|
||||
/**
|
||||
* @param {number} n
|
||||
* @param {number} i
|
||||
* @returns {number}
|
||||
*/
|
||||
const toAbsolute = (n, i) => n + (basePoint == null ? 0 : basePoint[i % 2]);
|
||||
|
||||
switch (pathDataItem.command) {
|
||||
case 'M':
|
||||
subPath = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
||||
points.list.push(subPath);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if (basePoint != null) {
|
||||
addPoint(subPath, [data[0], basePoint[1]]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
if (basePoint != null) {
|
||||
addPoint(subPath, [basePoint[0], data[0]]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
addPoint(subPath, data.slice(0, 2));
|
||||
prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; // Save control point for shorthand
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if (
|
||||
basePoint != null &&
|
||||
prev != null &&
|
||||
(prev.command == 'Q' || prev.command == 'T')
|
||||
) {
|
||||
ctrlPoint = [
|
||||
basePoint[0] + prevCtrlPoint[0],
|
||||
basePoint[1] + prevCtrlPoint[1],
|
||||
];
|
||||
addPoint(subPath, ctrlPoint);
|
||||
prevCtrlPoint = [data[0] - ctrlPoint[0], data[1] - ctrlPoint[1]];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
if (basePoint != null) {
|
||||
// Approximate cubic Bezier curve with middle points between control points
|
||||
addPoint(subPath, [
|
||||
0.5 * (basePoint[0] + data[0]),
|
||||
0.5 * (basePoint[1] + data[1]),
|
||||
]);
|
||||
}
|
||||
addPoint(subPath, [
|
||||
0.5 * (data[0] + data[2]),
|
||||
0.5 * (data[1] + data[3]),
|
||||
]);
|
||||
addPoint(subPath, [
|
||||
0.5 * (data[2] + data[4]),
|
||||
0.5 * (data[3] + data[5]),
|
||||
]);
|
||||
prevCtrlPoint = [data[4] - data[2], data[5] - data[3]]; // Save control point for shorthand
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if (
|
||||
basePoint != null &&
|
||||
prev != null &&
|
||||
(prev.command == 'C' || prev.command == 'S')
|
||||
) {
|
||||
addPoint(subPath, [
|
||||
basePoint[0] + 0.5 * prevCtrlPoint[0],
|
||||
basePoint[1] + 0.5 * prevCtrlPoint[1],
|
||||
]);
|
||||
ctrlPoint = [
|
||||
basePoint[0] + prevCtrlPoint[0],
|
||||
basePoint[1] + prevCtrlPoint[1],
|
||||
];
|
||||
}
|
||||
if (ctrlPoint != null) {
|
||||
addPoint(subPath, [
|
||||
0.5 * (ctrlPoint[0] + data[0]),
|
||||
0.5 * (ctrlPoint[1] + data[1]),
|
||||
]);
|
||||
}
|
||||
addPoint(subPath, [
|
||||
0.5 * (data[0] + data[2]),
|
||||
0.5 * (data[1] + data[3]),
|
||||
]);
|
||||
prevCtrlPoint = [data[2] - data[0], data[3] - data[1]];
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
if (basePoint != null) {
|
||||
// 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;
|
||||
|
||||
) {
|
||||
if (basePoint != null) {
|
||||
addPoint(subPath, [
|
||||
0.5 * (basePoint[0] + cData[0]),
|
||||
0.5 * (basePoint[1] + cData[1]),
|
||||
]);
|
||||
}
|
||||
addPoint(subPath, [
|
||||
0.5 * (cData[0] + cData[2]),
|
||||
0.5 * (cData[1] + cData[3]),
|
||||
]);
|
||||
addPoint(subPath, [
|
||||
0.5 * (cData[2] + cData[4]),
|
||||
0.5 * (cData[3] + cData[5]),
|
||||
]);
|
||||
if (curves.length) {
|
||||
addPoint(subPath, (basePoint = cData.slice(-2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Save final command coordinates
|
||||
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.
|
||||
*
|
||||
* @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];
|
||||
});
|
||||
|
||||
const lower = [];
|
||||
let minY = 0;
|
||||
let bottom = 0;
|
||||
for (let i = 0; i < points.list.length; i++) {
|
||||
while (
|
||||
lower.length >= 2 &&
|
||||
cross(lower[lower.length - 2], lower[lower.length - 1], points.list[i]) <=
|
||||
0
|
||||
) {
|
||||
lower.pop();
|
||||
}
|
||||
if (points.list[i][1] < points.list[minY][1]) {
|
||||
minY = i;
|
||||
bottom = lower.length;
|
||||
}
|
||||
lower.push(points.list[i]);
|
||||
}
|
||||
|
||||
const upper = [];
|
||||
let maxY = points.list.length - 1;
|
||||
let top = 0;
|
||||
for (let i = points.list.length; i--; ) {
|
||||
while (
|
||||
upper.length >= 2 &&
|
||||
cross(upper[upper.length - 2], upper[upper.length - 1], points.list[i]) <=
|
||||
0
|
||||
) {
|
||||
upper.pop();
|
||||
}
|
||||
if (points.list[i][1] > points.list[maxY][1]) {
|
||||
maxY = i;
|
||||
top = upper.length;
|
||||
}
|
||||
upper.push(points.list[i]);
|
||||
}
|
||||
|
||||
// last points are equal to starting points of the other part
|
||||
upper.pop();
|
||||
lower.pop();
|
||||
|
||||
const hullList = lower.concat(upper);
|
||||
|
||||
/** @type {Point} */
|
||||
const hull = {
|
||||
list: hullList,
|
||||
minX: 0, // by sorting
|
||||
maxX: lower.length,
|
||||
minY: bottom,
|
||||
maxY: (lower.length + top) % hullList.length,
|
||||
};
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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](http://snapsvg.io/) (Apache 2 license). Thanks
|
||||
* to Dmitry Baranovskiy for his great work!
|
||||
*
|
||||
* @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,
|
||||
y1,
|
||||
rx,
|
||||
ry,
|
||||
angle,
|
||||
large_arc_flag,
|
||||
sweep_flag,
|
||||
x2,
|
||||
y2,
|
||||
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 {number[]} */
|
||||
let res = [];
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
if (!recursive) {
|
||||
x1 = rotateX(x1, y1, -rad);
|
||||
y1 = rotateY(x1, y1, -rad);
|
||||
x2 = rotateX(x2, y2, -rad);
|
||||
y2 = rotateY(x2, y2, -rad);
|
||||
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;
|
||||
}
|
||||
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),
|
||||
),
|
||||
);
|
||||
var cx = (k * rx * y) / ry + (x1 + x2) / 2;
|
||||
var cy = (k * -ry * x) / rx + (y1 + y2) / 2;
|
||||
var f1 = Math.asin(Number(((y1 - cy) / ry).toFixed(9)));
|
||||
var f2 = Math.asin(Number(((y2 - cy) / ry).toFixed(9)));
|
||||
|
||||
f1 = x1 < cx ? Math.PI - f1 : f1;
|
||||
f2 = x2 < cx ? Math.PI - f2 : f2;
|
||||
f1 < 0 && (f1 = Math.PI * 2 + f1);
|
||||
f2 < 0 && (f2 = Math.PI * 2 + f2);
|
||||
if (sweep_flag && f1 > f2) {
|
||||
f1 = f1 - Math.PI * 2;
|
||||
}
|
||||
if (!sweep_flag && f2 > f1) {
|
||||
f2 = f2 - Math.PI * 2;
|
||||
}
|
||||
} else {
|
||||
f1 = recursive[0];
|
||||
f2 = recursive[1];
|
||||
cx = recursive[2];
|
||||
cy = recursive[3];
|
||||
}
|
||||
let df = f2 - f1;
|
||||
if (Math.abs(df) > _120) {
|
||||
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);
|
||||
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [
|
||||
f2,
|
||||
f2old,
|
||||
cx,
|
||||
cy,
|
||||
]);
|
||||
}
|
||||
df = f2 - f1;
|
||||
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);
|
||||
const newres = [];
|
||||
for (let i = 0, n = res.length; i < n; i++) {
|
||||
newres[i] =
|
||||
i % 2
|
||||
? rotateY(res[i - 1], res[i], rad)
|
||||
: rotateX(res[i], res[i + 1], rad);
|
||||
}
|
||||
return newres;
|
||||
}
|
||||
};
|
||||
755
Frontend-Learner/node_modules/svgo/plugins/_transforms.js
generated
vendored
Normal file
755
Frontend-Learner/node_modules/svgo/plugins/_transforms.js
generated
vendored
Normal file
|
|
@ -0,0 +1,755 @@
|
|||
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 regTransformSplit =
|
||||
/\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
|
||||
const regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
||||
|
||||
/**
|
||||
* Convert transform string to JS representation.
|
||||
*
|
||||
* @param {string} transformString
|
||||
* @returns {TransformItem[]} Object representation of transform, or an empty array if it was malformed.
|
||||
*/
|
||||
export const transform2js = (transformString) => {
|
||||
/** @type {TransformItem[]} */
|
||||
const transforms = [];
|
||||
/** @type {?TransformItem} */
|
||||
let currentTransform = null;
|
||||
|
||||
// split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
|
||||
for (const item of transformString.split(regTransformSplit)) {
|
||||
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 currentTransform == null || currentTransform.data.length == 0
|
||||
? []
|
||||
: transforms;
|
||||
};
|
||||
/**
|
||||
* Multiply transforms into one.
|
||||
*
|
||||
* @param {ReadonlyArray<TransformItem>} transforms
|
||||
* @returns {TransformItem}
|
||||
*/
|
||||
export const transformsMultiply = (transforms) => {
|
||||
const matrixData = transforms.map((transform) => {
|
||||
if (transform.name === 'matrix') {
|
||||
return transform.data;
|
||||
}
|
||||
return transformToMatrix(transform);
|
||||
});
|
||||
|
||||
const matrixTransform = {
|
||||
name: 'matrix',
|
||||
data:
|
||||
matrixData.length > 0 ? matrixData.reduce(multiplyTransformMatrices) : [],
|
||||
};
|
||||
|
||||
return matrixTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
* Math utilities in radians.
|
||||
*/
|
||||
const mth = {
|
||||
/**
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
rad: (deg) => {
|
||||
return (deg * Math.PI) / 180;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} rad
|
||||
* @returns {number}
|
||||
*/
|
||||
deg: (rad) => {
|
||||
return (rad * 180) / Math.PI;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
cos: (deg) => {
|
||||
return Math.cos(mth.rad(deg));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} val
|
||||
* @param {number} floatPrecision
|
||||
* @returns {number}
|
||||
*/
|
||||
acos: (val, floatPrecision) => {
|
||||
return toFixed(mth.deg(Math.acos(val)), floatPrecision);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
sin: (deg) => {
|
||||
return Math.sin(mth.rad(deg));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} val
|
||||
* @param {number} floatPrecision
|
||||
* @returns {number}
|
||||
*/
|
||||
asin: (val, floatPrecision) => {
|
||||
return toFixed(mth.deg(Math.asin(val)), floatPrecision);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} deg
|
||||
* @returns {number}
|
||||
*/
|
||||
tan: (deg) => {
|
||||
return Math.tan(mth.rad(deg));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} val
|
||||
* @param {number} floatPrecision
|
||||
* @returns {number}
|
||||
*/
|
||||
atan: (val, floatPrecision) => {
|
||||
return toFixed(mth.deg(Math.atan(val)), floatPrecision);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 decomposeQRAB = (matrix) => {
|
||||
const data = matrix.data;
|
||||
|
||||
const [a, b, c, d, e, f] = data;
|
||||
const delta = a * d - b * c;
|
||||
if (delta === 0) {
|
||||
return;
|
||||
}
|
||||
const r = Math.hypot(a, b);
|
||||
|
||||
if (r === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decomposition = [];
|
||||
const cosOfRotationAngle = a / r;
|
||||
|
||||
// [..., ..., ..., ..., tx, ty] → translate(tx, ty)
|
||||
if (e || f) {
|
||||
decomposition.push({
|
||||
name: 'translate',
|
||||
data: [e, f],
|
||||
});
|
||||
}
|
||||
|
||||
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.deg(Math.atan(ac_plus_bd / (a * a + b * b)))],
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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.
|
||||
*
|
||||
* @param {TransformItem} transform
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const transformToMatrix = (transform) => {
|
||||
if (transform.name === 'matrix') {
|
||||
return transform.data;
|
||||
}
|
||||
switch (transform.name) {
|
||||
case 'translate':
|
||||
// [1, 0, 0, 1, tx, ty]
|
||||
return [1, 0, 0, 1, transform.data[0], transform.data[1] || 0];
|
||||
case 'scale':
|
||||
// [sx, 0, 0, sy, 0, 0]
|
||||
return [
|
||||
transform.data[0],
|
||||
0,
|
||||
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]);
|
||||
var sin = mth.sin(transform.data[0]);
|
||||
var cx = transform.data[1] || 0;
|
||||
var cy = transform.data[2] || 0;
|
||||
return [
|
||||
cos,
|
||||
sin,
|
||||
-sin,
|
||||
cos,
|
||||
(1 - cos) * cx + sin * cy,
|
||||
(1 - cos) * cy - sin * cx,
|
||||
];
|
||||
case 'skewX':
|
||||
// [1, 0, tan(a), 1, 0, 0]
|
||||
return [1, 0, mth.tan(transform.data[0]), 1, 0, 0];
|
||||
case 'skewY':
|
||||
// [1, tan(a), 0, 1, 0, 0]
|
||||
return [1, mth.tan(transform.data[0]), 0, 1, 0, 0];
|
||||
default:
|
||||
throw Error(`Unknown transform ${transform.name}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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}})
|
||||
*
|
||||
* @param {[number, number]} cursor
|
||||
* @param {number[]} arc
|
||||
* @param {ReadonlyArray<number>} transform
|
||||
* @returns {number[]}
|
||||
*/
|
||||
export const transformArc = (cursor, arc, transform) => {
|
||||
const x = arc[5] - cursor[0];
|
||||
const y = arc[6] - cursor[1];
|
||||
let a = arc[0];
|
||||
let b = arc[1];
|
||||
const rot = (arc[2] * Math.PI) / 180;
|
||||
const cos = Math.cos(rot);
|
||||
const sin = Math.sin(rot);
|
||||
// skip if radius is 0
|
||||
if (a > 0 && b > 0) {
|
||||
let h =
|
||||
Math.pow(x * cos + y * sin, 2) / (4 * a * a) +
|
||||
Math.pow(y * cos - x * sin, 2) / (4 * b * b);
|
||||
if (h > 1) {
|
||||
h = Math.sqrt(h);
|
||||
a *= h;
|
||||
b *= h;
|
||||
}
|
||||
}
|
||||
const ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0];
|
||||
const m = multiplyTransformMatrices(transform, ellipse);
|
||||
// Decompose the new ellipse matrix
|
||||
const lastCol = m[2] * m[2] + m[3] * m[3];
|
||||
const squareSum = m[0] * m[0] + m[1] * m[1] + lastCol;
|
||||
const root =
|
||||
Math.hypot(m[0] - m[3], m[1] + m[2]) * Math.hypot(m[0] + m[3], m[1] - m[2]);
|
||||
|
||||
if (!root) {
|
||||
// circle
|
||||
arc[0] = arc[1] = Math.sqrt(squareSum / 2);
|
||||
arc[2] = 0;
|
||||
} else {
|
||||
const majorAxisSqr = (squareSum + root) / 2;
|
||||
const minorAxisSqr = (squareSum - root) / 2;
|
||||
const major = Math.abs(majorAxisSqr - lastCol) > 1e-6;
|
||||
const sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol;
|
||||
const rowsSum = m[0] * m[2] + m[1] * m[3];
|
||||
const term1 = m[0] * sub + m[2] * rowsSum;
|
||||
const term2 = m[1] * sub + m[3] * rowsSum;
|
||||
arc[0] = Math.sqrt(majorAxisSqr);
|
||||
arc[1] = Math.sqrt(minorAxisSqr);
|
||||
arc[2] =
|
||||
(((major ? term2 < 0 : term1 > 0) ? -1 : 1) *
|
||||
Math.acos((major ? term1 : term2) / Math.hypot(term1, term2)) *
|
||||
180) /
|
||||
Math.PI;
|
||||
}
|
||||
|
||||
if (transform[0] < 0 !== transform[3] < 0) {
|
||||
// Flip the sweep flag if coordinates are being flipped horizontally XOR vertically
|
||||
arc[4] = 1 - arc[4];
|
||||
}
|
||||
|
||||
return arc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Multiply transformation matrices.
|
||||
*
|
||||
* @param {ReadonlyArray<number>} a
|
||||
* @param {ReadonlyArray<number>} b
|
||||
* @returns {number[]}
|
||||
*/
|
||||
const multiplyTransformMatrices = (a, b) => {
|
||||
return [
|
||||
a[0] * b[0] + a[2] * b[1],
|
||||
a[1] * b[0] + a[3] * b[1],
|
||||
a[0] * b[2] + a[2] * b[3],
|
||||
a[1] * b[2] + a[3] * b[3],
|
||||
a[0] * b[4] + a[2] * b[5] + a[4],
|
||||
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;
|
||||
};
|
||||
86
Frontend-Learner/node_modules/svgo/plugins/addAttributesToSVGElement.js
generated
vendored
Normal file
86
Frontend-Learner/node_modules/svgo/plugins/addAttributesToSVGElement.js
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* @typedef AddAttributesToSVGElementParams
|
||||
* @property {string | Record<string, null | string>=} attribute
|
||||
* @property {Array<string | Record<string, null | string>>=} attributes
|
||||
*/
|
||||
|
||||
export const name = 'addAttributesToSVGElement';
|
||||
export const description = 'adds attributes to an outer <svg> element';
|
||||
|
||||
const ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.
|
||||
It should have a list of "attributes" or one "attribute".
|
||||
Config example:
|
||||
|
||||
plugins: [
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {
|
||||
attribute: "mySvg"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
plugins: [
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {
|
||||
attributes: ["mySvg", "size-big"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
plugins: [
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {
|
||||
attributes: [
|
||||
{
|
||||
focusable: false
|
||||
},
|
||||
{
|
||||
'data-image': icon
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
`;
|
||||
|
||||
/**
|
||||
* Add attributes to an outer <svg> element.
|
||||
*
|
||||
* @author April Arcus
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<AddAttributesToSVGElementParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
if (!Array.isArray(params.attributes) && !params.attribute) {
|
||||
console.error(ENOCLS);
|
||||
return null;
|
||||
}
|
||||
const attributes = params.attributes || [params.attribute];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
for (const attribute of attributes) {
|
||||
if (typeof attribute === 'string') {
|
||||
if (node.attributes[attribute] == null) {
|
||||
// @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-expect-error disallow explicit nullable attribute value
|
||||
node.attributes[key] = attribute[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
90
Frontend-Learner/node_modules/svgo/plugins/addClassesToSVGElement.js
generated
vendored
Normal file
90
Frontend-Learner/node_modules/svgo/plugins/addClassesToSVGElement.js
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
export const name = 'addClassesToSVGElement';
|
||||
export const description = 'adds classnames to an outer <svg> element';
|
||||
|
||||
const ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.
|
||||
It should have a list of classes in "classNames" or one "className".
|
||||
Config example:
|
||||
|
||||
plugins: [
|
||||
{
|
||||
name: "addClassesToSVGElement",
|
||||
params: {
|
||||
className: "mySvg"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
plugins: [
|
||||
{
|
||||
name: "addClassesToSVGElement",
|
||||
params: {
|
||||
classNames: ["mySvg", "size-big"]
|
||||
}
|
||||
}
|
||||
]
|
||||
`;
|
||||
|
||||
/**
|
||||
* Add classnames to an outer <svg> element. Example config:
|
||||
*
|
||||
* plugins: [
|
||||
* {
|
||||
* name: "addClassesToSVGElement",
|
||||
* params: {
|
||||
* className: "mySvg"
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* plugins: [
|
||||
* {
|
||||
* name: "addClassesToSVGElement",
|
||||
* params: {
|
||||
* classNames: ["mySvg", "size-big"]
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* @author April Arcus
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<AddClassesToSVGElementParams>}
|
||||
*/
|
||||
export const fn = (root, params, info) => {
|
||||
if (
|
||||
!(Array.isArray(params.classNames) && params.classNames.length !== 0) &&
|
||||
!params.className
|
||||
) {
|
||||
console.error(ENOCLS);
|
||||
return null;
|
||||
}
|
||||
const classNames = params.classNames || [params.className];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
const classList = new Set(
|
||||
node.attributes.class == null
|
||||
? null
|
||||
: node.attributes.class.split(' '),
|
||||
);
|
||||
for (const className of classNames) {
|
||||
if (className != null) {
|
||||
const classToAdd =
|
||||
typeof className === 'string'
|
||||
? className
|
||||
: className(node, info);
|
||||
classList.add(classToAdd);
|
||||
}
|
||||
}
|
||||
node.attributes.class = Array.from(classList).join(' ');
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
376
Frontend-Learner/node_modules/svgo/plugins/applyTransforms.js
generated
vendored
Normal file
376
Frontend-Learner/node_modules/svgo/plugins/applyTransforms.js
generated
vendored
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
import { path2js } from './_path.js';
|
||||
import {
|
||||
transform2js,
|
||||
transformArc,
|
||||
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;
|
||||
|
||||
/**
|
||||
* Apply transformation(s) to the Path data.
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<{
|
||||
* transformPrecision: number,
|
||||
* applyTransformsStroked: boolean,
|
||||
* }>}
|
||||
*/
|
||||
export const applyTransforms = (root, params) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.d == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// stroke and stroke-width can be redefined with <use>
|
||||
if (node.attributes.id != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
const transformStyle = computedStyle.transform;
|
||||
|
||||
// Transform overridden in <style> tag which is not considered
|
||||
if (
|
||||
transformStyle.type === 'static' &&
|
||||
transformStyle.value !== node.attributes.transform
|
||||
) {
|
||||
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;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
let { command, args } = pathItem;
|
||||
|
||||
// moveto (x y)
|
||||
if (command === 'M') {
|
||||
cursor[0] = args[0];
|
||||
cursor[1] = args[1];
|
||||
start[0] = cursor[0];
|
||||
start[1] = cursor[1];
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]);
|
||||
args[0] = x;
|
||||
args[1] = y;
|
||||
}
|
||||
if (command === 'm') {
|
||||
cursor[0] += args[0];
|
||||
cursor[1] += args[1];
|
||||
start[0] = cursor[0];
|
||||
start[1] = cursor[1];
|
||||
const [x, y] = transformRelativePoint(matrix, args[0], args[1]);
|
||||
args[0] = x;
|
||||
args[1] = y;
|
||||
}
|
||||
|
||||
// horizontal lineto (x)
|
||||
// convert to lineto to handle two-dimensional transforms
|
||||
if (command === 'H') {
|
||||
command = 'L';
|
||||
args = [args[0], cursor[1]];
|
||||
}
|
||||
if (command === 'h') {
|
||||
command = 'l';
|
||||
args = [args[0], 0];
|
||||
}
|
||||
|
||||
// vertical lineto (y)
|
||||
// convert to lineto to handle two-dimensional transforms
|
||||
if (command === 'V') {
|
||||
command = 'L';
|
||||
args = [cursor[0], args[0]];
|
||||
}
|
||||
if (command === 'v') {
|
||||
command = 'l';
|
||||
args = [0, args[0]];
|
||||
}
|
||||
|
||||
// lineto (x y)
|
||||
if (command === 'L') {
|
||||
cursor[0] = args[0];
|
||||
cursor[1] = args[1];
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]);
|
||||
args[0] = x;
|
||||
args[1] = y;
|
||||
}
|
||||
if (command === 'l') {
|
||||
cursor[0] += args[0];
|
||||
cursor[1] += args[1];
|
||||
const [x, y] = transformRelativePoint(matrix, args[0], args[1]);
|
||||
args[0] = x;
|
||||
args[1] = y;
|
||||
}
|
||||
|
||||
// curveto (x1 y1 x2 y2 x y)
|
||||
if (command === 'C') {
|
||||
cursor[0] = args[4];
|
||||
cursor[1] = args[5];
|
||||
const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]);
|
||||
const [x2, y2] = transformAbsolutePoint(matrix, args[2], args[3]);
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[4], args[5]);
|
||||
args[0] = x1;
|
||||
args[1] = y1;
|
||||
args[2] = x2;
|
||||
args[3] = y2;
|
||||
args[4] = x;
|
||||
args[5] = y;
|
||||
}
|
||||
if (command === 'c') {
|
||||
cursor[0] += args[4];
|
||||
cursor[1] += args[5];
|
||||
const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]);
|
||||
const [x2, y2] = transformRelativePoint(matrix, args[2], args[3]);
|
||||
const [x, y] = transformRelativePoint(matrix, args[4], args[5]);
|
||||
args[0] = x1;
|
||||
args[1] = y1;
|
||||
args[2] = x2;
|
||||
args[3] = y2;
|
||||
args[4] = x;
|
||||
args[5] = y;
|
||||
}
|
||||
|
||||
// smooth curveto (x2 y2 x y)
|
||||
if (command === 'S') {
|
||||
cursor[0] = args[2];
|
||||
cursor[1] = args[3];
|
||||
const [x2, y2] = transformAbsolutePoint(matrix, args[0], args[1]);
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]);
|
||||
args[0] = x2;
|
||||
args[1] = y2;
|
||||
args[2] = x;
|
||||
args[3] = y;
|
||||
}
|
||||
if (command === 's') {
|
||||
cursor[0] += args[2];
|
||||
cursor[1] += args[3];
|
||||
const [x2, y2] = transformRelativePoint(matrix, args[0], args[1]);
|
||||
const [x, y] = transformRelativePoint(matrix, args[2], args[3]);
|
||||
args[0] = x2;
|
||||
args[1] = y2;
|
||||
args[2] = x;
|
||||
args[3] = y;
|
||||
}
|
||||
|
||||
// quadratic Bézier curveto (x1 y1 x y)
|
||||
if (command === 'Q') {
|
||||
cursor[0] = args[2];
|
||||
cursor[1] = args[3];
|
||||
const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]);
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]);
|
||||
args[0] = x1;
|
||||
args[1] = y1;
|
||||
args[2] = x;
|
||||
args[3] = y;
|
||||
}
|
||||
if (command === 'q') {
|
||||
cursor[0] += args[2];
|
||||
cursor[1] += args[3];
|
||||
const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]);
|
||||
const [x, y] = transformRelativePoint(matrix, args[2], args[3]);
|
||||
args[0] = x1;
|
||||
args[1] = y1;
|
||||
args[2] = x;
|
||||
args[3] = y;
|
||||
}
|
||||
|
||||
// smooth quadratic Bézier curveto (x y)
|
||||
if (command === 'T') {
|
||||
cursor[0] = args[0];
|
||||
cursor[1] = args[1];
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]);
|
||||
args[0] = x;
|
||||
args[1] = y;
|
||||
}
|
||||
if (command === 't') {
|
||||
cursor[0] += args[0];
|
||||
cursor[1] += args[1];
|
||||
const [x, y] = transformRelativePoint(matrix, args[0], args[1]);
|
||||
args[0] = x;
|
||||
args[1] = y;
|
||||
}
|
||||
|
||||
// elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
|
||||
if (command === 'A') {
|
||||
transformArc(cursor, args, matrix);
|
||||
cursor[0] = args[5];
|
||||
cursor[1] = args[6];
|
||||
// reduce number of digits in rotation angle
|
||||
if (Math.abs(args[2]) > 80) {
|
||||
const a = args[0];
|
||||
const rotation = args[2];
|
||||
args[0] = args[1];
|
||||
args[1] = a;
|
||||
args[2] = rotation + (rotation > 0 ? -90 : 90);
|
||||
}
|
||||
const [x, y] = transformAbsolutePoint(matrix, args[5], args[6]);
|
||||
args[5] = x;
|
||||
args[6] = y;
|
||||
}
|
||||
if (command === 'a') {
|
||||
transformArc([0, 0], args, matrix);
|
||||
cursor[0] += args[5];
|
||||
cursor[1] += args[6];
|
||||
// reduce number of digits in rotation angle
|
||||
if (Math.abs(args[2]) > 80) {
|
||||
const a = args[0];
|
||||
const rotation = args[2];
|
||||
args[0] = args[1];
|
||||
args[1] = a;
|
||||
args[2] = rotation + (rotation > 0 ? -90 : 90);
|
||||
}
|
||||
const [x, y] = transformRelativePoint(matrix, args[5], args[6]);
|
||||
args[5] = x;
|
||||
args[6] = y;
|
||||
}
|
||||
|
||||
// closepath
|
||||
if (command === 'z' || command === 'Z') {
|
||||
cursor[0] = start[0];
|
||||
cursor[1] = start[1];
|
||||
}
|
||||
|
||||
pathItem.command = command;
|
||||
pathItem.args = args;
|
||||
}
|
||||
};
|
||||
53
Frontend-Learner/node_modules/svgo/plugins/cleanupAttrs.js
generated
vendored
Normal file
53
Frontend-Learner/node_modules/svgo/plugins/cleanupAttrs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @typedef CleanupAttrsParams
|
||||
* @property {boolean=} newlines
|
||||
* @property {boolean=} trim
|
||||
* @property {boolean=} spaces
|
||||
*/
|
||||
|
||||
export const name = 'cleanupAttrs';
|
||||
export const description =
|
||||
'cleanups attributes from newlines, trailing and repeating spaces';
|
||||
|
||||
const regNewlinesNeedSpace = /(\S)\r?\n(\S)/g;
|
||||
const regNewlines = /\r?\n/g;
|
||||
const regSpaces = /\s{2,}/g;
|
||||
|
||||
/**
|
||||
* Cleanup attributes values from newlines, trailing and repeating spaces.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
* @type {import('../lib/types.js').Plugin<CleanupAttrsParams>}
|
||||
*/
|
||||
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
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
regNewlinesNeedSpace,
|
||||
(match, p1, p2) => p1 + ' ' + p2,
|
||||
);
|
||||
// simple new line
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
regNewlines,
|
||||
'',
|
||||
);
|
||||
}
|
||||
if (trim) {
|
||||
node.attributes[name] = node.attributes[name].trim();
|
||||
}
|
||||
if (spaces) {
|
||||
node.attributes[name] = node.attributes[name].replace(
|
||||
regSpaces,
|
||||
' ',
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
164
Frontend-Learner/node_modules/svgo/plugins/cleanupEnableBackground.js
generated
vendored
Normal file
164
Frontend-Learner/node_modules/svgo/plugins/cleanupEnableBackground.js
generated
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import * as csstree from 'css-tree';
|
||||
import { visit } from '../lib/util/visit.js';
|
||||
|
||||
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.
|
||||
*
|
||||
* @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.js').Plugin}
|
||||
*/
|
||||
export const fn = (root) => {
|
||||
let hasFilter = false;
|
||||
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'filter') {
|
||||
hasFilter = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
/** @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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
266
Frontend-Learner/node_modules/svgo/plugins/cleanupIds.js
generated
vendored
Normal file
266
Frontend-Learner/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;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
150
Frontend-Learner/node_modules/svgo/plugins/cleanupListOfValues.js
generated
vendored
Normal file
150
Frontend-Learner/node_modules/svgo/plugins/cleanupListOfValues.js
generated
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { removeLeadingZero } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef CleanupListOfValuesParams
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} defaultPx
|
||||
* @property {boolean=} convertToPx
|
||||
*/
|
||||
|
||||
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|%)?$/;
|
||||
const regSeparator = /\s+,?\s*|,\s*/;
|
||||
const absoluteLengths = {
|
||||
// relative to px
|
||||
cm: 96 / 2.54,
|
||||
mm: 96 / 25.4,
|
||||
in: 96,
|
||||
pt: 4 / 3,
|
||||
pc: 16,
|
||||
px: 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Round list of values to the fixed precision.
|
||||
*
|
||||
* @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.js').Plugin<CleanupListOfValuesParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
floatPrecision = 3,
|
||||
leadingZero = true,
|
||||
defaultPx = true,
|
||||
convertToPx = true,
|
||||
} = params;
|
||||
|
||||
/**
|
||||
* @param {string} lists
|
||||
* @returns {string}
|
||||
*/
|
||||
const roundValues = (lists) => {
|
||||
const roundedList = [];
|
||||
|
||||
for (const elem of lists.split(regSeparator)) {
|
||||
const match = elem.match(regNumericValues);
|
||||
const matchNew = elem.match(/new/);
|
||||
|
||||
// if attribute value matches regNumericValues
|
||||
if (match) {
|
||||
// round it to the fixed precision
|
||||
let num = Number(Number(match[1]).toFixed(floatPrecision));
|
||||
/** @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),
|
||||
);
|
||||
|
||||
if (pxNum.toString().length < match[0].length) {
|
||||
num = pxNum;
|
||||
units = 'px';
|
||||
}
|
||||
}
|
||||
|
||||
// and remove leading zero
|
||||
let str;
|
||||
if (leadingZero) {
|
||||
str = removeLeadingZero(num);
|
||||
} else {
|
||||
str = num.toString();
|
||||
}
|
||||
|
||||
// remove default 'px' units
|
||||
if (defaultPx && units === 'px') {
|
||||
units = '';
|
||||
}
|
||||
|
||||
roundedList.push(str + units);
|
||||
}
|
||||
// if attribute value is "new"(only enable-background).
|
||||
else if (matchNew) {
|
||||
roundedList.push('new');
|
||||
} else if (elem) {
|
||||
roundedList.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
return roundedList.join(' ');
|
||||
};
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.points != null) {
|
||||
node.attributes.points = roundValues(node.attributes.points);
|
||||
}
|
||||
|
||||
if (node.attributes['enable-background'] != null) {
|
||||
node.attributes['enable-background'] = roundValues(
|
||||
node.attributes['enable-background'],
|
||||
);
|
||||
}
|
||||
|
||||
if (node.attributes.viewBox != null) {
|
||||
node.attributes.viewBox = roundValues(node.attributes.viewBox);
|
||||
}
|
||||
|
||||
if (node.attributes['stroke-dasharray'] != null) {
|
||||
node.attributes['stroke-dasharray'] = roundValues(
|
||||
node.attributes['stroke-dasharray'],
|
||||
);
|
||||
}
|
||||
|
||||
if (node.attributes.dx != null) {
|
||||
node.attributes.dx = roundValues(node.attributes.dx);
|
||||
}
|
||||
|
||||
if (node.attributes.dy != null) {
|
||||
node.attributes.dy = roundValues(node.attributes.dy);
|
||||
}
|
||||
|
||||
if (node.attributes.x != null) {
|
||||
node.attributes.x = roundValues(node.attributes.x);
|
||||
}
|
||||
|
||||
if (node.attributes.y != null) {
|
||||
node.attributes.y = roundValues(node.attributes.y);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
107
Frontend-Learner/node_modules/svgo/plugins/cleanupNumericValues.js
generated
vendored
Normal file
107
Frontend-Learner/node_modules/svgo/plugins/cleanupNumericValues.js
generated
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import { removeLeadingZero } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef CleanupNumericValuesParams
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} leadingZero
|
||||
* @property {boolean=} defaultPx
|
||||
* @property {boolean=} convertToPx
|
||||
*/
|
||||
|
||||
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|%)?$/;
|
||||
|
||||
const absoluteLengths = {
|
||||
// relative to px
|
||||
cm: 96 / 2.54,
|
||||
mm: 96 / 25.4,
|
||||
in: 96,
|
||||
pt: 4 / 3,
|
||||
pc: 16,
|
||||
px: 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Round numeric values to the fixed precision, remove default 'px' units.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<CleanupNumericValuesParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
floatPrecision = 3,
|
||||
leadingZero = true,
|
||||
defaultPx = true,
|
||||
convertToPx = true,
|
||||
} = params;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.viewBox != null) {
|
||||
const nums = node.attributes.viewBox.trim().split(/(?:\s,?|,)\s*/g);
|
||||
node.attributes.viewBox = nums
|
||||
.map((value) => {
|
||||
const num = Number(value);
|
||||
return Number.isNaN(num)
|
||||
? value
|
||||
: Number(num.toFixed(floatPrecision));
|
||||
})
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
// The `version` attribute is a text string and cannot be rounded
|
||||
if (name === 'version') {
|
||||
continue;
|
||||
}
|
||||
|
||||
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} */
|
||||
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,
|
||||
),
|
||||
);
|
||||
if (pxNum.toString().length < match[0].length) {
|
||||
num = pxNum;
|
||||
units = 'px';
|
||||
}
|
||||
}
|
||||
|
||||
// and remove leading zero
|
||||
let str;
|
||||
if (leadingZero) {
|
||||
str = removeLeadingZero(num);
|
||||
} else {
|
||||
str = num.toString();
|
||||
}
|
||||
|
||||
// remove default 'px' units
|
||||
if (defaultPx && units === 'px') {
|
||||
units = '';
|
||||
}
|
||||
|
||||
node.attributes[name] = str + units;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
133
Frontend-Learner/node_modules/svgo/plugins/collapseGroups.js
generated
vendored
Normal file
133
Frontend-Learner/node_modules/svgo/plugins/collapseGroups.js
generated
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { elemsGroups, inheritableAttrs } from './_collections.js';
|
||||
|
||||
export const name = 'collapseGroups';
|
||||
export const description = 'collapses useless groups';
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastNode} node
|
||||
* @param {string} name
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasAnimatedAttr = (node, name) => {
|
||||
if (node.type === 'element') {
|
||||
if (
|
||||
elemsGroups.animation.has(node.name) &&
|
||||
node.attributes.attributeName === name
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
for (const child of node.children) {
|
||||
if (hasAnimatedAttr(child, name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collapse useless groups.
|
||||
*
|
||||
* @example
|
||||
* <g>
|
||||
* <g attr1="val1">
|
||||
* <path d="..."/>
|
||||
* </g>
|
||||
* </g>
|
||||
* ⬇
|
||||
* <g>
|
||||
* <g>
|
||||
* <path attr1="val1" d="..."/>
|
||||
* </g>
|
||||
* </g>
|
||||
* ⬇
|
||||
* <path attr1="val1" d="..."/>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = (root) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
return {
|
||||
element: {
|
||||
exit: (node, parentNode) => {
|
||||
if (parentNode.type === 'root' || parentNode.name === 'switch') {
|
||||
return;
|
||||
}
|
||||
// non-empty groups
|
||||
if (node.name !== 'g' || node.children.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 &&
|
||||
!nodeHasFilter &&
|
||||
(node.attributes.class == null ||
|
||||
firstChild.attributes.class == null) &&
|
||||
((node.attributes['clip-path'] == null &&
|
||||
node.attributes.mask == null) ||
|
||||
(firstChild.name === 'g' &&
|
||||
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 (newChildElemAttrs[name] == null) {
|
||||
newChildElemAttrs[name] = value;
|
||||
} else if (name === 'transform') {
|
||||
newChildElemAttrs[name] = value + ' ' + newChildElemAttrs[name];
|
||||
} else if (newChildElemAttrs[name] === 'inherit') {
|
||||
newChildElemAttrs[name] = value;
|
||||
} else if (
|
||||
!inheritableAttrs.has(name) &&
|
||||
newChildElemAttrs[name] !== value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
node.attributes = {};
|
||||
firstChild.attributes = newChildElemAttrs;
|
||||
}
|
||||
}
|
||||
|
||||
// collapse groups without attributes
|
||||
if (Object.keys(node.attributes).length === 0) {
|
||||
// animation elements "add" attributes to group
|
||||
// group should be preserved
|
||||
for (const child of node.children) {
|
||||
if (
|
||||
child.type === 'element' &&
|
||||
elemsGroups.animation.has(child.name)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// replace current node with all its children
|
||||
const index = parentNode.children.indexOf(node);
|
||||
parentNode.children.splice(index, 1, ...node.children);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
178
Frontend-Learner/node_modules/svgo/plugins/convertColors.js
generated
vendored
Normal file
178
Frontend-Learner/node_modules/svgo/plugins/convertColors.js
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { colorsNames, colorsProps, colorsShortNames } from './_collections.js';
|
||||
import { includesUrlReference } from '../lib/svgo/tools.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
|
||||
*/
|
||||
|
||||
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*|\\s+)';
|
||||
const regRGB = new RegExp(
|
||||
'^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$',
|
||||
);
|
||||
const regHEX = /^#(([a-fA-F0-9])\2){3}$/;
|
||||
|
||||
/**
|
||||
* Convert [r, g, b] to #rrggbb.
|
||||
*
|
||||
* @see https://gist.github.com/983535
|
||||
*
|
||||
* @example
|
||||
* rgb2hex([255, 255, 255]) // '#ffffff'
|
||||
*
|
||||
* @author Jed Schmidt
|
||||
*
|
||||
* @param {ReadonlyArray<number>} param0
|
||||
* @returns {string}
|
||||
*/
|
||||
const convertRgbToHex = ([r, g, b]) => {
|
||||
// combine the octets into a 32-bit integer as: [1][r][g][b]
|
||||
const hexNumber =
|
||||
// operator precedence is (+) > (<<) > (|)
|
||||
((((256 + // [1][0]
|
||||
r) << // [1][r]
|
||||
8) | // [1][r][0]
|
||||
g) << // [1][r][g]
|
||||
8) | // [1][r][g][0]
|
||||
b;
|
||||
// serialize [1][r][g][b] to a hex string, and
|
||||
// remove the 1 to get the number with 0s intact
|
||||
return '#' + hexNumber.toString(16).slice(1).toUpperCase();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert different colors formats in element attributes to hex.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor
|
||||
* @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
|
||||
*
|
||||
* @example
|
||||
* Convert color name keyword to long hex:
|
||||
* fuchsia ➡ #ff00ff
|
||||
*
|
||||
* Convert rgb() to long hex:
|
||||
* rgb(255, 0, 255) ➡ #ff00ff
|
||||
* rgb(50%, 100, 100%) ➡ #7f64ff
|
||||
*
|
||||
* Convert long hex to short hex:
|
||||
* #aabbcc ➡ #abc
|
||||
*
|
||||
* Convert hex to short name
|
||||
* #000080 ➡ navy
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<ConvertColorsParams>}
|
||||
*/
|
||||
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 (colorsProps.has(name)) {
|
||||
let val = value;
|
||||
|
||||
// convert colors to currentColor
|
||||
if (currentColor && maskCounter === 0) {
|
||||
let matched;
|
||||
if (typeof currentColor === 'string') {
|
||||
matched = val === currentColor;
|
||||
} else if (currentColor instanceof RegExp) {
|
||||
matched = currentColor.exec(val) != null;
|
||||
} else {
|
||||
matched = val !== 'none';
|
||||
}
|
||||
if (matched) {
|
||||
val = 'currentColor';
|
||||
}
|
||||
}
|
||||
|
||||
// convert color name keyword to long hex
|
||||
if (names2hex) {
|
||||
const colorName = val.toLowerCase();
|
||||
if (colorsNames[colorName] != null) {
|
||||
val = colorsNames[colorName];
|
||||
}
|
||||
}
|
||||
|
||||
// convert rgb() to long hex
|
||||
if (rgb2hex) {
|
||||
const match = val.match(regRGB);
|
||||
if (match != null) {
|
||||
const nums = match.slice(1, 4).map((m) => {
|
||||
let n;
|
||||
if (m.indexOf('%') > -1) {
|
||||
n = Math.round(parseFloat(m) * 2.55);
|
||||
} else {
|
||||
n = Number(m);
|
||||
}
|
||||
return Math.max(0, Math.min(n, 255));
|
||||
});
|
||||
val = convertRgbToHex(nums);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
const match = regHEX.exec(val);
|
||||
if (match != null) {
|
||||
val = '#' + match[0][1] + match[0][3] + match[0][5];
|
||||
}
|
||||
}
|
||||
|
||||
// convert hex to short name
|
||||
if (shortname) {
|
||||
const colorName = val.toLowerCase();
|
||||
if (colorsShortNames[colorName] != null) {
|
||||
val = colorsShortNames[colorName];
|
||||
}
|
||||
}
|
||||
|
||||
node.attributes[name] = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
exit: (node) => {
|
||||
if (node.name === 'mask') {
|
||||
maskCounter--;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
35
Frontend-Learner/node_modules/svgo/plugins/convertEllipseToCircle.js
generated
vendored
Normal file
35
Frontend-Learner/node_modules/svgo/plugins/convertEllipseToCircle.js
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export const name = 'convertEllipseToCircle';
|
||||
export const description = 'converts non-eccentric <ellipse>s to <circle>s';
|
||||
|
||||
/**
|
||||
* Converts non-eccentric <ellipse>s to <circle>s.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/shapes.html
|
||||
*
|
||||
* @author Taylor Hunt
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'ellipse') {
|
||||
const rx = node.attributes.rx || '0';
|
||||
const ry = node.attributes.ry || '0';
|
||||
if (
|
||||
rx === ry ||
|
||||
rx === 'auto' ||
|
||||
ry === 'auto' // SVG2
|
||||
) {
|
||||
node.name = 'circle';
|
||||
const radius = rx === 'auto' ? ry : rx;
|
||||
delete node.attributes.rx;
|
||||
delete node.attributes.ry;
|
||||
node.attributes.r = radius;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
157
Frontend-Learner/node_modules/svgo/plugins/convertOneStopGradients.js
generated
vendored
Normal file
157
Frontend-Learner/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);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
1346
Frontend-Learner/node_modules/svgo/plugins/convertPathData.js
generated
vendored
Normal file
1346
Frontend-Learner/node_modules/svgo/plugins/convertPathData.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
163
Frontend-Learner/node_modules/svgo/plugins/convertShapeToPath.js
generated
vendored
Normal file
163
Frontend-Learner/node_modules/svgo/plugins/convertShapeToPath.js
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import { stringifyPathData } from '../lib/path.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef ConvertShapeToPathParams
|
||||
* @property {boolean=} convertArcs
|
||||
* @property {number=} floatPrecision
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/shapes.html
|
||||
*
|
||||
* @author Lev Solntsev
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<ConvertShapeToPathParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const { convertArcs = false, floatPrecision: precision } = params;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// convert rect to path
|
||||
if (
|
||||
node.name === 'rect' &&
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null &&
|
||||
node.attributes.rx == null &&
|
||||
node.attributes.ry == null
|
||||
) {
|
||||
const x = Number(node.attributes.x || '0');
|
||||
const y = Number(node.attributes.y || '0');
|
||||
const width = Number(node.attributes.width);
|
||||
const height = Number(node.attributes.height);
|
||||
// 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 {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [
|
||||
{ command: 'M', args: [x, y] },
|
||||
{ command: 'H', args: [x + width] },
|
||||
{ command: 'V', args: [y + height] },
|
||||
{ command: 'H', args: [x] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
node.name = 'path';
|
||||
node.attributes.d = stringifyPathData({ pathData, precision });
|
||||
delete node.attributes.x;
|
||||
delete node.attributes.y;
|
||||
delete node.attributes.width;
|
||||
delete node.attributes.height;
|
||||
}
|
||||
|
||||
// convert line to path
|
||||
if (node.name === 'line') {
|
||||
const x1 = Number(node.attributes.x1 || '0');
|
||||
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 {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [
|
||||
{ command: 'M', args: [x1, y1] },
|
||||
{ command: 'L', args: [x2, y2] },
|
||||
];
|
||||
node.name = 'path';
|
||||
node.attributes.d = stringifyPathData({ pathData, precision });
|
||||
delete node.attributes.x1;
|
||||
delete node.attributes.y1;
|
||||
delete node.attributes.x2;
|
||||
delete node.attributes.y2;
|
||||
}
|
||||
|
||||
// convert polyline and polygon to path
|
||||
if (
|
||||
(node.name === 'polyline' || node.name === 'polygon') &&
|
||||
node.attributes.points != null
|
||||
) {
|
||||
const coords = (node.attributes.points.match(regNumber) || []).map(
|
||||
Number,
|
||||
);
|
||||
if (coords.length < 4) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
/** @type {import('../lib/types.js').PathDataItem[]} */
|
||||
const pathData = [];
|
||||
for (let i = 0; i < coords.length; i += 2) {
|
||||
pathData.push({
|
||||
command: i === 0 ? 'M' : 'L',
|
||||
args: coords.slice(i, i + 2),
|
||||
});
|
||||
}
|
||||
if (node.name === 'polygon') {
|
||||
pathData.push({ command: 'z', args: [] });
|
||||
}
|
||||
node.name = 'path';
|
||||
node.attributes.d = stringifyPathData({ pathData, precision });
|
||||
delete node.attributes.points;
|
||||
}
|
||||
|
||||
// optionally convert circle
|
||||
if (node.name === 'circle' && convertArcs) {
|
||||
const cx = Number(node.attributes.cx || '0');
|
||||
const cy = Number(node.attributes.cy || '0');
|
||||
const r = Number(node.attributes.r || '0');
|
||||
if (Number.isNaN(cx - cy + r)) {
|
||||
return;
|
||||
}
|
||||
/** @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] },
|
||||
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
node.name = 'path';
|
||||
node.attributes.d = stringifyPathData({ pathData, precision });
|
||||
delete node.attributes.cx;
|
||||
delete node.attributes.cy;
|
||||
delete node.attributes.r;
|
||||
}
|
||||
|
||||
// optionally convert ellipse
|
||||
if (node.name === 'ellipse' && convertArcs) {
|
||||
const ecx = Number(node.attributes.cx || '0');
|
||||
const ecy = Number(node.attributes.cy || '0');
|
||||
const rx = Number(node.attributes.rx || '0');
|
||||
const ry = Number(node.attributes.ry || '0');
|
||||
if (Number.isNaN(ecx - ecy + rx - ry)) {
|
||||
return;
|
||||
}
|
||||
/** @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] },
|
||||
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
node.name = 'path';
|
||||
node.attributes.d = stringifyPathData({ pathData, precision });
|
||||
delete node.attributes.cx;
|
||||
delete node.attributes.cy;
|
||||
delete node.attributes.rx;
|
||||
delete node.attributes.ry;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
145
Frontend-Learner/node_modules/svgo/plugins/convertStyleToAttrs.js
generated
vendored
Normal file
145
Frontend-Learner/node_modules/svgo/plugins/convertStyleToAttrs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import { attrsGroups } from './_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef ConvertStyleToAttrsParams
|
||||
* @property {boolean=} keepImportant
|
||||
*/
|
||||
|
||||
export const name = 'convertStyleToAttrs';
|
||||
export const description = 'converts style to attributes';
|
||||
|
||||
/**
|
||||
* @param {...string} args
|
||||
* @returns {string}
|
||||
*/
|
||||
const g = (...args) => {
|
||||
return '(?:' + args.join('|') + ')';
|
||||
};
|
||||
|
||||
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">
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<ConvertStyleToAttrsParams>}
|
||||
*/
|
||||
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 = node.attributes.style.replace(
|
||||
regStripComments,
|
||||
(match) => {
|
||||
return match[0] == '/'
|
||||
? ''
|
||||
: match[0] == '\\' && /[-g-z]/i.test(match[1])
|
||||
? match[1]
|
||||
: match;
|
||||
},
|
||||
);
|
||||
|
||||
regDeclarationBlock.lastIndex = 0;
|
||||
for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
|
||||
if (!keepImportant || !rule[3]) {
|
||||
styles.push([rule[1], rule[2]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (styles.length) {
|
||||
styles = styles.filter(function (style) {
|
||||
if (style[0]) {
|
||||
const prop = style[0].toLowerCase();
|
||||
let val = style[1];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
309
Frontend-Learner/node_modules/svgo/plugins/convertTransform.js
generated
vendored
Normal file
309
Frontend-Learner/node_modules/svgo/plugins/convertTransform.js
generated
vendored
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
import {
|
||||
js2transform,
|
||||
matrixToTransform,
|
||||
roundTransform,
|
||||
transform2js,
|
||||
transformsMultiply,
|
||||
} from './_transforms.js';
|
||||
|
||||
/**
|
||||
* @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,
|
||||
* convert long translate, scale or rotate transform notations to the shorts ones,
|
||||
* convert transforms to the matrices and multiply them all into one,
|
||||
* remove useless transforms.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/coords.html#TransformMatrixDefined
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<ConvertTransformParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
convertToShorts = true,
|
||||
// degPrecision = 3, // transformPrecision (or matrix precision) - 2 by default
|
||||
degPrecision,
|
||||
floatPrecision = 3,
|
||||
transformPrecision = 5,
|
||||
matrixToTransform = true,
|
||||
shortTranslate = true,
|
||||
shortScale = true,
|
||||
shortRotate = true,
|
||||
removeUseless = true,
|
||||
collapseIntoOne = true,
|
||||
leadingZero = true,
|
||||
negativeExtraSpace = false,
|
||||
} = params;
|
||||
const newParams = {
|
||||
convertToShorts,
|
||||
degPrecision,
|
||||
floatPrecision,
|
||||
transformPrecision,
|
||||
matrixToTransform,
|
||||
shortTranslate,
|
||||
shortScale,
|
||||
shortRotate,
|
||||
removeUseless,
|
||||
collapseIntoOne,
|
||||
leadingZero,
|
||||
negativeExtraSpace,
|
||||
};
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.attributes.transform != null) {
|
||||
convertTransform(node, 'transform', newParams);
|
||||
}
|
||||
|
||||
if (node.attributes.gradientTransform != null) {
|
||||
convertTransform(node, 'gradientTransform', newParams);
|
||||
}
|
||||
|
||||
if (node.attributes.patternTransform != null) {
|
||||
convertTransform(node, 'patternTransform', newParams);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastElement} item
|
||||
* @param {string} attrName
|
||||
* @param {TransformParams} params
|
||||
*/
|
||||
const convertTransform = (item, attrName, params) => {
|
||||
let data = transform2js(item.attributes[attrName]);
|
||||
params = definePrecision(data, params);
|
||||
|
||||
if (params.collapseIntoOne && data.length > 1) {
|
||||
data = [transformsMultiply(data)];
|
||||
}
|
||||
|
||||
if (params.convertToShorts) {
|
||||
data = convertToShorts(data, params);
|
||||
} else {
|
||||
data.forEach((item) => roundTransform(item, params));
|
||||
}
|
||||
|
||||
if (params.removeUseless) {
|
||||
data = removeUseless(data);
|
||||
}
|
||||
|
||||
if (data.length) {
|
||||
item.attributes[attrName] = js2transform(data, params);
|
||||
} else {
|
||||
delete item.attributes[attrName];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 (roughly).
|
||||
* - `transformPrecision` - 2 or floatPrecision whichever is lower. Can be set in params.
|
||||
*
|
||||
* 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 = [];
|
||||
for (const item of data) {
|
||||
if (item.name == 'matrix') {
|
||||
matrixData.push(...item.data.slice(0, 4));
|
||||
}
|
||||
}
|
||||
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,
|
||||
);
|
||||
numberOfDigits = Math.max.apply(
|
||||
Math,
|
||||
matrixData.map(
|
||||
(n) => n.toString().replace(/\D+/g, '').length, // Number of digits in a number. 123.45 → 5
|
||||
),
|
||||
);
|
||||
}
|
||||
// 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, numberOfDigits - 2),
|
||||
);
|
||||
}
|
||||
return newParams;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns number of digits after the point.
|
||||
*
|
||||
* @example 0.125 → 3
|
||||
* @param {number} n
|
||||
* @returns {number}
|
||||
*/
|
||||
const floatDigits = (n) => {
|
||||
const str = n.toString();
|
||||
return str.slice(str.indexOf('.')).length - 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert transforms to the shorthand alternatives.
|
||||
*
|
||||
* @param {TransformItem[]} transforms
|
||||
* @param {TransformParams} params
|
||||
* @returns {TransformItem[]}
|
||||
*/
|
||||
const convertToShorts = (transforms, params) => {
|
||||
for (let i = 0; i < transforms.length; i++) {
|
||||
let transform = transforms[i];
|
||||
|
||||
// convert matrix to the short aliases
|
||||
if (params.matrixToTransform && transform.name === 'matrix') {
|
||||
const decomposed = matrixToTransform(transform, params);
|
||||
if (
|
||||
js2transform(decomposed, params).length <=
|
||||
js2transform([transform], params).length
|
||||
) {
|
||||
transforms.splice(i, 1, ...decomposed);
|
||||
}
|
||||
transform = transforms[i];
|
||||
}
|
||||
|
||||
// fixed-point numbers
|
||||
// 12.754997 → 12.755
|
||||
roundTransform(transform, params);
|
||||
|
||||
// convert long translate transform notation to the shorts one
|
||||
// translate(10 0) → translate(10)
|
||||
if (
|
||||
params.shortTranslate &&
|
||||
transform.name === 'translate' &&
|
||||
transform.data.length === 2 &&
|
||||
!transform.data[1]
|
||||
) {
|
||||
transform.data.pop();
|
||||
}
|
||||
|
||||
// convert long scale transform notation to the shorts one
|
||||
// scale(2 2) → scale(2)
|
||||
if (
|
||||
params.shortScale &&
|
||||
transform.name === 'scale' &&
|
||||
transform.data.length === 2 &&
|
||||
transform.data[0] === transform.data[1]
|
||||
) {
|
||||
transform.data.pop();
|
||||
}
|
||||
|
||||
// convert long rotate transform notation to the short one
|
||||
// translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy)
|
||||
if (
|
||||
params.shortRotate &&
|
||||
transforms[i - 2]?.name === 'translate' &&
|
||||
transforms[i - 1].name === 'rotate' &&
|
||||
transforms[i].name === 'translate' &&
|
||||
transforms[i - 2].data[0] === -transforms[i].data[0] &&
|
||||
transforms[i - 2].data[1] === -transforms[i].data[1]
|
||||
) {
|
||||
transforms.splice(i - 2, 3, {
|
||||
name: 'rotate',
|
||||
data: [
|
||||
transforms[i - 1].data[0],
|
||||
transforms[i - 2].data[0],
|
||||
transforms[i - 2].data[1],
|
||||
],
|
||||
});
|
||||
|
||||
// splice compensation
|
||||
i -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return transforms;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove useless transforms.
|
||||
*
|
||||
* @param {ReadonlyArray<TransformItem>} transforms
|
||||
* @returns {TransformItem[]}
|
||||
*/
|
||||
const removeUseless = (transforms) => {
|
||||
return transforms.filter((transform) => {
|
||||
// translate(0), rotate(0[, cx, cy]), skewX(0), skewY(0)
|
||||
if (
|
||||
(['translate', 'rotate', 'skewX', 'skewY'].indexOf(transform.name) > -1 &&
|
||||
(transform.data.length == 1 || transform.name == 'rotate') &&
|
||||
!transform.data[0]) ||
|
||||
// translate(0, 0)
|
||||
(transform.name == 'translate' &&
|
||||
!transform.data[0] &&
|
||||
!transform.data[1]) ||
|
||||
// scale(1)
|
||||
(transform.name == 'scale' &&
|
||||
transform.data[0] == 1 &&
|
||||
(transform.data.length < 2 || transform.data[1] == 1)) ||
|
||||
// matrix(1 0 0 1 0 0)
|
||||
(transform.name == 'matrix' &&
|
||||
transform.data[0] == 1 &&
|
||||
transform.data[3] == 1 &&
|
||||
!(
|
||||
transform.data[1] ||
|
||||
transform.data[2] ||
|
||||
transform.data[4] ||
|
||||
transform.data[5]
|
||||
))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
393
Frontend-Learner/node_modules/svgo/plugins/inlineStyles.js
generated
vendored
Normal file
393
Frontend-Learner/node_modules/svgo/plugins/inlineStyles.js
generated
vendored
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
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 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.
|
||||
*/
|
||||
|
||||
export const name = 'inlineStyles';
|
||||
export const description = 'inline styles (additional options)';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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 preservedPseudos = [
|
||||
...pseudoClasses.functional,
|
||||
...pseudoClasses.treeStructural,
|
||||
];
|
||||
|
||||
/**
|
||||
* Merges styles from style nodes into inline styles.
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<InlineStylesParams>}
|
||||
* @author strarsis <strarsis@gmail.com>
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
onlyMatchedOnce = true,
|
||||
removeMatchedSelectors = true,
|
||||
useMqs = ['', 'screen'],
|
||||
usePseudos = [''],
|
||||
} = params;
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* node: import('../lib/types.js').XastElement,
|
||||
* parentNode: import('../lib/types.js').XastParent,
|
||||
* cssAst: csstree.StyleSheet
|
||||
* }[]}
|
||||
*/
|
||||
const styles = [];
|
||||
/**
|
||||
* @type {{
|
||||
* node: csstree.Selector,
|
||||
* item: csstree.ListItem<csstree.CssNode>,
|
||||
* rule: csstree.Rule,
|
||||
* matchedElements?: import('../lib/types.js').XastElement[]
|
||||
* }[]}
|
||||
*/
|
||||
const selectors = [];
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'foreignObject') {
|
||||
return visitSkip;
|
||||
}
|
||||
if (node.name !== 'style' || node.children.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
node.attributes.type != null &&
|
||||
node.attributes.type !== '' &&
|
||||
node.attributes.type !== 'text/css'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
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, {
|
||||
parseValue: false,
|
||||
parseCustomProperty: false,
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (cssAst.type === 'StyleSheet') {
|
||||
styles.push({ node, parentNode, cssAst });
|
||||
}
|
||||
|
||||
// collect selectors
|
||||
csstree.walk(cssAst, {
|
||||
visit: 'Rule',
|
||||
enter(node) {
|
||||
const atrule = this.atrule;
|
||||
|
||||
// skip media queries not included into useMqs param
|
||||
let mediaQuery = '';
|
||||
if (atrule != null) {
|
||||
mediaQuery = atrule.name;
|
||||
if (atrule.prelude != null) {
|
||||
mediaQuery += ` ${csstree.generate(atrule.prelude)}`;
|
||||
}
|
||||
}
|
||||
if (!useMqs.includes(mediaQuery)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
root: {
|
||||
exit: () => {
|
||||
if (styles.length === 0) {
|
||||
return;
|
||||
}
|
||||
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 {import('../lib/types.js').XastElement[]} */
|
||||
const matchedElements = [];
|
||||
try {
|
||||
for (const node of querySelectorAll(root, selectorText)) {
|
||||
if (node.type === 'element') {
|
||||
matchedElements.push(node);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
// nothing selected
|
||||
if (matchedElements.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// apply styles to matched elements
|
||||
// skip selectors that match more than once if option onlyMatchedOnce is enabled
|
||||
if (onlyMatchedOnce && matchedElements.length > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// apply <style/> to matched elements
|
||||
for (const selectedEl of matchedElements) {
|
||||
const styleDeclarationList = csstree.parse(
|
||||
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) {
|
||||
if (firstListItem == null) {
|
||||
firstListItem = item;
|
||||
}
|
||||
|
||||
styleDeclarationItems.set(node.property.toLowerCase(), item);
|
||||
},
|
||||
});
|
||||
// merge declarations
|
||||
csstree.walk(selector.rule, {
|
||||
visit: 'Declaration',
|
||||
enter(ruleDeclaration) {
|
||||
// existing inline styles have higher priority
|
||||
// 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 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.insert(
|
||||
ruleDeclarationItem,
|
||||
firstListItem,
|
||||
);
|
||||
} else if (
|
||||
matchedItem.data.important !== true &&
|
||||
ruleDeclaration.important === true
|
||||
) {
|
||||
styleDeclarationList.children.replace(
|
||||
matchedItem,
|
||||
ruleDeclarationItem,
|
||||
);
|
||||
styleDeclarationItems.set(property, ruleDeclarationItem);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const newStyles = csstree.generate(styleDeclarationList);
|
||||
if (newStyles.length !== 0) {
|
||||
selectedEl.attributes.style = newStyles;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
removeMatchedSelectors &&
|
||||
matchedElements.length !== 0 &&
|
||||
selector.rule.prelude.type === 'SelectorList'
|
||||
) {
|
||||
// clean up matching simple selectors if option removeMatchedSelectors is enabled
|
||||
selector.rule.prelude.children.remove(selector.item);
|
||||
}
|
||||
selector.matchedElements = matchedElements;
|
||||
}
|
||||
|
||||
// no further processing required
|
||||
if (!removeMatchedSelectors) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clean up matched class + ID attribute values
|
||||
for (const selector of sortedSelectors) {
|
||||
if (selector.matchedElements == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (onlyMatchedOnce && selector.matchedElements.length > 1) {
|
||||
// skip selectors that match more than once if option onlyMatchedOnce is enabled
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const selectedEl of selector.matchedElements) {
|
||||
// class
|
||||
const classList = new Set(
|
||||
selectedEl.attributes.class == null
|
||||
? null
|
||||
: selectedEl.attributes.class.split(' '),
|
||||
);
|
||||
|
||||
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 {
|
||||
selectedEl.attributes.class = Array.from(classList).join(' ');
|
||||
}
|
||||
|
||||
// ID
|
||||
const firstSubSelector = selector.node.children.first;
|
||||
if (
|
||||
firstSubSelector?.type === 'IdSelector' &&
|
||||
selectedEl.attributes.id === firstSubSelector.name &&
|
||||
!selectors.some((selector) =>
|
||||
includesAttrSelector(
|
||||
selector.item,
|
||||
'id',
|
||||
firstSubSelector.name,
|
||||
true,
|
||||
),
|
||||
)
|
||||
) {
|
||||
delete selectedEl.attributes.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const style of styles) {
|
||||
csstree.walk(style.cssAst, {
|
||||
visit: 'Rule',
|
||||
enter: function (node, item, list) {
|
||||
// clean up <style/> rulesets without any css selectors left
|
||||
if (
|
||||
node.type === 'Rule' &&
|
||||
node.prelude.type === 'SelectorList' &&
|
||||
node.prelude.children.isEmpty
|
||||
) {
|
||||
list.remove(item);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (style.cssAst.children.isEmpty) {
|
||||
// remove empty style element
|
||||
detachNodeFromParent(style.node, style.parentNode);
|
||||
} else {
|
||||
// update style element if any styles left
|
||||
const firstChild = style.node.children[0];
|
||||
if (firstChild.type === 'text' || firstChild.type === 'cdata') {
|
||||
firstChild.value = csstree.generate(style.cssAst);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
169
Frontend-Learner/node_modules/svgo/plugins/mergePaths.js
generated
vendored
Normal file
169
Frontend-Learner/node_modules/svgo/plugins/mergePaths.js
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
import { intersects, js2path, path2js } from './_path.js';
|
||||
import { includesUrlReference } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @typedef MergePathsParams
|
||||
* @property {boolean=} force
|
||||
* @property {number=} floatPrecision
|
||||
* @property {boolean=} noSpaceAfterFlags
|
||||
*/
|
||||
|
||||
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.js').Plugin<MergePathsParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
force = false,
|
||||
floatPrecision = 3,
|
||||
noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||||
} = params;
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
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];
|
||||
|
||||
if (
|
||||
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;
|
||||
}
|
||||
|
||||
if (
|
||||
child.type !== 'element' ||
|
||||
child.name !== 'path' ||
|
||||
child.children.length !== 0 ||
|
||||
child.attributes.d == null
|
||||
) {
|
||||
if (prevPathData) {
|
||||
updatePreviousPath(prevChild, prevPathData);
|
||||
}
|
||||
prevChild = child;
|
||||
continue;
|
||||
}
|
||||
|
||||
const computedStyle = computeStyle(stylesheet, child);
|
||||
if (
|
||||
computedStyle['marker-start'] ||
|
||||
computedStyle['marker-mid'] ||
|
||||
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 areAttrsEqual = childAttrs.some((attr) => {
|
||||
return (
|
||||
attr !== 'd' &&
|
||||
prevChild.type === 'element' &&
|
||||
prevChild.attributes[attr] !== child.attributes[attr]
|
||||
);
|
||||
});
|
||||
|
||||
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),
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
81
Frontend-Learner/node_modules/svgo/plugins/mergeStyles.js
generated
vendored
Normal file
81
Frontend-Learner/node_modules/svgo/plugins/mergeStyles.js
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
|
||||
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.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
/** @type {?import('../lib/types.js').XastElement} */
|
||||
let firstStyleElement = null;
|
||||
let collectedStyles = '';
|
||||
/** @type {'text' | 'cdata'} */
|
||||
let styleContentType = 'text';
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// skip <foreignObject> content
|
||||
if (node.name === 'foreignObject') {
|
||||
return visitSkip;
|
||||
}
|
||||
|
||||
// collect style elements
|
||||
if (node.name !== 'style') {
|
||||
return;
|
||||
}
|
||||
|
||||
// skip <style> with invalid type attribute
|
||||
if (
|
||||
node.attributes.type != null &&
|
||||
node.attributes.type !== '' &&
|
||||
node.attributes.type !== 'text/css'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// extract style element content
|
||||
let css = '';
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'text') {
|
||||
css += child.value;
|
||||
}
|
||||
if (child.type === 'cdata') {
|
||||
styleContentType = 'cdata';
|
||||
css += child.value;
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty style elements
|
||||
if (css.trim().length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// collect css and wrap with media query if present in attribute
|
||||
if (node.attributes.media == null) {
|
||||
collectedStyles += css;
|
||||
} else {
|
||||
collectedStyles += `@media ${node.attributes.media}{${css}}`;
|
||||
delete node.attributes.media;
|
||||
}
|
||||
|
||||
// combine collected styles in the first style element
|
||||
if (firstStyleElement == null) {
|
||||
firstStyleElement = node;
|
||||
} else {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
/** @type {import('../lib/types.js').XastChild} */
|
||||
const child = { type: styleContentType, value: collectedStyles };
|
||||
firstStyleElement.children = [child];
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
155
Frontend-Learner/node_modules/svgo/plugins/minifyStyles.js
generated
vendored
Normal file
155
Frontend-Learner/node_modules/svgo/plugins/minifyStyles.js
generated
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import * as csso from 'csso';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { hasScripts } from '../lib/svgo/tools.js';
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export const name = 'minifyStyles';
|
||||
export const description = 'minifies styles and removes unused styles';
|
||||
|
||||
/**
|
||||
* Minifies styles (<style> element + style attribute) using CSSO.
|
||||
*
|
||||
* @author strarsis <strarsis@gmail.com>
|
||||
* @type {import('../lib/types.js').Plugin<MinifyStylesParams>}
|
||||
*/
|
||||
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. For example, the document
|
||||
* contains scripts or in attributes.
|
||||
*/
|
||||
let forceUsageDeoptimized = false;
|
||||
|
||||
if (typeof usage === 'boolean') {
|
||||
enableTagsUsage = usage;
|
||||
enableIdsUsage = usage;
|
||||
enableClassesUsage = usage;
|
||||
} else if (usage) {
|
||||
enableTagsUsage = usage.tags == null ? true : usage.tags;
|
||||
enableIdsUsage = usage.ids == null ? true : usage.ids;
|
||||
enableClassesUsage = usage.classes == null ? true : usage.classes;
|
||||
forceUsageDeoptimized = usage.force == null ? false : usage.force;
|
||||
}
|
||||
|
||||
let deoptimized = false;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// detect deoptimizations
|
||||
if (hasScripts(node)) {
|
||||
deoptimized = true;
|
||||
}
|
||||
|
||||
// collect tags, ids and classes usage
|
||||
tagsUsage.add(node.name);
|
||||
if (node.attributes.id != null) {
|
||||
idsUsage.add(node.attributes.id);
|
||||
}
|
||||
if (node.attributes.class != null) {
|
||||
for (const className of node.attributes.class.split(/\s+/)) {
|
||||
classesUsage.add(className);
|
||||
}
|
||||
}
|
||||
// collect style elements or elements with style attribute
|
||||
if (node.name === 'style' && node.children.length !== 0) {
|
||||
styleElements.set(node, parentNode);
|
||||
} else if (node.attributes.style != null) {
|
||||
elementsWithStyleAttributes.push(node);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
root: {
|
||||
exit: () => {
|
||||
/** @type {csso.Usage} */
|
||||
const cssoUsage = {};
|
||||
if (!deoptimized || forceUsageDeoptimized) {
|
||||
if (enableTagsUsage) {
|
||||
cssoUsage.tags = Array.from(tagsUsage);
|
||||
}
|
||||
if (enableIdsUsage) {
|
||||
cssoUsage.ids = Array.from(idsUsage);
|
||||
}
|
||||
if (enableClassesUsage) {
|
||||
cssoUsage.classes = Array.from(classesUsage);
|
||||
}
|
||||
}
|
||||
// minify style elements
|
||||
for (const [styleNode, styleNodeParent] of styleElements.entries()) {
|
||||
if (
|
||||
styleNode.children[0].type === 'text' ||
|
||||
styleNode.children[0].type === 'cdata'
|
||||
) {
|
||||
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 optimization into separate plugin
|
||||
if (cssText.indexOf('>') >= 0 || cssText.indexOf('<') >= 0) {
|
||||
styleNode.children[0].type = 'cdata';
|
||||
styleNode.children[0].value = minified;
|
||||
} else {
|
||||
styleNode.children[0].type = 'text';
|
||||
styleNode.children[0].value = minified;
|
||||
}
|
||||
}
|
||||
}
|
||||
// minify style attributes
|
||||
for (const node of elementsWithStyleAttributes) {
|
||||
// style attribute
|
||||
const elemStyle = node.attributes.style;
|
||||
node.attributes.style = csso.minifyBlock(elemStyle, {
|
||||
...params,
|
||||
}).css;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
129
Frontend-Learner/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
generated
vendored
Normal file
129
Frontend-Learner/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
generated
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { visit } from '../lib/util/visit.js';
|
||||
import { inheritableAttrs, pathElems } from './_collections.js';
|
||||
|
||||
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
|
||||
*
|
||||
* @example
|
||||
* <g attr1="val1">
|
||||
* <g attr2="val2">
|
||||
* text
|
||||
* </g>
|
||||
* <circle attr2="val2" attr3="val3"/>
|
||||
* </g>
|
||||
* ⬇
|
||||
* <g attr1="val1" attr2="val2">
|
||||
* <g>
|
||||
* text
|
||||
* </g>
|
||||
* <circle attr3="val3"/>
|
||||
* </g>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = (root) => {
|
||||
// find if any style element is present
|
||||
let deoptimizedWithStyles = false;
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'style') {
|
||||
deoptimizedWithStyles = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
element: {
|
||||
exit: (node) => {
|
||||
// process only groups with more than 1 child
|
||||
if (node.name !== 'g' || node.children.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// deoptimize the plugin when style elements are present
|
||||
// selectors may rely on id, classes or tag names
|
||||
if (deoptimizedWithStyles) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find common attributes in group children.
|
||||
*
|
||||
* @type {Map<string, string>}
|
||||
*/
|
||||
const commonAttributes = new Map();
|
||||
let initial = true;
|
||||
let everyChildIsPath = true;
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element') {
|
||||
if (!pathElems.has(child.name)) {
|
||||
everyChildIsPath = false;
|
||||
}
|
||||
if (initial) {
|
||||
initial = false;
|
||||
// collect all inheritable attributes from first child element
|
||||
for (const [name, value] of Object.entries(child.attributes)) {
|
||||
// consider only inheritable attributes
|
||||
if (inheritableAttrs.has(name)) {
|
||||
commonAttributes.set(name, value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// exclude uncommon attributes from initial list
|
||||
for (const [name, value] of commonAttributes) {
|
||||
if (child.attributes[name] !== value) {
|
||||
commonAttributes.delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
) {
|
||||
commonAttributes.delete('transform');
|
||||
}
|
||||
|
||||
// preserve transform when all children are paths
|
||||
// so the transform could be applied to path data by other plugins
|
||||
if (everyChildIsPath) {
|
||||
commonAttributes.delete('transform');
|
||||
}
|
||||
|
||||
// add common children attributes to group
|
||||
for (const [name, value] of commonAttributes) {
|
||||
if (name === 'transform') {
|
||||
if (node.attributes.transform != null) {
|
||||
node.attributes.transform = `${node.attributes.transform} ${value}`;
|
||||
} else {
|
||||
node.attributes.transform = value;
|
||||
}
|
||||
} else {
|
||||
node.attributes[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// delete common attributes from children
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element') {
|
||||
for (const [name] of commonAttributes) {
|
||||
delete child.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
64
Frontend-Learner/node_modules/svgo/plugins/moveGroupAttrsToElems.js
generated
vendored
Normal file
64
Frontend-Learner/node_modules/svgo/plugins/moveGroupAttrsToElems.js
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { pathElems, referencesProps } from './_collections.js';
|
||||
import { includesUrlReference } from '../lib/svgo/tools.js';
|
||||
|
||||
export const name = 'moveGroupAttrsToElems';
|
||||
export const description =
|
||||
'moves some group attributes to the content elements';
|
||||
|
||||
const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text'];
|
||||
|
||||
/**
|
||||
* Move group attrs to the content elements.
|
||||
*
|
||||
* @example
|
||||
* <g transform="scale(2)">
|
||||
* <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>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
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 node.attributes.transform;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
273
Frontend-Learner/node_modules/svgo/plugins/prefixIds.js
generated
vendored
Normal file
273
Frontend-Learner/node_modules/svgo/plugins/prefixIds.js
generated
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
import * as csstree from 'css-tree';
|
||||
import { referencesProps } from './_collections.js';
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
export const name = 'prefixIds';
|
||||
export const description = 'prefix IDs';
|
||||
|
||||
/**
|
||||
* Extract basename from path.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
const getBasename = (path) => {
|
||||
// extract everything after latest slash or backslash
|
||||
const matched = /[/\\]?([^/\\]+)$/.exec(path);
|
||||
if (matched) {
|
||||
return matched[1];
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Escapes a string for being used as ID.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
const escapeIdentifierName = (str) => {
|
||||
return str.replace(/[. ]/g, '_');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
*/
|
||||
const unquote = (string) => {
|
||||
if (
|
||||
(string.startsWith('"') && string.endsWith('"')) ||
|
||||
(string.startsWith("'") && string.endsWith("'"))
|
||||
) {
|
||||
return string.slice(1, -1);
|
||||
}
|
||||
return 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 = (prefixGenerator, body) => {
|
||||
const prefix = prefixGenerator(body);
|
||||
if (body.startsWith(prefix)) {
|
||||
return body;
|
||||
}
|
||||
return prefix + body;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = (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.js').Plugin<PrefixIdsParams>}
|
||||
*/
|
||||
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) => {
|
||||
/**
|
||||
* @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 "#".
|
||||
*/
|
||||
const prefixGenerator = (id) =>
|
||||
generatePrefix(id, node, info, prefix, delim, prefixMap);
|
||||
|
||||
// prefix id/class selectors and url() references in styles
|
||||
if (node.name === 'style') {
|
||||
// skip empty <style/> elements
|
||||
if (node.children.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const child of node.children) {
|
||||
if (child.type !== 'text' && child.type !== 'cdata') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const cssText = child.value;
|
||||
/** @type {?csstree.CssNode} */
|
||||
let cssAst;
|
||||
try {
|
||||
cssAst = csstree.parse(cssText, {
|
||||
parseValue: true,
|
||||
parseCustomProperty: false,
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// prefix an ID attribute value
|
||||
if (
|
||||
prefixIds &&
|
||||
node.attributes.id != null &&
|
||||
node.attributes.id.length !== 0
|
||||
) {
|
||||
node.attributes.id = prefixId(prefixGenerator, node.attributes.id);
|
||||
}
|
||||
|
||||
// prefix a class attribute value
|
||||
if (
|
||||
prefixClassNames &&
|
||||
node.attributes.class != null &&
|
||||
node.attributes.class.length !== 0
|
||||
) {
|
||||
node.attributes.class = node.attributes.class
|
||||
.split(/\s+/)
|
||||
.map((name) => prefixId(prefixGenerator, name))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
// 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(
|
||||
prefixGenerator,
|
||||
node.attributes[name],
|
||||
);
|
||||
if (prefixed != null) {
|
||||
node.attributes[name] = prefixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(
|
||||
/\burl\((["'])?(#.+?)\1\)/gi,
|
||||
(match, _, url) => {
|
||||
const prefixed = prefixReference(prefixGenerator, url);
|
||||
if (prefixed == null) {
|
||||
return match;
|
||||
}
|
||||
return `url(${prefixed})`;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// prefix begin/end attribute value
|
||||
for (const name of ['begin', 'end']) {
|
||||
if (
|
||||
node.attributes[name] != null &&
|
||||
node.attributes[name].length !== 0
|
||||
) {
|
||||
const parts = node.attributes[name].split(/\s*;\s+/).map((val) => {
|
||||
if (val.endsWith('.end') || val.endsWith('.start')) {
|
||||
const [id, postfix] = val.split('.');
|
||||
return `${prefixId(prefixGenerator, id)}.${postfix}`;
|
||||
}
|
||||
return val;
|
||||
});
|
||||
node.attributes[name] = parts.join('; ');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
77
Frontend-Learner/node_modules/svgo/plugins/preset-default.js
generated
vendored
Normal file
77
Frontend-Learner/node_modules/svgo/plugins/preset-default.js
generated
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
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: 'preset-default',
|
||||
plugins: [
|
||||
removeDoctype,
|
||||
removeXMLProcInst,
|
||||
removeComments,
|
||||
removeDeprecatedAttrs,
|
||||
removeMetadata,
|
||||
removeEditorsNSData,
|
||||
cleanupAttrs,
|
||||
mergeStyles,
|
||||
inlineStyles,
|
||||
minifyStyles,
|
||||
cleanupIds,
|
||||
removeUselessDefs,
|
||||
cleanupNumericValues,
|
||||
convertColors,
|
||||
removeUnknownsAndDefaults,
|
||||
removeNonInheritableGroupAttrs,
|
||||
removeUselessStrokeAndFill,
|
||||
cleanupEnableBackground,
|
||||
removeHiddenElems,
|
||||
removeEmptyText,
|
||||
convertShapeToPath,
|
||||
convertEllipseToCircle,
|
||||
moveElemsAttrsToGroup,
|
||||
moveGroupAttrsToElems,
|
||||
collapseGroups,
|
||||
convertPathData,
|
||||
convertTransform,
|
||||
removeEmptyAttrs,
|
||||
removeEmptyContainers,
|
||||
mergePaths,
|
||||
removeUnusedNS,
|
||||
sortAttrs,
|
||||
sortDefsChildren,
|
||||
removeDesc,
|
||||
],
|
||||
});
|
||||
|
||||
export default presetDefault;
|
||||
95
Frontend-Learner/node_modules/svgo/plugins/removeAttributesBySelector.js
generated
vendored
Normal file
95
Frontend-Learner/node_modules/svgo/plugins/removeAttributesBySelector.js
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { querySelectorAll } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeAttributesBySelector';
|
||||
export const description =
|
||||
'removes attributes of elements that match a css selector';
|
||||
|
||||
/**
|
||||
* Removes attributes of elements that match a css selector.
|
||||
*
|
||||
* @example
|
||||
* <caption>A selector removing a single attribute</caption>
|
||||
* plugins: [
|
||||
* {
|
||||
* name: "removeAttributesBySelector",
|
||||
* params: {
|
||||
* selector: "[fill='#00ff00']"
|
||||
* attributes: "fill"
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
|
||||
* ↓
|
||||
* <rect x="0" y="0" width="100" height="100" stroke="#00ff00"/>
|
||||
*
|
||||
* <caption>A selector removing multiple attributes</caption>
|
||||
* plugins: [
|
||||
* {
|
||||
* name: "removeAttributesBySelector",
|
||||
* params: {
|
||||
* selector: "[fill='#00ff00']",
|
||||
* attributes: [
|
||||
* "fill",
|
||||
* "stroke"
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
|
||||
* ↓
|
||||
* <rect x="0" y="0" width="100" height="100"/>
|
||||
*
|
||||
* <caption>Multiple selectors removing attributes</caption>
|
||||
* plugins: [
|
||||
* {
|
||||
* name: "removeAttributesBySelector",
|
||||
* params: {
|
||||
* selectors: [
|
||||
* {
|
||||
* selector: "[fill='#00ff00']",
|
||||
* attributes: "fill"
|
||||
* },
|
||||
* {
|
||||
* selector: "#remove",
|
||||
* attributes: [
|
||||
* "stroke",
|
||||
* "id"
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/>
|
||||
* ↓
|
||||
* <rect x="0" y="0" width="100" height="100"/>
|
||||
*
|
||||
* @link https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors
|
||||
*
|
||||
* @author Bradley Mease
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<any>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const selectors = Array.isArray(params.selectors)
|
||||
? params.selectors
|
||||
: [params];
|
||||
for (const { selector, attributes } of selectors) {
|
||||
const nodes = querySelectorAll(root, selector);
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'element') {
|
||||
if (Array.isArray(attributes)) {
|
||||
for (const name of attributes) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
} else {
|
||||
delete node.attributes[attributes];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
153
Frontend-Learner/node_modules/svgo/plugins/removeAttrs.js
generated
vendored
Normal file
153
Frontend-Learner/node_modules/svgo/plugins/removeAttrs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* @typedef RemoveAttrsParams
|
||||
* @property {string=} elemSeparator
|
||||
* @property {boolean=} preserveCurrentColor
|
||||
* @property {string | string[]} attrs
|
||||
*/
|
||||
|
||||
export const name = 'removeAttrs';
|
||||
export const description = 'removes specified attributes';
|
||||
|
||||
const DEFAULT_SEPARATOR = ':';
|
||||
const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter.
|
||||
It should have a pattern to remove, otherwise the plugin is a noop.
|
||||
Config example:
|
||||
|
||||
plugins: [
|
||||
{
|
||||
name: "removeAttrs",
|
||||
params: {
|
||||
attrs: "(fill|stroke)"
|
||||
}
|
||||
}
|
||||
]
|
||||
`;
|
||||
|
||||
/**
|
||||
* Remove attributes
|
||||
*
|
||||
* @example elemSeparator
|
||||
* format: string
|
||||
*
|
||||
* @example preserveCurrentColor
|
||||
* format: boolean
|
||||
*
|
||||
* @example attrs:
|
||||
*
|
||||
* format: [ element* : attribute* : value* ]
|
||||
*
|
||||
* element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used)
|
||||
* attribute : regexp (wrapped into ^...$)
|
||||
* value : regexp (wrapped into ^...$), single * or omitted > all values
|
||||
*
|
||||
* examples:
|
||||
*
|
||||
* > basic: remove fill attribute
|
||||
* ---
|
||||
* removeAttrs:
|
||||
* attrs: 'fill'
|
||||
*
|
||||
* > remove fill attribute on path element
|
||||
* ---
|
||||
* attrs: 'path:fill'
|
||||
*
|
||||
* > remove fill attribute on path element where value is none
|
||||
* ---
|
||||
* attrs: 'path:fill:none'
|
||||
*
|
||||
*
|
||||
* > remove all fill and stroke attribute
|
||||
* ---
|
||||
* attrs:
|
||||
* - 'fill'
|
||||
* - 'stroke'
|
||||
*
|
||||
* [is same as]
|
||||
*
|
||||
* attrs: '(fill|stroke)'
|
||||
*
|
||||
* [is same as]
|
||||
*
|
||||
* attrs: '*:(fill|stroke)'
|
||||
*
|
||||
* [is same as]
|
||||
*
|
||||
* attrs: '.*:(fill|stroke)'
|
||||
*
|
||||
* [is same as]
|
||||
*
|
||||
* attrs: '.*:(fill|stroke):.*'
|
||||
*
|
||||
*
|
||||
* > remove all stroke related attributes
|
||||
* ----
|
||||
* attrs: 'stroke.*'
|
||||
*
|
||||
*
|
||||
* @author Benny Schudel
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveAttrsParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
if (typeof params.attrs == 'undefined') {
|
||||
console.warn(ENOATTRS);
|
||||
return null;
|
||||
}
|
||||
|
||||
const elemSeparator =
|
||||
typeof params.elemSeparator == 'string'
|
||||
? params.elemSeparator
|
||||
: DEFAULT_SEPARATOR;
|
||||
const preserveCurrentColor =
|
||||
typeof params.preserveCurrentColor == 'boolean'
|
||||
? params.preserveCurrentColor
|
||||
: false;
|
||||
const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs];
|
||||
|
||||
return {
|
||||
element: {
|
||||
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)) {
|
||||
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, '.*'].join(elemSeparator);
|
||||
}
|
||||
|
||||
// create regexps for element, attribute name, and attribute value
|
||||
const list = pattern.split(elemSeparator).map((value) => {
|
||||
// adjust single * to match anything
|
||||
if (value === '*') {
|
||||
value = '.*';
|
||||
}
|
||||
return new RegExp(['^', value, '$'].join(''), 'i');
|
||||
});
|
||||
|
||||
// matches element
|
||||
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' && isCurrentColor;
|
||||
const isStrokeCurrentColor =
|
||||
preserveCurrentColor && name == 'stroke' && isCurrentColor;
|
||||
if (
|
||||
!isFillCurrentColor &&
|
||||
!isStrokeCurrentColor &&
|
||||
// matches attribute name
|
||||
list[1].test(name) &&
|
||||
// matches attribute value
|
||||
list[2].test(value)
|
||||
) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
54
Frontend-Learner/node_modules/svgo/plugins/removeComments.js
generated
vendored
Normal file
54
Frontend-Learner/node_modules/svgo/plugins/removeComments.js
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveCommentsParams
|
||||
* @property {ReadonlyArray<RegExp | string> | false=} preservePatterns
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* @example
|
||||
* <!-- Generator: Adobe Illustrator 15.0.0, SVG Export
|
||||
* Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveCommentsParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
const { preservePatterns = DEFAULT_PRESERVE_PATTERNS } = params;
|
||||
|
||||
return {
|
||||
comment: {
|
||||
enter: (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
Frontend-Learner/node_modules/svgo/plugins/removeDeprecatedAttrs.js
generated
vendored
Normal file
120
Frontend-Learner/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,
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
42
Frontend-Learner/node_modules/svgo/plugins/removeDesc.js
generated
vendored
Normal file
42
Frontend-Learner/node_modules/svgo/plugins/removeDesc.js
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveDescParams
|
||||
* @property {boolean=} removeAny
|
||||
*/
|
||||
|
||||
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 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.js').Plugin<RemoveDescParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const { removeAny = false } = params;
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'desc') {
|
||||
if (
|
||||
removeAny ||
|
||||
node.children.length === 0 ||
|
||||
(node.children[0].type === 'text' &&
|
||||
standardDescs.test(node.children[0].value))
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
41
Frontend-Learner/node_modules/svgo/plugins/removeDimensions.js
generated
vendored
Normal file
41
Frontend-Learner/node_modules/svgo/plugins/removeDimensions.js
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
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
|
||||
*
|
||||
* @example
|
||||
* <svg width="100" height="50" />
|
||||
* ↓
|
||||
* <svg viewBox="0 0 100 50" />
|
||||
*
|
||||
* @author Benny Schudel
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
38
Frontend-Learner/node_modules/svgo/plugins/removeDoctype.js
generated
vendored
Normal file
38
Frontend-Learner/node_modules/svgo/plugins/removeDoctype.js
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeDoctype';
|
||||
export const description = 'removes doctype declaration';
|
||||
|
||||
/**
|
||||
* Remove DOCTYPE declaration.
|
||||
*
|
||||
* "Unfortunately the SVG DTDs are a source of so many
|
||||
* issues that the SVG WG has decided not to write one
|
||||
* for the upcoming SVG 1.2 standard. In fact SVG WG
|
||||
* members are even telling people not to use a DOCTYPE
|
||||
* declaration in SVG 1.0 and 1.1 documents"
|
||||
* https://jwatt.org/svg/authoring/#doctype-declaration
|
||||
*
|
||||
* @example
|
||||
* <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
* q"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
*
|
||||
* @example
|
||||
* <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
* "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [
|
||||
* <!-- an internal subset can be embedded here -->
|
||||
* ]>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
doctype: {
|
||||
enter: (node, parentNode) => {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
66
Frontend-Learner/node_modules/svgo/plugins/removeEditorsNSData.js
generated
vendored
Normal file
66
Frontend-Learner/node_modules/svgo/plugins/removeEditorsNSData.js
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { editorNamespaces } from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveEditorsNSDataParams
|
||||
* @property {string[]=} additionalNamespaces
|
||||
*/
|
||||
|
||||
export const name = 'removeEditorsNSData';
|
||||
export const description =
|
||||
'removes editors namespaces, elements and attributes';
|
||||
|
||||
/**
|
||||
* Remove editors namespaces, elements and attributes.
|
||||
*
|
||||
* @example
|
||||
* <svg xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
|
||||
* <sodipodi:namedview/>
|
||||
* <path sodipodi:nodetypes="cccc"/>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveEditorsNSDataParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
let namespaces = [...editorNamespaces];
|
||||
if (Array.isArray(params.additionalNamespaces)) {
|
||||
namespaces = [...editorNamespaces, ...params.additionalNamespaces];
|
||||
}
|
||||
/** @type {string[]} */
|
||||
const prefixes = [];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// 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)) {
|
||||
prefixes.push(name.slice('xmlns:'.length));
|
||||
// <svg xmlns:sodipodi="">
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove editor attributes, for example
|
||||
// <* sodipodi:*="">
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (name.includes(':')) {
|
||||
const [prefix] = name.split(':');
|
||||
if (prefixes.includes(prefix)) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove editor elements, for example
|
||||
// <sodipodi:*>
|
||||
if (node.name.includes(':')) {
|
||||
const [prefix] = node.name.split(':');
|
||||
if (prefixes.includes(prefix)) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
77
Frontend-Learner/node_modules/svgo/plugins/removeElementsByAttr.js
generated
vendored
Normal file
77
Frontend-Learner/node_modules/svgo/plugins/removeElementsByAttr.js
generated
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveElementsByAttrParams
|
||||
* @property {string | string[]=} id
|
||||
* @property {string | string[]=} class
|
||||
*/
|
||||
|
||||
export const name = 'removeElementsByAttr';
|
||||
export const description =
|
||||
'removes arbitrary elements by ID or className (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove arbitrary SVG elements by ID or className.
|
||||
*
|
||||
* @example id
|
||||
* > single: remove element with ID of `elementID`
|
||||
* ---
|
||||
* removeElementsByAttr:
|
||||
* id: 'elementID'
|
||||
*
|
||||
* > list: remove multiple elements by ID
|
||||
* ---
|
||||
* removeElementsByAttr:
|
||||
* id:
|
||||
* - 'elementID'
|
||||
* - 'anotherID'
|
||||
*
|
||||
* @example class
|
||||
* > single: remove all elements with class of `elementClass`
|
||||
* ---
|
||||
* removeElementsByAttr:
|
||||
* class: 'elementClass'
|
||||
*
|
||||
* > list: remove all elements with class of `elementClass` or `anotherClass`
|
||||
* ---
|
||||
* removeElementsByAttr:
|
||||
* class:
|
||||
* - 'elementClass'
|
||||
* - 'anotherClass'
|
||||
*
|
||||
* @author Eli Dupuis (@elidupuis)
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveElementsByAttrParams>}
|
||||
*/
|
||||
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];
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// remove element if it's `id` matches configured `id` params
|
||||
if (node.attributes.id != null && ids.length !== 0) {
|
||||
if (ids.includes(node.attributes.id)) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
// remove element if it's `class` contains any of the configured `class` params
|
||||
if (node.attributes.class && classes.length !== 0) {
|
||||
const classList = node.attributes.class.split(' ');
|
||||
for (const item of classes) {
|
||||
if (classList.includes(item)) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
29
Frontend-Learner/node_modules/svgo/plugins/removeEmptyAttrs.js
generated
vendored
Normal file
29
Frontend-Learner/node_modules/svgo/plugins/removeEmptyAttrs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { attrsGroups } from './_collections.js';
|
||||
|
||||
export const name = 'removeEmptyAttrs';
|
||||
export const description = 'removes empty attributes';
|
||||
|
||||
/**
|
||||
* Remove attributes with empty values.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (
|
||||
value === '' &&
|
||||
// empty conditional processing attributes prevents elements from rendering
|
||||
!attrsGroups.conditionalProcessing.has(name)
|
||||
) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
67
Frontend-Learner/node_modules/svgo/plugins/removeEmptyContainers.js
generated
vendored
Normal file
67
Frontend-Learner/node_modules/svgo/plugins/removeEmptyContainers.js
generated
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { elemsGroups } from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
|
||||
export const name = 'removeEmptyContainers';
|
||||
export const description = 'removes empty container elements';
|
||||
|
||||
/**
|
||||
* Remove empty containers.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/intro.html#TermContainerElement
|
||||
*
|
||||
* @example
|
||||
* <defs/>
|
||||
*
|
||||
* @example
|
||||
* <g><marker><a/></marker></g>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
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.has(node.name) ||
|
||||
node.children.length !== 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// empty patterns may contain reusable configuration
|
||||
if (
|
||||
node.name === 'pattern' &&
|
||||
Object.keys(node.attributes).length !== 0
|
||||
) {
|
||||
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);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
56
Frontend-Learner/node_modules/svgo/plugins/removeEmptyText.js
generated
vendored
Normal file
56
Frontend-Learner/node_modules/svgo/plugins/removeEmptyText.js
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveEmptyTextParams
|
||||
* @property {boolean=} text
|
||||
* @property {boolean=} tspan
|
||||
* @property {boolean=} tref
|
||||
*/
|
||||
|
||||
export const name = 'removeEmptyText';
|
||||
export const description = 'removes empty <text> elements';
|
||||
|
||||
/**
|
||||
* Remove empty Text elements.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/text.html
|
||||
*
|
||||
* @example
|
||||
* Remove empty text element:
|
||||
* <text/>
|
||||
*
|
||||
* Remove empty tspan element:
|
||||
* <tspan/>
|
||||
*
|
||||
* Remove tref with empty xlink:href attribute:
|
||||
* <tref xlink:href=""/>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveEmptyTextParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const { text = true, tspan = true, tref = true } = params;
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// Remove empty text element
|
||||
if (text && node.name === 'text' && node.children.length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
// Remove empty tspan element
|
||||
if (tspan && node.name === 'tspan' && node.children.length === 0) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
// Remove tref with empty xlink:href attribute
|
||||
if (
|
||||
tref &&
|
||||
node.name === 'tref' &&
|
||||
node.attributes['xlink:href'] == null
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
459
Frontend-Learner/node_modules/svgo/plugins/removeHiddenElems.js
generated
vendored
Normal file
459
Frontend-Learner/node_modules/svgo/plugins/removeHiddenElems.js
generated
vendored
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
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';
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
const nonRendering = elemsGroups.nonRendering;
|
||||
|
||||
export const name = 'removeHiddenElems';
|
||||
export const description =
|
||||
'removes hidden elements (zero sized, with absent attributes)';
|
||||
|
||||
/**
|
||||
* Remove hidden elements with disabled rendering:
|
||||
* - display="none"
|
||||
* - opacity="0"
|
||||
* - circle with zero radius
|
||||
* - ellipse with zero x-axis or y-axis radius
|
||||
* - rectangle with zero width or height
|
||||
* - pattern with zero width or height
|
||||
* - image with zero width or height
|
||||
* - path with empty data
|
||||
* - polyline with empty points
|
||||
* - polygon with empty points
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveHiddenElemsParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
isHidden = true,
|
||||
displayNone = true,
|
||||
opacity0 = true,
|
||||
circleR0 = true,
|
||||
ellipseRX0 = true,
|
||||
ellipseRY0 = true,
|
||||
rectWidth0 = true,
|
||||
rectHeight0 = true,
|
||||
patternWidth0 = true,
|
||||
patternHeight0 = true,
|
||||
imageWidth0 = true,
|
||||
imageHeight0 = true,
|
||||
pathEmptyD = true,
|
||||
polylineEmptyPoints = true,
|
||||
polygonEmptyPoints = true,
|
||||
} = 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);
|
||||
if (
|
||||
isHidden &&
|
||||
computedStyle.visibility &&
|
||||
computedStyle.visibility.type === 'static' &&
|
||||
computedStyle.visibility.value === 'hidden' &&
|
||||
// keep if any descendant enables visibility
|
||||
querySelector(node, '[visibility=visible]') == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// display="none"
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
|
||||
// "A value of display: none indicates that the given element
|
||||
// and its children shall not be rendered directly"
|
||||
if (
|
||||
displayNone &&
|
||||
computedStyle.display &&
|
||||
computedStyle.display.type === 'static' &&
|
||||
computedStyle.display.value === 'none' &&
|
||||
// markers with display: none still rendered
|
||||
node.name !== 'marker'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Circles with zero radius
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <circle r="0">
|
||||
if (
|
||||
circleR0 &&
|
||||
node.name === 'circle' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.r === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ellipse with zero x-axis radius
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <ellipse rx="0">
|
||||
if (
|
||||
ellipseRX0 &&
|
||||
node.name === 'ellipse' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.rx === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ellipse with zero y-axis radius
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <ellipse ry="0">
|
||||
if (
|
||||
ellipseRY0 &&
|
||||
node.name === 'ellipse' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.ry === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rectangle with zero width
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <rect width="0">
|
||||
if (
|
||||
rectWidth0 &&
|
||||
node.name === 'rect' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rectangle with zero height
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <rect height="0">
|
||||
if (
|
||||
rectHeight0 &&
|
||||
rectWidth0 &&
|
||||
node.name === 'rect' &&
|
||||
node.children.length === 0 &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pattern with zero width
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
||||
//
|
||||
// <pattern width="0">
|
||||
if (
|
||||
patternWidth0 &&
|
||||
node.name === 'pattern' &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pattern with zero height
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element (i.e., no paint is applied)"
|
||||
//
|
||||
// <pattern height="0">
|
||||
if (
|
||||
patternHeight0 &&
|
||||
node.name === 'pattern' &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Image with zero width
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <image width="0">
|
||||
if (
|
||||
imageWidth0 &&
|
||||
node.name === 'image' &&
|
||||
node.attributes.width === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Image with zero height
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute
|
||||
// "A value of zero disables rendering of the element"
|
||||
//
|
||||
// <image height="0">
|
||||
if (
|
||||
imageHeight0 &&
|
||||
node.name === 'image' &&
|
||||
node.attributes.height === '0'
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Path with empty data
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/paths.html#DAttribute
|
||||
//
|
||||
// <path d=""/>
|
||||
if (pathEmptyD && node.name === 'path') {
|
||||
if (node.attributes.d == null) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
const pathData = parsePathData(node.attributes.d);
|
||||
if (pathData.length === 0) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
// keep single point paths for markers
|
||||
if (
|
||||
pathData.length === 1 &&
|
||||
computedStyle['marker-start'] == null &&
|
||||
computedStyle['marker-end'] == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Polyline with empty points
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute
|
||||
//
|
||||
// <polyline points="">
|
||||
if (
|
||||
polylineEmptyPoints &&
|
||||
node.name === 'polyline' &&
|
||||
node.attributes.points == null
|
||||
) {
|
||||
removeElement(node, parentNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Polygon with empty points
|
||||
//
|
||||
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
|
||||
//
|
||||
// <polygon points="">
|
||||
if (
|
||||
polygonEmptyPoints &&
|
||||
node.name === 'polygon' &&
|
||||
node.attributes.points == null
|
||||
) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
25
Frontend-Learner/node_modules/svgo/plugins/removeMetadata.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/svgo/plugins/removeMetadata.js
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeMetadata';
|
||||
export const description = 'removes <metadata>';
|
||||
|
||||
/**
|
||||
* Remove <metadata>.
|
||||
*
|
||||
* https://www.w3.org/TR/SVG11/metadata.html
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'metadata') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
36
Frontend-Learner/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
generated
vendored
Normal file
36
Frontend-Learner/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import {
|
||||
attrsGroups,
|
||||
inheritableAttrs,
|
||||
presentationNonInheritableGroupAttrs,
|
||||
} from './_collections.js';
|
||||
|
||||
export const name = 'removeNonInheritableGroupAttrs';
|
||||
export const description =
|
||||
"removes non-inheritable group's presentational attributes";
|
||||
|
||||
/**
|
||||
* Remove non-inheritable group's "presentation" attributes.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
129
Frontend-Learner/node_modules/svgo/plugins/removeOffCanvasPaths.js
generated
vendored
Normal file
129
Frontend-Learner/node_modules/svgo/plugins/removeOffCanvasPaths.js
generated
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
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)';
|
||||
|
||||
/**
|
||||
* Remove elements that are drawn outside of the viewBox.
|
||||
*
|
||||
* @author JoshyPHP
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
/**
|
||||
* @type {?{
|
||||
* top: number,
|
||||
* right: number,
|
||||
* bottom: number,
|
||||
* left: number,
|
||||
* width: number,
|
||||
* height: number
|
||||
* }}
|
||||
*/
|
||||
let viewBoxData = null;
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
let viewBox = '';
|
||||
// find viewBox
|
||||
if (node.attributes.viewBox != null) {
|
||||
// remove commas and plus signs, normalize and trim whitespace
|
||||
viewBox = node.attributes.viewBox;
|
||||
} else if (
|
||||
node.attributes.height != null &&
|
||||
node.attributes.width != null
|
||||
) {
|
||||
viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`;
|
||||
}
|
||||
|
||||
// parse viewBox
|
||||
// remove commas and plus signs, normalize and trim whitespace
|
||||
viewBox = viewBox
|
||||
.replace(/[,+]|px/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/^\s*|\s*$/g, '');
|
||||
// ensure that the dimensions are 4 values separated by space
|
||||
const m =
|
||||
/^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(
|
||||
viewBox,
|
||||
);
|
||||
if (m == null) {
|
||||
return;
|
||||
}
|
||||
const left = Number.parseFloat(m[1]);
|
||||
const top = Number.parseFloat(m[2]);
|
||||
const width = Number.parseFloat(m[3]);
|
||||
const height = Number.parseFloat(m[4]);
|
||||
|
||||
// store the viewBox boundaries
|
||||
viewBoxData = {
|
||||
left,
|
||||
top,
|
||||
right: left + width,
|
||||
bottom: top + height,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
// consider that any item with a transform attribute is visible
|
||||
if (node.attributes.transform != null) {
|
||||
return visitSkip;
|
||||
}
|
||||
|
||||
if (
|
||||
node.name === 'path' &&
|
||||
node.attributes.d != null &&
|
||||
viewBoxData != null
|
||||
) {
|
||||
const pathData = parsePathData(node.attributes.d);
|
||||
|
||||
// consider that an M command within the viewBox is visible
|
||||
let visible = false;
|
||||
for (const pathDataItem of pathData) {
|
||||
if (pathDataItem.command === 'M') {
|
||||
const [x, y] = pathDataItem.args;
|
||||
if (
|
||||
x >= viewBoxData.left &&
|
||||
x <= viewBoxData.right &&
|
||||
y >= viewBoxData.top &&
|
||||
y <= viewBoxData.bottom
|
||||
) {
|
||||
visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathData.length === 2) {
|
||||
// close the path too short for intersects()
|
||||
pathData.push({ command: 'z', args: [] });
|
||||
}
|
||||
|
||||
const { left, top, width, height } = viewBoxData;
|
||||
/** @type {ReadonlyArray<import('../lib/types.js').PathDataItem>} */
|
||||
const viewBoxPathData = [
|
||||
{ command: 'M', args: [left, top] },
|
||||
{ command: 'h', args: [width] },
|
||||
{ command: 'v', args: [height] },
|
||||
{ command: 'H', args: [left] },
|
||||
{ command: 'z', args: [] },
|
||||
];
|
||||
|
||||
if (intersects(viewBoxPathData, pathData) === false) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
29
Frontend-Learner/node_modules/svgo/plugins/removeRasterImages.js
generated
vendored
Normal file
29
Frontend-Learner/node_modules/svgo/plugins/removeRasterImages.js
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeRasterImages';
|
||||
export const description = 'removes raster images (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove raster images references in <image>.
|
||||
*
|
||||
* @see https://bugs.webkit.org/show_bug.cgi?id=63548
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (
|
||||
node.name === 'image' &&
|
||||
node.attributes['xlink:href'] != null &&
|
||||
/(\.|image\/)(jpe?g|png|gif)/.test(node.attributes['xlink:href'])
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
63
Frontend-Learner/node_modules/svgo/plugins/removeScripts.js
generated
vendored
Normal file
63
Frontend-Learner/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);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
25
Frontend-Learner/node_modules/svgo/plugins/removeStyleElement.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/svgo/plugins/removeStyleElement.js
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeStyleElement';
|
||||
export const description = 'removes <style> element (disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove <style>.
|
||||
*
|
||||
* https://www.w3.org/TR/SVG11/styling.html#StyleElement
|
||||
*
|
||||
* @author Betsy Dupuis
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'style') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
25
Frontend-Learner/node_modules/svgo/plugins/removeTitle.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/svgo/plugins/removeTitle.js
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeTitle';
|
||||
export const description = 'removes <title>';
|
||||
|
||||
/**
|
||||
* Remove <title>.
|
||||
*
|
||||
* https://developer.mozilla.org/docs/Web/SVG/Element/title
|
||||
*
|
||||
* @author Igor Kalashnikov
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'title') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
214
Frontend-Learner/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
generated
vendored
Normal file
214
Frontend-Learner/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
generated
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
import {
|
||||
attrsGroups,
|
||||
attrsGroupsDefaults,
|
||||
elems,
|
||||
elemsGroups,
|
||||
presentationNonInheritableGroupAttrs,
|
||||
} from './_collections.js';
|
||||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { visitSkip } from '../lib/util/visit.js';
|
||||
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
||||
|
||||
/**
|
||||
* @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>>} */
|
||||
const allowedChildrenPerElement = new Map();
|
||||
/** @type {Map<string, Set<string>>} */
|
||||
const allowedAttributesPerElement = new Map();
|
||||
/** @type {Map<string, Map<string, string>>} */
|
||||
const attributesDefaultsPerElement = new Map();
|
||||
|
||||
for (const [name, config] of Object.entries(elems)) {
|
||||
/** @type {Set<string>} */
|
||||
const allowedChildren = new Set();
|
||||
if (config.content) {
|
||||
for (const elementName of config.content) {
|
||||
allowedChildren.add(elementName);
|
||||
}
|
||||
}
|
||||
if (config.contentGroups) {
|
||||
for (const contentGroupName of config.contentGroups) {
|
||||
const elemsGroup = elemsGroups[contentGroupName];
|
||||
if (elemsGroup) {
|
||||
for (const elementName of elemsGroup) {
|
||||
allowedChildren.add(elementName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @type {Set<string>} */
|
||||
const allowedAttributes = new Set();
|
||||
if (config.attrs) {
|
||||
for (const attrName of config.attrs) {
|
||||
allowedAttributes.add(attrName);
|
||||
}
|
||||
}
|
||||
/** @type {Map<string, string>} */
|
||||
const attributesDefaults = new Map();
|
||||
if (config.defaults) {
|
||||
for (const [attrName, defaultValue] of Object.entries(config.defaults)) {
|
||||
attributesDefaults.set(attrName, defaultValue);
|
||||
}
|
||||
}
|
||||
for (const attrsGroupName of config.attrsGroups) {
|
||||
const attrsGroup = attrsGroups[attrsGroupName];
|
||||
if (attrsGroup) {
|
||||
for (const attrName of attrsGroup) {
|
||||
allowedAttributes.add(attrName);
|
||||
}
|
||||
}
|
||||
const groupDefaults = attrsGroupsDefaults[attrsGroupName];
|
||||
if (groupDefaults) {
|
||||
for (const [attrName, defaultValue] of Object.entries(groupDefaults)) {
|
||||
attributesDefaults.set(attrName, defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
allowedChildrenPerElement.set(name, allowedChildren);
|
||||
allowedAttributesPerElement.set(name, allowedAttributes);
|
||||
attributesDefaultsPerElement.set(name, attributesDefaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unknown elements content and attributes,
|
||||
* remove attributes with default values.
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveUnknownsAndDefaultsParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
unknownContent = true,
|
||||
unknownAttrs = true,
|
||||
defaultAttrs = true,
|
||||
defaultMarkupDeclarations = true,
|
||||
uselessOverrides = true,
|
||||
keepDataAttrs = true,
|
||||
keepAriaAttrs = true,
|
||||
keepRoleAttr = false,
|
||||
} = 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
|
||||
if (node.name.includes(':')) {
|
||||
return;
|
||||
}
|
||||
// skip visiting foreignObject subtree
|
||||
if (node.name === 'foreignObject') {
|
||||
return visitSkip;
|
||||
}
|
||||
|
||||
// remove unknown element's content
|
||||
if (unknownContent && parentNode.type === 'element') {
|
||||
const allowedChildren = allowedChildrenPerElement.get(
|
||||
parentNode.name,
|
||||
);
|
||||
if (allowedChildren == null || allowedChildren.size === 0) {
|
||||
// remove unknown elements
|
||||
if (allowedChildrenPerElement.get(node.name) == null) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// remove not allowed children
|
||||
if (allowedChildren.has(node.name) === false) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowedAttributes = allowedAttributesPerElement.get(node.name);
|
||||
const attributesDefaults = attributesDefaultsPerElement.get(node.name);
|
||||
const computedParentStyle =
|
||||
parentNode.type === 'element'
|
||||
? computeStyle(stylesheet, parentNode)
|
||||
: null;
|
||||
|
||||
// remove element's unknown attrs and attrs with default values
|
||||
for (const [name, value] of Object.entries(node.attributes)) {
|
||||
if (keepDataAttrs && name.startsWith('data-')) {
|
||||
continue;
|
||||
}
|
||||
if (keepAriaAttrs && name.startsWith('aria-')) {
|
||||
continue;
|
||||
}
|
||||
if (keepRoleAttr && name === 'role') {
|
||||
continue;
|
||||
}
|
||||
// skip xmlns attribute
|
||||
if (name === 'xmlns') {
|
||||
continue;
|
||||
}
|
||||
// skip namespaced attributes except xml:* and xlink:*
|
||||
if (name.includes(':')) {
|
||||
const [prefix] = name.split(':');
|
||||
if (prefix !== 'xml' && prefix !== 'xlink') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
unknownAttrs &&
|
||||
allowedAttributes &&
|
||||
allowedAttributes.has(name) === false
|
||||
) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
if (
|
||||
defaultAttrs &&
|
||||
node.attributes.id == null &&
|
||||
attributesDefaults &&
|
||||
attributesDefaults.get(name) === value
|
||||
) {
|
||||
// keep defaults if parent has own or inherited style
|
||||
if (computedParentStyle?.[name] == null) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
if (uselessOverrides && node.attributes.id == null) {
|
||||
const style = computedParentStyle?.[name];
|
||||
if (
|
||||
presentationNonInheritableGroupAttrs.has(name) === false &&
|
||||
style != null &&
|
||||
style.type === 'static' &&
|
||||
style.value === value
|
||||
) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
55
Frontend-Learner/node_modules/svgo/plugins/removeUnusedNS.js
generated
vendored
Normal file
55
Frontend-Learner/node_modules/svgo/plugins/removeUnusedNS.js
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
export const name = 'removeUnusedNS';
|
||||
export const description = 'removes unused namespaces declaration';
|
||||
|
||||
/**
|
||||
* Remove unused namespaces declaration from svg element
|
||||
* which are not used in elements or attributes
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
/** @type {Set<string>} */
|
||||
const unusedNamespaces = new Set();
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// collect all namespaces from svg element
|
||||
// (such as xmlns:xlink="http://www.w3.org/1999/xlink")
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (name.startsWith('xmlns:')) {
|
||||
const local = name.slice('xmlns:'.length);
|
||||
unusedNamespaces.add(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unusedNamespaces.size !== 0) {
|
||||
// preserve namespace used in nested elements names
|
||||
if (node.name.includes(':')) {
|
||||
const [ns] = node.name.split(':');
|
||||
if (unusedNamespaces.has(ns)) {
|
||||
unusedNamespaces.delete(ns);
|
||||
}
|
||||
}
|
||||
// preserve namespace used in nested elements attributes
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (name.includes(':')) {
|
||||
const [ns] = name.split(':');
|
||||
unusedNamespaces.delete(ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
exit: (node, parentNode) => {
|
||||
// remove unused namespace attributes from svg element
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
for (const name of unusedNamespaces) {
|
||||
delete node.attributes[`xmlns:${name}`];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
50
Frontend-Learner/node_modules/svgo/plugins/removeUselessDefs.js
generated
vendored
Normal file
50
Frontend-Learner/node_modules/svgo/plugins/removeUselessDefs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
import { elemsGroups } from './_collections.js';
|
||||
|
||||
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.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
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);
|
||||
}
|
||||
node.children = usefulNodes;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastElement} node
|
||||
* @param {import('../lib/types.js').XastElement[]} usefulNodes
|
||||
*/
|
||||
const collectUsefulNodes = (node, usefulNodes) => {
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element') {
|
||||
if (child.attributes.id != null || child.name === 'style') {
|
||||
usefulNodes.push(child);
|
||||
} else {
|
||||
collectUsefulNodes(child, usefulNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
145
Frontend-Learner/node_modules/svgo/plugins/removeUselessStrokeAndFill.js
generated
vendored
Normal file
145
Frontend-Learner/node_modules/svgo/plugins/removeUselessStrokeAndFill.js
generated
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
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';
|
||||
|
||||
/**
|
||||
* @typedef RemoveUselessStrokeAndFillParams
|
||||
* @property {boolean=} stroke
|
||||
* @property {boolean=} fill
|
||||
* @property {boolean=} removeNone
|
||||
*/
|
||||
|
||||
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.js').Plugin<RemoveUselessStrokeAndFillParams>}
|
||||
*/
|
||||
export const fn = (root, params) => {
|
||||
const {
|
||||
stroke: removeStroke = true,
|
||||
fill: removeFill = true,
|
||||
removeNone = false,
|
||||
} = params;
|
||||
|
||||
// style and script elements deoptimize this plugin
|
||||
let hasStyleOrScript = false;
|
||||
visit(root, {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'style' || hasScripts(node)) {
|
||||
hasStyleOrScript = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
if (hasStyleOrScript) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
// id attribute deoptimize the whole subtree
|
||||
if (node.attributes.id != null) {
|
||||
return visitSkip;
|
||||
}
|
||||
if (!elemsGroups.shape.has(node.name)) {
|
||||
return;
|
||||
}
|
||||
const computedStyle = computeStyle(stylesheet, node);
|
||||
const stroke = computedStyle.stroke;
|
||||
const strokeOpacity = computedStyle['stroke-opacity'];
|
||||
const strokeWidth = computedStyle['stroke-width'];
|
||||
const markerEnd = computedStyle['marker-end'];
|
||||
const fill = computedStyle.fill;
|
||||
const fillOpacity = computedStyle['fill-opacity'];
|
||||
const computedParentStyle =
|
||||
parentNode.type === 'element'
|
||||
? computeStyle(stylesheet, parentNode)
|
||||
: null;
|
||||
const parentStroke =
|
||||
computedParentStyle == null ? null : computedParentStyle.stroke;
|
||||
|
||||
// remove stroke*
|
||||
if (removeStroke) {
|
||||
if (
|
||||
stroke == null ||
|
||||
(stroke.type === 'static' && stroke.value == 'none') ||
|
||||
(strokeOpacity != null &&
|
||||
strokeOpacity.type === 'static' &&
|
||||
strokeOpacity.value === '0') ||
|
||||
(strokeWidth != null &&
|
||||
strokeWidth.type === 'static' &&
|
||||
strokeWidth.value === '0')
|
||||
) {
|
||||
// stroke-width may affect the size of marker-end
|
||||
// marker is not visible when stroke-width is 0
|
||||
if (
|
||||
(strokeWidth != null &&
|
||||
strokeWidth.type === 'static' &&
|
||||
strokeWidth.value === '0') ||
|
||||
markerEnd == null
|
||||
) {
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (name.startsWith('stroke')) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
// set explicit none to not inherit from parent
|
||||
if (
|
||||
parentStroke != null &&
|
||||
parentStroke.type === 'static' &&
|
||||
parentStroke.value !== 'none'
|
||||
) {
|
||||
node.attributes.stroke = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove fill*
|
||||
if (removeFill) {
|
||||
if (
|
||||
(fill != null && fill.type === 'static' && fill.value === 'none') ||
|
||||
(fillOpacity != null &&
|
||||
fillOpacity.type === 'static' &&
|
||||
fillOpacity.value === '0')
|
||||
) {
|
||||
for (const name of Object.keys(node.attributes)) {
|
||||
if (name.startsWith('fill-')) {
|
||||
delete node.attributes[name];
|
||||
}
|
||||
}
|
||||
if (
|
||||
fill == null ||
|
||||
(fill.type === 'static' && fill.value !== 'none')
|
||||
) {
|
||||
node.attributes.fill = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removeNone) {
|
||||
if (
|
||||
(stroke == null || node.attributes.stroke === 'none') &&
|
||||
((fill != null &&
|
||||
fill.type === 'static' &&
|
||||
fill.value === 'none') ||
|
||||
node.attributes.fill === 'none')
|
||||
) {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
47
Frontend-Learner/node_modules/svgo/plugins/removeViewBox.js
generated
vendored
Normal file
47
Frontend-Learner/node_modules/svgo/plugins/removeViewBox.js
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
export const name = 'removeViewBox';
|
||||
export const description = 'removes viewBox attribute when possible';
|
||||
|
||||
const viewBoxElems = new Set(['pattern', 'svg', 'symbol']);
|
||||
|
||||
/**
|
||||
* Remove viewBox attr which coincides with a width/height box.
|
||||
*
|
||||
* @see https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
|
||||
*
|
||||
* @example
|
||||
* <svg width="100" height="50" viewBox="0 0 100 50">
|
||||
* ⬇
|
||||
* <svg width="100" height="50">
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (
|
||||
viewBoxElems.has(node.name) &&
|
||||
node.attributes.viewBox != null &&
|
||||
node.attributes.width != null &&
|
||||
node.attributes.height != null
|
||||
) {
|
||||
// TODO remove width/height for such case instead
|
||||
if (node.name === 'svg' && parentNode.type !== 'root') {
|
||||
return;
|
||||
}
|
||||
const nums = node.attributes.viewBox.split(/[ ,]+/g);
|
||||
if (
|
||||
nums[0] === '0' &&
|
||||
nums[1] === '0' &&
|
||||
node.attributes.width.replace(/px$/, '') === nums[2] && // could use parseFloat too
|
||||
node.attributes.height.replace(/px$/, '') === nums[3]
|
||||
) {
|
||||
delete node.attributes.viewBox;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
27
Frontend-Learner/node_modules/svgo/plugins/removeXMLNS.js
generated
vendored
Normal file
27
Frontend-Learner/node_modules/svgo/plugins/removeXMLNS.js
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
export const name = 'removeXMLNS';
|
||||
export const description =
|
||||
'removes xmlns attribute (for inline svg, disabled by default)';
|
||||
|
||||
/**
|
||||
* Remove the xmlns attribute when present.
|
||||
*
|
||||
* @example
|
||||
* <svg viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg">
|
||||
* ↓
|
||||
* <svg viewBox="0 0 100 50">
|
||||
*
|
||||
* @author Ricardo Tomasi
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'svg') {
|
||||
delete node.attributes.xmlns;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
26
Frontend-Learner/node_modules/svgo/plugins/removeXMLProcInst.js
generated
vendored
Normal file
26
Frontend-Learner/node_modules/svgo/plugins/removeXMLProcInst.js
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { detachNodeFromParent } from '../lib/xast.js';
|
||||
|
||||
export const name = 'removeXMLProcInst';
|
||||
export const description = 'removes XML processing instructions';
|
||||
|
||||
/**
|
||||
* Remove XML Processing Instruction.
|
||||
*
|
||||
* @example
|
||||
* <?xml version="1.0" encoding="utf-8"?>
|
||||
*
|
||||
* @author Kir Belevich
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
instruction: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'xml') {
|
||||
detachNodeFromParent(node, parentNode);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
227
Frontend-Learner/node_modules/svgo/plugins/removeXlink.js
generated
vendored
Normal file
227
Frontend-Learner/node_modules/svgo/plugins/removeXlink.js
generated
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
import { elems } from './_collections.js';
|
||||
|
||||
/**
|
||||
* @typedef RemoveXlinkParams
|
||||
* @property {boolean=} includeLegacy
|
||||
* By default this plugin ignores legacy elements that were deprecated or
|
||||
* removed in SVG 2. Set to true to force performing operations on those too.
|
||||
*/
|
||||
|
||||
export const name = 'removeXlink';
|
||||
export const description =
|
||||
'remove xlink namespace and replaces attributes with the SVG 2 equivalent where applicable';
|
||||
|
||||
/** URI indicating the Xlink namespace. */
|
||||
const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink';
|
||||
|
||||
/**
|
||||
* Map of `xlink:show` values to the SVG 2 `target` attribute values.
|
||||
*
|
||||
* @type {Record<string, string>}
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:show#usage_notes
|
||||
*/
|
||||
const SHOW_TO_TARGET = {
|
||||
new: '_blank',
|
||||
replace: '_self',
|
||||
};
|
||||
|
||||
/**
|
||||
* Elements that use xlink:href, but were deprecated in SVG 2 and therefore
|
||||
* don't support the SVG 2 href attribute.
|
||||
*
|
||||
* @type {Set<string>}
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Attribute/href
|
||||
*/
|
||||
const LEGACY_ELEMENTS = new Set([
|
||||
'cursor',
|
||||
'filter',
|
||||
'font-face-uri',
|
||||
'glyphRef',
|
||||
'tref',
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {import('../lib/types.js').XastElement} node
|
||||
* @param {ReadonlyArray<string>} prefixes
|
||||
* @param {string} attr
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const findPrefixedAttrs = (node, prefixes, attr) => {
|
||||
return prefixes
|
||||
.map((prefix) => `${prefix}:${attr}`)
|
||||
.filter((attr) => node.attributes[attr] != null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes XLink namespace prefixes and converts references to XLink attributes
|
||||
* to the native SVG equivalent.
|
||||
*
|
||||
* XLink namespace is deprecated in SVG 2.
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<RemoveXlinkParams>}
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href
|
||||
*/
|
||||
export const fn = (_, params) => {
|
||||
const { includeLegacy } = params;
|
||||
|
||||
/**
|
||||
* XLink namespace prefixes that are currently in the stack.
|
||||
*
|
||||
* @type {string[]}
|
||||
*/
|
||||
const xlinkPrefixes = [];
|
||||
|
||||
/**
|
||||
* Namespace prefixes that exist in {@link xlinkPrefixes} but were overridden
|
||||
* in a child element to point to another namespace, and is not treated as an
|
||||
* XLink attribute.
|
||||
*
|
||||
* @type {string[]}
|
||||
*/
|
||||
const overriddenPrefixes = [];
|
||||
|
||||
/**
|
||||
* Namespace prefixes that were used in one of the {@link LEGACY_ELEMENTS}.
|
||||
*
|
||||
* @type {string[]}
|
||||
*/
|
||||
const usedInLegacyElement = [];
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
for (const [key, value] of Object.entries(node.attributes)) {
|
||||
if (key.startsWith('xmlns:')) {
|
||||
const prefix = key.split(':', 2)[1];
|
||||
|
||||
if (value === XLINK_NAMESPACE) {
|
||||
xlinkPrefixes.push(prefix);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (xlinkPrefixes.includes(prefix)) {
|
||||
overriddenPrefixes.push(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
overriddenPrefixes.some((prefix) => xlinkPrefixes.includes(prefix))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const showAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'show');
|
||||
let showHandled = node.attributes.target != null;
|
||||
for (let i = showAttrs.length - 1; i >= 0; i--) {
|
||||
const attr = showAttrs[i];
|
||||
const value = node.attributes[attr];
|
||||
const mapping = SHOW_TO_TARGET[value];
|
||||
|
||||
if (showHandled || mapping == null) {
|
||||
delete node.attributes[attr];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mapping !== elems[node.name]?.defaults?.target) {
|
||||
node.attributes.target = mapping;
|
||||
}
|
||||
|
||||
delete node.attributes[attr];
|
||||
showHandled = true;
|
||||
}
|
||||
|
||||
const titleAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'title');
|
||||
for (let i = titleAttrs.length - 1; i >= 0; i--) {
|
||||
const attr = titleAttrs[i];
|
||||
const value = node.attributes[attr];
|
||||
const hasTitle = node.children.filter(
|
||||
(child) => child.type === 'element' && child.name === 'title',
|
||||
);
|
||||
|
||||
if (hasTitle.length > 0) {
|
||||
delete node.attributes[attr];
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @type {import('../lib/types.js').XastElement} */
|
||||
const titleTag = {
|
||||
type: 'element',
|
||||
name: 'title',
|
||||
attributes: {},
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Object.defineProperty(titleTag, 'parentNode', {
|
||||
writable: true,
|
||||
value: node,
|
||||
});
|
||||
|
||||
node.children.unshift(titleTag);
|
||||
delete node.attributes[attr];
|
||||
}
|
||||
|
||||
const hrefAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'href');
|
||||
|
||||
if (
|
||||
hrefAttrs.length > 0 &&
|
||||
LEGACY_ELEMENTS.has(node.name) &&
|
||||
!includeLegacy
|
||||
) {
|
||||
hrefAttrs
|
||||
.map((attr) => attr.split(':', 1)[0])
|
||||
.forEach((prefix) => usedInLegacyElement.push(prefix));
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = hrefAttrs.length - 1; i >= 0; i--) {
|
||||
const attr = hrefAttrs[i];
|
||||
const value = node.attributes[attr];
|
||||
|
||||
if (node.attributes.href != null) {
|
||||
delete node.attributes[attr];
|
||||
continue;
|
||||
}
|
||||
|
||||
node.attributes.href = value;
|
||||
delete node.attributes[attr];
|
||||
}
|
||||
},
|
||||
exit: (node) => {
|
||||
for (const [key, value] of Object.entries(node.attributes)) {
|
||||
const [prefix, attr] = key.split(':', 2);
|
||||
|
||||
if (
|
||||
xlinkPrefixes.includes(prefix) &&
|
||||
!overriddenPrefixes.includes(prefix) &&
|
||||
!usedInLegacyElement.includes(prefix) &&
|
||||
!includeLegacy
|
||||
) {
|
||||
delete node.attributes[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.startsWith('xmlns:') && !usedInLegacyElement.includes(attr)) {
|
||||
if (value === XLINK_NAMESPACE) {
|
||||
const index = xlinkPrefixes.indexOf(attr);
|
||||
xlinkPrefixes.splice(index, 1);
|
||||
delete node.attributes[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (overriddenPrefixes.includes(prefix)) {
|
||||
const index = overriddenPrefixes.indexOf(attr);
|
||||
overriddenPrefixes.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
175
Frontend-Learner/node_modules/svgo/plugins/reusePaths.js
generated
vendored
Normal file
175
Frontend-Learner/node_modules/svgo/plugins/reusePaths.js
generated
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
import { collectStylesheet } from '../lib/style.js';
|
||||
import { detachNodeFromParent, querySelectorAll } from '../lib/xast.js';
|
||||
|
||||
export const name = 'reusePaths';
|
||||
export const description =
|
||||
'Finds <path> elements with the same d, fill, and ' +
|
||||
'stroke, and converts them to <use> elements ' +
|
||||
'referencing a single <path> def.';
|
||||
|
||||
/**
|
||||
* Finds <path> elements with the same d, fill, and stroke, and converts them to
|
||||
* <use> elements referencing a single <path> def.
|
||||
*
|
||||
* @author Jacob Howcroft
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = (root) => {
|
||||
const stylesheet = collectStylesheet(root);
|
||||
|
||||
/** @type {Map<string, import('../lib/types.js').XastElement[]>} */
|
||||
const paths = new Map();
|
||||
|
||||
/**
|
||||
* Reference to the first defs element that is a direct child of the svg
|
||||
* element if one exists.
|
||||
*
|
||||
* @type {import('../lib/types.js').XastElement}
|
||||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/defs
|
||||
*/
|
||||
let svgDefs;
|
||||
|
||||
/**
|
||||
* Set of hrefs that reference the id of another node.
|
||||
*
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const hrefs = new Set();
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node, parentNode) => {
|
||||
if (node.name === 'path' && node.attributes.d != null) {
|
||||
const d = node.attributes.d;
|
||||
const fill = node.attributes.fill || '';
|
||||
const stroke = node.attributes.stroke || '';
|
||||
const key = d + ';s:' + stroke + ';f:' + fill;
|
||||
let list = paths.get(key);
|
||||
if (list == null) {
|
||||
list = [];
|
||||
paths.set(key, list);
|
||||
}
|
||||
list.push(node);
|
||||
}
|
||||
|
||||
if (
|
||||
svgDefs == null &&
|
||||
node.name === 'defs' &&
|
||||
parentNode.type === 'element' &&
|
||||
parentNode.name === 'svg'
|
||||
) {
|
||||
svgDefs = node;
|
||||
}
|
||||
|
||||
if (node.name === 'use') {
|
||||
for (const name of ['href', 'xlink:href']) {
|
||||
const href = node.attributes[name];
|
||||
|
||||
if (href != null && href.startsWith('#') && href.length > 1) {
|
||||
hrefs.add(href.slice(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
exit: (node, parentNode) => {
|
||||
if (node.name === 'svg' && parentNode.type === 'root') {
|
||||
let defsTag = svgDefs;
|
||||
|
||||
if (defsTag == null) {
|
||||
defsTag = {
|
||||
type: 'element',
|
||||
name: 'defs',
|
||||
attributes: {},
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
for (const list of paths.values()) {
|
||||
if (list.length > 1) {
|
||||
/** @type {import('../lib/types.js').XastElement} */
|
||||
const reusablePath = {
|
||||
type: 'element',
|
||||
name: 'path',
|
||||
attributes: {},
|
||||
children: [],
|
||||
};
|
||||
|
||||
for (const attr of ['fill', 'stroke', 'd']) {
|
||||
if (list[0].attributes[attr] != null) {
|
||||
reusablePath.attributes[attr] = list[0].attributes[attr];
|
||||
}
|
||||
}
|
||||
|
||||
const originalId = list[0].attributes.id;
|
||||
if (
|
||||
originalId == null ||
|
||||
hrefs.has(originalId) ||
|
||||
stylesheet.rules.some(
|
||||
(rule) => rule.selector === `#${originalId}`,
|
||||
)
|
||||
) {
|
||||
reusablePath.attributes.id = 'reuse-' + index++;
|
||||
} else {
|
||||
reusablePath.attributes.id = originalId;
|
||||
delete list[0].attributes.id;
|
||||
}
|
||||
defsTag.children.push(reusablePath);
|
||||
// convert paths to <use>
|
||||
for (const pathNode of list) {
|
||||
delete pathNode.attributes.d;
|
||||
delete pathNode.attributes.stroke;
|
||||
delete pathNode.attributes.fill;
|
||||
|
||||
if (
|
||||
defsTag.children.includes(pathNode) &&
|
||||
pathNode.children.length === 0
|
||||
) {
|
||||
if (Object.keys(pathNode.attributes).length === 0) {
|
||||
detachNodeFromParent(pathNode, defsTag);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.keys(pathNode.attributes).length === 1 &&
|
||||
pathNode.attributes.id != null
|
||||
) {
|
||||
detachNodeFromParent(pathNode, defsTag);
|
||||
const selector = `[xlink\\:href=#${pathNode.attributes.id}], [href=#${pathNode.attributes.id}]`;
|
||||
for (const child of querySelectorAll(node, selector)) {
|
||||
if (child.type !== 'element') {
|
||||
continue;
|
||||
}
|
||||
for (const name of ['href', 'xlink:href']) {
|
||||
if (child.attributes[name] != null) {
|
||||
child.attributes[name] =
|
||||
'#' + reusablePath.attributes.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pathNode.name = 'use';
|
||||
pathNode.attributes['xlink:href'] =
|
||||
'#' + reusablePath.attributes.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defsTag.children.length !== 0) {
|
||||
if (node.attributes['xmlns:xlink'] == null) {
|
||||
node.attributes['xmlns:xlink'] = 'http://www.w3.org/1999/xlink';
|
||||
}
|
||||
|
||||
if (svgDefs == null) {
|
||||
node.children.unshift(defsTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
113
Frontend-Learner/node_modules/svgo/plugins/sortAttrs.js
generated
vendored
Normal file
113
Frontend-Learner/node_modules/svgo/plugins/sortAttrs.js
generated
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
* @typedef SortAttrsParams
|
||||
* @property {ReadonlyArray<string>=} order
|
||||
* @property {'front' | 'alphabetical'=} xmlnsOrder
|
||||
*/
|
||||
|
||||
export const name = 'sortAttrs';
|
||||
export const description = 'Sort element attributes for better compression';
|
||||
|
||||
/**
|
||||
* Sort element attributes for better compression
|
||||
*
|
||||
* @author Nikolay Frantsev
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin<SortAttrsParams>}
|
||||
*/
|
||||
export const fn = (_root, params) => {
|
||||
const {
|
||||
order = [
|
||||
'id',
|
||||
'width',
|
||||
'height',
|
||||
'x',
|
||||
'x1',
|
||||
'x2',
|
||||
'y',
|
||||
'y1',
|
||||
'y2',
|
||||
'cx',
|
||||
'cy',
|
||||
'r',
|
||||
'fill',
|
||||
'stroke',
|
||||
'marker',
|
||||
'd',
|
||||
'points',
|
||||
],
|
||||
xmlnsOrder = 'front',
|
||||
} = params;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {number}
|
||||
*/
|
||||
const getNsPriority = (name) => {
|
||||
if (xmlnsOrder === 'front') {
|
||||
// put xmlns first
|
||||
if (name === 'xmlns') {
|
||||
return 3;
|
||||
}
|
||||
// xmlns:* attributes second
|
||||
if (name.startsWith('xmlns:')) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
// other namespaces after and sort them alphabetically
|
||||
if (name.includes(':')) {
|
||||
return 1;
|
||||
}
|
||||
// other attributes
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {[string, string]} param0
|
||||
* @param {[string, string]} param1
|
||||
* @returns {number}
|
||||
*/
|
||||
const compareAttrs = ([aName], [bName]) => {
|
||||
// sort namespaces
|
||||
const aPriority = getNsPriority(aName);
|
||||
const bPriority = getNsPriority(bName);
|
||||
const priorityNs = bPriority - aPriority;
|
||||
if (priorityNs !== 0) {
|
||||
return priorityNs;
|
||||
}
|
||||
// extract the first part from attributes
|
||||
// for example "fill" from "fill" and "fill-opacity"
|
||||
const [aPart] = aName.split('-');
|
||||
const [bPart] = bName.split('-');
|
||||
// rely on alphabetical sort when the first part is the same
|
||||
if (aPart !== bPart) {
|
||||
const aInOrderFlag = order.includes(aPart) ? 1 : 0;
|
||||
const bInOrderFlag = order.includes(bPart) ? 1 : 0;
|
||||
// sort by position in order param
|
||||
if (aInOrderFlag === 1 && bInOrderFlag === 1) {
|
||||
return order.indexOf(aPart) - order.indexOf(bPart);
|
||||
}
|
||||
// put attributes from order param before others
|
||||
const priorityOrder = bInOrderFlag - aInOrderFlag;
|
||||
if (priorityOrder !== 0) {
|
||||
return priorityOrder;
|
||||
}
|
||||
}
|
||||
// sort alphabetically
|
||||
return aName < bName ? -1 : 1;
|
||||
};
|
||||
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
const attrs = Object.entries(node.attributes);
|
||||
attrs.sort(compareAttrs);
|
||||
/** @type {Record<string, string>} */
|
||||
const sortedAttributes = {};
|
||||
for (const [name, value] of attrs) {
|
||||
sortedAttributes[name] = value;
|
||||
}
|
||||
node.attributes = sortedAttributes;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
55
Frontend-Learner/node_modules/svgo/plugins/sortDefsChildren.js
generated
vendored
Normal file
55
Frontend-Learner/node_modules/svgo/plugins/sortDefsChildren.js
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
export const name = 'sortDefsChildren';
|
||||
export const description = 'Sorts children of <defs> to improve compression';
|
||||
|
||||
/**
|
||||
* Sorts children of defs in order to improve compression. Sorted first by
|
||||
* frequency then by element name length then by element name (to ensure
|
||||
* grouping).
|
||||
*
|
||||
* @author David Leston
|
||||
*
|
||||
* @type {import('../lib/types.js').Plugin}
|
||||
*/
|
||||
export const fn = () => {
|
||||
return {
|
||||
element: {
|
||||
enter: (node) => {
|
||||
if (node.name === 'defs') {
|
||||
/** @type {Map<string, number>} */
|
||||
const frequencies = new Map();
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element') {
|
||||
const frequency = frequencies.get(child.name);
|
||||
if (frequency == null) {
|
||||
frequencies.set(child.name, 1);
|
||||
} else {
|
||||
frequencies.set(child.name, frequency + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
node.children.sort((a, b) => {
|
||||
if (a.type !== 'element' || b.type !== 'element') {
|
||||
return 0;
|
||||
}
|
||||
const aFrequency = frequencies.get(a.name);
|
||||
const bFrequency = frequencies.get(b.name);
|
||||
if (aFrequency != null && bFrequency != null) {
|
||||
const frequencyComparison = bFrequency - aFrequency;
|
||||
if (frequencyComparison !== 0) {
|
||||
return frequencyComparison;
|
||||
}
|
||||
}
|
||||
const lengthComparison = b.name.length - a.name.length;
|
||||
if (lengthComparison !== 0) {
|
||||
return lengthComparison;
|
||||
}
|
||||
if (a.name !== b.name) {
|
||||
return a.name > b.name ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
7
Frontend-Learner/node_modules/svgo/types/lib/builtin.d.ts
generated
vendored
Normal file
7
Frontend-Learner/node_modules/svgo/types/lib/builtin.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* 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: ReadonlyArray<{ [Name in keyof import("./types.js").PluginsParams]: import("./types.js").BuiltinPluginOrPreset<Name, import("./types.js").PluginsParams[Name]>; }[keyof import("./types.js").PluginsParams]>;
|
||||
15
Frontend-Learner/node_modules/svgo/types/lib/parser.d.ts
generated
vendored
Normal file
15
Frontend-Learner/node_modules/svgo/types/lib/parser.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export class SvgoParserError extends Error {
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {number} line
|
||||
* @param {number} column
|
||||
* @param {string} source
|
||||
* @param {string=} file
|
||||
*/
|
||||
constructor(message: string, line: number, column: number, source: string, file?: string | undefined);
|
||||
reason: string;
|
||||
line: number;
|
||||
column: number;
|
||||
source: string;
|
||||
}
|
||||
export function parseSvg(data: string, from?: string | undefined): import("./types.js").XastRoot;
|
||||
8
Frontend-Learner/node_modules/svgo/types/lib/path.d.ts
generated
vendored
Normal file
8
Frontend-Learner/node_modules/svgo/types/lib/path.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export function parsePathData(string: string): import("./types.js").PathDataItem[];
|
||||
export function stringifyPathData({ pathData, precision, disableSpaceAfterFlags, }: StringifyPathDataOptions): string;
|
||||
export type ReadNumberState = "none" | "sign" | "whole" | "decimal_point" | "decimal" | "e" | "exponent_sign" | "exponent";
|
||||
export type StringifyPathDataOptions = {
|
||||
pathData: ReadonlyArray<import("./types.js").PathDataItem>;
|
||||
precision?: number | undefined;
|
||||
disableSpaceAfterFlags?: boolean | undefined;
|
||||
};
|
||||
7
Frontend-Learner/node_modules/svgo/types/lib/stringifier.d.ts
generated
vendored
Normal file
7
Frontend-Learner/node_modules/svgo/types/lib/stringifier.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export function stringifySvg(data: import("./types.js").XastRoot, userOptions?: import("./types.js").StringifyOptions | undefined): string;
|
||||
export type Options = Required<import("./types.js").StringifyOptions>;
|
||||
export type State = {
|
||||
indent: string;
|
||||
textContext: import("./types.js").XastElement | null;
|
||||
indentLevel: number;
|
||||
};
|
||||
5
Frontend-Learner/node_modules/svgo/types/lib/style.d.ts
generated
vendored
Normal file
5
Frontend-Learner/node_modules/svgo/types/lib/style.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export function compareSpecificity(a: import("./types.js").Specificity, b: import("./types.js").Specificity): number;
|
||||
export function collectStylesheet(root: import("./types.js").XastRoot): import("./types.js").Stylesheet;
|
||||
export function computeStyle(stylesheet: import("./types.js").Stylesheet, node: import("./types.js").XastElement): import("./types.js").ComputedStyles;
|
||||
export function includesAttrSelector(selector: csstree.ListItem<csstree.CssNode> | string, name: string, value?: string | null, traversed?: boolean): boolean;
|
||||
import * as csstree from 'css-tree';
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue