Repository: prettier/prettier (JavaScript)
Date: 2021-10-08 14:58:13+00:00
Task type: Bug Fix

Issue:
Weird wrapping for SCSS @use rules
<!--

BEFORE SUBMITTING AN ISSUE:

1.  Search for your issue on GitHub: https://github.com/prettier/prettier/issues
    A large number of opened issues are duplicates of existing issues.
    If someone has already opened an issue for what you are experiencing,
    you do not need to open a new issue — please add a 👍 reaction to the
    existing issue instead.

2.  We get a lot of requests for adding options, but Prettier is
    built on the principle of being opinionated about code formatting.
    This means we add options only in the case of strict technical necessity.
    Find out more: https://prettier.io/docs/en/option-philosophy.html

Don't fill the form below manually! Let a program create a report for you:

1. Go to https://prettier.io/playground
2. Paste your code and set options
3. Press the "Report issue" button in the lower right

-->

**Prettier 2.4.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEABArgZzgAgDpoBmAngDYDmAVgIYD0ARtdgTgO4CWMAFjgBR5QcQnABJC1ALbtSxALQTq7KEj4Dh6nACV09dgGsANGo1DNEehBgQjgkzkzFM8CbPTsbd+9SiZZ2AE7shB7CAJQhQmLQMLKscOzkXDAqAMwArAAMEaJcAIyyhNGx8YnJOJlZxpFcAEwFRXEJSSoV2SJcKfWwxU1lFQKhANwgBiAQAA4w7NCYyKDU-v4QrAAKCwizKNSkrNSOIyD0-tRgenAwAMqScAAySnDI4qTYo0cnZ5fjJ0rkyDD+6DgozgEnocAAJuCITdvOR0NRyHAAGIQfwKGBTKC-LboKwHJISUgAdS4nDgmC+YDgFw2nHYADdOMRkOBMLNRkoAjAVsdyApHtsXiBKJgAB4XH6kOAARXQlgeSCeQq+-gCLMwYDZB3GgVgRPY4O4yAAHFkQDqINgicdxiydeS4P56Q9RgBHOXwHkTTYgJiyKBwCEQg7+ODu9ihnkI-mKwVAkDYKR-AHxzCSmUehVK+Mwaj0fWGrjIGqjf6KUg-ADCEAkMZA5LSBywcAAKnnNtnRvTAQBJKBQ2AXMCBSYAQX7FxgZCzcYAvrOgA)

Not sure which version this was introduced.
Previously this did not happen.

But the current SCSS syntax using `@use` gets wrapped in a weird way.
The opening parentheses is wrapped with a indent, and also all of  the code inside it.

**Input:**

```scss
@use "@fylgja/base" with (
    $family-main: (
        Rubik,
        Roboto,
        system-ui,
        sans-serif,
    ),
    $font-weight: 350,
    $h1-font-weight: 500,
    $h2-font-weight: 500,
    $h3-font-weight: 500
);
```

**Output:**

```scss
@use "@fylgja/base" with
  (
    $family-main: (
      Rubik,
      Roboto,
      system-ui,
      sans-serif,
    ),
    $font-weight: 350,
    $h1-font-weight: 500,
    $h2-font-weight: 500,
    $h3-font-weight: 500
  );
```

**Expected behavior:**

```scss
@use "@fylgja/base" with (
    $family-main: (
        Rubik,
        Roboto,
        system-ui,
        sans-serif,
    ),
    $font-weight: 350,
    $h1-font-weight: 500,
    $h2-font-weight: 500,
    $h3-font-weight: 500
);
```



Issue discussion:
hm, both looks good...
I think the indent should not be there @alexander-akait 
But it is also inconsistent with the SCSS map wrapping which does not wrap the opening parentheses to the next line

