It appears you have a well-structured Git repository with various files, including SVG icons and HTML documents. Here's a brief overview:
This commit is contained in:
528
backend/node_modules/svgo/lib/svgo/coa.js
generated
vendored
Normal file
528
backend/node_modules/svgo/lib/svgo/coa.js
generated
vendored
Normal file
@@ -0,0 +1,528 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const colors = require('picocolors');
|
||||
const { loadConfig, optimize } = require('../svgo-node.js');
|
||||
const { builtin } = require('../builtin.js');
|
||||
const PKG = require('../../package.json');
|
||||
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');
|
||||
|
||||
const regSVGFile = /\.svg$/i;
|
||||
|
||||
/**
|
||||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT.
|
||||
*
|
||||
* @param {string} path
|
||||
*/
|
||||
function checkIsDir(path) {
|
||||
try {
|
||||
return fs.lstatSync(path).isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function makeProgram(program) {
|
||||
program
|
||||
.name(PKG.name)
|
||||
.description(PKG.description, {
|
||||
INPUT: 'Alias to --input',
|
||||
})
|
||||
.version(PKG.version, '-v, --version')
|
||||
.arguments('[INPUT...]')
|
||||
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN')
|
||||
.option('-s, --string <STRING>', 'Input SVG data string')
|
||||
.option(
|
||||
'-f, --folder <FOLDER>',
|
||||
'Input folder, optimize and rewrite all *.svg files',
|
||||
)
|
||||
.option(
|
||||
'-o, --output <OUTPUT...>',
|
||||
'Output file or folder (by default the same as the input), "-" for STDOUT',
|
||||
)
|
||||
.option(
|
||||
'-p, --precision <INTEGER>',
|
||||
'Set number of digits in the fractional part, overrides plugins params',
|
||||
)
|
||||
.option('--config <CONFIG>', 'Custom config file, only .js is supported')
|
||||
.option(
|
||||
'--datauri <FORMAT>',
|
||||
'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)',
|
||||
)
|
||||
.option(
|
||||
'--multipass',
|
||||
'Pass over SVGs multiple times to ensure all optimizations are applied',
|
||||
)
|
||||
.option('--pretty', 'Make SVG pretty printed')
|
||||
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs')
|
||||
.option(
|
||||
'--eol <EOL>',
|
||||
'Line break to use when outputting SVG: lf, crlf. If unspecified, uses platform default.',
|
||||
)
|
||||
.option('--final-newline', 'Ensure SVG ends with a line break')
|
||||
.option(
|
||||
'-r, --recursive',
|
||||
"Use with '--folder'. Optimizes *.svg files in folders recursively.",
|
||||
)
|
||||
.option(
|
||||
'--exclude <PATTERN...>',
|
||||
"Use with '--folder'. Exclude files matching regular expression pattern.",
|
||||
)
|
||||
.option(
|
||||
'-q, --quiet',
|
||||
'Only output error messages, not regular status messages',
|
||||
)
|
||||
.option('--show-plugins', 'Show available plugins and exit')
|
||||
// used by picocolors internally
|
||||
.option('--no-color', 'Output plain text without color')
|
||||
.action(action);
|
||||
};
|
||||
|
||||
async function action(args, opts, command) {
|
||||
var input = opts.input || args;
|
||||
var output = opts.output;
|
||||
var config = {};
|
||||
|
||||
if (opts.precision != null) {
|
||||
const number = Number.parseInt(opts.precision, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '-p, --precision' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
opts.precision = number;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.datauri != null) {
|
||||
if (
|
||||
opts.datauri !== 'base64' &&
|
||||
opts.datauri !== 'enc' &&
|
||||
opts.datauri !== 'unenc'
|
||||
) {
|
||||
console.error(
|
||||
"error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.indent != null) {
|
||||
const number = Number.parseInt(opts.indent, 10);
|
||||
if (Number.isNaN(number)) {
|
||||
console.error(
|
||||
"error: option '--indent' argument must be an integer number",
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
opts.indent = number;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.eol != null && opts.eol !== 'lf' && opts.eol !== 'crlf') {
|
||||
console.error(
|
||||
"error: option '--eol' must have one of the following values: 'lf' or 'crlf'",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// --show-plugins
|
||||
if (opts.showPlugins) {
|
||||
showAvailablePlugins();
|
||||
return;
|
||||
}
|
||||
|
||||
// w/o anything
|
||||
if (
|
||||
(input.length === 0 || input[0] === '-') &&
|
||||
!opts.string &&
|
||||
!opts.stdin &&
|
||||
!opts.folder &&
|
||||
process.stdin.isTTY === true
|
||||
) {
|
||||
return command.help();
|
||||
}
|
||||
|
||||
if (
|
||||
typeof process == 'object' &&
|
||||
process.versions &&
|
||||
process.versions.node &&
|
||||
PKG &&
|
||||
PKG.engines.node
|
||||
) {
|
||||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
|
||||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
|
||||
throw Error(
|
||||
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --config
|
||||
const loadedConfig = await loadConfig(opts.config);
|
||||
if (loadedConfig != null) {
|
||||
config = loadedConfig;
|
||||
}
|
||||
|
||||
// --quiet
|
||||
if (opts.quiet) {
|
||||
config.quiet = opts.quiet;
|
||||
}
|
||||
|
||||
// --recursive
|
||||
if (opts.recursive) {
|
||||
config.recursive = opts.recursive;
|
||||
}
|
||||
|
||||
// --exclude
|
||||
config.exclude = opts.exclude
|
||||
? opts.exclude.map((pattern) => RegExp(pattern))
|
||||
: [];
|
||||
|
||||
// --precision
|
||||
if (opts.precision != null) {
|
||||
var precision = Math.min(Math.max(0, opts.precision), 20);
|
||||
config.floatPrecision = precision;
|
||||
}
|
||||
|
||||
// --multipass
|
||||
if (opts.multipass) {
|
||||
config.multipass = true;
|
||||
}
|
||||
|
||||
// --pretty
|
||||
if (opts.pretty) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.pretty = true;
|
||||
if (opts.indent != null) {
|
||||
config.js2svg.indent = opts.indent;
|
||||
}
|
||||
}
|
||||
|
||||
// --eol
|
||||
if (opts.eol) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.eol = opts.eol;
|
||||
}
|
||||
|
||||
// --final-newline
|
||||
if (opts.finalNewline) {
|
||||
config.js2svg = config.js2svg || {};
|
||||
config.js2svg.finalNewline = true;
|
||||
}
|
||||
|
||||
// --output
|
||||
if (output) {
|
||||
if (input.length && input[0] != '-') {
|
||||
if (output.length == 1 && checkIsDir(output[0])) {
|
||||
var dir = output[0];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
output[i] = checkIsDir(input[i])
|
||||
? input[i]
|
||||
: path.resolve(dir, path.basename(input[i]));
|
||||
}
|
||||
} else if (output.length < input.length) {
|
||||
output = output.concat(input.slice(output.length));
|
||||
}
|
||||
}
|
||||
} else if (input.length) {
|
||||
output = input;
|
||||
} else if (opts.string) {
|
||||
output = '-';
|
||||
}
|
||||
|
||||
if (opts.datauri) {
|
||||
config.datauri = opts.datauri;
|
||||
}
|
||||
|
||||
// --folder
|
||||
if (opts.folder) {
|
||||
var ouputFolder = (output && output[0]) || opts.folder;
|
||||
await optimizeFolder(config, opts.folder, ouputFolder);
|
||||
}
|
||||
|
||||
// --input
|
||||
if (input.length !== 0) {
|
||||
// STDIN
|
||||
if (input[0] === '-') {
|
||||
return new Promise((resolve, reject) => {
|
||||
var data = '',
|
||||
file = output[0];
|
||||
|
||||
process.stdin
|
||||
.on('data', (chunk) => (data += chunk))
|
||||
.once('end', () =>
|
||||
processSVGData(config, null, data, file).then(resolve, reject),
|
||||
);
|
||||
});
|
||||
// file
|
||||
} else {
|
||||
await Promise.all(
|
||||
input.map((file, n) => optimizeFile(config, file, output[n])),
|
||||
);
|
||||
}
|
||||
|
||||
// --string
|
||||
} else if (opts.string) {
|
||||
var data = decodeSVGDatauri(opts.string);
|
||||
|
||||
return processSVGData(config, null, data, output[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG files in a directory.
|
||||
*
|
||||
* @param {Object} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
*/
|
||||
function optimizeFolder(config, dir, output) {
|
||||
if (!config.quiet) {
|
||||
console.log(`Processing directory '${dir}':\n`);
|
||||
}
|
||||
return fs.promises
|
||||
.readdir(dir)
|
||||
.then((files) => processDirectory(config, dir, files, output));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process given files, take only SVG.
|
||||
*
|
||||
* @param {Object} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Promise}
|
||||
*/
|
||||
function processDirectory(config, dir, files, output) {
|
||||
// take only *.svg files, recursively if necessary
|
||||
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
|
||||
|
||||
return svgFilesDescriptions.length
|
||||
? Promise.all(
|
||||
svgFilesDescriptions.map((fileDescription) =>
|
||||
optimizeFile(
|
||||
config,
|
||||
fileDescription.inputPath,
|
||||
fileDescription.outputPath,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Promise.reject(
|
||||
new Error(`No SVG files have been found in '${dir}' directory.`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SVG files descriptions.
|
||||
*
|
||||
* @param {Object} config options
|
||||
* @param {string} dir input directory
|
||||
* @param {Array} files list of file names in the directory
|
||||
* @param {string} output output directory
|
||||
* @return {Array}
|
||||
*/
|
||||
function getFilesDescriptions(config, dir, files, output) {
|
||||
const filesInThisFolder = files
|
||||
.filter(
|
||||
(name) =>
|
||||
regSVGFile.test(name) &&
|
||||
!config.exclude.some((regExclude) => regExclude.test(name)),
|
||||
)
|
||||
.map((name) => ({
|
||||
inputPath: path.resolve(dir, name),
|
||||
outputPath: path.resolve(output, name),
|
||||
}));
|
||||
|
||||
return config.recursive
|
||||
? [].concat(
|
||||
filesInThisFolder,
|
||||
files
|
||||
.filter((name) => checkIsDir(path.resolve(dir, name)))
|
||||
.map((subFolderName) => {
|
||||
const subFolderPath = path.resolve(dir, subFolderName);
|
||||
const subFolderFiles = fs.readdirSync(subFolderPath);
|
||||
const subFolderOutput = path.resolve(output, subFolderName);
|
||||
return getFilesDescriptions(
|
||||
config,
|
||||
subFolderPath,
|
||||
subFolderFiles,
|
||||
subFolderOutput,
|
||||
);
|
||||
})
|
||||
.reduce((a, b) => [].concat(a, b), []),
|
||||
)
|
||||
: filesInThisFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SVG file and pass to processing.
|
||||
*
|
||||
* @param {Object} config options
|
||||
* @param {string} file
|
||||
* @param {string} output
|
||||
* @return {Promise}
|
||||
*/
|
||||
function optimizeFile(config, file, output) {
|
||||
return fs.promises.readFile(file, 'utf8').then(
|
||||
(data) => processSVGData(config, { path: file }, data, output, file),
|
||||
(error) => checkOptimizeFileError(config, file, output, error),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize SVG data.
|
||||
*
|
||||
* @param {Object} config options
|
||||
* @param {string} data SVG content to optimize
|
||||
* @param {string} output where to write optimized file
|
||||
* @param {string} [input] input file name (being used if output is a directory)
|
||||
* @return {Promise}
|
||||
*/
|
||||
function processSVGData(config, info, data, output, input) {
|
||||
var startTime = Date.now(),
|
||||
prevFileSize = Buffer.byteLength(data, 'utf8');
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = optimize(data, { ...config, ...info });
|
||||
} catch (error) {
|
||||
if (error.name === 'SvgoParserError') {
|
||||
console.error(colors.red(error.toString()));
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (config.datauri) {
|
||||
result.data = encodeSVGDatauri(result.data, config.datauri);
|
||||
}
|
||||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
|
||||
processingTime = Date.now() - startTime;
|
||||
|
||||
return writeOutput(input, output, result.data).then(
|
||||
function () {
|
||||
if (!config.quiet && output != '-') {
|
||||
if (input) {
|
||||
console.log(`\n${path.basename(input)}:`);
|
||||
}
|
||||
printTimeInfo(processingTime);
|
||||
printProfitInfo(prevFileSize, resultFileSize);
|
||||
}
|
||||
},
|
||||
(error) =>
|
||||
Promise.reject(
|
||||
new Error(
|
||||
error.code === 'ENOTDIR'
|
||||
? `Error: output '${output}' is not a directory.`
|
||||
: error,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write result of an optimization.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output output file name. '-' for stdout
|
||||
* @param {string} data data to write
|
||||
* @return {Promise}
|
||||
*/
|
||||
function writeOutput(input, output, data) {
|
||||
if (output == '-') {
|
||||
process.stdout.write(data);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(output), { recursive: true });
|
||||
|
||||
return fs.promises
|
||||
.writeFile(output, data, 'utf8')
|
||||
.catch((error) => checkWriteFileError(input, output, data, error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write time taken to optimize.
|
||||
*
|
||||
* @param {number} time time in milliseconds.
|
||||
*/
|
||||
function printTimeInfo(time) {
|
||||
console.log(`Done in ${time} ms!`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write optimizing stats in a human-readable format.
|
||||
*
|
||||
* @param {number} inBytes size before optimization.
|
||||
* @param {number} outBytes size after optimization.
|
||||
*/
|
||||
function printProfitInfo(inBytes, outBytes) {
|
||||
const profitPercent = 100 - (outBytes * 100) / inBytes;
|
||||
/** @type {[string, Function]} */
|
||||
const ui = profitPercent < 0 ? ['+', colors.red] : ['-', colors.green];
|
||||
|
||||
console.log(
|
||||
Math.round((inBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
ui[0],
|
||||
ui[1](Math.abs(Math.round(profitPercent * 10) / 10) + '%'),
|
||||
'=',
|
||||
Math.round((outBytes / 1024) * 1000) / 1000 + ' KiB',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for errors, if it's a dir optimize the dir.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
*/
|
||||
function checkOptimizeFileError(config, input, output, error) {
|
||||
if (error.code == 'EISDIR') {
|
||||
return optimizeFolder(config, input, output);
|
||||
} else if (error.code == 'ENOENT') {
|
||||
return Promise.reject(
|
||||
new Error(`Error: no such file or directory '${error.path}'.`),
|
||||
);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for saving file error. If the output is a dir, then write file there.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} output
|
||||
* @param {string} data
|
||||
* @param {Error} error
|
||||
* @return {Promise}
|
||||
*/
|
||||
function checkWriteFileError(input, output, data, error) {
|
||||
if (error.code == 'EISDIR' && input) {
|
||||
return fs.promises.writeFile(
|
||||
path.resolve(output, path.basename(input)),
|
||||
data,
|
||||
'utf8',
|
||||
);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Show list of available plugins with short description. */
|
||||
function showAvailablePlugins() {
|
||||
const list = builtin
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((plugin) => ` [ ${colors.green(plugin.name)} ] ${plugin.description}`)
|
||||
.join('\n');
|
||||
console.log('Currently available plugins:\n' + list);
|
||||
}
|
||||
|
||||
module.exports.checkIsDir = checkIsDir;
|
2
backend/node_modules/svgo/lib/svgo/css-select-adapter.d.ts
generated
vendored
Normal file
2
backend/node_modules/svgo/lib/svgo/css-select-adapter.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
declare let obj: any;
|
||||
export = obj;
|
120
backend/node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
Normal file
120
backend/node_modules/svgo/lib/svgo/css-select-adapter.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
const isTag = (node) => {
|
||||
return node.type === 'element';
|
||||
};
|
||||
|
||||
const existsOne = (test, elems) => {
|
||||
return elems.some((elem) => {
|
||||
if (isTag(elem)) {
|
||||
return test(elem) || existsOne(test, getChildren(elem));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getAttributeValue = (elem, name) => {
|
||||
return elem.attributes[name];
|
||||
};
|
||||
|
||||
const getChildren = (node) => {
|
||||
return node.children || [];
|
||||
};
|
||||
|
||||
const getName = (elemAst) => {
|
||||
return elemAst.name;
|
||||
};
|
||||
|
||||
const getParent = (node) => {
|
||||
return node.parentNode || null;
|
||||
};
|
||||
|
||||
const getSiblings = (elem) => {
|
||||
var parent = getParent(elem);
|
||||
return parent ? getChildren(parent) : [];
|
||||
};
|
||||
|
||||
const getText = (node) => {
|
||||
if (node.children[0].type === 'text' && node.children[0].type === 'cdata') {
|
||||
return node.children[0].value;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const hasAttrib = (elem, name) => {
|
||||
return elem.attributes[name] !== undefined;
|
||||
};
|
||||
|
||||
const removeSubsets = (nodes) => {
|
||||
let idx = nodes.length;
|
||||
let node;
|
||||
let ancestor;
|
||||
let replace;
|
||||
// Check if each node (or one of its ancestors) is already contained in the
|
||||
// array.
|
||||
while (--idx > -1) {
|
||||
node = ancestor = nodes[idx];
|
||||
// Temporarily remove the node under consideration
|
||||
nodes[idx] = null;
|
||||
replace = true;
|
||||
while (ancestor) {
|
||||
if (nodes.includes(ancestor)) {
|
||||
replace = false;
|
||||
nodes.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
ancestor = getParent(ancestor);
|
||||
}
|
||||
// If the node has been found to be unique, re-insert it.
|
||||
if (replace) {
|
||||
nodes[idx] = node;
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
const findAll = (test, elems) => {
|
||||
const result = [];
|
||||
for (const elem of elems) {
|
||||
if (isTag(elem)) {
|
||||
if (test(elem)) {
|
||||
result.push(elem);
|
||||
}
|
||||
result.push(...findAll(test, getChildren(elem)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const findOne = (test, elems) => {
|
||||
for (const elem of elems) {
|
||||
if (isTag(elem)) {
|
||||
if (test(elem)) {
|
||||
return elem;
|
||||
}
|
||||
const result = findOne(test, getChildren(elem));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const svgoCssSelectAdapter = {
|
||||
isTag,
|
||||
existsOne,
|
||||
getAttributeValue,
|
||||
getChildren,
|
||||
getName,
|
||||
getParent,
|
||||
getSiblings,
|
||||
getText,
|
||||
hasAttrib,
|
||||
removeSubsets,
|
||||
findAll,
|
||||
findOne,
|
||||
};
|
||||
|
||||
module.exports = svgoCssSelectAdapter;
|
61
backend/node_modules/svgo/lib/svgo/plugins.js
generated
vendored
Normal file
61
backend/node_modules/svgo/lib/svgo/plugins.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const { visit } = require('../xast.js');
|
||||
|
||||
/**
|
||||
* Plugins engine.
|
||||
*
|
||||
* @module plugins
|
||||
*
|
||||
* @param {Object} ast input ast
|
||||
* @param {Object} info extra information
|
||||
* @param {Array} plugins plugins object from config
|
||||
* @return {Object} output ast
|
||||
*/
|
||||
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => {
|
||||
for (const plugin of plugins) {
|
||||
const override = overrides?.[plugin.name];
|
||||
if (override === false) {
|
||||
continue;
|
||||
}
|
||||
const params = { ...plugin.params, ...globalOverrides, ...override };
|
||||
|
||||
const visitor = plugin.fn(ast, params, info);
|
||||
if (visitor != null) {
|
||||
visit(ast, visitor);
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.invokePlugins = invokePlugins;
|
||||
|
||||
const createPreset = ({ name, plugins }) => {
|
||||
return {
|
||||
name,
|
||||
fn: (ast, params, info) => {
|
||||
const { floatPrecision, overrides } = params;
|
||||
const globalOverrides = {};
|
||||
if (floatPrecision != null) {
|
||||
globalOverrides.floatPrecision = floatPrecision;
|
||||
}
|
||||
if (overrides) {
|
||||
const pluginNames = plugins.map(({ name }) => name);
|
||||
for (const pluginName of Object.keys(overrides)) {
|
||||
if (!pluginNames.includes(pluginName)) {
|
||||
console.warn(
|
||||
`You are trying to configure ${pluginName} which is not part of ${name}.\n` +
|
||||
`Try to put it before or after, for example\n\n` +
|
||||
`plugins: [\n` +
|
||||
` {\n` +
|
||||
` name: '${name}',\n` +
|
||||
` },\n` +
|
||||
` '${pluginName}'\n` +
|
||||
`]\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
invokePlugins(ast, info, plugins, overrides, globalOverrides);
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.createPreset = createPreset;
|
245
backend/node_modules/svgo/lib/svgo/tools.js
generated
vendored
Normal file
245
backend/node_modules/svgo/lib/svgo/tools.js
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../lib/types').XastElement} XastElement
|
||||
* @typedef {import('../types').PathDataCommand} PathDataCommand
|
||||
* @typedef {import('../types').DataUri} DataUri
|
||||
*/
|
||||
|
||||
const { attrsGroups, referencesProps } = require('../../plugins/_collections');
|
||||
|
||||
const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g;
|
||||
const regReferencesHref = /^#(.+?)$/;
|
||||
const regReferencesBegin = /(\w+)\.[a-zA-Z]/;
|
||||
|
||||
/**
|
||||
* Encode plain SVG data string into Data URI string.
|
||||
*
|
||||
* @type {(str: string, type?: DataUri) => string}
|
||||
*/
|
||||
exports.encodeSVGDatauri = (str, type) => {
|
||||
var prefix = 'data:image/svg+xml';
|
||||
if (!type || type === 'base64') {
|
||||
// base64
|
||||
prefix += ';base64,';
|
||||
str = prefix + Buffer.from(str).toString('base64');
|
||||
} else if (type === 'enc') {
|
||||
// URI encoded
|
||||
str = prefix + ',' + encodeURIComponent(str);
|
||||
} else if (type === 'unenc') {
|
||||
// unencoded
|
||||
str = prefix + ',' + str;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode SVG Data URI string into plain SVG string.
|
||||
*
|
||||
* @type {(str: string) => string}
|
||||
*/
|
||||
exports.decodeSVGDatauri = (str) => {
|
||||
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
|
||||
var match = regexp.exec(str);
|
||||
|
||||
// plain string
|
||||
if (!match) return str;
|
||||
|
||||
var data = match[3];
|
||||
|
||||
if (match[2]) {
|
||||
// base64
|
||||
str = Buffer.from(data, 'base64').toString('utf8');
|
||||
} else if (data.charAt(0) === '%') {
|
||||
// URI encoded
|
||||
str = decodeURIComponent(data);
|
||||
} else if (data.charAt(0) === '<') {
|
||||
// unencoded
|
||||
str = data;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* noSpaceAfterFlags?: boolean,
|
||||
* leadingZero?: boolean,
|
||||
* negativeExtraSpace?: boolean
|
||||
* }} CleanupOutDataParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a row of numbers to an optimized string view.
|
||||
*
|
||||
* @example
|
||||
* [0, -1, .5, .5] → "0-1 .5.5"
|
||||
*
|
||||
* @type {(data: number[], params: CleanupOutDataParams, command?: PathDataCommand) => string}
|
||||
*/
|
||||
exports.cleanupOutData = (data, params, command) => {
|
||||
let str = '';
|
||||
let delimiter;
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
let prev;
|
||||
|
||||
data.forEach((item, i) => {
|
||||
// space delimiter by default
|
||||
delimiter = ' ';
|
||||
|
||||
// no extra space in front of first number
|
||||
if (i == 0) delimiter = '';
|
||||
|
||||
// no extra space after 'arcto' command flags(large-arc and sweep flags)
|
||||
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
|
||||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
|
||||
var pos = i % 7;
|
||||
if (pos == 4 || pos == 5) delimiter = '';
|
||||
}
|
||||
|
||||
// remove floating-point numbers leading zeros
|
||||
// 0.5 → .5
|
||||
// -0.5 → -.5
|
||||
const itemStr = params.leadingZero
|
||||
? removeLeadingZero(item)
|
||||
: item.toString();
|
||||
|
||||
// no extra space in front of negative number or
|
||||
// in front of a floating number if a previous number is floating too
|
||||
if (
|
||||
params.negativeExtraSpace &&
|
||||
delimiter != '' &&
|
||||
(item < 0 || (itemStr.charAt(0) === '.' && prev % 1 !== 0))
|
||||
) {
|
||||
delimiter = '';
|
||||
}
|
||||
// save prev item value
|
||||
prev = item;
|
||||
str += delimiter + itemStr;
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove floating-point numbers leading zero.
|
||||
*
|
||||
* @param {number} value
|
||||
* @returns {string}
|
||||
* @example
|
||||
* 0.5 → .5
|
||||
* -0.5 → -.5
|
||||
*/
|
||||
const removeLeadingZero = (value) => {
|
||||
const strValue = value.toString();
|
||||
|
||||
if (0 < value && value < 1 && strValue.startsWith('0')) {
|
||||
return strValue.slice(1);
|
||||
}
|
||||
|
||||
if (-1 < value && value < 0 && strValue[1] === '0') {
|
||||
return strValue[0] + strValue.slice(2);
|
||||
}
|
||||
|
||||
return strValue;
|
||||
};
|
||||
exports.removeLeadingZero = removeLeadingZero;
|
||||
|
||||
/**
|
||||
* If the current node contains any scripts. This does not check parents or
|
||||
* children of the node, only the properties and attributes of the node itself.
|
||||
*
|
||||
* @param {XastElement} node Current node to check against.
|
||||
* @returns {boolean} If the current node contains scripts.
|
||||
*/
|
||||
const hasScripts = (node) => {
|
||||
if (node.name === 'script' && node.children.length !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.name === 'a') {
|
||||
const hasJsLinks = Object.entries(node.attributes).some(
|
||||
([attrKey, attrValue]) =>
|
||||
(attrKey === 'href' || attrKey.endsWith(':href')) &&
|
||||
attrValue != null &&
|
||||
attrValue.trimStart().startsWith('javascript:'),
|
||||
);
|
||||
|
||||
if (hasJsLinks) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const eventAttrs = [
|
||||
...attrsGroups.animationEvent,
|
||||
...attrsGroups.documentEvent,
|
||||
...attrsGroups.documentElementEvent,
|
||||
...attrsGroups.globalEvent,
|
||||
...attrsGroups.graphicalEvent,
|
||||
];
|
||||
|
||||
return eventAttrs.some((attr) => node.attributes[attr] != null);
|
||||
};
|
||||
exports.hasScripts = hasScripts;
|
||||
|
||||
/**
|
||||
* For example, a string that contains one or more of following would match and
|
||||
* return true:
|
||||
*
|
||||
* * `url(#gradient001)`
|
||||
* * `url('#gradient001')`
|
||||
*
|
||||
* @param {string} body
|
||||
* @returns {boolean} If the given string includes a URL reference.
|
||||
*/
|
||||
const includesUrlReference = (body) => {
|
||||
return new RegExp(regReferencesUrl).test(body);
|
||||
};
|
||||
exports.includesUrlReference = includesUrlReference;
|
||||
|
||||
/**
|
||||
* @param {string} attribute
|
||||
* @param {string} value
|
||||
* @returns {string[]}
|
||||
*/
|
||||
const findReferences = (attribute, value) => {
|
||||
const results = [];
|
||||
|
||||
if (referencesProps.has(attribute)) {
|
||||
const matches = value.matchAll(regReferencesUrl);
|
||||
for (const match of matches) {
|
||||
results.push(match[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute === 'href' || attribute.endsWith(':href')) {
|
||||
const match = regReferencesHref.exec(value);
|
||||
if (match != null) {
|
||||
results.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute === 'begin') {
|
||||
const match = regReferencesBegin.exec(value);
|
||||
if (match != null) {
|
||||
results.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return results.map((body) => decodeURI(body));
|
||||
};
|
||||
exports.findReferences = findReferences;
|
||||
|
||||
/**
|
||||
* Does the same as {@link Number.toFixed} but without casting
|
||||
* the return value to a string.
|
||||
*
|
||||
* @param {number} num
|
||||
* @param {number} precision
|
||||
* @returns {number}
|
||||
*/
|
||||
const toFixed = (num, precision) => {
|
||||
const pow = 10 ** precision;
|
||||
return Math.round(num * pow) / pow;
|
||||
};
|
||||
exports.toFixed = toFixed;
|
Reference in New Issue
Block a user