diff --git a/packages/core/integration-tests/test/css.js b/packages/core/integration-tests/test/css.js index e4abcd6287e..128a08c1619 100644 --- a/packages/core/integration-tests/test/css.js +++ b/packages/core/integration-tests/test/css.js @@ -244,6 +244,18 @@ describe('css', () => { ); }); + it('should handle quote in CSS URL correctly', async function () { + await bundle(path.join(__dirname, '/integration/css-url-quote/index.css')); + + let css = await outputFS.readFile(path.join(distDir, 'index.css'), 'utf8'); + + assert( + css.includes( + 'url("data:image/svg+xml;utf8,with quote \\" and escape \\\\");', + ), + ); + }); + it('should ignore url() with IE behavior specifiers', async function () { let b = await bundle( path.join(__dirname, '/integration/css-url-behavior/index.css'), diff --git a/packages/core/integration-tests/test/integration/css-url-quote/index.css b/packages/core/integration-tests/test/integration/css-url-quote/index.css new file mode 100644 index 00000000000..0da4f256ed0 --- /dev/null +++ b/packages/core/integration-tests/test/integration/css-url-quote/index.css @@ -0,0 +1,5 @@ +.quotes { + background-image: url('data:image/svg+xml;utf8,with quote " and escape \\'); + width: 100px; + height: 100px; +} diff --git a/packages/core/integration-tests/test/integration/css-url-quote/index.js b/packages/core/integration-tests/test/integration/css-url-quote/index.js new file mode 100644 index 00000000000..14400a424cd --- /dev/null +++ b/packages/core/integration-tests/test/integration/css-url-quote/index.js @@ -0,0 +1,5 @@ +require('./index.css'); + +module.exports = function () { + return 2; +}; diff --git a/packages/core/utils/src/replaceBundleReferences.js b/packages/core/utils/src/replaceBundleReferences.js index 8161ac299ad..25eaf2c996c 100644 --- a/packages/core/utils/src/replaceBundleReferences.js +++ b/packages/core/utils/src/replaceBundleReferences.js @@ -33,6 +33,7 @@ export function replaceURLReferences({ bundleGraph, contents, map, + getReplacement = s => s, relative = true, }: {| bundle: NamedBundle, @@ -40,6 +41,7 @@ export function replaceURLReferences({ contents: string, relative?: boolean, map?: ?SourceMap, + getReplacement?: string => string, |}): {|+contents: string, +map: ?SourceMap|} { let replacements = new Map(); let urlDependencies = []; @@ -61,7 +63,7 @@ export function replaceURLReferences({ if (resolved == null) { replacements.set(placeholder, { from: placeholder, - to: dependency.specifier, + to: getReplacement(dependency.specifier), }); continue; } @@ -79,6 +81,7 @@ export function replaceURLReferences({ fromBundle: bundle, toBundle: resolved, relative, + getReplacement, }), ); } @@ -156,11 +159,13 @@ export function getURLReplacement({ fromBundle, toBundle, relative, + getReplacement, }: {| dependency: Dependency, fromBundle: NamedBundle, toBundle: NamedBundle, relative: boolean, + getReplacement?: string => string, |}): {|from: string, to: string|} { let to; @@ -191,9 +196,10 @@ export function getURLReplacement({ let placeholder = dependency.meta?.placeholder ?? dependency.id; invariant(typeof placeholder === 'string'); + return { from: placeholder, - to, + to: getReplacement ? getReplacement(to) : to, }; } diff --git a/packages/packagers/css/src/CSSPackager.js b/packages/packagers/css/src/CSSPackager.js index 23be7bae163..ae2fbbef49b 100644 --- a/packages/packagers/css/src/CSSPackager.js +++ b/packages/packagers/css/src/CSSPackager.js @@ -129,6 +129,7 @@ export default (new Packager({ bundleGraph, contents, map, + getReplacement: escapeString, })); return replaceInlineReferences({ @@ -138,7 +139,7 @@ export default (new Packager({ getInlineBundleContents, getInlineReplacement: (dep, inlineType, contents) => ({ from: getSpecifier(dep), - to: contents, + to: escapeString(contents), }), map, }); @@ -153,6 +154,10 @@ export function getSpecifier(dep: Dependency): string { return dep.id; } +function escapeString(contents: string): string { + return contents.replace(/(["\\])/g, '\\$1'); +} + async function processCSSModule( options, logger, diff --git a/packages/packagers/html/src/HTMLPackager.js b/packages/packagers/html/src/HTMLPackager.js index b30faf3a110..f92089f5f12 100644 --- a/packages/packagers/html/src/HTMLPackager.js +++ b/packages/packagers/html/src/HTMLPackager.js @@ -74,6 +74,7 @@ export default (new Packager({ bundleGraph, contents: html, relative: false, + getReplacement: contents => contents.replace(/"/g, '"'), }); return replaceInlineReferences({ diff --git a/packages/packagers/js/src/index.js b/packages/packagers/js/src/index.js index 58c96fa4fb8..73f1ebc0aa6 100644 --- a/packages/packagers/js/src/index.js +++ b/packages/packagers/js/src/index.js @@ -72,6 +72,7 @@ export default (new Packager({ bundleGraph, contents, map, + getReplacement: s => JSON.stringify(s).slice(1, -1), })); } diff --git a/packages/packagers/raw-url/src/RawUrlPackager.js b/packages/packagers/raw-url/src/RawUrlPackager.js index cd73ce2bac2..e6641bf415b 100644 --- a/packages/packagers/raw-url/src/RawUrlPackager.js +++ b/packages/packagers/raw-url/src/RawUrlPackager.js @@ -17,6 +17,7 @@ export default (new Packager({ bundleGraph, contents: await assets[0].getCode(), relative: false, + getReplacement: s => s, }); return {contents}; }, diff --git a/packages/packagers/svg/src/SVGPackager.js b/packages/packagers/svg/src/SVGPackager.js index a2dd81e9004..3ee06bd7d20 100644 --- a/packages/packagers/svg/src/SVGPackager.js +++ b/packages/packagers/svg/src/SVGPackager.js @@ -58,6 +58,7 @@ export default (new Packager({ bundleGraph, contents: svg, relative: false, + getReplacement: contents => contents.replace(/"/g, '"'), }); return replaceInlineReferences({ diff --git a/packages/packagers/xml/src/XMLPackager.js b/packages/packagers/xml/src/XMLPackager.js index 7e267a2fb41..290c0c73ad9 100644 --- a/packages/packagers/xml/src/XMLPackager.js +++ b/packages/packagers/xml/src/XMLPackager.js @@ -68,6 +68,7 @@ export default (new Packager({ bundleGraph, contents: code, relative: false, + getReplacement: contents => contents.replace(/"/g, '"'), }); return replaceInlineReferences({