Solution patch:
diff --git a/changelog_unreleased/scss/11637.md b/changelog_unreleased/scss/11637.md
new file mode 100644
index 000000000000..424a5534787b
--- /dev/null
+++ b/changelog_unreleased/scss/11637.md
@@ -0,0 +1,21 @@
+#### Improve `with (...)` formatting (#11637 by @sosukesuzuki)
+
+<!-- prettier-ignore -->
+```scss
+// Input
+@use 'library' with (
+  $black: #222,
+  $border-radius: 0.1rem $font-family: 'Helvetica, sans-serif'
+);
+
+// Prettier stable
+@use 'library' with
+  ($black: #222, $border-radius: 0.1rem $font-family: 'Helvetica, sans-serif');
+
+// Prettier main
+@use 'library' with (
+  $black: #222,
+  $border-radius: 0.1rem $font-family: 'Helvetica, sans-serif'
+);
+
+```
diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js
index 0c326d6d26bd..b5940e2957ae 100644
--- a/src/language-css/printer-postcss.js
+++ b/src/language-css/printer-postcss.js
@@ -74,6 +74,7 @@ const {
   isColorAdjusterFuncNode,
   lastLineHasInlineComment,
   isAtWordPlaceholderNode,
+  isConfigurationNode,
 } = require("./utils.js");
 const { locStart, locEnd } = require("./loc.js");
 
@@ -807,6 +808,19 @@ function genericPrint(path, options, print) {
           continue;
         }
 
+        if (
+          iNode.value === "with" &&
+          iNextNode &&
+          iNextNode.type === "value-paren_group" &&
+          iNextNode.open &&
+          iNextNode.open.value === "(" &&
+          iNextNode.close &&
+          iNextNode.close.value === ")"
+        ) {
+          parts.push(" ");
+          continue;
+        }
+
         // Be default all values go through `line`
         parts.push(line);
       }
