0

We are having some annoying HTML formatting issues using Angular, ESLint and Prettier within VSCode. For example, the following HTML

Van<span *ngIf="someCondition">af</span>

is rewritten as

Van
<span *ngIf="someCondition">af</span>

This introduces unwanted whitespace in the output. I have tried many things but I cannot get all the pieces working nicely together. Running Prettier directly on the input file gives no error.

We have a monorepo with several projects underneath a single PROJECT_DIR inside a container workbench.

VSCode settings.json:

{
  "editor.tabSize": 2,
  "[html]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint",
    "editor.formatOnSave": true
  },
  "eslint.format.enable": true,
  "eslint.options": {
    "extensions": [".ts", ".html"]
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html"
  ],
  "eslint.workingDirectories": ["PROJECT_DIR"],
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": false
  },
  "prettier.configPath": "PROJECT_DIR/.prettierrc.json"
}

ESLint configuration PROJECT_DIR/.eslint.json:

{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "parserOptions": {
        "createDefaultProgram": true
      },
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
        "plugin:prettier/recommended"
      ],
      "rules": {
        /**
         * Any TypeScript source code (NOT TEMPLATE) related rules you wish to use/reconfigure over and above the
         * recommended set provided by the @angular-eslint project would go here.
         */
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "app",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "app",
            "style": "kebab-case"
          }
        ],
        /**
         * Always require explicit accessibility keyword except for constructors.
         */
        "@typescript-eslint/explicit-member-accessibility": [
          "error",
          {
            "accessibility": "explicit",
            "overrides": {
              "constructors": "no-public"
            }
          }
        ],
        "@typescript-eslint/typedef": [
          "error",
          {
            "arrayDestructuring": true,
            "arrowParameter": true,
            "memberVariableDeclaration": true,
            "objectDestructuring": true,
            "parameter": true,
            "propertyDeclaration": true,
            "variableDeclaration": true,
            "variableDeclarationIgnoreFunction": true
          }
        ],
        /**
         * Generate a warning on unused variables, except for a few known names
         * and names starting with an underscore.
         */
        "no-unused-vars": "off",
        "@typescript-eslint/no-unused-vars": [
          "warn",
          {
            "vars": "all",
            "varsIgnorePattern": "^(_hostClass)$",
            "args": "all",
            "argsIgnorePattern": "^_"
          }
        ],
        /**
         * Do not allow the var keyword, use const or let instead.
         */
        "no-var": "error",
        /**
         * Do not allow useless constructors. Disable the ESLint base rule
         * as it can report incorrect errors.
         */
        "no-useless-constructor": "off",
        "@typescript-eslint/no-useless-constructor": ["error"],
        /**
         * Do not allow empty functions. Disable the ESLint base rule
         * as it can report incorrect errors.
         */
        "no-empty-function": "off",
        "@typescript-eslint/no-empty-function": ["error"],
        /**
         * Do not allow empty interfaces.
         */
        "@typescript-eslint/no-empty-interface": "error"
      }
    },
    // NOTE: WE ARE NOT APPLYING PRETTIER IN THIS OVERRIDE, ONLY @ANGULAR-ESLINT/TEMPLATE
    {
      "files": ["*.html"],
      "extends": ["plugin:@angular-eslint/template/recommended"],
      "rules": {}
    },
    // NOTE: WE ARE NOT APPLYING @ANGULAR-ESLINT/TEMPLATE IN THIS OVERRIDE, ONLY PRETTIER
    {
      "files": ["*.html"],
      "excludedFiles": ["*inline-template-*.component.html"],
      "extends": ["plugin:prettier/recommended"],
      "rules": {
        // NOTE: WE ARE OVERRIDING THE DEFAULT CONFIG TO ALWAYS SET THE PARSER TO ANGULAR (SEE BELOW)
        "prettier/prettier": [
          "error",
          {
            "parser": "angular"
          }
        ]
      }
    }
  ]
}

Prettier configuration PROJECT_DIR/.prettierrc.json:

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "useTabs": false,
  "printWidth": 100,
  "endOfLine": "auto",
  "overrides": [
    {
      "files": ["*.component.html"],
      "options": {
        "parser": "angular",
        "htmlWhitespaceSensitivity": "strict"
      }
    },
    {
      "files": ["*.html"],
      "options": {
        "singleQuote": false,
        "htmlWhitespaceSensitivity": "ignore"
      }
    },
    {
      "files": ["*.scss", "*.css"],
      "options": {
        "singleQuote": false
      }
    }
  ]
}
Kees de Bruin
  • 175
  • 2
  • 16

0 Answers0