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';
const { selectAll, selectOne, is } = require('css-select');
const { parseName } = require('./tools.js');
const svgoCssSelectAdapter = require('./css-select-adapter');
var cssSelectOpts = {
@ -81,7 +82,7 @@ JSAPI.prototype.isElem = function (param) {
* @return {Object} element
*/
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;
};
@ -181,19 +182,23 @@ JSAPI.prototype.hasAttrLocal = function (localName, val) {
return this.someAttr(callback);
function nameTest(attr) {
return attr.local === localName;
const { local } = parseName(attr.name);
return local === localName;
}
function stringValueTest(attr) {
return attr.local === localName && val == attr.value;
const { local } = parseName(attr.name);
return local === localName && val == attr.value;
}
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) {
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) {
attr = attr || {};
if (
attr.name === undefined ||
attr.prefix === undefined ||
attr.local === undefined
)
return false;
if (attr.name === undefined) return false;
this.attrs = this.attrs || {};
this.attrs[attr.name] = attr;

View File

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

View File

@ -138,3 +138,30 @@ var removeLeadingZero = function (num) {
return strNum;
};
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 {
elem.addAttr({
name: 'stroke-width',
prefix: '',
local: 'stroke-width',
value: strokeWidth.replace(regNumericValues, function (num) {
return removeLeadingZero(num * scale);
}),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
'use strict';
const { parseName } = require('../lib/svgo/tools.js');
exports.type = 'full';
exports.active = true;
@ -48,9 +50,10 @@ exports.fn = function (data) {
if (item.isElem('svg')) {
item.eachAttr(function (attr) {
const { prefix, local } = parseName(attr.name);
// collect namespaces
if (attr.prefix === 'xmlns' && attr.local) {
xmlnsCollection.push(attr.local);
if (prefix === 'xmlns' && local) {
xmlnsCollection.push(local);
}
});
@ -62,14 +65,16 @@ exports.fn = function (data) {
}
if (xmlnsCollection.length) {
const { prefix } = parseName(item.elem);
// check item for the ns-attrs
if (item.prefix) {
removeNSfromCollection(item.prefix);
if (prefix) {
removeNSfromCollection(prefix);
}
// check each attr for the ns-attrs
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({
name: 'stroke',
value: 'none',
prefix: '',
local: 'stroke',
});
}
}
@ -86,8 +84,6 @@ exports.fn = function (item, params) {
item.addAttr({
name: 'fill',
value: 'none',
prefix: '',
local: 'fill',
});
}
}

View File

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

View File

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

View File

@ -12,13 +12,9 @@ describe('svgo api', function () {
it('should be able to create content item', function () {
var item = createContentItem({
elem: 'elementName',
prefix: 'prefixName',
local: 'localName',
});
expect(item).to.be.instanceOf(JSAPI);
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 () {

View File

@ -103,14 +103,6 @@ describe('svg2js', function () {
it('should have property elem: "svg"', function () {
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 () {
@ -146,17 +138,6 @@ describe('svg2js', function () {
'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 = {
name: 'test',
value: 3,
prefix: '',
local: 'test',
};
it('svg should have property "addAttr"', function () {
@ -353,31 +332,6 @@ describe('svg2js', function () {
).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 () {
expect(root.content[3].addAttr()).to.be.false;
});