@@ -872,6 +886,10 @@ function genericPrint(path, options, print) {
       const lastItem = getLast(node.groups);
       const isLastItemComment = lastItem && lastItem.type === "value-comment";
       const isKey = isKeyInValuePairNode(node, parentNode);
+      const isConfiguration = isConfigurationNode(node, parentNode);
+
+      const shouldBreak = isConfiguration || (isSCSSMapItem && !isKey);
+      const shouldDedent = isConfiguration || isKey;
 
       const printed = group(
         [
@@ -915,11 +933,11 @@ function genericPrint(path, options, print) {
           node.close ? print("close") : "",
         ],
         {
-          shouldBreak: isSCSSMapItem && !isKey,
+          shouldBreak,
         }
       );
 
-      return isKey ? dedent(printed) : printed;
+      return shouldDedent ? dedent(printed) : printed;
     }
     case "value-func": {
       return [
diff --git a/src/language-css/utils.js b/src/language-css/utils.js
index 63e24da2560f..a56d0ec9013e 100644
--- a/src/language-css/utils.js
+++ b/src/language-css/utils.js
@@ -484,6 +484,30 @@ function isModuleRuleName(name) {
   return moduleRuleNames.has(name);
 }
 
+function isConfigurationNode(node, parentNode) {
+  if (
+    !node.open ||
+    node.open.value !== "(" ||
+    !node.close ||
+    node.close.value !== ")" ||
+    node.groups.some((group) => group.type !== "value-comma_group")
+  ) {
+    return false;
+  }
+  if (parentNode.type === "value-comma_group") {
+    const prevIdx = parentNode.groups.indexOf(node) - 1;
+    const maybeWithNode = parentNode.groups[prevIdx];
+    if (
+      maybeWithNode &&
+      maybeWithNode.type === "value-word" &&
+      maybeWithNode.value === "with"
+    ) {
+      return true;
+    }
+  }
+  return false;
+}
+
 module.exports = {
   getAncestorCounter,
   getAncestorNode,
@@ -539,4 +563,5 @@ module.exports = {
   stringifyNode,
   isAtWordPlaceholderNode,
   isModuleRuleName,
+  isConfigurationNode,
 };


Test patch:
diff --git a/tests/format/scss/configuration/__snapshots__/jsfmt.spec.js.snap b/tests/format/scss/configuration/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..2acdbfd02d9d
--- /dev/null
+++ b/tests/format/scss/configuration/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,94 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`use.scss - {"trailingComma":"none"} format 1`] = `
+====================================options=====================================
+parsers: ["scss"]
+printWidth: 80
+trailingComma: "none"
+                                                                                | printWidth
+=====================================input======================================
+@use "@fylgja/base" with (
+  $family-main: (
+  Rubik,
+    Roboto,
+    system-ui,
+    sans-serif,
+  ),
+  $font-weight: 350,
+  $h1-font-weight: 500,
+  $h2-font-weight: 500,
+  $h3-font-weight: 500
+);
+
+@use 'library' with (
+  $black: #222,
+  $border-radius: 0.1rem
+);
+
+=====================================output=====================================
+@use "@fylgja/base" with (
+  $family-main: (
+    Rubik,
+    Roboto,
+    system-ui,
+    sans-serif
+  ),
+  $font-weight: 350,
+  $h1-font-weight: 500,
+  $h2-font-weight: 500,
+  $h3-font-weight: 500
+);
+
+@use "library" with (
+  $black: #222,
+  $border-radius: 0.1rem
+);
+
+================================================================================
+`;
+
+exports[`use.scss format 1`] = `
+====================================options=====================================
+parsers: ["scss"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+@use "@fylgja/base" with (
+  $family-main: (
+  Rubik,
+    Roboto,
+    system-ui,
+    sans-serif,
+  ),
+  $font-weight: 350,
+  $h1-font-weight: 500,
+  $h2-font-weight: 500,
+  $h3-font-weight: 500
+);
+
+@use 'library' with (
+  $black: #222,
+  $border-radius: 0.1rem
+);
+
+=====================================output=====================================
+@use "@fylgja/base" with (
+  $family-main: (
+    Rubik,
+    Roboto,
+    system-ui,
+    sans-serif,
+  ),
+  $font-weight: 350,
+  $h1-font-weight: 500,
+  $h2-font-weight: 500,
+  $h3-font-weight: 500
+);
+
+@use "library" with (
+  $black: #222,
+  $border-radius: 0.1rem
+);
+
+================================================================================
+`;
diff --git a/tests/format/scss/configuration/jsfmt.spec.js b/tests/format/scss/configuration/jsfmt.spec.js
new file mode 100644
index 000000000000..f2558a4a9996
--- /dev/null
+++ b/tests/format/scss/configuration/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["scss"], { trailingComma: "none" });
+run_spec(__dirname, ["scss"]);
diff --git a/tests/format/scss/configuration/use.scss b/tests/format/scss/configuration/use.scss
new file mode 100644
index 000000000000..eb2f20905577
--- /dev/null
+++ b/tests/format/scss/configuration/use.scss
@@ -0,0 +1,17 @@
+@use "@fylgja/base" with (
+  $family-main: (
+  Rubik,
+    Roboto,
+    system-ui,
+    sans-serif,
+  ),
+  $font-weight: 350,
+  $h1-font-weight: 500,
+  $h2-font-weight: 500,
+  $h3-font-weight: 500
+);
+
+@use 'library' with (
+  $black: #222,
+  $border-radius: 0.1rem
+);
diff --git a/tests/format/scss/quotes/__snapshots__/jsfmt.spec.js.snap b/tests/format/scss/quotes/__snapshots__/jsfmt.spec.js.snap
index 06419858dada..c0c60483be4f 100644
--- a/tests/format/scss/quotes/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/scss/quotes/__snapshots__/jsfmt.spec.js.snap
@@ -34,8 +34,10 @@ singleQuote: true
 =====================================output=====================================
 @use 'library';
 
-@use 'library' with
-  ($black: #222, $border-radius: 0.1rem $font-family: 'Helvetica, sans-serif');
+@use 'library' with (
+  $black: #222,
+  $border-radius: 0.1rem $font-family: 'Helvetica, sans-serif'
+);
 
 @use 'library' as *;
 
@@ -89,8 +91,10 @@ printWidth: 80
 =====================================output=====================================
 @use "library";
 
-@use "library" with
-  ($black: #222, $border-radius: 0.1rem $font-family: "Helvetica, sans-serif");
+@use "library" with (
+  $black: #222,
+  $border-radius: 0.1rem $font-family: "Helvetica, sans-serif"
+);
 
 @use "library" as *;