Skip to content

Commit f706b07

Browse files
committed
deps: upgrade to sax v1.5.0
1 parent b37d90e commit f706b07

File tree

9 files changed

+255
-28
lines changed

9 files changed

+255
-28
lines changed

.github/workflows/publish.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
on:
2+
release:
3+
types: [created]
4+
workflow_dispatch:
5+
6+
permissions:
7+
id-token: write
8+
contents: read
9+
10+
jobs:
11+
publish-npm:
12+
runs-on: ubuntu-latest
13+
env:
14+
GH_TOKEN: ${{ github.token }}
15+
steps:
16+
- uses: actions/checkout@v6
17+
- uses: actions/setup-node@v6
18+
with:
19+
node-version: 16
20+
registry-url: https://registry.npmjs.org
21+
- run: yarn install
22+
- run: |
23+
VERSION=$(jq -r .version <package.json)
24+
if [[ "$VERSION" = *"-rc."* ]]; then
25+
yarn npm publish --access public --tag rc
26+
else
27+
LATEST_VERSION="$(gh release view --json tagName -q '.tagName' --repo ${{ github.repository }})"
28+
if [[ "${{ github.ref_name }}" = "$LATEST_VERSION" ]]; then
29+
yarn npm publish --access public
30+
else
31+
MAJOR="$(awk -F '.' '{ print $1 }' <<< "$VERSION")"
32+
yarn npm publish --access public --tag "v$MAJOR"
33+
fi
34+
fi
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
diff --git a/lib/sax.js b/lib/sax.js
2+
index d35adf1a319b7f064d7a7e43c46630ed5ef587ff..4c5a08844f3be4ddceb12285d3fd133213dcc7e0 100644
3+
--- a/lib/sax.js
4+
+++ b/lib/sax.js
5+
@@ -4,8 +4,6 @@
6+
return new SAXParser(strict, opt)
7+
}
8+
sax.SAXParser = SAXParser
9+
- sax.SAXStream = SAXStream
10+
- sax.createStream = createStream
11+
12+
// When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns.
13+
// When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)),
14+
@@ -191,123 +189,6 @@
15+
},
16+
}
17+
18+
- var Stream
19+
- try {
20+
- Stream = require('stream').Stream
21+
- } catch (ex) {
22+
- Stream = function () {}
23+
- }
24+
- if (!Stream) Stream = function () {}
25+
-
26+
- var streamWraps = sax.EVENTS.filter(function (ev) {
27+
- return ev !== 'error' && ev !== 'end'
28+
- })
29+
-
30+
- function createStream(strict, opt) {
31+
- return new SAXStream(strict, opt)
32+
- }
33+
-
34+
- function SAXStream(strict, opt) {
35+
- if (!(this instanceof SAXStream)) {
36+
- return new SAXStream(strict, opt)
37+
- }
38+
-
39+
- Stream.apply(this)
40+
-
41+
- this._parser = new SAXParser(strict, opt)
42+
- this.writable = true
43+
- this.readable = true
44+
-
45+
- var me = this
46+
-
47+
- this._parser.onend = function () {
48+
- me.emit('end')
49+
- }
50+
-
51+
- this._parser.onerror = function (er) {
52+
- me.emit('error', er)
53+
-
54+
- // if didn't throw, then means error was handled.
55+
- // go ahead and clear error, so we can write again.
56+
- me._parser.error = null
57+
- }
58+
-
59+
- this._decoder = null
60+
-
61+
- streamWraps.forEach(function (ev) {
62+
- Object.defineProperty(me, 'on' + ev, {
63+
- get: function () {
64+
- return me._parser['on' + ev]
65+
- },
66+
- set: function (h) {
67+
- if (!h) {
68+
- me.removeAllListeners(ev)
69+
- me._parser['on' + ev] = h
70+
- return h
71+
- }
72+
- me.on(ev, h)
73+
- },
74+
- enumerable: true,
75+
- configurable: false,
76+
- })
77+
- })
78+
- }
79+
-
80+
- SAXStream.prototype = Object.create(Stream.prototype, {
81+
- constructor: {
82+
- value: SAXStream,
83+
- },
84+
- })
85+
-
86+
- SAXStream.prototype.write = function (data) {
87+
- if (
88+
- typeof Buffer === 'function' &&
89+
- typeof Buffer.isBuffer === 'function' &&
90+
- Buffer.isBuffer(data)
91+
- ) {
92+
- if (!this._decoder) {
93+
- this._decoder = new TextDecoder('utf8')
94+
- }
95+
- data = this._decoder.decode(data, { stream: true })
96+
- }
97+
-
98+
- this._parser.write(data.toString())
99+
- this.emit('data', data)
100+
- return true
101+
- }
102+
-
103+
- SAXStream.prototype.end = function (chunk) {
104+
- if (chunk && chunk.length) {
105+
- this.write(chunk)
106+
- }
107+
- // Flush any remaining decoded data from the TextDecoder
108+
- if (this._decoder) {
109+
- var remaining = this._decoder.decode()
110+
- if (remaining) {
111+
- this._parser.write(remaining)
112+
- this.emit('data', remaining)
113+
- }
114+
- }
115+
- this._parser.end()
116+
- return true
117+
- }
118+
-
119+
- SAXStream.prototype.on = function (ev, handler) {
120+
- var me = this
121+
- if (!me._parser['on' + ev] && streamWraps.indexOf(ev) !== -1) {
122+
- me._parser['on' + ev] = function () {
123+
- var args =
124+
- arguments.length === 1 ?
125+
- [arguments[0]]
126+
- : Array.apply(null, arguments)
127+
- args.splice(0, 0, ev)
128+
- me.emit.apply(me, args)
129+
- }
130+
- }
131+
-
132+
- return Stream.prototype.on.call(me, ev, handler)
133+
- }
134+
-
135+
// this really needs to be replaced with character classes.
136+
// XML allows all manner of ridiculous numbers and digits.
137+
var CDATA = '[CDATA['

