Skip to content

Commit f1bfdf7

Browse files
authored
Fix url() serialization
* Handle escaped characters * Output the URL string with quotes Closes #103.
1 parent 6bc1894 commit f1bfdf7

File tree

3 files changed

+164
-16
lines changed

3 files changed

+164
-16
lines changed

lib/parsers.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ var lengthRegEx = new RegExp(
3333
);
3434
var percentRegEx = new RegExp(`^${NUMBER}%$`);
3535
var angleRegEx = new RegExp(`^${NUMBER}(?:deg|g?rad|turn)$`);
36-
var urlRegEx = /^url\(\s*([^)]*)\s*\)$/;
36+
var urlRegEx = /^url\(\s*((?:[^)]|\\\))*)\s*\)$/;
3737
var stringRegEx = /^("[^"]*"|'[^']*')$/;
3838
var varRegEx = /^var\(|(?<=[*/\s(])var\(/;
3939
var calcRegEx =
@@ -218,24 +218,45 @@ exports.parseUrl = function parseUrl(val) {
218218
str = str.substr(1, str.length - 2);
219219
}
220220

221+
var urlstr = '';
222+
var escaped = false;
221223
var i;
222224
for (i = 0; i < str.length; i++) {
223225
switch (str[i]) {
226+
case '\\':
227+
if (escaped) {
228+
urlstr += '\\\\';
229+
escaped = false;
230+
} else {
231+
escaped = true;
232+
}
233+
break;
224234
case '(':
225235
case ')':
226236
case ' ':
227237
case '\t':
228238
case '\n':
229239
case "'":
240+
if (!escaped) {
241+
return undefined;
242+
}
243+
urlstr += str[i];
244+
escaped = false;
245+
break;
230246
case '"':
231-
return undefined;
232-
case '\\':
233-
i++;
247+
if (!escaped) {
248+
return undefined;
249+
}
250+
urlstr += '\\"';
251+
escaped = false;
234252
break;
253+
default:
254+
urlstr += str[i];
255+
escaped = false;
235256
}
236257
}
237258

238-
return 'url(' + str + ')';
259+
return 'url("' + urlstr + '")';
239260
};
240261

241262
exports.parseString = function parseString(val) {

test/CSSStyleDeclaration.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ describe('CSSStyleDeclaration', () => {
9797
var style = new CSSStyleDeclaration();
9898
style.background = 'blue url(http://www.example.com/some_img.jpg)';
9999
assert.strictEqual(style.backgroundColor, 'blue');
100-
assert.strictEqual(style.backgroundImage, 'url(http://www.example.com/some_img.jpg)');
101-
assert.strictEqual(style.background, 'blue url(http://www.example.com/some_img.jpg)');
100+
assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")');
101+
assert.strictEqual(style.background, 'blue url("http://www.example.com/some_img.jpg")');
102102
style.border = '0 solid black';
103103
assert.strictEqual(style.borderWidth, '0px');
104104
assert.strictEqual(style.borderStyle, 'solid');
@@ -225,8 +225,8 @@ describe('CSSStyleDeclaration', () => {
225225
var style = new CSSStyleDeclaration();
226226
style.background = 'rgb(0, 0, 0) url(/something/somewhere.jpg)';
227227
assert.strictEqual(style.backgroundColor, 'rgb(0, 0, 0)');
228-
assert.strictEqual(style.backgroundImage, 'url(/something/somewhere.jpg)');
229-
assert.strictEqual(style.cssText, 'background: rgb(0, 0, 0) url(/something/somewhere.jpg);');
228+
assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")');
229+
assert.strictEqual(style.cssText, 'background: rgb(0, 0, 0) url("/something/somewhere.jpg");');
230230
style = new CSSStyleDeclaration();
231231
style.border = ' 1px solid black ';
232232
assert.strictEqual(style.border, '1px solid black');
@@ -546,11 +546,11 @@ describe('CSSStyleDeclaration', () => {
546546
it('url parsing works with quotes', () => {
547547
var style = new CSSStyleDeclaration();
548548
style.backgroundImage = 'url(http://some/url/here1.png)';
549-
assert.strictEqual(style.backgroundImage, 'url(http://some/url/here1.png)');
549+
assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")');
550550
style.backgroundImage = "url('http://some/url/here2.png')";
551-
assert.strictEqual(style.backgroundImage, 'url(http://some/url/here2.png)');
551+
assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")');
552552
style.backgroundImage = 'url("http://some/url/here3.png")';
553-
assert.strictEqual(style.backgroundImage, 'url(http://some/url/here3.png)');
553+
assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")');
554554
});
555555

556556
it('setting 0 to a padding or margin works', () => {

test/parsers.js

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,133 @@ describe('parseMeasurement', () => {
176176
it.todo('test');
177177
});
178178
describe('parseUrl', () => {
179+
it('should return undefined', () => {
180+
let input = 'url(var(--foo))';
181+
let output = parsers.parseUrl(input);
182+
183+
assert.strictEqual(output, undefined);
184+
});
185+
186+
it('should return quoted url string', () => {
187+
let input = 'url(sample.png)';
188+
let output = parsers.parseUrl(input);
189+
190+
assert.strictEqual(output, 'url("sample.png")');
191+
});
192+
193+
it('should return quoted url string', () => {
194+
let input = "url('sample.png')";
195+
let output = parsers.parseUrl(input);
196+
197+
assert.strictEqual(output, 'url("sample.png")');
198+
});
199+
200+
it('should return quoted url string', () => {
201+
let input = 'url("sample.png")';
202+
let output = parsers.parseUrl(input);
203+
204+
assert.strictEqual(output, 'url("sample.png")');
205+
});
206+
207+
it('should return quoted url string without escape', () => {
208+
let input = 'url(sample\\-escaped.png)';
209+
let output = parsers.parseUrl(input);
210+
211+
assert.strictEqual(output, 'url("sample-escaped.png")');
212+
});
213+
214+
it('should return quoted url string with escape', () => {
215+
let input = 'url(sample\\\\-escaped.png)';
216+
let output = parsers.parseUrl(input);
217+
218+
assert.strictEqual(output, 'url("sample\\\\-escaped.png")');
219+
});
220+
221+
it('should return undefined', () => {
222+
let input = 'url(sample unescaped -space.png)';
223+
let output = parsers.parseUrl(input);
224+
225+
assert.strictEqual(output, undefined);
226+
});
227+
228+
it('should return quoted url string without escape', () => {
229+
let input = 'url(sample\\ escaped\\ -space.png)';
230+
let output = parsers.parseUrl(input);
231+
232+
assert.strictEqual(output, 'url("sample escaped -space.png")');
233+
});
234+
235+
it('should return undefined', () => {
236+
let input = 'url(sample\tunescaped\t-tab.png)';
237+
let output = parsers.parseUrl(input);
238+
239+
assert.strictEqual(output, undefined);
240+
});
241+
242+
it('should return quoted url string without escape', () => {
243+
let input = 'url(sample\\\tescaped\\\t-tab.png)';
244+
let output = parsers.parseUrl(input);
245+
246+
assert.strictEqual(output, 'url("sample\tescaped\t-tab.png")');
247+
});
248+
249+
it('should return undefined', () => {
250+
let input = 'url(sample\nunescaped\n-lf.png)';
251+
let output = parsers.parseUrl(input);
252+
253+
assert.strictEqual(output, undefined);
254+
});
255+
256+
it('should return quoted url string without escape', () => {
257+
let input = 'url(sample\\\nescaped\\\n-lf.png)';
258+
let output = parsers.parseUrl(input);
259+
260+
assert.strictEqual(output, 'url("sample\nescaped\n-lf.png")');
261+
});
262+
263+
it('should return undefined', () => {
264+
let input = "url(sample'unescaped'-quote.png)";
265+
let output = parsers.parseUrl(input);
266+
267+
assert.strictEqual(output, undefined);
268+
});
269+
270+
it('should return quoted url string without escape', () => {
271+
let input = "url(sample\\'escaped\\'-quote.png)";
272+
let output = parsers.parseUrl(input);
273+
274+
// prettier-ignore
275+
assert.strictEqual(output, "url(\"sample'escaped'-quote.png\")");
276+
});
277+
278+
it('should return undefined', () => {
279+
let input = 'url(sample"unescaped"-double-quote.png)';
280+
let output = parsers.parseUrl(input);
281+
282+
assert.strictEqual(output, undefined);
283+
});
284+
285+
it('should return quoted url string with escape', () => {
286+
let input = 'url(sample\\"escaped\\"-double-quote.png)';
287+
let output = parsers.parseUrl(input);
288+
289+
assert.strictEqual(output, 'url("sample\\"escaped\\"-double-quote.png")');
290+
});
291+
292+
it('should return quoted empty url string', () => {
293+
let input = 'url()';
294+
let output = parsers.parseUrl(input);
295+
296+
assert.strictEqual(output, 'url("")');
297+
});
298+
299+
it('should return quoted empty url string', () => {
300+
let input = 'url("")';
301+
let output = parsers.parseUrl(input);
302+
303+
assert.strictEqual(output, 'url("")');
304+
});
305+
179306
it.todo('test');
180307
});
181308
describe('parseString', () => {
@@ -314,28 +441,28 @@ describe('parseImage', () => {
314441
let input = 'url(example.png)';
315442
let output = parsers.parseImage(input);
316443

317-
assert.strictEqual(output, 'url(example.png)');
444+
assert.strictEqual(output, 'url("example.png")');
318445
});
319446

320447
it('should return value', () => {
321448
let input = 'url(example.png), url("example2.png")';
322449
let output = parsers.parseImage(input);
323450

324-
assert.strictEqual(output, 'url(example.png), url(example2.png)');
451+
assert.strictEqual(output, 'url("example.png"), url("example2.png")');
325452
});
326453

327454
it('should return value', () => {
328455
let input = 'none, url(example.png)';
329456
let output = parsers.parseImage(input);
330457

331-
assert.strictEqual(output, 'none, url(example.png)');
458+
assert.strictEqual(output, 'none, url("example.png")');
332459
});
333460

334461
it('should return value', () => {
335462
let input = 'linear-gradient(green, blue), url(example.png)';
336463
let output = parsers.parseImage(input);
337464

338-
assert.strictEqual(output, 'linear-gradient(green, blue), url(example.png)');
465+
assert.strictEqual(output, 'linear-gradient(green, blue), url("example.png")');
339466
});
340467

341468
it('should return value as is if var() is included', () => {

0 commit comments

Comments
 (0)