Socket
Socket
Sign inDemoInstall

svgo

Package Overview
Dependencies
17
Maintainers
3
Versions
99
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.7.0 to 2.8.0

lib/stringifier.js

35

lib/svgo-node.js

@@ -18,17 +18,24 @@ 'use strict';

let config;
try {
// dynamic import expects file url instead of path and may fail
// when windows path is provided
const { default: imported } = await import(pathToFileURL(configFile));
config = imported;
} catch (importError) {
// TODO remove require in v3
// at the moment dynamic import may randomly fail with segfault
// to workaround this for some users .cjs extension is loaded
// exclusively with require
if (configFile.endsWith('.cjs')) {
config = require(configFile);
} else {
try {
config = require(configFile);
} catch (requireError) {
// throw original error if es module is detected
if (requireError.code === 'ERR_REQUIRE_ESM') {
throw importError;
} else {
throw requireError;
// dynamic import expects file url instead of path and may fail
// when windows path is provided
const { default: imported } = await import(pathToFileURL(configFile));
config = imported;
} catch (importError) {
// TODO remove require in v3
try {
config = require(configFile);
} catch (requireError) {
// throw original error if es module is detected
if (requireError.code === 'ERR_REQUIRE_ESM') {
throw importError;
} else {
throw requireError;
}
}

@@ -35,0 +42,0 @@ }

7

lib/svgo.js

@@ -9,3 +9,3 @@ 'use strict';

const { parseSvg } = require('./parser.js');
const js2svg = require('./svgo/js2svg.js');
const { stringifySvg } = require('./stringifier.js');
const { invokePlugins } = require('./svgo/plugins.js');

@@ -57,6 +57,3 @@ const JSAPI = require('./svgo/jsAPI.js');

svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides);
svgjs = js2svg(svgjs, config.js2svg);
if (svgjs.error) {
throw Error(svgjs.error);
}
svgjs = stringifySvg(svgjs, config.js2svg);
if (svgjs.data.length < prevResultSize) {

@@ -63,0 +60,0 @@ input = svgjs.data;

'use strict';
const FS = require('fs');
const PATH = require('path');
const { green, red } = require('nanocolors');
const fs = require('fs');
const path = require('path');
const colors = require('picocolors');
const { loadConfig, optimize } = require('../svgo-node.js');

@@ -19,3 +19,3 @@ const pluginsMap = require('../../plugins/plugins.js');

try {
return FS.lstatSync(path).isDirectory();
return fs.lstatSync(path).isDirectory();
} catch (e) {

@@ -77,2 +77,4 @@ return false;

.option('--show-plugins', 'Show available plugins and exit')
// used by picocolors internally
.option('--no-color', 'Output plain text without color')
.action(action);

@@ -223,3 +225,3 @@ };

? input[i]
: PATH.resolve(dir, PATH.basename(input[i]));
: path.resolve(dir, path.basename(input[i]));
}

@@ -289,3 +291,3 @@ } else if (output.length < input.length) {

}
return FS.promises
return fs.promises
.readdir(dir)

@@ -338,4 +340,4 @@ .then((files) => processDirectory(config, dir, files, output));

.map((name) => ({
inputPath: PATH.resolve(dir, name),
outputPath: PATH.resolve(output, name),
inputPath: path.resolve(dir, name),
outputPath: path.resolve(output, name),
}));

@@ -347,7 +349,7 @@

files
.filter((name) => checkIsDir(PATH.resolve(dir, name)))
.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);
const subFolderPath = path.resolve(dir, subFolderName);
const subFolderFiles = fs.readdirSync(subFolderPath);
const subFolderOutput = path.resolve(output, subFolderName);
return getFilesDescriptions(

@@ -373,3 +375,3 @@ config,

function optimizeFile(config, file, output) {
return FS.promises.readFile(file, 'utf8').then(
return fs.promises.readFile(file, 'utf8').then(
(data) =>

@@ -395,3 +397,3 @@ processSVGData(config, { input: 'file', path: file }, data, output, file),

if (result.modernError) {
console.error(red(result.modernError.toString()));
console.error(colors.red(result.modernError.toString()));
process.exit(1);

@@ -409,3 +411,3 @@ }

if (input) {
console.log(`\n${PATH.basename(input)}:`);
console.log(`\n${path.basename(input)}:`);
}

@@ -440,5 +442,5 @@ printTimeInfo(processingTime);

FS.mkdirSync(PATH.dirname(output), { recursive: true });
fs.mkdirSync(path.dirname(output), { recursive: true });
return FS.promises
return fs.promises
.writeFile(output, data, 'utf8')

@@ -468,3 +470,3 @@ .catch((error) => checkWriteFileError(input, output, data, error));

(profitPercents < 0 ? ' + ' : ' - ') +
green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
colors.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
' = ' +

@@ -505,4 +507,4 @@ Math.round((outBytes / 1024) * 1000) / 1000 +

if (error.code == 'EISDIR' && input) {
return FS.promises.writeFile(
PATH.resolve(output, PATH.basename(input)),
return fs.promises.writeFile(
path.resolve(output, path.basename(input)),
data,

@@ -522,3 +524,3 @@ 'utf8'

.sort(([a], [b]) => a.localeCompare(b))
.map(([name, plugin]) => ` [ ${green(name)} ] ${plugin.description}`)
.map(([name, plugin]) => ` [ ${colors.green(name)} ] ${plugin.description}`)
.join('\n');

@@ -525,0 +527,0 @@ console.log('Currently available plugins:\n' + list);

@@ -89,2 +89,18 @@ 'use strict';

}
if (overrides) {
for (const [pluginName, override] of Object.entries(overrides)) {
if (override === true) {
console.warn(
`You are trying to enable ${pluginName} which is not part of preset.\n` +
`Try to put it before or after preset, for example\n\n` +
`plugins: [\n` +
` {\n` +
` name: 'preset-default',\n` +
` },\n` +
` 'cleanupListOfValues'\n` +
`]\n`
);
}
}
}
return invokePlugins(ast, info, plugins, overrides, globalOverrides);

@@ -91,0 +107,0 @@ },

@@ -54,2 +54,31 @@ export type XastDoctype = {

export type StringifyOptions = {
doctypeStart?: string;
doctypeEnd?: string;
procInstStart?: string;
procInstEnd?: string;
tagOpenStart?: string;
tagOpenEnd?: string;
tagCloseStart?: string;
tagCloseEnd?: string;
tagShortStart?: string;
tagShortEnd?: string;
attrStart?: string;
attrEnd?: string;
commentStart?: string;
commentEnd?: string;
cdataStart?: string;
cdataEnd?: string;
textStart?: string;
textEnd?: string;
indent?: number | string;
regEntities?: RegExp;
regValEntities?: RegExp;
encodeEntity?: (char: string) => string;
pretty?: boolean;
useShortTags?: boolean;
eol?: 'lf' | 'crlf';
finalNewline?: boolean;
};
type VisitorNode<Node> = {

@@ -56,0 +85,0 @@ enter?: (node: Node, parentNode: XastParent) => void | symbol;

{
"packageManager": "yarn@2.4.3",
"name": "svgo",
"version": "2.7.0",
"version": "2.8.0",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",
"license": "MIT",
"keywords": [

@@ -42,5 +44,3 @@ "svgo",

"main": "./lib/svgo-node.js",
"bin": {
"svgo": "./bin/svgo"
},
"bin": "./bin/svgo",
"files": [

@@ -53,4 +53,7 @@ "bin",

],
"engines": {
"node": ">=10.13.0"
},
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest --maxWorkers=3 --coverage",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --maxWorkers=4 --coverage",
"lint": "eslint --ignore-path .gitignore . && prettier --check \"**/*.js\" --ignore-path .gitignore",

@@ -107,3 +110,3 @@ "fix": "eslint --ignore-path .gitignore --fix . && prettier --write \"**/*.js\" --ignore-path .gitignore",

"csso": "^4.2.0",
"nanocolors": "^0.1.12",
"picocolors": "^1.0.0",
"stable": "^0.1.8"

@@ -120,4 +123,3 @@ },

"eslint": "^7.32.0",
"jest": "^27.2.1",
"mock-stdin": "^1.0.0",
"jest": "^27.2.5",
"node-fetch": "^2.6.2",

@@ -130,10 +132,5 @@ "pixelmatch": "^5.2.1",

"rollup-plugin-terser": "^7.0.2",
"strip-ansi": "^6.0.0",
"tar-stream": "^2.2.0",
"typescript": "^4.4.3"
},
"engines": {
"node": ">=10.13.0"
},
"license": "MIT"
}
}
'use strict';
/**
* @typedef {import('../lib/types').Specificity} Specificity
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
*/
const csstree = require('css-tree');
const { querySelectorAll, closestByName } = require('../lib/xast.js');
const cssTools = require('../lib/css-tools');
// @ts-ignore not defined in @types/csso
const specificity = require('csso/lib/restructure/prepare/specificity');
const stable = require('stable');
const {
visitSkip,
querySelectorAll,
detachNodeFromParent,
} = require('../lib/xast.js');
exports.type = 'visitor';
exports.name = 'inlineStyles';
exports.type = 'full';
exports.active = true;
exports.description = 'inline styles (additional options)';
exports.params = {
onlyMatchedOnce: true,
removeMatchedSelectors: true,
useMqs: ['', 'screen'],
usePseudos: [''],
/**
* Compares two selector specificities.
* extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
*
* @type {(a: Specificity, b: Specificity) => number}
*/
const compareSpecificity = (a, b) => {
for (var i = 0; i < 4; i += 1) {
if (a[i] < b[i]) {
return -1;
} else if (a[i] > b[i]) {
return 1;
}
}
return 0;
};
exports.description = 'inline styles (additional options)';
/**

@@ -41,265 +60,321 @@ * Moves + merges styles from style elements to element styles

*
* @param {Object} root document element
* @param {Object} opts plugin params
* @author strarsis <strarsis@gmail.com>
*
* @author strarsis <strarsis@gmail.com>
* @type {import('../lib/types').Plugin<{
* onlyMatchedOnce?: boolean,
* removeMatchedSelectors?: boolean,
* useMqs?: Array<string>,
* usePseudos?: Array<string>
* }>}
*/
exports.fn = function (root, opts) {
// collect <style/>s
var styleEls = querySelectorAll(root, 'style');
exports.fn = (root, params) => {
const {
onlyMatchedOnce = true,
removeMatchedSelectors = true,
useMqs = ['', 'screen'],
usePseudos = [''],
} = params;
//no <styles/>s, nothing to do
if (styleEls.length === 0) {
return root;
}
/**
* @type {Array<{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }>}
*/
const styles = [];
/**
* @type {Array<{
* node: csstree.Selector,
* item: csstree.ListItem<csstree.CssNode>,
* rule: csstree.Rule,
* matchedElements?: Array<XastElement>
* }>}
*/
let selectors = [];
var styles = [],
selectors = [];
return {
element: {
enter: (node, parentNode) => {
// skip <foreignObject /> content
if (node.name === 'foreignObject') {
return visitSkip;
}
// collect only non-empty <style /> elements
if (node.name !== 'style' || node.children.length === 0) {
return;
}
// values other than the empty string or text/css are not used
if (
node.attributes.type != null &&
node.attributes.type !== '' &&
node.attributes.type !== 'text/css'
) {
return;
}
// parse css in style element
let cssText = '';
for (const child of node.children) {
if (child.type === 'text' || child.type === 'cdata') {
cssText += child.value;
}
}
/**
* @type {null | csstree.CssNode}
*/
let cssAst = null;
try {
cssAst = csstree.parse(cssText, {
parseValue: false,
parseCustomProperty: false,
});
} catch {
return;
}
if (cssAst.type === 'StyleSheet') {
styles.push({ node, parentNode, cssAst });
}
for (var styleEl of styleEls) {
// values other than the empty string or text/css are not used
if (
styleEl.attributes.type != null &&
styleEl.attributes.type !== '' &&
styleEl.attributes.type !== 'text/css'
) {
continue;
}
// skip empty <style/>s or <foreignObject> content.
if (
styleEl.children.length === 0 ||
closestByName(styleEl, 'foreignObject')
) {
continue;
}
// collect selectors
csstree.walk(cssAst, {
visit: 'Selector',
enter(node, item) {
const atrule = this.atrule;
const rule = this.rule;
if (rule == null) {
return;
}
var cssStr = cssTools.getCssStr(styleEl);
// skip media queries not included into useMqs param
let mq = '';
if (atrule != null) {
mq = atrule.name;
if (atrule.prelude != null) {
mq += ` ${csstree.generate(atrule.prelude)}`;
}
}
if (useMqs.includes(mq) === false) {
return;
}
// collect <style/>s and their css ast
var cssAst = {};
try {
cssAst = csstree.parse(cssStr, {
parseValue: false,
parseCustomProperty: false,
});
} catch (parseError) {
// console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError);
continue;
}
/**
* @type {Array<{
* item: csstree.ListItem<csstree.CssNode>,
* list: csstree.List<csstree.CssNode>
* }>}
*/
const pseudos = [];
if (node.type === 'Selector') {
node.children.each((childNode, childItem, childList) => {
if (
childNode.type === 'PseudoClassSelector' ||
childNode.type === 'PseudoElementSelector'
) {
pseudos.push({ item: childItem, list: childList });
}
});
}
styles.push({
styleEl: styleEl,
cssAst: cssAst,
});
// skip pseudo classes and pseudo elements not includes into usePseudos param
const pseudoSelectors = csstree.generate({
type: 'Selector',
children: new csstree.List().fromArray(
pseudos.map((pseudo) => pseudo.item.data)
),
});
if (usePseudos.includes(pseudoSelectors) === false) {
return;
}
selectors = selectors.concat(cssTools.flattenToSelectors(cssAst));
}
// remove pseudo classes and elements to allow querySelector match elements
// TODO this is not very accurate since some pseudo classes like first-child
// are used for selection
for (const pseudo of pseudos) {
pseudo.list.remove(pseudo.item);
}
// filter for mediaqueries to be used or without any mediaquery
var selectorsMq = cssTools.filterByMqs(selectors, opts.useMqs);
selectors.push({ node, item, rule });
},
});
},
},
// filter for pseudo elements to be used
var selectorsPseudo = cssTools.filterByPseudos(selectorsMq, opts.usePseudos);
root: {
exit: () => {
if (styles.length === 0) {
return;
}
// stable sort selectors
const sortedSelectors = stable(selectors, (a, b) => {
const aSpecificity = specificity(a.item.data);
const bSpecificity = specificity(b.item.data);
return compareSpecificity(aSpecificity, bSpecificity);
}).reverse();
// remove PseudoClass from its SimpleSelector for proper matching
cssTools.cleanPseudos(selectorsPseudo);
for (const selector of sortedSelectors) {
// match selectors
const selectorText = csstree.generate(selector.item.data);
/**
* @type {Array<XastElement>}
*/
const matchedElements = [];
try {
for (const node of querySelectorAll(root, selectorText)) {
if (node.type === 'element') {
matchedElements.push(node);
}
}
} catch (selectError) {
continue;
}
// nothing selected
if (matchedElements.length === 0) {
continue;
}
// stable sort selectors
var sortedSelectors = cssTools.sortSelectors(selectorsPseudo).reverse();
// apply styles to matched elements
// skip selectors that match more than once if option onlyMatchedOnce is enabled
if (onlyMatchedOnce && matchedElements.length > 1) {
continue;
}
var selector, selectedEl;
// apply <style/> to matched elements
for (const selectedEl of matchedElements) {
const styleDeclarationList = csstree.parse(
selectedEl.attributes.style == null
? ''
: selectedEl.attributes.style,
{
context: 'declarationList',
parseValue: false,
}
);
if (styleDeclarationList.type !== 'DeclarationList') {
continue;
}
const styleDeclarationItems = new Map();
csstree.walk(styleDeclarationList, {
visit: 'Declaration',
enter(node, item) {
styleDeclarationItems.set(node.property, 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 matchedItem = styleDeclarationItems.get(
ruleDeclaration.property
);
const ruleDeclarationItem =
styleDeclarationList.children.createItem(ruleDeclaration);
if (matchedItem == null) {
styleDeclarationList.children.append(ruleDeclarationItem);
} else if (
matchedItem.data.important !== true &&
ruleDeclaration.important === true
) {
styleDeclarationList.children.replace(
matchedItem,
ruleDeclarationItem
);
styleDeclarationItems.set(
ruleDeclaration.property,
ruleDeclarationItem
);
}
},
});
selectedEl.attributes.style =
csstree.generate(styleDeclarationList);
}
// match selectors
for (selector of sortedSelectors) {
var selectorStr = csstree.generate(selector.item.data),
selectedEls = null;
try {
selectedEls = querySelectorAll(root, selectorStr);
} catch (selectError) {
// console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError);
continue;
}
if (selectedEls.length === 0) {
// nothing selected
continue;
}
selector.selectedEls = selectedEls;
}
// apply <style/> styles to matched elements
for (selector of sortedSelectors) {
if (!selector.selectedEls) {
continue;
}
if (
opts.onlyMatchedOnce &&
selector.selectedEls !== null &&
selector.selectedEls.length > 1
) {
// skip selectors that match more than once if option onlyMatchedOnce is enabled
continue;
}
// apply <style/> to matched elements
for (selectedEl of selector.selectedEls) {
if (selector.rule === null) {
continue;
}
const styleDeclarationList = csstree.parse(
selectedEl.attributes.style == null ? '' : selectedEl.attributes.style,
{
context: 'declarationList',
parseValue: false,
}
);
const styleDeclarationItems = new Map();
csstree.walk(styleDeclarationList, {
visit: 'Declaration',
enter(node, item) {
styleDeclarationItems.set(node.property, 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 matchedItem = styleDeclarationItems.get(
ruleDeclaration.property
);
const ruleDeclarationItem =
styleDeclarationList.children.createItem(ruleDeclaration);
if (matchedItem == null) {
styleDeclarationList.children.append(ruleDeclarationItem);
} else if (
matchedItem.data.important !== true &&
ruleDeclaration.important === true
if (
removeMatchedSelectors &&
matchedElements.length !== 0 &&
selector.rule.prelude.type === 'SelectorList'
) {
styleDeclarationList.children.replace(
matchedItem,
ruleDeclarationItem
);
styleDeclarationItems.set(
ruleDeclaration.property,
ruleDeclarationItem
);
// clean up matching simple selectors if option removeMatchedSelectors is enabled
selector.rule.prelude.children.remove(selector.item);
}
},
});
selectedEl.attributes.style = csstree.generate(styleDeclarationList);
}
selector.matchedElements = matchedElements;
}
if (
opts.removeMatchedSelectors &&
selector.selectedEls !== null &&
selector.selectedEls.length > 0
) {
// clean up matching simple selectors if option removeMatchedSelectors is enabled
selector.rule.prelude.children.remove(selector.item);
}
}
// no further processing required
if (removeMatchedSelectors === false) {
return;
}
if (!opts.removeMatchedSelectors) {
return root; // no further processing required
}
// clean up matched class + ID attribute values
for (const selector of sortedSelectors) {
if (selector.matchedElements == null) {
continue;
}
// clean up matched class + ID attribute values
for (selector of sortedSelectors) {
if (!selector.selectedEls) {
continue;
}
if (onlyMatchedOnce && selector.matchedElements.length > 1) {
// skip selectors that match more than once if option onlyMatchedOnce is enabled
continue;
}
if (
opts.onlyMatchedOnce &&
selector.selectedEls !== null &&
selector.selectedEls.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(' ')
);
const firstSubSelector = selector.node.children.first();
if (
firstSubSelector != null &&
firstSubSelector.type === 'ClassSelector'
) {
classList.delete(firstSubSelector.name);
}
if (classList.size === 0) {
delete selectedEl.attributes.class;
} else {
selectedEl.attributes.class = Array.from(classList).join(' ');
}
for (selectedEl of selector.selectedEls) {
// class
const classList = new Set(
selectedEl.attributes.class == null
? null
: selectedEl.attributes.class.split(' ')
);
const firstSubSelector = selector.item.data.children.first();
if (firstSubSelector.type === 'ClassSelector') {
classList.delete(firstSubSelector.name);
}
if (classList.size === 0) {
delete selectedEl.attributes.class;
} else {
selectedEl.attributes.class = Array.from(classList).join(' ');
}
// ID
if (firstSubSelector.type === 'IdSelector') {
if (selectedEl.attributes.id === firstSubSelector.name) {
delete selectedEl.attributes.id;
// ID
if (
firstSubSelector != null &&
firstSubSelector.type === 'IdSelector'
) {
if (selectedEl.attributes.id === firstSubSelector.name) {
delete selectedEl.attributes.id;
}
}
}
}
}
}
}
// clean up now empty elements
for (var style of styles) {
csstree.walk(style.cssAst, {
visit: 'Rule',
enter: function (node, item, list) {
// clean up <style/> atrules without any rulesets left
if (
node.type === 'Atrule' &&
// only Atrules containing rulesets
node.block !== null &&
node.block.children.isEmpty()
) {
list.remove(item);
return;
}
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);
}
},
});
// clean up <style/> rulesets without any css selectors left
if (node.type === 'Rule' && node.prelude.children.isEmpty()) {
list.remove(item);
if (style.cssAst.children.isEmpty()) {
// remove emtpy 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);
}
}
}
},
});
if (style.cssAst.children.isEmpty()) {
// clean up now emtpy <style/>s
var styleParentEl = style.styleEl.parentNode;
styleParentEl.spliceContent(
styleParentEl.children.indexOf(style.styleEl),
1
);
if (
styleParentEl.name === 'defs' &&
styleParentEl.children.length === 0
) {
// also clean up now empty <def/>s
var defsParentEl = styleParentEl.parentNode;
defsParentEl.spliceContent(
defsParentEl.children.indexOf(styleParentEl),
1
);
}
continue;
}
// update existing, left over <style>s
cssTools.setCssStr(style.styleEl, csstree.generate(style.cssAst));
}
return root;
},
};
};

@@ -209,11 +209,12 @@ 'use strict';

) {
// extract id reference from url(...) value
const matches = /url\((.*?)\)/gi.exec(node.attributes[name]);
if (matches != null) {
const value = matches[1];
const prefixed = prefixReference(prefix, value);
if (prefixed != null) {
node.attributes[name] = `url(${prefixed})`;
node.attributes[name] = node.attributes[name].replace(
/url\((.*?)\)/gi,
(match, url) => {
const prefixed = prefixReference(prefix, url);
if (prefixed == null) {
return match;
}
return `url(${prefixed})`;
}
}
);
}

@@ -220,0 +221,0 @@ }

@@ -5,8 +5,5 @@ 'use strict';

exports.type = 'visitor';
exports.name = 'removeEmptyAttrs';
exports.type = 'perItem';
exports.active = true;
exports.description = 'removes empty attributes';

@@ -17,19 +14,22 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
* @author Kir Belevich
*
* @author Kir Belevich
* @type {import('../lib/types').Plugin<void>}
*/
exports.fn = function (item) {
if (item.type === 'element') {
for (const [name, value] of Object.entries(item.attributes)) {
if (
value === '' &&
// empty conditional processing attributes prevents elements from rendering
attrsGroups.conditionalProcessing.includes(name) === false
) {
delete item.attributes[name];
}
}
}
exports.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.includes(name) === false
) {
delete node.attributes[name];
}
}
},
},
};
};

