1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-29 20:21:14 +03:00

Remove prefix/local support in elements and attributes (#1413)

These parts of element and attribute name are easy to extract.
Now we can easily replace attrs with xast attributes object.
This commit is contained in:
Bogdan Chadkin
2021-03-10 13:26:15 +03:00
committed by GitHub
parent 2458b70f31
commit d1d6e5efe8
21 changed files with 76 additions and 126 deletions

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const { selectAll, selectOne, is } = require('css-select'); const { selectAll, selectOne, is } = require('css-select');
const { parseName } = require('./tools.js');
const svgoCssSelectAdapter = require('./css-select-adapter'); const svgoCssSelectAdapter = require('./css-select-adapter');
var cssSelectOpts = { var cssSelectOpts = {
@ -81,7 +82,7 @@ JSAPI.prototype.isElem = function (param) {
* @return {Object} element * @return {Object} element
*/ */
JSAPI.prototype.renameElem = function (name) { JSAPI.prototype.renameElem = function (name) {
if (name && typeof name === 'string') this.elem = this.local = name; if (name && typeof name === 'string') this.elem = name;
return this; return this;
}; };
@ -181,19 +182,23 @@ JSAPI.prototype.hasAttrLocal = function (localName, val) {
return this.someAttr(callback); return this.someAttr(callback);
function nameTest(attr) { function nameTest(attr) {
return attr.local === localName; const { local } = parseName(attr.name);
return local === localName;
} }
function stringValueTest(attr) { function stringValueTest(attr) {
return attr.local === localName && val == attr.value; const { local } = parseName(attr.name);
return local === localName && val == attr.value;
} }
function regexpValueTest(attr) { function regexpValueTest(attr) {
return attr.local === localName && val.test(attr.value); const { local } = parseName(attr.name);
return local === localName && val.test(attr.value);
} }
function funcValueTest(attr) { function funcValueTest(attr) {
return attr.local === localName && val(attr.value); const { local } = parseName(attr.name);
return local === localName && val(attr.value);
} }
}; };
@ -271,12 +276,7 @@ JSAPI.prototype.removeAttr = function (name, val, recursive) {
JSAPI.prototype.addAttr = function (attr) { JSAPI.prototype.addAttr = function (attr) {
attr = attr || {}; attr = attr || {};
if ( if (attr.name === undefined) return false;
attr.name === undefined ||
attr.prefix === undefined ||
attr.local === undefined
)
return false;
this.attrs = this.attrs || {}; this.attrs = this.attrs || {};
this.attrs[attr.name] = attr; this.attrs[attr.name] = attr;

View File

@ -73,8 +73,6 @@ module.exports = function (data) {
sax.onopentag = function (data) { sax.onopentag = function (data) {
var elem = { var elem = {
elem: data.name, elem: data.name,
prefix: data.prefix,
local: data.local,
attrs: {}, attrs: {},
}; };
@ -96,8 +94,6 @@ module.exports = function (data) {
elem.attrs[name] = { elem.attrs[name] = {
name: name, name: name,
value: attr.value, value: attr.value,
prefix: attr.prefix,
local: attr.local,
}; };
} }
} }

View File

@ -138,3 +138,30 @@ var removeLeadingZero = function (num) {
return strNum; return strNum;
}; };
exports.removeLeadingZero = removeLeadingZero; exports.removeLeadingZero = removeLeadingZero;
const parseName = (name) => {
if (name == null) {
return {
prefix: '',
local: '',
};
}
if (name === 'xmlns') {
return {
prefix: 'xmlns',
local: '',
};
}
const chunks = name.split(':');
if (chunks.length === 1) {
return {
prefix: '',
local: chunks[0],
};
}
return {
prefix: chunks[0],
local: chunks[1],
};
};
exports.parseName = parseName;

View File

@ -172,8 +172,6 @@ exports.applyTransforms = function (elem, path, params) {
} else { } else {
elem.addAttr({ elem.addAttr({
name: 'stroke-width', name: 'stroke-width',
prefix: '',
local: 'stroke-width',
value: strokeWidth.replace(regNumericValues, function (num) { value: strokeWidth.replace(regNumericValues, function (num) {
return removeLeadingZero(num * scale); return removeLeadingZero(num * scale);
}), }),

View File

@ -65,8 +65,6 @@ exports.fn = function (data, params) {
if (!svg.hasAttr(attribute)) { if (!svg.hasAttr(attribute)) {
svg.addAttr({ svg.addAttr({
name: attribute, name: attribute,
prefix: '',
local: attribute,
}); });
} }
} else if (typeof attribute === 'object') { } else if (typeof attribute === 'object') {
@ -75,8 +73,6 @@ exports.fn = function (data, params) {
svg.addAttr({ svg.addAttr({
name: key, name: key,
value: attribute[key], value: attribute[key],
prefix: '',
local: key,
}); });
} }
}); });

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
const { parseName } = require('../lib/svgo/tools.js');
exports.type = 'full'; exports.type = 'full';
exports.active = true; exports.active = true;
@ -157,13 +159,14 @@ exports.fn = function (data, params) {
return; return;
} }
// save references // save references
const { local } = parseName(attr.name);
if ( if (
referencesProps.has(attr.name) && referencesProps.has(attr.name) &&
(match = attr.value.match(regReferencesUrl)) (match = attr.value.match(regReferencesUrl))
) { ) {
key = match[2]; // url() reference key = match[2]; // url() reference
} else if ( } else if (
(attr.local === 'href' && (local === 'href' &&
(match = attr.value.match(regReferencesHref))) || (match = attr.value.match(regReferencesHref))) ||
(attr.name === 'begin' && (attr.name === 'begin' &&
(match = attr.value.match(regReferencesBegin))) (match = attr.value.match(regReferencesBegin)))

View File

@ -32,8 +32,6 @@ exports.fn = function (item) {
item.addAttr({ item.addAttr({
name: 'r', name: 'r',
value: radius, value: radius,
prefix: '',
local: 'r',
}); });
} }
} }

View File

@ -58,8 +58,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'd', name: 'd',
value: stringifyPathData({ pathData, precision }), value: stringifyPathData({ pathData, precision }),
prefix: '',
local: 'd',
}); });
item.renameElem('path').removeAttr(['x', 'y', 'width', 'height']); item.renameElem('path').removeAttr(['x', 'y', 'width', 'height']);
} }
@ -77,8 +75,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'd', name: 'd',
value: stringifyPathData({ pathData, precision }), value: stringifyPathData({ pathData, precision }),
prefix: '',
local: 'd',
}); });
item.renameElem('path').removeAttr(['x1', 'y1', 'x2', 'y2']); item.renameElem('path').removeAttr(['x1', 'y1', 'x2', 'y2']);
} }
@ -102,8 +98,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'd', name: 'd',
value: stringifyPathData({ pathData, precision }), value: stringifyPathData({ pathData, precision }),
prefix: '',
local: 'd',
}); });
item.renameElem('path').removeAttr('points'); item.renameElem('path').removeAttr('points');
} }
@ -124,8 +118,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'd', name: 'd',
value: stringifyPathData({ pathData, precision }), value: stringifyPathData({ pathData, precision }),
prefix: '',
local: 'd',
}); });
item.renameElem('path').removeAttr(['cx', 'cy', 'r']); item.renameElem('path').removeAttr(['cx', 'cy', 'r']);
} }
@ -147,8 +139,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'd', name: 'd',
value: stringifyPathData({ pathData, precision }), value: stringifyPathData({ pathData, precision }),
prefix: '',
local: 'd',
}); });
item.renameElem('path').removeAttr(['cx', 'cy', 'rx', 'ry']); item.renameElem('path').removeAttr(['cx', 'cy', 'rx', 'ry']);
} }

