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

Add better parser errors (#1553)

Old SVGO errors were not very helpful. Packages like cssnano
(postcss-svgo) had to deal with a lot of issues which are hard to debug
with old errors.

```
Error: Error in parsing SVG: Unquoted attribute value
Line: 1
Column: 29
Char: 6
File: input.svg
```

New errors are more informative and may solve many struggles

```
Error: SvgoParserError: input.svg:2:29: Unquoted attribute value

  1 | <svg viewBox="0 0 120 120">
> 2 |   <circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
    |                             ^
  3 | </svg>
  4 |
```
This commit is contained in:
Bogdan Chadkin
2021-09-12 01:09:10 +03:00
committed by GitHub
parent e8321f0c27
commit 77102ed096
12 changed files with 327 additions and 139 deletions

View File

@ -29,7 +29,12 @@ const optimize = (input, config) => {
} }
for (let i = 0; i < maxPassCount; i += 1) { for (let i = 0; i < maxPassCount; i += 1) {
info.multipassCount = i; info.multipassCount = i;
svgjs = svg2js(input); // TODO throw this error in v3
try {
svgjs = svg2js(input, config.path);
} catch (error) {
return { error: error.toString(), modernError: error };
}
if (svgjs.error != null) { if (svgjs.error != null) {
if (config.path != null) { if (config.path != null) {
svgjs.path = config.path; svgjs.path = config.path;

View File

@ -12,12 +12,11 @@ test('allow to setup default preset', () => {
<circle fill="#ff0000" cx="60" cy="60" r="50"/> <circle fill="#ff0000" cx="60" cy="60" r="50"/>
</svg> </svg>
`; `;
expect( const { data } = optimize(svg, {
optimize(svg, { plugins: ['preset-default'],
plugins: ['preset-default'], js2svg: { pretty: true, indent: 2 },
js2svg: { pretty: true, indent: 2 }, });
}).data expect(data).toMatchInlineSnapshot(`
).toMatchInlineSnapshot(`
"<svg viewBox=\\"0 0 120 120\\"> "<svg viewBox=\\"0 0 120 120\\">
<circle fill=\\"red\\" cx=\\"60\\" cy=\\"60\\" r=\\"50\\"/> <circle fill=\\"red\\" cx=\\"60\\" cy=\\"60\\" r=\\"50\\"/>
</svg> </svg>
@ -35,24 +34,23 @@ test('allow to disable and customize plugins in preset', () => {
<circle fill="#ff0000" cx="60" cy="60" r="50"/> <circle fill="#ff0000" cx="60" cy="60" r="50"/>
</svg> </svg>
`; `;
expect( const { data } = optimize(svg, {
optimize(svg, { plugins: [
plugins: [ {
{ name: 'preset-default',
name: 'preset-default', params: {
params: { overrides: {
overrides: { removeXMLProcInst: false,
removeXMLProcInst: false, removeDesc: {
removeDesc: { removeAny: false,
removeAny: false,
},
}, },
}, },
}, },
], },
js2svg: { pretty: true, indent: 2 }, ],
}).data js2svg: { pretty: true, indent: 2 },
).toMatchInlineSnapshot(` });
expect(data).toMatchInlineSnapshot(`
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?> "<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
<svg viewBox=\\"0 0 120 120\\"> <svg viewBox=\\"0 0 120 120\\">
<desc> <desc>
@ -186,19 +184,18 @@ test('allow to customize precision for preset', () => {
<circle fill="#ff0000" cx="60.444444" cy="60" r="50"/> <circle fill="#ff0000" cx="60.444444" cy="60" r="50"/>
</svg> </svg>
`; `;
expect( const { data } = optimize(svg, {
optimize(svg, { plugins: [
plugins: [ {
{ name: 'preset-default',
name: 'preset-default', params: {
params: { floatPrecision: 4,
floatPrecision: 4,
},
}, },
], },
js2svg: { pretty: true, indent: 2 }, ],
}).data js2svg: { pretty: true, indent: 2 },
).toMatchInlineSnapshot(` });
expect(data).toMatchInlineSnapshot(`
"<svg viewBox=\\"0 0 120 120\\"> "<svg viewBox=\\"0 0 120 120\\">
<circle fill=\\"red\\" cx=\\"60.4444\\" cy=\\"60\\" r=\\"50\\"/> <circle fill=\\"red\\" cx=\\"60.4444\\" cy=\\"60\\" r=\\"50\\"/>
</svg> </svg>
@ -212,27 +209,118 @@ test('plugin precision should override preset precision', () => {
<circle fill="#ff0000" cx="60.444444" cy="60" r="50"/> <circle fill="#ff0000" cx="60.444444" cy="60" r="50"/>
</svg> </svg>
`; `;
expect( const { data } = optimize(svg, {
optimize(svg, { plugins: [
plugins: [ {
{ name: 'preset-default',
name: 'preset-default', params: {
params: { floatPrecision: 4,
floatPrecision: 4, overrides: {
overrides: { cleanupNumericValues: {
cleanupNumericValues: { floatPrecision: 5,
floatPrecision: 5,
},
}, },
}, },
}, },
], },
js2svg: { pretty: true, indent: 2 }, ],
}).data js2svg: { pretty: true, indent: 2 },
).toMatchInlineSnapshot(` });
expect(data).toMatchInlineSnapshot(`
"<svg viewBox=\\"0 0 120 120\\"> "<svg viewBox=\\"0 0 120 120\\">
<circle fill=\\"red\\" cx=\\"60.44444\\" cy=\\"60\\" r=\\"50\\"/> <circle fill=\\"red\\" cx=\\"60.44444\\" cy=\\"60\\" r=\\"50\\"/>
</svg> </svg>
" "
`); `);
}); });
test('provides informative error in result', () => {
const svg = `<svg viewBox="0 0 120 120">
<circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
</svg>
`;
const { modernError: error } = optimize(svg, { path: 'test.svg' });
expect(error.name).toEqual('SvgoParserError');
expect(error.message).toEqual('test.svg:2:33: Unquoted attribute value');
expect(error.reason).toEqual('Unquoted attribute value');
expect(error.line).toEqual(2);
expect(error.column).toEqual(33);
expect(error.source).toEqual(svg);
});
test('provides code snippet in rendered error', () => {
const svg = `<svg viewBox="0 0 120 120">
<circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
</svg>
`;
const { modernError: error } = optimize(svg, { path: 'test.svg' });
expect(error.toString())
.toEqual(`SvgoParserError: test.svg:2:29: Unquoted attribute value
1 | <svg viewBox="0 0 120 120">
> 2 | <circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
| ^
3 | </svg>
4 |
`);
});
test('supports errors without path', () => {
const svg = `<svg viewBox="0 0 120 120">
<circle/>
<circle/>
<circle/>
<circle/>
<circle/>
<circle/>
<circle/>
<circle/>
<circle/>
<circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
</svg>
`;
const { modernError: error } = optimize(svg);
expect(error.toString())
.toEqual(`SvgoParserError: <input>:11:29: Unquoted attribute value
9 | <circle/>
10 | <circle/>
> 11 | <circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
| ^
12 | </svg>
13 |
`);
});
test('slices long line in error code snippet', () => {
const svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" viewBox="0 0 230 120">
<path d="M318.198 551.135 530.33 918.56l-289.778-77.646 38.823-144.889c77.646-289.778 294.98-231.543 256.156-86.655s178.51 203.124 217.334 58.235q58.234-217.334 250.955 222.534t579.555 155.292z stroke-width="1.5" fill="red" stroke="red" />
</svg>
`;
const { modernError: error } = optimize(svg);
expect(error.toString())
.toEqual(`SvgoParserError: <input>:2:211: Invalid attribute name
1 | …-0.dtd" viewBox="0 0 230 120">
> 2 | …7.334 250.955 222.534t579.555 155.292z stroke-width="1.5" fill="red" strok…
| ^
3 |
4 |
`);
});
test('provides legacy error message', () => {
const svg = `<svg viewBox="0 0 120 120">
<circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
</svg>
`;
const { error } = optimize(svg, { path: 'test.svg' });
expect(error)
.toEqual(`SvgoParserError: test.svg:2:29: Unquoted attribute value
1 | <svg viewBox="0 0 120 120">
> 2 | <circle fill="#ff0000" cx=60.444444" cy="60" r="50"/>
| ^
3 | </svg>
4 |
`);
});

View File

@ -2,7 +2,7 @@
const FS = require('fs'); const FS = require('fs');
const PATH = require('path'); const PATH = require('path');
const { green } = require('colorette'); const { green, red } = require('colorette');
const { loadConfig, optimize } = require('../svgo-node.js'); const { loadConfig, optimize } = require('../svgo-node.js');
const pluginsMap = require('../../plugins/plugins.js'); const pluginsMap = require('../../plugins/plugins.js');
const PKG = require('../../package.json'); const PKG = require('../../package.json');
@ -383,12 +383,9 @@ function processSVGData(config, info, data, output, input) {
prevFileSize = Buffer.byteLength(data, 'utf8'); prevFileSize = Buffer.byteLength(data, 'utf8');
const result = optimize(data, { ...config, ...info }); const result = optimize(data, { ...config, ...info });
if (result.error) { if (result.modernError) {
let message = result.error; console.error(red(result.modernError.toString()));
if (result.path != null) { process.exit(1);
message += `\nFile: ${result.path}`;
}
throw Error(message);
} }
if (config.datauri) { if (config.datauri) {
result.data = encodeSVGDatauri(result.data, config.datauri); result.data = encodeSVGDatauri(result.data, config.datauri);

View File

@ -4,6 +4,55 @@ const SAX = require('@trysound/sax');
const JSAPI = require('./jsAPI.js'); const JSAPI = require('./jsAPI.js');
const { textElems } = require('../../plugins/_collections.js'); const { textElems } = require('../../plugins/_collections.js');
class SvgoParserError extends Error {
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 entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
const config = { const config = {
@ -20,7 +69,7 @@ const config = {
* *
* @param {String} data input data * @param {String} data input data
*/ */
module.exports = function (data) { module.exports = function (data, from) {
const sax = SAX.parser(config.strict, config); const sax = SAX.parser(config.strict, config);
const root = new JSAPI({ type: 'root', children: [] }); const root = new JSAPI({ type: 'root', children: [] });
let current = root; let current = root;
@ -115,16 +164,18 @@ module.exports = function (data) {
}; };
sax.onerror = function (e) { sax.onerror = function (e) {
e.message = 'Error in parsing SVG: ' + e.message; const error = new SvgoParserError(
if (e.message.indexOf('Unexpected end') < 0) { e.reason,
throw e; e.line + 1,
e.column,
data,
from
);
if (e.message.indexOf('Unexpected end') === -1) {
throw error;
} }
}; };
try { sax.write(data).close();
sax.write(data).close(); return root;
return root;
} catch (e) {
return { error: e.message };
}
}; };

79
package-lock.json generated
View File

@ -617,6 +617,17 @@
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"slash": "^3.0.0", "slash": "^3.0.0",
"strip-ansi": "^6.0.0" "strip-ansi": "^6.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
} }
}, },
"@jest/environment": { "@jest/environment": {
@ -867,9 +878,9 @@
"dev": true "dev": true
}, },
"@trysound/sax": { "@trysound/sax": {
"version": "0.1.1", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
"integrity": "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==" "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="
}, },
"@types/babel__core": { "@types/babel__core": {
"version": "7.1.15", "version": "7.1.15",
@ -1403,6 +1414,17 @@
"string-width": "^4.2.0", "string-width": "^4.2.0",
"strip-ansi": "^6.0.0", "strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
} }
}, },
"co": { "co": {
@ -1852,6 +1874,15 @@
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true "dev": true
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
} }
} }
}, },
@ -3946,6 +3977,17 @@
"requires": { "requires": {
"char-regex": "^1.0.2", "char-regex": "^1.0.2",
"strip-ansi": "^6.0.0" "strip-ansi": "^6.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
} }
}, },
"string-width": { "string-width": {
@ -3957,6 +3999,17 @@
"emoji-regex": "^8.0.0", "emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0", "is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0" "strip-ansi": "^6.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
} }
}, },
"string_decoder": { "string_decoder": {
@ -4059,6 +4112,15 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true "dev": true
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
} }
} }
}, },
@ -4339,6 +4401,17 @@
"ansi-styles": "^4.0.0", "ansi-styles": "^4.0.0",
"string-width": "^4.1.0", "string-width": "^4.1.0",
"strip-ansi": "^6.0.0" "strip-ansi": "^6.0.0"
},
"dependencies": {
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.0"
}
}
} }
}, },
"wrappy": { "wrappy": {

View File

@ -92,7 +92,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@trysound/sax": "0.1.1", "@trysound/sax": "0.2.0",
"colorette": "^1.3.0", "colorette": "^1.3.0",
"commander": "^7.2.0", "commander": "^7.2.0",
"css-select": "^4.1.3", "css-select": "^4.1.3",
@ -118,6 +118,7 @@
"prettier": "^2.3.2", "prettier": "^2.3.2",
"rollup": "^2.56.3", "rollup": "^2.56.3",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"strip-ansi": "^6.0.0",
"tar-stream": "^2.2.0", "tar-stream": "^2.2.0",
"typescript": "^4.4.2" "typescript": "^4.4.2"
}, },

33
test/cli/cli.test.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
const { spawn } = require('child_process');
const stripAnsi = require('strip-ansi');
test('should exit with 1 code on syntax error', async () => {
const proc = spawn('node', ['../../bin/svgo', 'invalid.svg'], {
cwd: __dirname,
});
const [code, stderr] = await Promise.all([
new Promise((resolve) => {
proc.on('close', (code) => {
resolve(code);
});
}),
new Promise((resolve) => {
proc.stderr.on('data', (error) => {
resolve(error.toString());
});
}),
]);
expect(code).toEqual(1);
expect(stripAnsi(stderr))
.toEqual(`SvgoParserError: invalid.svg:2:27: Unquoted attribute value
1 | <svg>
> 2 | <rect x="0" y="0" width=10" height="20" />
| ^
3 | </svg>
4 |
`);
});

3
test/cli/invalid.svg Normal file
View File

@ -0,0 +1,3 @@
<svg>
<rect x="0" y="0" width=10" height="20" />
</svg>

After

Width:  |  Height:  |  Size: 58 B

View File

@ -341,46 +341,4 @@ describe('svg2js', function () {
}); });
}); });
}); });
describe('malformed svg', function () {
var filepath = PATH.resolve(__dirname, './test.bad.svg'),
root,
error;
beforeAll(function (done) {
FS.readFile(filepath, 'utf8', function (err, data) {
if (err) {
throw err;
}
try {
root = SVG2JS(data);
} catch (e) {
error = e;
}
done();
});
});
describe('root', function () {
it('should have property "error"', function () {
expect(root).toHaveProperty('error');
});
});
describe('root.error', function () {
it('should be "Error in parsing SVG: Unexpected close tag"', function () {
expect(root.error).toEqual(
'Error in parsing SVG: Unexpected close tag\nLine: 10\nColumn: 15\nChar: >'
);
});
});
describe('error', function () {
it('should not be thrown', function () {
expect(error).not.toEqual(expect.anything());
});
});
});
}); });

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="120px" height="120px" viewBox="0 0 120 120"
enable-background="new 0 0 120 120" xml:space="preserve"
>
style type="text/css"><![CDATA[
svg { fill: red; }
]]></style>
<g>
<g>
<circle fill="#ff0000" cx="60px" cy="60px" r="50px"/>
<text>test</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 692 B

View File

@ -46,14 +46,6 @@ describe('svgo', () => {
const result = optimize(original, { input: 'file', path: 'input.svg' }); const result = optimize(original, { input: 'file', path: 'input.svg' });
expect(normalize(result.data)).toEqual(expected); expect(normalize(result.data)).toEqual(expected);
}); });
it('should handle parse error', async () => {
const fixture = await fs.promises.readFile(
path.resolve(__dirname, 'invalid.svg')
);
const result = optimize(fixture, { input: 'file', path: 'input.svg' });
expect(result.error).toMatch(/Error in parsing SVG/);
expect(result.path).toEqual('input.svg');
});
it('should handle empty svg tag', async () => { it('should handle empty svg tag', async () => {
const result = optimize('<svg />', { input: 'file', path: 'input.svg' }); const result = optimize('<svg />', { input: 'file', path: 'input.svg' });
expect(result.data).toEqual('<svg/>'); expect(result.data).toEqual('<svg/>');

View File

@ -10,7 +10,12 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"noImplicitAny": true "noImplicitAny": true
}, },
"include": ["plugins/**/*", "lib/xast.test.js", "lib/path.test.js"], "include": [
"plugins/**/*",
"lib/xast.test.js",
"lib/path.test.js",
"test/cli/**/*"
],
"exclude": [ "exclude": [
"plugins/_applyTransforms.js", "plugins/_applyTransforms.js",
"plugins/collapseGroups.js", "plugins/collapseGroups.js",