@@ -28,3 +28,4 @@ 'use strict';

delete item.attributes.xmlns;
delete item.attributes['xmlns:xlink'];
}
};

@@ -66,12 +66,14 @@ <div align="center">

// enable a built-in plugin by name
'builtinPluginName',
'prefixIds',
// or by expanded version
{
name: 'builtinPluginName',
name: 'prefixIds',
},
// some plugins allow/require to pass options
{
name: 'builtinPluginName',
name: 'prefixIds',
params: {
optionName: 'optionValue',
prefix: 'my-prefix',
},

@@ -94,17 +96,20 @@ },

// customize options for plugins included in preset
builtinPluginName: {
optionName: 'optionValue',
inlineStyles: {
onlyMatchedOnce: false,
},
// or disable plugins
anotherBuiltinPlugin: false,
removeDoctype: false,
},
},
},
// Enable builtin plugin not included in preset
'moreBuiltinPlugin',
// Enable and configure builtin plugin not included in preset
// enable builtin plugin not included in default preset
'prefixIds',
// enable and configure builtin plugin not included in preset
{
name: 'manyBuiltInPlugin',
name: 'sortAttrs',
params: {
optionName: 'value',
xmlnsOrder: 'alphabetical',
},

@@ -111,0 +116,0 @@ },

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc