diff --git a/changelog_unreleased/javascript/17398.md b/changelog_unreleased/javascript/17398.md new file mode 100644 index 000000000..a45a7831f --- /dev/null +++ b/changelog_unreleased/javascript/17398.md @@ -0,0 +1,19 @@ +#### Preserve spaces between CSS words and embedded expression (#17398 by @sosukesuzuki) + + +```jsx +// Input +const Heading = styled.h1` + font-size: var(--font-size-h${level}); +`; + +// Prettier stable +const Heading = styled.h1` + font-size: var(--font-size-h ${level}); +`; + +// Prettier main +const Heading = styled.h1` + font-size: var(--font-size-h${level}); +`; +``` diff --git a/src/language-css/loc.js b/src/language-css/loc.js index c01e5f896..6b4ea4dc4 100644 --- a/src/language-css/loc.js +++ b/src/language-css/loc.js @@ -2,6 +2,14 @@ import isNonEmptyArray from "../utils/is-non-empty-array.js"; import lineColumnToIndex from "../utils/line-column-to-index.js"; import { skipEverythingButNewLine } from "../utils/skip.js"; +function fixValueWordLoc(node, originalIndex) { + const { value } = node; + if (value === "-" || value === "--" || value.charAt(0) !== "-") { + return originalIndex; + } + return originalIndex - (value.charAt(1) === "-" ? 2 : 1); +} + function calculateLocStart(node, text) { // `postcss>=8` if (typeof node.source?.start?.offset === "number") { @@ -10,6 +18,9 @@ function calculateLocStart(node, text) { // value-* nodes have this if (typeof node.sourceIndex === "number") { + if (node.type === "value-word") { + return fixValueWordLoc(node, node.sourceIndex); + } return node.sourceIndex; } @@ -33,7 +44,11 @@ function calculateLocEnd(node, text) { if (node.source) { if (node.source.end) { - return lineColumnToIndex(node.source.end, text); + const index = lineColumnToIndex(node.source.end, text); + if (node.type === "value-word") { + return fixValueWordLoc(node, index); + } + return index; } if (isNonEmptyArray(node.nodes)) { diff --git a/src/language-css/print/comma-separated-value-group.js b/src/language-css/print/comma-separated-value-group.js index ab4cdce7f..37f112255 100644 --- a/src/language-css/print/comma-separated-value-group.js +++ b/src/language-css/print/comma-separated-value-group.js @@ -147,11 +147,15 @@ function printCommaSeparatedValueGroup(path, options, print) { continue; } - // styled.div` background: var(--${one}); ` + // We should keep spaces between words in a embedded JS expression + // examples: + // styled.div` font-size: var(--font-size-h${({ level }) => level}); `; + // styled.div` grid-area: area-${({ area }) => area}; `; + // styled.div` border: 1px ${solid} red; `; if ( iNode.type === "value-word" && - iNode.value.endsWith("-") && - isAtWordPlaceholderNode(iNextNode) + isAtWordPlaceholderNode(iNextNode) && + locEnd(iNode) === locStart(iNextNode) ) { continue; } diff --git a/tests/format/js/multiparser-css/__snapshots__/format.test.js.snap b/tests/format/js/multiparser-css/__snapshots__/format.test.js.snap index b64f76fdc..2fdb0057b 100644 --- a/tests/format/js/multiparser-css/__snapshots__/format.test.js.snap +++ b/tests/format/js/multiparser-css/__snapshots__/format.test.js.snap @@ -480,6 +480,56 @@ const paragraph3 = css\` ================================================================================ `; +exports[`issue-16692.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "typescript", "flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +const c1 = styled.div\` + border: 1px \${solid} red; +\`; + +const c2 = styled.div\` + font-size: var(--font-size-h\${level}); +\`; + +const c3 = styled.div\` + grid-area: area-\${area}; +\`; + +const c4 = styled.div\` + grid-area: var(--\${one}); +\`; + +const c5 = styled.div\` + font-size: var(--font-size-h \${level}); +\`; + +=====================================output===================================== +const c1 = styled.div\` + border: 1px \${solid} red; +\`; + +const c2 = styled.div\` + font-size: var(--font-size-h\${level}); +\`; + +const c3 = styled.div\` + grid-area: area-\${area}; +\`; + +const c4 = styled.div\` + grid-area: var(--\${one}); +\`; + +const c5 = styled.div\` + font-size: var(--font-size-h \${level}); +\`; + +================================================================================ +`; + exports[`styled-components.js format 1`] = ` ====================================options===================================== parsers: ["babel", "typescript", "flow"] diff --git a/tests/format/js/multiparser-css/issue-16692.js b/tests/format/js/multiparser-css/issue-16692.js new file mode 100644 index 000000000..40febe8e1 --- /dev/null +++ b/tests/format/js/multiparser-css/issue-16692.js @@ -0,0 +1,19 @@ +const c1 = styled.div` + border: 1px ${solid} red; +`; + +const c2 = styled.div` + font-size: var(--font-size-h${level}); +`; + +const c3 = styled.div` + grid-area: area-${area}; +`; + +const c4 = styled.div` + grid-area: var(--${one}); +`; + +const c5 = styled.div` + font-size: var(--font-size-h ${level}); +`;