Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Airbnb, ESLint, Prettier conflict over Switch and Case indentation

I am setting up my React Redux project to use ESLint with Airbnb configs and Prettier. I've been modifying certain things to how I want them, but now I've run into a problem with indentations on switch & case statements that I can't seem to fix.

I am editing my project in VSCode. I click on the errors and fix with ESLint, reducing the indent by 4 spaces, but then more errors show up from Prettier, asking to re-indent everything by another 4 spaces.

I want to change the current indentation width. I want it set to 4 spaces when I use tabs. It's not a must, but if it is possible, I would prefer that the case keywords in switch blocks to be indented.


List of packages that pertain to my ESLint configuration:
  • prettier
  • eslint
  • eslint-config-airbnb
  • eslint-plugin-import
  • eslint-plugin-jsx-a11y
  • eslint-plugin-react
  • eslint-import-resolver-webpack
  • eslint-config-prettier
  • eslint-plugin-prettier

Relevant portion of my .eslintrc.json
{
    "extends": ["airbnb", "prettier", "prettier/react"],
    "plugins": ["react", "prettier"],
    "rules": {
        "react/jsx-filename-extension": [
            1,
            {
                "extensions": [".js", "jsx"]
            }
        ],
        "prettier/prettier": "error",
        "indent": ["error", 4, { "SwitchCase": 1 }],
        "react/jsx-indent": ["error", 4],
        "react/jsx-indent-props": ["error", 4],
        "class-methods-use-this": 0,
        "no-else-return": 0,
        "no-plusplus": [2, { "allowForLoopAfterthoughts": true }],
        "no-param-reassign": 0
    },

My .prettierrc config-file:
The setting below is necessary for Prettier to format indentations harmoniously with the rest of the development environments configurations while using ESLint.
  "prettier": {
    "tabWidth": 4
  }

Here is the script I am formatting:
It is a hangman game.
    switch (action.type) {
        case GUESS_LETTER:
            return Object.assign(
                {},
                state,
                state.guessWord.includes(action.guessLetter)
                    ? addCorrectGuess(
                          state.rightLetters.slice(), // <-- error here!
                          action.guessLetter, // <-- error here!
                          state.guessWord // <-- error here!
                      ) // <-- error here!
                    : addWrongGuess(
                          state.wrongLetters.slice(), // <-- error here!
                          action.guessLetter // <-- error here!
                      ) // <-- error here!
            );

My first attempt to get this working was to add { "SwitchCase": 1 } to the ESLint config file. That reduced the number of errors (which was almost the entire block), but there are still errors. I can't figure out where the conflict is exactly.

[UPDATE] To my eternal shame, I believe I discovered the problem. I simply removed the configuration for intends from the ESLint configuration. I removed this:

"indent": ["error", 4, { "SwitchCase": 1 }],
"react/jsx-indent": ["error", 4],
"react/jsx-indent-props": ["error", 4],

And now it seems to be behaving normally. I assume this is because it screws up Prettier when trying to handle indents. I only needed the configuration for Prettier.

Lesson: test more by removing configurations which may be causing conflict before posting.

like image 583
cdpautsch Avatar asked May 23 '19 17:05

cdpautsch


Video Answer


2 Answers

To make sure this is marked as solved:

I simply removed the configuration for indents from the ESLint configuration. I removed this:

"indent": ["error", 4, { "SwitchCase": 1 }],
"react/jsx-indent": ["error", 4],
"react/jsx-indent-props": ["error", 4],

And now it seems to be behaving normally. I assume this is because it screws up Prettier when trying to handle indents. I only needed the configuration for Prettier.

Lesson: test more by removing configurations which may be causing conflict before posting.

like image 80
cdpautsch Avatar answered Jan 01 '23 07:01

cdpautsch


I had the same problem as mentioned in the question above, however I figured out how to solve it in a way that not only could the "indent" setting be kept, but so it can be adjusted, allowing for tabWidth to be set as you like.

First I want to note, so we're all on the same page, what the set-up I am using for linting/formatting exactly is:

  1. Source Code Editor: V.S. Code
  2. Linting Utility: ESLint
  3. Opinionated Formatter: Prettier
  4. Style-Guide: AirBnB

Additionally I also equip the following, which I download using NPM:

  1. eslint-plugin-prettier
  2. eslint-config-prettier
  3. eslint-config-airbnb

For those familiar with linting while using V.S. Code, you guys know that what I am using is actually the short list. There is all kinds of other configs, plugins & extensions that can be obtained. The important thing to note here, is that I am using a tried & true setup, that allows Prettier, and ESLint to work harmoniously together.

With that said, even having a good configuration, and all the correct extensions I still had a problem with the above rule, the same as the developer who posted the question. I knew the problem had to do with ESLint's rules, and how they were being configured in my .eslintrc.json file. After much research, and near mastering ESLint, I found that I was correct. There are a few ESLint rules, (3 ESLint rules, and 1 Prettier Setting), that you need to have configured properly, however; at the same time you need to make sure your .prettierrc file settings are in-sync with your .eslintrc.json file rules. Once you understand it all, it is actually not as complicated as it sounds.




STEP #1 Know what tabWidth you want and set it in .prettierrc


  • First off you need to decide what you want your styles tab-width to be. (Most Style-Guides set there tab-width at (2x Space-Width), but many dev's will change there settings to a (4x Space-Width). Any thing other than 2x and 4x is considered taboo.)

  • Go ahead and open your .prettierrc file (or what ever mimetype of prettier configuration file you use), then set 'tabWidth' to your liking, just make sure that you stick with a consistant tabWidth from here on out, becuase ESLint needs to be in sync with prettier.


Your '.prettierrc' file should look similar to this:

{
    "printWidth": 90,
    "tabWidth": 4,
    "useTabs": false,
    "trailingComma": "all",
    "singleQuote": true,
    "semi": true,
    "bracketSpacing": false,
}




STEP #2: set the "max-len" rule in .eslintrc.json**


  • open up your .eslintrc.json file (or whatever version of ESLint configuration file mime-type that you use), and set the "max-len" rule in the rules property of your eslintrc.json. The rule is not in the file by default so you will have to write it in. To see how, review the Example I Created Below.
  • Be sure that the "tabWidth" property within ESLint's "max-len" rule, holds exactly the same value, as the "tabWidth" setting within your .prettierrc file.
  • There are some other rule options here that are important to set properly as well. The first, 'code', sets the max amount of chars each line can hold, aka(max-line-length). When "code" is set ESlint will not allow more characters than what "code" has been set to. The catch is that you absolutely must Make sure that the "printWidth" setting in your .prettierrc file, which you probably will have to write in, is set to the same value as the "code" property's value in your .eslintrc.json max-len rule.
  • "ignoreUrls" can be set either way and make sure the rule does throw an error by leaving the word "error" as it is.

Here is an example of my max-len rule in ESLint

"rules": {
    "max-len": [
        "error",
        {
            "code": 90,
            "tabWidth": 4,
            "ignoreUrls": true
        }
    ],
}

See the .prettierrc example above, to view how to set "printWidth"

STEP #3: ESLint's "no-tabs" Rule

  • The "no-tabs" rule, is a 'must set', w/o it your linting & formatting > can unsynchronize.
  • You can set "allowIndentationTabs" as you like, Allowing for, or disallowing indentation tabs won't make a difference.

What the no-tabs rule should look like:

"rules": {
    "no-tabs": ["error", {"allowIndentationTabs": true}],

    "max-len": [
        "error",
        {
            "code": 90,
            "tabWidth": 4,
            "ignoreUrls": true
        }
    ],
}

STEP #4: ESLint's "indent" Rule, by far the most important rule of this discussion

Seriously this one is important. Its the one that gets thrown out in an attempt to fix the switch indentation problem, which doesn't give you control over other settings, like setting your own tab-width. The "indent" rule.

  • First make sure indent is active by setting error to error as you see it, you could also set it to warn, but its better to set it as error so prettier knows to fix it imeadiatly. The next setting is the width of your indentations, which has to be the same exact value as your tabWidth in your .prettierrc file, and as in the "max-len" ESLint rule.
"indent": ["error", 4, {"SwitchCase": 1}],
  • The last settings in the brackets, "SwitchCase" can be confusing. No one explained any of this to me at first so I had to figure it out on my own, and this screwed me up a bit. The number that "SwitchCase" gets set to is not like the 4 before it. The 4 that comes before it is the initial setting of the "indent" rule and can stand alone like this:
"indent": 4; // throws error
// or
"indent": ["warn", 4] // Throws warning, which is somtimes less annoying than an error

So it is important to note that the value 4 that you see in the "indent" rule, is how many spaces an indent will be. This led me to believe that the 1 after "SwitchCase" meant how many spaces that the switch keyword case will be indented.

Example:

switch(x){
    case 1:
        break;
}

That is not how it works though. The number that rule "SwitchCase" is set to, tells the editor how many times to indent the keyword 'case'. So if the "indent" rule is set to 4, and the "SwitchCase" rule is set to 4, then ESLint will expect an indentation of 4x4=16. At the end of the day ESLint will actually expect more than 16, because ESLint is going to take on all the indentation from different levels of blocks that the switch statement is found in, this ends up leading to ESLint expecting 24-36 spaces of indentation, that's crazy.

"SwitchCase" needs to be set to 1.

"rules": {
    "indent": ["error", 4, {"SwitchCase": 1}],
    
    "no-tabs": ["error", {"allowIndentationTabs": true}],

    "max-len": [
        "error",
        {
            "code": 90,
            "tabWidth": 4,
            "ignoreUrls": true
        }
    ],
}

If you set all the rules right that should work for you

If it is still not working, try starting with a clean slate, delete your .eslintrc.json file, and create a new eslintrc.json file, and build it anew. You shouldn't have to start over with a new .prettierrc file.

To Wrap Up

Prettier wants to indent everything uninformed like. So if tabWidth is set to 4, and tabWidth & indent is set to 4 in ESlint then every block gets indented 4 spaces be prettier, no more and no less.

The whole problem initially is that, the style guides often ask that the keyword case not be indented, so when ESLint is setup, that is what it is configured to do by the NPM config you install upon initializing ESLint. However that does not work with prettier.

This answer wound up much longer than I thought it would. Getting Prettier, ESLint & VS Code to work harmoniously together is a very involved task, especially for first timers. I use Babel on top of this AST stack (I made that term up lol, what else would you call it?). If you want to be able to lint documents that contain TC39 ECMAScript Stage-3 Proposals, this includes features such as Private-Members, Static Methods, ect..., then you will need to use configure ESLint to use Babel's parser.

like image 37
j D3V Avatar answered Jan 01 '23 08:01

j D3V