lib/parser.js

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
* @typedef {import('./types').XastParent} XastParent
1313
*/
1414

15-
// @ts-ignore sax will be replaced with something else later
16-
const SAX = require('@trysound/sax');
15+
const SAX = require('sax');
1716
const JSAPI = require('./svgo/jsAPI.js');
1817
const { textElems } = require('../plugins/_collections.js');
1918

@@ -82,6 +81,7 @@ const config = {
8281
lowercase: true,
8382
xmlns: true,
8483
position: true,
84+
unparsedEntities: true,
8585
};
8686

8787
/**
@@ -113,9 +113,6 @@ const parseSvg = (data, from) => {
113113
return wrapped;
114114
};
115115

116-
/**
117-
* @type {(doctype: string) => void}
118-
*/
119116
sax.ondoctype = (doctype) => {
120117
/**
121118
* @type {XastDoctype}
@@ -183,9 +180,6 @@ const parseSvg = (data, from) => {
183180
pushToContent(node);
184181
};
185182

186-
/**
187-
* @type {(data: { name: string, attributes: Record<string, { value: string }>}) => void}
188-
*/
189183
sax.onopentag = (data) => {
190184
/**
191185
* @type {XastElement}
@@ -204,9 +198,6 @@ const parseSvg = (data, from) => {
204198
stack.push(element);
205199
};
206200

207-
/**
208-
* @type {(text: string) => void}
209-
*/
210201
sax.ontext = (text) => {
211202
if (current.type === 'element') {
212203
// prevent trimming of meaningful whitespace inside textual tags
@@ -237,14 +228,12 @@ const parseSvg = (data, from) => {
237228
current = stack[stack.length - 1];
238229
};
239230

240-
/**
241-
* @type {(e: any) => void}
242-
*/
243231
sax.onerror = (e) => {
232+
const reason = e.message.split('\n')[0];
244233
const error = new SvgoParserError(
245-
e.reason,
246-
e.line + 1,
247-
e.column,
234+
reason,
235+
sax.line + 1,
236+
sax.column,
248237
data,
249238
from
250239
);

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"packageManager": "yarn@2.4.3",
33
"name": "svgo",
4-
"version": "2.8.0",
4+
"version": "2.8.1",
55
"description": "Nodejs-based tool for optimizing SVG vector graphics files",
66
"license": "MIT",
77
"keywords": [
@@ -100,12 +100,12 @@
100100
]
101101
},
102102
"dependencies": {
103-
"@trysound/sax": "0.2.0",
104103
"commander": "^7.2.0",
105104
"css-select": "^4.1.3",
106105
"css-tree": "^1.1.3",
107106
"csso": "^4.2.0",
108107
"picocolors": "^1.0.0",
108+
"sax": "^1.5.0",
109109
"stable": "^0.1.8"
110110
},
111111
"devDependencies": {
@@ -115,6 +115,7 @@
115115
"@types/css-tree": "^1.0.6",
116116
"@types/csso": "^4.2.0",
117117
"@types/jest": "^27.0.1",
118+
"@types/sax": "^1.2.7",
118119
"del": "^6.0.0",
119120
"eslint": "^7.32.0",
120121
"jest": "^27.2.5",
@@ -127,5 +128,8 @@
127128
"rollup-plugin-terser": "^7.0.2",
128129
"tar-stream": "^2.2.0",
129130
"typescript": "^4.4.3"
131+
},
132+
"resolutions": {
133+
"sax@^1.5.0": "patch:sax@npm%3A1.5.0#./.yarn/patches/sax-npm-1.5.0-d40bca2226.patch"
130134
}
131135
}

test/regression-extract.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const extractTarGz = async (url, baseDir, include) => {
3939
next();
4040
});
4141
const response = await fetch(url);
42-
await pipeline(response.body, extract);
42+
await pipeline(response.body, zlib.createGunzip(), extract);
4343
};
4444

4545
(async () => {

test/svgo/billion-laughs-flat.svg

Lines changed: 8 additions & 0 deletions
Loading

test/svgo/billion-laughs.svg

Lines changed: 15 additions & 0 deletions
Loading

test/svgo/billion-laughs.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const { readFile } = require('node:fs/promises');
2+
const path = require('path');
3+
const { optimize } = require('../../lib/svgo.js');
4+
5+
describe('svgo', () => {
6+
it('should throw on excessive expansion depth', async () => {
7+
const original = await readFile(
8+
path.join(__dirname, 'billion-laughs.svg'),
9+
'utf-8',
10+
);
11+
const result = optimize(original);
12+
expect(result.error).toMatch('Parsed entity depth exceeds max entity depth');
13+
});
14+
15+
it('should throw on excessive expansion count', async () => {
16+
const original = await readFile(
17+
path.join(__dirname, 'billion-laughs-flat.svg'),
18+
'utf-8',
19+
);
20+
const result = optimize(original);
21+
expect(result.error).toMatch('Parsed entity count exceeds max entity count');
22+
});
23+
});

0 commit comments

Comments
 (0)