javascriptvue.jseslintvisual-studio-codeeslint-plugin-vue

How to resolve eslint(vue/html-closing-bracket-newline) conflict


On save, VSCode is fixing eslint is fixing all the rules. How to fix the below conflict?

Expected Indentation

Expected Indentation

Unexpected Indentation

Unexpected Indentation

VScode Plugins in use:

 [
    "formulahendry.auto-close-tag",
    "msjsdiag.debugger-for-chrome",
    "hookyqr.beautify",
    "mikestead.dotenv",
    "dbaeumer.vscode-eslint",
    "donjayamanne.githistory",
    "eamodio.gitlens",
    "sidthesloth.html5-boilerplate",
    "ecmel.vscode-html-css",
    "abusaidm.html-snippets",
    "wix.vscode-import-cost",
    "lonefy.vscode-js-css-html-formatter",
    "eg2.vscode-npm-script",
    "christian-kohler.npm-intellisense",
    "sibiraj-s.vscode-scss-formatter",
    "octref.vetur",
    "blanu.vscode-styled-jsx",
    "jcbuisson.vue",
    "hollowtree.vue-snippets",
    "wscats.vue",
    "sdras.vue-vscode-snippets",
    "dariofuzinato.vue-peek",
]

Error

Error

Here is the config in use:

 'vue/html-closing-bracket-newline': [
  'error',
  {
    singleline: 'never',
    multiline: 'never'
  }
],
'indent': ['error', 2],
'vue/html-indent': ['error', 2],
'vue/script-indent': ['error', 2],
'vue/multiline-html-element-content-newline': 0

VSCode settings

 {
    "editor.formatOnSave": true,
    "[javascript]": {
        "editor.formatOnSave": true
    },
    "eslint.alwaysShowStatus": true,
    "files.autoSave": "onFocusChange",
    "emmet.includeLanguages": {
        "javascript": "javascriptreact",
        "vue-html": "html",
        "plaintext": "jade",
        "edge": "html"
    },
    "emmet.syntaxProfiles": {
        "javascript": "jsx"
    },
    "emmet.triggerExpansionOnTab": true,
    "emmet.showSuggestionsAsSnippets": true,
    "files.associations": {
        "*.js": "javascriptreact"
    },
    "editor.fontSize": 14,
    "git.enableSmartCommit": true,
    "git.confirmSync": false,
    "search.exclude": {
        "**/.git": true,
        "**/node_modules": true,
        "**/bower_components": true,
        "**/tmp": true,
        "**/.bin": true,
        "**/.next": true,
        "**/__snapshots__/**": true,
        "**/coverage/**": true,
        "**/report/**": true
    },
    "javascript.updateImportsOnFileMove.enabled": "always",
    "explorer.confirmDragAndDrop": false,
    "explorer.confirmDelete": false,
    "diffEditor.ignoreTrimWhitespace": false,
    "workbench.editor.enablePreviewFromQuickOpen": false,
    "files.exclude": {
        ".next": true,
        "*.log": true,
        "**/__pycache__": true,
        "**/node_modules": true,
        "**/o": true,
        "dist": true,
        "geckodriver.log": true,
        "package-lock.json": true,
        "yarn.lock": true
    },
    "window.zoomLevel": 1,
    "editor.find.globalFindClipboard": true,
    "editor.fontLigatures": true,
    "editor.formatOnType": true,
    "team.showWelcomeMessage": false,
    "git.autofetch": true,
    "workbench.startupEditor": "newUntitledFile",
    "editor.codeActionsOnSave": {
        // For ESLint
        "source.fixAll.eslint": true,
        // For TSLint
        "source.fixAll.tslint": true,
        // For Stylelint
        "source.fixAll.stylelint": true
    },
    "launch": {},
    "workbench.colorCustomizations": {},
    "javascript.validate.enable": true,
    "javascript.suggestionActions.enabled": false,
    "editor.insertSpaces": false,
    "editor.detectIndentation": false,
    "prettier.disableLanguages": [],
    "vetur.format.defaultFormatter.js": "vscode-typescript",
    "vetur.format.defaultFormatter.html": "js-beautify-html",
    "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
    "eslint.validate": [
        "vue",
        "html",
        "javascript",
        "typescript",
        "javascriptreact",
        "typescriptreact"
    ]
}

