Skip to content

Commit 8b0bc7c

Browse files
authored
Preserve url quotes (#47)
* Preserve original quotes when rewriting CSS url() paths - important for inline SVG files which often have spaces * Found an example in the wild with the 'charset=' part left off. This is supported by https://css-tricks.com/lodge/svg/09-svg-data-uris/ ... not sure why we aren't just testing for the 'data:' prefix here? * Not sure why this is now coming back with a double quote after recent changes here; it's supposed to preserve the single quote from style.css??
1 parent 6728d12 commit 8b0bc7c

File tree

3 files changed

+26
-17
lines changed

3 files changed

+26
-17
lines changed

src/snapshot.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,29 @@ function extractOrigin(url: string): string {
6666
return origin;
6767
}
6868

69-
const URL_IN_CSS_REF = /url\((?:'([^']*)'|"([^"]*)"|([^)]*))\)/gm;
69+
const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")([^"]*)"|([^)]*))\)/gm;
7070
const RELATIVE_PATH = /^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/).*/;
71-
const DATA_URI = /^(data:)([\w\/\+\-]+);(charset=[\w-]+|base64).*,(.*)/i;
71+
const DATA_URI = /^(data:)([\w\/\+\-]+);(charset=[\w-]+|base64|utf-?8).*,(.*)/i;
7272
export function absoluteToStylesheet(
7373
cssText: string | null,
7474
href: string,
7575
): string {
7676
return (cssText || '').replace(
7777
URL_IN_CSS_REF,
78-
(origin, path1, path2, path3) => {
78+
(origin, quote1, path1, quote2, path2, path3) => {
7979
const filePath = path1 || path2 || path3;
80+
const maybe_quote = quote1 || quote2 || '';
8081
if (!filePath) {
8182
return origin;
8283
}
8384
if (!RELATIVE_PATH.test(filePath)) {
84-
return `url('${filePath}')`;
85+
return `url(${maybe_quote}${filePath}${maybe_quote})`;
8586
}
8687
if (DATA_URI.test(filePath)) {
87-
return `url(${filePath})`;
88+
return `url(${maybe_quote}${filePath}${maybe_quote})`;
8889
}
8990
if (filePath[0] === '/') {
90-
return `url('${extractOrigin(href) + filePath}')`;
91+
return `url(${maybe_quote}${extractOrigin(href) + filePath}${maybe_quote})`;
9192
}
9293
const stack = href.split('/');
9394
const parts = filePath.split('/');
@@ -101,7 +102,7 @@ export function absoluteToStylesheet(
101102
stack.push(part);
102103
}
103104
}
104-
return `url('${stack.join('/')}')`;
105+
return `url(${maybe_quote}${stack.join('/')}${maybe_quote})`;
105106
},
106107
);
107108
}

test/__snapshots__/integration.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ exports[`[html file]: with-style-sheet.html 1`] = `
258258
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" />
259259
<meta http-equiv=\\"X-UA-Compatible\\" content=\\"ie=edge\\" />
260260
<title>with style sheet</title>
261-
<style>body { margin: 0px; background: url('http://localhost:3030/a.jpg'); }p { color: red; background: url('http://localhost:3030/css/b.jpg'); }body &gt; p { color: yellow; }</style>
261+
<style>body { margin: 0px; background: url(\\"http://localhost:3030/a.jpg\\"); }p { color: red; background: url(\\"http://localhost:3030/css/b.jpg\\"); }body &gt; p { color: yellow; }</style>
262262
</head><body>
263263
</body></html>"
264264
`;
@@ -269,7 +269,7 @@ exports[`[html file]: with-style-sheet-with-import.html 1`] = `
269269
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" />
270270
<meta http-equiv=\\"X-UA-Compatible\\" content=\\"ie=edge\\" />
271271
<title>with style sheet with import</title>
272-
<style>body { margin: 0px; background: url('http://localhost:3030/a.jpg'); }p { color: red; background: url('http://localhost:3030/css/b.jpg'); }body &gt; p { color: yellow; }</style>
272+
<style>body { margin: 0px; background: url(\\"http://localhost:3030/a.jpg\\"); }p { color: red; background: url(\\"http://localhost:3030/css/b.jpg\\"); }body &gt; p { color: yellow; }</style>
273273
</head><body>
274274
</body></html>"
275275
`;

