From 4979f58c15cd7601aea9543c3a0e048fd24b7baa Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Mon, 14 Aug 2017 08:57:16 +0200 Subject: [PATCH 1/2] Normalize quotes in CSS like in JS --- src/printer-postcss.js | 16 +- src/printer.js | 90 +----- src/util.js | 95 +++++- .../__snapshots__/jsfmt.spec.js.snap | 4 +- .../__snapshots__/jsfmt.spec.js.snap | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 4 +- .../__snapshots__/jsfmt.spec.js.snap | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 279 ++++++++++++++++++ tests/css_quotes/jsfmt.spec.js | 2 + tests/css_quotes/quotes.css | 67 +++++ .../content/__snapshots__/jsfmt.spec.js.snap | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 2 +- 12 files changed, 460 insertions(+), 105 deletions(-) create mode 100644 tests/css_quotes/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/css_quotes/jsfmt.spec.js create mode 100644 tests/css_quotes/quotes.css diff --git a/src/printer-postcss.js b/src/printer-postcss.js index db7cda39..cb7c7c03 100644 --- a/src/printer-postcss.js +++ b/src/printer-postcss.js @@ -163,7 +163,7 @@ function genericPrint(path, options, print) { return concat([n.value, " "]); } case "media-value": { - return n.value; + return adjustStrings(n.value, options); } case "media-keyword": { return n.value; @@ -198,7 +198,7 @@ function genericPrint(path, options, print) { "[", n.attribute, n.operator ? n.operator : "", - n.value ? n.value : "", + n.value ? adjustStrings(n.value, options) : "", n.insensitive ? " i" : "", "]" ]); @@ -337,11 +337,7 @@ function genericPrint(path, options, print) { return concat([n.value, " "]); } case "value-string": { - return concat([ - n.quoted ? n.raws.quote : "", - n.value, - n.quoted ? n.raws.quote : "" - ]); + return util.printString(n.raws.quote + n.value + n.raws.quote, options); } case "value-atword": { return concat(["@", n.value]); @@ -404,4 +400,10 @@ function printValue(value) { return value; } +const STRING_REGEX = /(['"])(?:(?!\1)[^\\]|\\[\s\S])*\1/g; + +function adjustStrings(value, options) { + return value.replace(STRING_REGEX, match => util.printString(match, options)); +} + module.exports = genericPrint; diff --git a/src/printer.js b/src/printer.js index 654bd971..fd9e4bb6 100644 --- a/src/printer.js +++ b/src/printer.js @@ -4320,97 +4320,9 @@ function adjustClause(node, clause, forceSpace) { function nodeStr(node, options, isFlowOrTypeScriptDirectiveLiteral) { const raw = rawText(node); - // `rawContent` is the string exactly like it appeared in the input source - // code, with its enclosing quote. - const rawContent = raw.slice(1, -1); - - const double = { quote: '"', regex: /"/g }; - const single = { quote: "'", regex: /'/g }; - - const preferred = options.singleQuote ? single : double; - const alternate = preferred === single ? double : single; - - let shouldUseAlternateQuote = false; const isDirectiveLiteral = isFlowOrTypeScriptDirectiveLiteral || node.type === "DirectiveLiteral"; - - let canChangeDirectiveQuotes = false; - - // If `rawContent` contains at least one of the quote preferred for enclosing - // the string, we might want to enclose with the alternate quote instead, to - // minimize the number of escaped quotes. - // Also check for the alternate quote, to determine if we're allowed to swap - // the quotes on a DirectiveLiteral. - if ( - rawContent.includes(preferred.quote) || - rawContent.includes(alternate.quote) - ) { - const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length; - const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length; - - shouldUseAlternateQuote = numPreferredQuotes > numAlternateQuotes; - } else { - canChangeDirectiveQuotes = true; - } - - const enclosingQuote = - options.parser === "json" - ? double.quote - : shouldUseAlternateQuote ? alternate.quote : preferred.quote; - - // Directives are exact code unit sequences, which means that you can't - // change the escape sequences they use. - // See https://github.com/prettier/prettier/issues/1555 - // and https://tc39.github.io/ecma262/#directive-prologue - if (isDirectiveLiteral) { - if (canChangeDirectiveQuotes) { - return enclosingQuote + rawContent + enclosingQuote; - } - return raw; - } - - // It might sound unnecessary to use `makeString` even if `node.raw` already - // is enclosed with `enclosingQuote`, but it isn't. `node.raw` could contain - // unnecessary escapes (such as in `"\'"`). Always using `makeString` makes - // sure that we consistently output the minimum amount of escaped quotes. - return makeString(rawContent, enclosingQuote); -} - -function makeString(rawContent, enclosingQuote) { - const otherQuote = enclosingQuote === '"' ? "'" : '"'; - - // Matches _any_ escape and unescaped quotes (both single and double). - const regex = /\\([\s\S])|(['"])/g; - - // Escape and unescape single and double quotes as needed to be able to - // enclose `rawContent` with `enclosingQuote`. - const newContent = rawContent.replace(regex, (match, escaped, quote) => { - // If we matched an escape, and the escaped character is a quote of the - // other type than we intend to enclose the string with, there's no need for - // it to be escaped, so return it _without_ the backslash. - if (escaped === otherQuote) { - return escaped; - } - - // If we matched an unescaped quote and it is of the _same_ type as we - // intend to enclose the string with, it must be escaped, so return it with - // a backslash. - if (quote === enclosingQuote) { - return "\\" + quote; - } - - if (quote) { - return quote; - } - - // Unescape any unnecessarily escaped character. - // Adapted from https://github.com/eslint/eslint/blob/de0b4ad7bd820ade41b1f606008bea68683dc11a/lib/rules/no-useless-escape.js#L27 - return /^[^\\nrvtbfux\r\n\u2028\u2029"'0-7]$/.test(escaped) - ? escaped - : "\\" + escaped; - }); - - return enclosingQuote + newContent + enclosingQuote; + return util.printString(raw, options, isDirectiveLiteral); } function printRegex(node) { diff --git a/src/util.js b/src/util.js index 14b70cc4..114171e8 100644 --- a/src/util.js +++ b/src/util.js @@ -475,6 +475,98 @@ function getAlignmentSize(value, tabWidth, startIndex) { return size; } +function printString(raw, options, isDirectiveLiteral) { + // `rawContent` is the string exactly like it appeared in the input source + // code, without its enclosing quotes. + const rawContent = raw.slice(1, -1); + + const double = { quote: '"', regex: /"/g }; + const single = { quote: "'", regex: /'/g }; + + const preferred = options.singleQuote ? single : double; + const alternate = preferred === single ? double : single; + + let shouldUseAlternateQuote = false; + let canChangeDirectiveQuotes = false; + + // If `rawContent` contains at least one of the quote preferred for enclosing + // the string, we might want to enclose with the alternate quote instead, to + // minimize the number of escaped quotes. + // Also check for the alternate quote, to determine if we're allowed to swap + // the quotes on a DirectiveLiteral. + if ( + rawContent.includes(preferred.quote) || + rawContent.includes(alternate.quote) + ) { + const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length; + const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length; + + shouldUseAlternateQuote = numPreferredQuotes > numAlternateQuotes; + } else { + canChangeDirectiveQuotes = true; + } + + const enclosingQuote = + options.parser === "json" + ? double.quote + : shouldUseAlternateQuote ? alternate.quote : preferred.quote; + + // Directives are exact code unit sequences, which means that you can't + // change the escape sequences they use. + // See https://github.com/prettier/prettier/issues/1555 + // and https://tc39.github.io/ecma262/#directive-prologue + if (isDirectiveLiteral) { + if (canChangeDirectiveQuotes) { + return enclosingQuote + rawContent + enclosingQuote; + } + return raw; + } + + // It might sound unnecessary to use `makeString` even if the string already + // is enclosed with `enclosingQuote`, but it isn't. The string could contain + // unnecessary escapes (such as in `"\'"`). Always using `makeString` makes + // sure that we consistently output the minimum amount of escaped quotes. + return makeString(rawContent, enclosingQuote, options.parser !== "postcss"); +} + +function makeString(rawContent, enclosingQuote, unescapeUnnecessaryEscapes) { + const otherQuote = enclosingQuote === '"' ? "'" : '"'; + + // Matches _any_ escape and unescaped quotes (both single and double). + const regex = /\\([\s\S])|(['"])/g; + + // Escape and unescape single and double quotes as needed to be able to + // enclose `rawContent` with `enclosingQuote`. + const newContent = rawContent.replace(regex, (match, escaped, quote) => { + // If we matched an escape, and the escaped character is a quote of the + // other type than we intend to enclose the string with, there's no need for + // it to be escaped, so return it _without_ the backslash. + if (escaped === otherQuote) { + return escaped; + } + + // If we matched an unescaped quote and it is of the _same_ type as we + // intend to enclose the string with, it must be escaped, so return it with + // a backslash. + if (quote === enclosingQuote) { + return "\\" + quote; + } + + if (quote) { + return quote; + } + + // Unescape any unnecessarily escaped character. + // Adapted from https://github.com/eslint/eslint/blob/de0b4ad7bd820ade41b1f606008bea68683dc11a/lib/rules/no-useless-escape.js#L27 + return unescapeUnnecessaryEscapes && + /^[^\\nrvtbfux\r\n\u2028\u2029"'0-7]$/.test(escaped) + ? escaped + : "\\" + escaped; + }); + + return enclosingQuote + newContent + enclosingQuote; +} + module.exports = { getPrecedence, shouldFlatten, @@ -500,5 +592,6 @@ module.exports = { hasBlockComments, isBlockComment, hasClosureCompilerTypeCastComment, - getAlignmentSize + getAlignmentSize, + printString }; diff --git a/tests/css_comments/__snapshots__/jsfmt.spec.js.snap b/tests/css_comments/__snapshots__/jsfmt.spec.js.snap index b1b50c0c..091612c6 100644 --- a/tests/css_comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_comments/__snapshots__/jsfmt.spec.js.snap @@ -34,8 +34,8 @@ exports[`bug.css 1`] = ` @font-face { src: url(if( $bootstrap-sass-asset-helper, - twbs-font-path('#{$icon-font-path}#{$icon-font-name}.eot'), - '#{$icon-font-path}#{$icon-font-name}.eot' + twbs-font-path("#{$icon-font-path}#{$icon-font-name}.eot"), + "#{$icon-font-path}#{$icon-font-name}.eot" )); } // Catchall baseclass diff --git a/tests/css_import/__snapshots__/jsfmt.spec.js.snap b/tests/css_import/__snapshots__/jsfmt.spec.js.snap index b1eece2f..8e86bed1 100644 --- a/tests/css_import/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_import/__snapshots__/jsfmt.spec.js.snap @@ -12,6 +12,6 @@ exports[`url.css 1`] = ` $dir: 'fonts'; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @import url('foo'); -$dir: 'fonts'; +$dir: "fonts"; `; diff --git a/tests/css_indent/__snapshots__/jsfmt.spec.js.snap b/tests/css_indent/__snapshots__/jsfmt.spec.js.snap index f547dcca..cf5c783e 100644 --- a/tests/css_indent/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_indent/__snapshots__/jsfmt.spec.js.snap @@ -29,8 +29,8 @@ a { } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a { - ~ .Pagination-itemWrapper:not(.is-separator):not([data-priority^='#{$priority}']) - ~ .Pagination-itemWrapper.is-separator[data-priority^='#{$priority}'] { + ~ .Pagination-itemWrapper:not(.is-separator):not([data-priority^="#{$priority}"]) + ~ .Pagination-itemWrapper.is-separator[data-priority^="#{$priority}"] { display: flex; } } diff --git a/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap b/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap index e2a54a61..80a218f7 100644 --- a/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap @@ -21,7 +21,7 @@ exports[`inline_url.css 1`] = ` } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .breadItem { - background-image: url('/images/product/simple_product_manager/breadcrumb/chevron_right.png'); + background-image: url("/images/product/simple_product_manager/breadcrumb/chevron_right.png"); background-image: url(/images/product/simple_product_manager/breadcrumb/chevron_right.png); -fb-sprite: url(fbglyph:cross-outline, fig-white); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=); diff --git a/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap b/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..e5ce5163 --- /dev/null +++ b/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,279 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`quotes.css 1`] = ` +@supports (content: one "two" three 'four') { + a[href="foo" y], + abbr[title^='It\\'s a trap!'], + img[src=""] { + /* Simple strings. */ + content: "abc"; + content: 'abc'; + + /* Escape. */ + content: '\\A'; + + /* Emoji. */ + content: '🐶'; + + /* Empty string. */ + content: ""; + content: ''; + + /* Single double quote. */ + content: "\\""; + content: '"'; + + /* Single single quote. */ + content: "'"; + content: '\\''; + + /* One of each. */ + content: "\\"'"; + content: '"\\''; + + /* One of each with unnecessary escapes. */ + content: "\\"\\'"; + content: '\\"\\''; + + /* More double quotes than single quotes. */ + content: "\\"'\\""; + content: '"\\'"'; + + /* More single quotes than double quotes. */ + content: "\\"''"; + content: '"\\'\\''; + + /* Two of each. */ + content: "\\"\\"''"; + content: '""\\'\\''; + + /* Single backslash. */ + content: '\\\\'; + content: "\\\\"; + + /* Backslases. */ + content: "\\"\\\\\\"\\\\\\\\\\" '\\'\\\\'\\\\\\'\\\\\\\\'"; + content: '\\'\\\\\\'\\\\\\\\\\' "\\"\\\\"\\\\\\"\\\\\\\\"'; + + /* Somewhat more real-word example. */ + content: "He's sayin': \\"How's it goin'?\\" Don't ask me why."; + content: 'He\\'s sayin\\': "How\\'s it goin\\'?" Don\\'t ask me why.'; + + /* Somewhat more real-word example 2. */ + content: "var backslash = \\"\\\\\\", doubleQuote = '\\"';"; + content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; + + /* Leave all "escapes" alone. */ + content: "\\Abc4 foo \\n" /* "comment" */ "\\end"; + content: '\\Abc4 foo \\n' /* 'comment' */ '\\end'; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@supports (content: one "two" three "four") { + a[href="foo" y], + abbr[title^="It's a trap!"], + img[src=""] { + /* Simple strings. */ + content: "abc"; + content: "abc"; + + /* Escape. */ + content: "\\A"; + + /* Emoji. */ + content: "🐶"; + + /* Empty string. */ + content: ""; + content: ""; + + /* Single double quote. */ + content: '"'; + content: '"'; + + /* Single single quote. */ + content: "'"; + content: "'"; + + /* One of each. */ + content: "\\"'"; + content: "\\"'"; + + /* One of each with unnecessary escapes. */ + content: "\\"'"; + content: "\\"'"; + + /* More double quotes than single quotes. */ + content: '"\\'"'; + content: '"\\'"'; + + /* More single quotes than double quotes. */ + content: "\\"''"; + content: "\\"''"; + + /* Two of each. */ + content: "\\"\\"''"; + content: "\\"\\"''"; + + /* Single backslash. */ + content: "\\\\"; + content: "\\\\"; + + /* Backslases. */ + content: "\\"\\\\\\"\\\\\\\\\\" ''\\\\'\\\\'\\\\\\\\'"; + content: '\\'\\\\\\'\\\\\\\\\\' ""\\\\"\\\\"\\\\\\\\"'; + + /* Somewhat more real-word example. */ + content: "He's sayin': \\"How's it goin'?\\" Don't ask me why."; + content: "He's sayin': \\"How's it goin'?\\" Don't ask me why."; + + /* Somewhat more real-word example 2. */ + content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; + content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; + + /* Leave all "escapes" alone. */ + content: "\\Abc4 foo \\n" "\\end"; + content: "\\Abc4 foo \\n" "\\end"; + } +} + +`; + +exports[`quotes.css 2`] = ` +@supports (content: one "two" three 'four') { + a[href="foo" y], + abbr[title^='It\\'s a trap!'], + img[src=""] { + /* Simple strings. */ + content: "abc"; + content: 'abc'; + + /* Escape. */ + content: '\\A'; + + /* Emoji. */ + content: '🐶'; + + /* Empty string. */ + content: ""; + content: ''; + + /* Single double quote. */ + content: "\\""; + content: '"'; + + /* Single single quote. */ + content: "'"; + content: '\\''; + + /* One of each. */ + content: "\\"'"; + content: '"\\''; + + /* One of each with unnecessary escapes. */ + content: "\\"\\'"; + content: '\\"\\''; + + /* More double quotes than single quotes. */ + content: "\\"'\\""; + content: '"\\'"'; + + /* More single quotes than double quotes. */ + content: "\\"''"; + content: '"\\'\\''; + + /* Two of each. */ + content: "\\"\\"''"; + content: '""\\'\\''; + + /* Single backslash. */ + content: '\\\\'; + content: "\\\\"; + + /* Backslases. */ + content: "\\"\\\\\\"\\\\\\\\\\" '\\'\\\\'\\\\\\'\\\\\\\\'"; + content: '\\'\\\\\\'\\\\\\\\\\' "\\"\\\\"\\\\\\"\\\\\\\\"'; + + /* Somewhat more real-word example. */ + content: "He's sayin': \\"How's it goin'?\\" Don't ask me why."; + content: 'He\\'s sayin\\': "How\\'s it goin\\'?" Don\\'t ask me why.'; + + /* Somewhat more real-word example 2. */ + content: "var backslash = \\"\\\\\\", doubleQuote = '\\"';"; + content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; + + /* Leave all "escapes" alone. */ + content: "\\Abc4 foo \\n" /* "comment" */ "\\end"; + content: '\\Abc4 foo \\n' /* 'comment' */ '\\end'; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@supports (content: one 'two' three 'four') { + a[href='foo' y], + abbr[title^="It's a trap!"], + img[src=''] { + /* Simple strings. */ + content: 'abc'; + content: 'abc'; + + /* Escape. */ + content: '\\A'; + + /* Emoji. */ + content: '🐶'; + + /* Empty string. */ + content: ''; + content: ''; + + /* Single double quote. */ + content: '"'; + content: '"'; + + /* Single single quote. */ + content: "'"; + content: "'"; + + /* One of each. */ + content: '"\\''; + content: '"\\''; + + /* One of each with unnecessary escapes. */ + content: '"\\''; + content: '"\\''; + + /* More double quotes than single quotes. */ + content: '"\\'"'; + content: '"\\'"'; + + /* More single quotes than double quotes. */ + content: "\\"''"; + content: "\\"''"; + + /* Two of each. */ + content: '""\\'\\''; + content: '""\\'\\''; + + /* Single backslash. */ + content: '\\\\'; + content: '\\\\'; + + /* Backslases. */ + content: "\\"\\\\\\"\\\\\\\\\\" ''\\\\'\\\\'\\\\\\\\'"; + content: '\\'\\\\\\'\\\\\\\\\\' ""\\\\"\\\\"\\\\\\\\"'; + + /* Somewhat more real-word example. */ + content: "He's sayin': \\"How's it goin'?\\" Don't ask me why."; + content: "He's sayin': \\"How's it goin'?\\" Don't ask me why."; + + /* Somewhat more real-word example 2. */ + content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; + content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; + + /* Leave all "escapes" alone. */ + content: '\\Abc4 foo \\n' '\\end'; + content: '\\Abc4 foo \\n' '\\end'; + } +} + +`; diff --git a/tests/css_quotes/jsfmt.spec.js b/tests/css_quotes/jsfmt.spec.js new file mode 100644 index 00000000..b20d64e4 --- /dev/null +++ b/tests/css_quotes/jsfmt.spec.js @@ -0,0 +1,2 @@ +run_spec(__dirname, { parser: "postcss" }); +run_spec(__dirname, { parser: "postcss", singleQuote: true }); diff --git a/tests/css_quotes/quotes.css b/tests/css_quotes/quotes.css new file mode 100644 index 00000000..6bd51a39 --- /dev/null +++ b/tests/css_quotes/quotes.css @@ -0,0 +1,67 @@ +@supports (content: one "two" three 'four') { + a[href="foo" y], + abbr[title^='It\'s a trap!'], + img[src=""] { + /* Simple strings. */ + content: "abc"; + content: 'abc'; + + /* Escape. */ + content: '\A'; + + /* Emoji. */ + content: '🐶'; + + /* Empty string. */ + content: ""; + content: ''; + + /* Single double quote. */ + content: "\""; + content: '"'; + + /* Single single quote. */ + content: "'"; + content: '\''; + + /* One of each. */ + content: "\"'"; + content: '"\''; + + /* One of each with unnecessary escapes. */ + content: "\"\'"; + content: '\"\''; + + /* More double quotes than single quotes. */ + content: "\"'\""; + content: '"\'"'; + + /* More single quotes than double quotes. */ + content: "\"''"; + content: '"\'\''; + + /* Two of each. */ + content: "\"\"''"; + content: '""\'\''; + + /* Single backslash. */ + content: '\\'; + content: "\\"; + + /* Backslases. */ + content: "\"\\\"\\\\\" '\'\\'\\\'\\\\'"; + content: '\'\\\'\\\\\' "\"\\"\\\"\\\\"'; + + /* Somewhat more real-word example. */ + content: "He's sayin': \"How's it goin'?\" Don't ask me why."; + content: 'He\'s sayin\': "How\'s it goin\'?" Don\'t ask me why.'; + + /* Somewhat more real-word example 2. */ + content: "var backslash = \"\\\", doubleQuote = '\"';"; + content: 'var backslash = "\\", doubleQuote = \'"\';'; + + /* Leave all "escapes" alone. */ + content: "\Abc4 foo \n" /* "comment" */ "\end"; + content: '\Abc4 foo \n' /* 'comment' */ '\end'; + } +} diff --git a/tests/stylefmt/content/__snapshots__/jsfmt.spec.js.snap b/tests/stylefmt/content/__snapshots__/jsfmt.spec.js.snap index 09a19541..cf6f6b56 100644 --- a/tests/stylefmt/content/__snapshots__/jsfmt.spec.js.snap +++ b/tests/stylefmt/content/__snapshots__/jsfmt.spec.js.snap @@ -6,7 +6,7 @@ exports[`content.css 1`] = ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ div { content: " test 1/2 "; - content: ' test 1+2'; + content: " test 1+2"; } `; diff --git a/tests/stylefmt/font-face/__snapshots__/jsfmt.spec.js.snap b/tests/stylefmt/font-face/__snapshots__/jsfmt.spec.js.snap index 3e8bc04c..fdbb9927 100644 --- a/tests/stylefmt/font-face/__snapshots__/jsfmt.spec.js.snap +++ b/tests/stylefmt/font-face/__snapshots__/jsfmt.spec.js.snap @@ -4,7 +4,7 @@ exports[`font-face.css 1`] = ` @font-face {font-family:'HelveticaNeueW02-45Ligh';src:url("/fonts/pictos-web.eot");src:local("☺"),url("/fonts/pictos-web.woff") format("woff"),url("/fonts/pictos-web.ttf") format("truetype"),url("/fonts/pictos-web.svg#webfontIyfZbseF") format("svg");font-weight:normal;font-style:normal;} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @font-face { - font-family: 'HelveticaNeueW02-45Ligh'; + font-family: "HelveticaNeueW02-45Ligh"; src: url("/fonts/pictos-web.eot"); src: local("☺"), url("/fonts/pictos-web.woff") format("woff"), url("/fonts/pictos-web.ttf") format("truetype"), From b286ed171e53b5488fd4168993a4a63218a93933 Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Tue, 15 Aug 2017 07:58:19 +0200 Subject: [PATCH 2/2] Fix AST_COMPARE for CSS strings --- src/clean-ast.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/clean-ast.js b/src/clean-ast.js index 257079d8..1fd4d0aa 100644 --- a/src/clean-ast.js +++ b/src/clean-ast.js @@ -82,6 +82,17 @@ function massageAST(ast) { newObj.value = newObj.value.toLowerCase(); } + if ( + (ast.type === "media-value" || + ast.type === "selector-attribute" || + ast.type === "value-string") && + newObj.value + ) { + newObj.value = newObj.value + .replace(/'/g, '"') + .replace(/\\([^a-fA-F\d])/g, "$1"); + } + // (TypeScript) Ignore `static` in `constructor(static p) {}` // and `export` in `constructor(export p) {}` if (