Solution

  • 4 space/tab is a pain and its a challenge to fix. Hence sticking to 2 space along with few changes in settings to match my requirement.

    .vscode/settings.json

    {
      "editor.formatOnSave": true,
      "[javascript]": {
        "editor.tabSize": 4,
        "editor.insertSpaces": true
      },
      "[vue]": {
        "editor.tabSize": 4,
        "editor.insertSpaces": true
      },
      "eslint.alwaysShowStatus": true,
      "files.autoSave": "onFocusChange",
      "emmet.includeLanguages": {
        "javascript": "javascriptreact",
        "vue-html": "html",
        "plaintext": "jade",
        "edge": "html"
      },
      "emmet.syntaxProfiles": {
        "javascript": "jsx"
      },
      "emmet.triggerExpansionOnTab": true,
      "emmet.showSuggestionsAsSnippets": true,
      "files.associations": {
        "*.js": "javascriptreact"
      },
      "editor.fontSize": 14,
      "git.enableSmartCommit": true,
      "git.confirmSync": false,
      "search.exclude": {
        "**/.git": true,
        "**/node_modules": true,
        "**/bower_components": true,
        "**/tmp": true,
        "**/.bin": true,
        "**/.next": true,
        "**/__snapshots__/**": true,
        "**/coverage/**": true,
        "**/report/**": true
      },
      "javascript.updateImportsOnFileMove.enabled": "always",
      "explorer.confirmDragAndDrop": false,
      "explorer.confirmDelete": false,
      "diffEditor.ignoreTrimWhitespace": false,
      "workbench.editor.enablePreviewFromQuickOpen": false,
      "files.exclude": {
        ".next": true,
        "*.log": true,
        "**/__pycache__": true,
        "**/node_modules": true,
        "**/o": true,
        "dist": true,
        "geckodriver.log": true,
        "package-lock.json": true,
        "yarn.lock": true
      },
      "window.zoomLevel": 1,
      "editor.find.globalFindClipboard": true,
      "editor.fontLigatures": true,
      "editor.formatOnType": true,
      "team.showWelcomeMessage": false,
      "git.autofetch": true,
      "workbench.startupEditor": "newUntitledFile",
      "editor.codeActionsOnSave": {
        // For ESLint
        "source.fixAll.eslint": true,
        // For TSLint
        "source.fixAll.tslint": true,
        // For Stylelint
        "source.fixAll.stylelint": true
      },
      "launch": {},
      "workbench.colorCustomizations": {},
      "javascript.validate.enable": true,
      "javascript.suggestionActions.enabled": false,
      "editor.insertSpaces": false,
      "editor.detectIndentation": false,
      "prettier.disableLanguages": [],
      "vetur.format.defaultFormatter.html": "prettyhtml",
      "vetur.format.defaultFormatter.css": "prettier",
      "vetur.format.defaultFormatter.postcss": "prettier",
      "vetur.format.defaultFormatter.scss": "prettier",
      "vetur.format.defaultFormatter.less": "prettier",
      "vetur.format.defaultFormatter.stylus": "stylus-supremacy",
      "vetur.format.defaultFormatter.js": "prettier",
      "vetur.format.defaultFormatter.ts": "prettier",
      "vetur.format.defaultFormatter.sass": "sass-formatter",
      "vetur.validation.template": true,
      "vetur.format.defaultFormatterOptions": {
        "prettyhtml": {
          "printWidth": 100, // No line exceeds 100 characters
          "singleQuote": false // Prefer double quotes over single quotes
        }
      },
      "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
      "eslint.validate": ["javascript", "javascriptreact", "html", "vue"],
      "eslint.options": {
        "extensions": [".js", ".vue"]
      },
      "eslint.probe": [
        "javascript",
        "javascriptreact",
        "typescript",
        "typescriptreact",
        "html",
        "vue"
      ]
    }
    

    package.json devDependencies

    "devDependencies": {
        "@vue/cli-plugin-babel": "4.2.3",
        "@vue/cli-plugin-eslint": "^4.3.1",
        "@vue/cli-plugin-router": "4.2.3",
        "@vue/cli-service": "4.2.3",
        "@vue/eslint-config-prettier": "6.0.0",
        "@vue/eslint-config-standard": "^5.1.2",
        "babel-eslint": "^10.1.0",
        "eslint": "6.8.0",
        "eslint-config-airbnb": "^18.1.0",
        "eslint-config-import": "0.13.0",
        "eslint-config-prettier": "^6.11.0",
        "eslint-plugin-es-beautifier": "^1.0.1",
        "eslint-plugin-import": "^2.20.2",
        "eslint-plugin-modules": "^1.1.1",
        "eslint-plugin-node": "^11.1.0",
        "eslint-plugin-prettier": "^3.1.3",
        "eslint-plugin-prettier-vue": "^2.1.0",
        "eslint-plugin-promise": "^4.2.1",
        "eslint-plugin-standard": "^4.0.0",
        "eslint-plugin-vue": "^6.2.2",
        "express": "^4.17.1",
        "fibers": "4.0.2",
        "gulp": "4.0.2",
        "gulp-append-prepend": "1.0.8",
        "husky": "^4.2.5",
        "jest": "^25.4.0",
        "jest-sonar-reporter": "^2.0.0",
        "json-server": "^0.16.1",
        "node-sass": "4.13.1",
        "nodemon": "^2.0.3",
        "pretty-quick": "^2.0.1",
        "sass": "1.26.3",
        "sass-loader": "^8.0.2",
        "vue-jest": "^3.0.5",
        "vue-template-compiler": "2.6.11"
      },
    

    .eslintrc.js

    module.exports = {
      root: true,
    
      env: {
        node: true,
        jest: true
      },
    
      extends: ['plugin:vue/essential', '@vue/standard'],
    
      rules: {
        quotes: [
          2,
          'single',
          {
            avoidEscape: true
          }
        ],
        'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
        'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
        'no-unused-vars': [
          'error',
          {
            vars: 'all',
            varsIgnorePattern: 'css',
            args: 'all'
          }
        ],
        'no-use-before-define': [
          'error',
          {
            functions: false,
            classes: true
          }
        ],
        'no-var': 'error',
        'prefer-const': 2,
        eqeqeq: [
          'error',
          'always',
          {
            null: 'ignore'
          }
        ],
        'no-array-constructor': 'error',
        'no-new-object': 'error',
        'no-bitwise': 'error',
        'no-redeclare': 2,
        'no-proto': 2,
        'no-invalid-regexp': 2,
        'vue/script-indent': ['error', 2],
        'vue/html-closing-bracket-newline': [
          'error',
          {
            singleline: 'never',
            multiline: 'always'
          }
        ],
        'space-before-function-paren': [2, 'never'],
        'no-new': 0,
        'no-implied-eval': 0,
        'handle-callback-err': 0,
        'no-extend-native': 0,
        indent: ['error', 2, { SwitchCase: 1 }],
        semi: [2, 'always'],
        'vue/html-indent': [
          'error',
          2,
          {
            attribute: 1,
            baseIndent: 1,
            closeBracket: 0,
            alignAttributesVertically: true,
            ignores: ['pre', 'textarea', 'span']
          }
        ],
        'no-tabs': 0,
        'vue/singleline-html-element-content-newline': [
          'error',
          {
            ignoreWhenNoAttributes: true,
            ignoreWhenEmpty: true,
            ignores: ['pre', 'textarea', 'span']
          }
        ]
      },
    
      parserOptions: {
        parser: 'babel-eslint'
      },
    
      globals: {
        console: true,
        alert: true,
        document: true,
        __dirname: true,
        require: true,
        window: true,
        process: true,
        module: true,
        define: true,
        _: true,
        Promise: true,
        setTimeout: true,
        clearTimeout: true,
        mount: true,
        clearInterval: true,
        setInterval: true,
        $: false,
        MutationObserver: false,
        Map: false,
        localStorage: true
      }
    };
    

    .prettierrc.js

    module.exports = {
      singleQuote: true,
      tabWidth: 2
    };
    

    .jshintrc

    {
        "esversion": 9
    }
    

    .editorconfig

    [*.{js,jsx,ts,tsx,vue}]
    indent_style = space
    indent_size = 2
    trim_trailing_whitespace = true
    insert_final_newline = true
    

    That's it, and everything working as per expectations. Hope this helps someone.