test/snapshot.test.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,32 @@ describe('absolute url to stylesheet', () => {
77

88
it('can handle relative path', () => {
99
expect(absoluteToStylesheet('url(a.jpg)', href)).to.equal(
10-
`url('http://localhost/css/a.jpg')`,
10+
`url(http://localhost/css/a.jpg)`,
1111
);
1212
});
1313

1414
it('can handle same level path', () => {
1515
expect(absoluteToStylesheet('url("./a.jpg")', href)).to.equal(
16-
`url('http://localhost/css/a.jpg')`,
16+
`url("http://localhost/css/a.jpg")`,
1717
);
1818
});
1919

2020
it('can handle parent level path', () => {
2121
expect(absoluteToStylesheet('url("../a.jpg")', href)).to.equal(
22-
`url('http://localhost/a.jpg')`,
22+
`url("http://localhost/a.jpg")`,
2323
);
2424
});
2525

2626
it('can handle absolute path', () => {
2727
expect(absoluteToStylesheet('url("/a.jpg")', href)).to.equal(
28-
`url('http://localhost/a.jpg')`,
28+
`url("http://localhost/a.jpg")`,
2929
);
3030
});
3131

3232
it('can handle external path', () => {
3333
expect(
3434
absoluteToStylesheet('url("http://localhost/a.jpg")', href),
35-
).to.equal(`url('http://localhost/a.jpg')`);
35+
).to.equal(`url("http://localhost/a.jpg")`);
3636
});
3737

3838
it('can handle single quote path', () => {
@@ -43,7 +43,7 @@ describe('absolute url to stylesheet', () => {
4343

4444
it('can handle no quote path', () => {
4545
expect(absoluteToStylesheet('url(./a.jpg)', href)).to.equal(
46-
`url('http://localhost/css/a.jpg')`,
46+
`url(http://localhost/css/a.jpg)`,
4747
);
4848
});
4949

@@ -54,8 +54,8 @@ describe('absolute url to stylesheet', () => {
5454
href,
5555
),
5656
).to.equal(
57-
`background-image: url('http://localhost/css/images/b.jpg');` +
58-
`background: #aabbcc url('http://localhost/css/images/a.jpg') 50% 50% repeat;`,
57+
`background-image: url(http://localhost/css/images/b.jpg);` +
58+
`background: #aabbcc url(http://localhost/css/images/a.jpg) 50% 50% repeat;`,
5959
);
6060
});
6161

@@ -71,6 +71,14 @@ describe('absolute url to stylesheet', () => {
7171
).to.equal('url(data:application/font-woff;base64,d09GMgABAAAAAAm)');
7272
});
7373

74+
it('preserves quotes around inline svgs with spaces', () => {
75+
expect(
76+
absoluteToStylesheet("url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M3'/%3E%3C/svg%3E\")", href),
77+
).to.equal("url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M3'/%3E%3C/svg%3E\")");
78+
expect(
79+
absoluteToStylesheet('url(\'data:image/svg+xml;utf8,<svg width="28" height="32" viewBox="0 0 28 32" xmlns="http://www.w3.org/2000/svg"><path d="M27 14C28" fill="white"/></svg>\')', href),
80+
).to.equal('url(\'data:image/svg+xml;utf8,<svg width="28" height="32" viewBox="0 0 28 32" xmlns="http://www.w3.org/2000/svg"><path d="M27 14C28" fill="white"/></svg>\')');
81+
});
7482
it('can handle empty path', () => {
7583
expect(absoluteToStylesheet(`url('')`, href)).to.equal(`url('')`);
7684
});

0 commit comments

Comments
 (0)