View File

@ -104,8 +104,6 @@ exports.fn = function (item, params) {
attrs[prop] = { attrs[prop] = {
name: prop, name: prop,
value: val, value: val,
local: prop,
prefix: '',
}; };
return false; return false;

View File

@ -100,9 +100,7 @@ function intersectInheritableAttrs(a, b) {
b.hasOwnProperty(n) && b.hasOwnProperty(n) &&
inheritableAttrs.indexOf(n) > -1 && inheritableAttrs.indexOf(n) > -1 &&
attr.name === b[n].name && attr.name === b[n].name &&
attr.value === b[n].value && attr.value === b[n].value
attr.prefix === b[n].prefix &&
attr.local === b[n].local
) { ) {
c[n] = attr; c[n] = attr;
} }

View File

@ -50,8 +50,6 @@ exports.fn = function (item) {
} else { } else {
inner.addAttr({ inner.addAttr({
name: attr.name, name: attr.name,
local: attr.local,
prefix: attr.prefix,
value: attr.value, value: attr.value,
}); });
} }

View File

@ -38,8 +38,6 @@ exports.fn = function (item) {
Number(item.attr('width').value) + Number(item.attr('width').value) +
' ' + ' ' +
Number(item.attr('height').value), Number(item.attr('height').value),
prefix: '',
local: 'viewBox',
}); });
item.removeAttr('width'); item.removeAttr('width');
item.removeAttr('height'); item.removeAttr('height');

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
const { parseName } = require('../lib/svgo/tools.js');
exports.type = 'perItem'; exports.type = 'perItem';
exports.active = true; exports.active = true;
@ -35,11 +37,9 @@ exports.fn = function (item, params) {
if (item.elem) { if (item.elem) {
if (item.isElem('svg')) { if (item.isElem('svg')) {
item.eachAttr(function (attr) { item.eachAttr(function (attr) {
if ( const { prefix, local } = parseName(attr.name);
attr.prefix === 'xmlns' && if (prefix === 'xmlns' && editorNamespaces.includes(attr.value)) {
editorNamespaces.indexOf(attr.value) > -1 prefixes.push(local);
) {
prefixes.push(attr.local);
// <svg xmlns:sodipodi=""> // <svg xmlns:sodipodi="">
item.removeAttr(attr.name); item.removeAttr(attr.name);
@ -49,13 +49,15 @@ exports.fn = function (item, params) {
// <* sodipodi:*=""> // <* sodipodi:*="">
item.eachAttr(function (attr) { item.eachAttr(function (attr) {
if (prefixes.indexOf(attr.prefix) > -1) { const { prefix } = parseName(attr.name);
if (prefixes.includes(prefix)) {
item.removeAttr(attr.name); item.removeAttr(attr.name);
} }
}); });
// <sodipodi:*> // <sodipodi:*>
if (prefixes.indexOf(item.prefix) > -1) { const { prefix } = parseName(item.elem);
if (prefixes.includes(prefix)) {
return false; return false;
} }
} }

View File

@ -103,13 +103,9 @@ function parseViewBox(svg) {
var path = new JSAPI({ var path = new JSAPI({
elem: 'path', elem: 'path',
prefix: '',
local: 'path',
}); });
path.addAttr({ path.addAttr({
name: 'd', name: 'd',
prefix: '',
local: 'd',
value: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z', value: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z',
}); });

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
const { parseName } = require('../lib/svgo/tools.js');
exports.type = 'perItem'; exports.type = 'perItem';
exports.active = true; exports.active = true;
@ -66,7 +68,7 @@ for (const elem of Object.values(elems)) {
*/ */
exports.fn = function (item, params) { exports.fn = function (item, params) {
// elems w/o namespace prefix // elems w/o namespace prefix
if (item.isElem() && !item.prefix) { if (item.isElem() && !parseName(item.elem).prefix) {
var elem = item.elem; var elem = item.elem;
// remove unknown element's content // remove unknown element's content
@ -79,7 +81,7 @@ exports.fn = function (item, params) {
item.content.forEach(function (content, i) { item.content.forEach(function (content, i) {
if ( if (
content.isElem() && content.isElem() &&
!content.prefix && !parseName(content.elem).prefix &&
((elems[elem].content && // Do we have a record of its permitted content? ((elems[elem].content && // Do we have a record of its permitted content?
elems[elem].content.indexOf(content.elem) === -1) || elems[elem].content.indexOf(content.elem) === -1) ||
(!elems[elem].content && // we dont know about its permitted content (!elems[elem].content && // we dont know about its permitted content
@ -93,9 +95,10 @@ exports.fn = function (item, params) {
// remove element's unknown attrs and attrs with default values // remove element's unknown attrs and attrs with default values
if (elems[elem] && elems[elem].attrs) { if (elems[elem] && elems[elem].attrs) {
item.eachAttr(function (attr) { item.eachAttr(function (attr) {
const { prefix } = parseName(attr.name);
if ( if (
attr.name !== 'xmlns' && attr.name !== 'xmlns' &&
(attr.prefix === 'xml' || !attr.prefix) && (prefix === 'xml' || !prefix) &&
(!params.keepDataAttrs || attr.name.indexOf('data-') != 0) && (!params.keepDataAttrs || attr.name.indexOf('data-') != 0) &&
(!params.keepAriaAttrs || attr.name.indexOf('aria-') != 0) && (!params.keepAriaAttrs || attr.name.indexOf('aria-') != 0) &&
(!params.keepRoleAttr || attr.name != 'role') (!params.keepRoleAttr || attr.name != 'role')

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
const { parseName } = require('../lib/svgo/tools.js');
exports.type = 'full'; exports.type = 'full';
exports.active = true; exports.active = true;
@ -48,9 +50,10 @@ exports.fn = function (data) {
if (item.isElem('svg')) { if (item.isElem('svg')) {
item.eachAttr(function (attr) { item.eachAttr(function (attr) {
const { prefix, local } = parseName(attr.name);
// collect namespaces // collect namespaces
if (attr.prefix === 'xmlns' && attr.local) { if (prefix === 'xmlns' && local) {
xmlnsCollection.push(attr.local); xmlnsCollection.push(local);
} }
}); });
@ -62,14 +65,16 @@ exports.fn = function (data) {
} }
if (xmlnsCollection.length) { if (xmlnsCollection.length) {
const { prefix } = parseName(item.elem);
// check item for the ns-attrs // check item for the ns-attrs
if (item.prefix) { if (prefix) {
removeNSfromCollection(item.prefix); removeNSfromCollection(prefix);
} }
// check each attr for the ns-attrs // check each attr for the ns-attrs
item.eachAttr(function (attr) { item.eachAttr(function (attr) {
removeNSfromCollection(attr.prefix); const { prefix } = parseName(attr.name);
removeNSfromCollection(prefix);
}); });
} }

View File

@ -66,8 +66,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'stroke', name: 'stroke',
value: 'none', value: 'none',
prefix: '',
local: 'stroke',
}); });
} }
} }
@ -86,8 +84,6 @@ exports.fn = function (item, params) {
item.addAttr({ item.addAttr({
name: 'fill', name: 'fill',
value: 'none', value: 'none',
prefix: '',
local: 'fill',
}); });
} }
} }

View File

@ -39,8 +39,6 @@ exports.fn = function (data) {
if (!hasSeen.elem.hasAttr('id')) { if (!hasSeen.elem.hasAttr('id')) {
hasSeen.elem.addAttr({ hasSeen.elem.addAttr({
name: 'id', name: 'id',
local: 'id',
prefix: '',
value: 'reuse-' + count++, value: 'reuse-' + count++,
}); });
} }
@ -52,8 +50,6 @@ exports.fn = function (data) {
const defsTag = new JSAPI( const defsTag = new JSAPI(
{ {
elem: 'defs', elem: 'defs',
prefix: '',
local: 'defs',
content: [], content: [],
attrs: [], attrs: [],
}, },
@ -89,8 +85,6 @@ function convertToUse(item, href) {
item.removeAttr('fill'); item.removeAttr('fill');
item.addAttr({ item.addAttr({
name: 'xlink:href', name: 'xlink:href',
local: 'xlink:href',
prefix: 'none',
value: '#' + href, value: '#' + href,
}); });
delete item.pathJS; delete item.pathJS;

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
const { parseName } = require('../lib/svgo/tools.js');
exports.type = 'perItem'; exports.type = 'perItem';
exports.active = false; exports.active = false;
@ -48,13 +50,15 @@ exports.fn = function (item, params) {
}); });
attrs.sort(function (a, b) { attrs.sort(function (a, b) {
if (a.prefix != b.prefix) { const { prefix: prefixA } = parseName(a.name);
const { prefix: prefixB } = parseName(b.name);
if (prefixA != prefixB) {
// xmlns attributes implicitly have the prefix xmlns // xmlns attributes implicitly have the prefix xmlns
if (xmlnsOrder == 'front') { if (xmlnsOrder == 'front') {
if (a.prefix == 'xmlns') return -1; if (prefixA === 'xmlns') return -1;
if (b.prefix == 'xmlns') return 1; if (prefixB === 'xmlns') return 1;
} }
return a.prefix < b.prefix ? -1 : 1; return prefixA < prefixB ? -1 : 1;
} }
var aindex = orderlen; var aindex = orderlen;

View File

@ -12,13 +12,9 @@ describe('svgo api', function () {
it('should be able to create content item', function () { it('should be able to create content item', function () {
var item = createContentItem({ var item = createContentItem({
elem: 'elementName', elem: 'elementName',
prefix: 'prefixName',
local: 'localName',
}); });
expect(item).to.be.instanceOf(JSAPI); expect(item).to.be.instanceOf(JSAPI);
expect(item).to.have.ownProperty('elem').equal('elementName'); expect(item).to.have.ownProperty('elem').equal('elementName');
expect(item).to.have.ownProperty('prefix').equal('prefixName');
expect(item).to.have.ownProperty('local').equal('localName');
}); });
it('should be able create content item without argument', function () { it('should be able create content item without argument', function () {

View File

@ -103,14 +103,6 @@ describe('svg2js', function () {
it('should have property elem: "svg"', function () { it('should have property elem: "svg"', function () {
expect(root.content[3]).to.have.property('elem', 'svg'); expect(root.content[3]).to.have.property('elem', 'svg');
}); });
it('should have property prefix: ""', function () {
expect(root.content[3]).to.have.property('prefix', '');
});
it('should have property local: "svg"', function () {
expect(root.content[3]).to.have.property('local', 'svg');
});
}); });
describe('attributes', function () { describe('attributes', function () {
@ -146,17 +138,6 @@ describe('svg2js', function () {
'1.1' '1.1'
); );
}); });
it('should have property prefix: ""', function () {
expect(root.content[3].attrs.version).to.have.property('prefix', '');
});
it('should have property local: "version"', function () {
expect(root.content[3].attrs.version).to.have.property(
'local',
'version'
);
});
}); });
}); });
@ -335,8 +316,6 @@ describe('svg2js', function () {
var attr = { var attr = {
name: 'test', name: 'test',
value: 3, value: 3,
prefix: '',
local: 'test',
}; };
it('svg should have property "addAttr"', function () { it('svg should have property "addAttr"', function () {
@ -353,31 +332,6 @@ describe('svg2js', function () {
).to.be.an.instanceOf(Object); ).to.be.an.instanceOf(Object);
}); });
it('svg.addAttr({ name: "trololo" }) should be false', function () {
expect(root.content[3].addAttr({ name: 'trololo' })).to.be.false;
});
it('svg.addAttr({ name: "trololo", value: 3 }) should be false', function () {
expect(root.content[3].addAttr({ name: 'trololo', value: 3 })).to.be
.false;
});
it('svg.addAttr({ name: "trololo", value: 3, prefix: "" }) should be false', function () {
expect(
root.content[3].addAttr({ name: 'trololo', value: 3, prefix: '' })
).to.be.false;
});
it('svg.addAttr({ name: "trololo", value: 3, local: "trololo" }) should be false', function () {
expect(
root.content[3].addAttr({
name: 'trololo',
value: 3,
local: 'trololo',
})
).to.be.false;
});
it('svg.addAttr() should be false', function () { it('svg.addAttr() should be false', function () {
expect(root.content[3].addAttr()).to.be.false; expect(root.content[3].addAttr()).to.be.false;
}); });