How I Resolved Vue.js, VSCode, Vetur, Prettyhtml, and Prettier Formatting and ES Lint Issues

I spent way too much time this weekend debugging Vue.js - Vetur - Prettyhtml - Prettier - Beautify - Eslint issues.

Here’s what I discovered:

By default, Vetur (The VS Code Vue.js Extension) uses Prettyhtml as the default html formatter. Which wraps Prettier and adds a bunch of formatting on top of it. Vetur uses Prettyhtml, which wraps Prettier. I chose ES Lint + Prettier when I created my project…given all that I went through to get this right, you might want to try to use the ESLint + Standard settings instead.

Here’s a video that I created to walk you through all of this:

Scroll down to the bottom of this post for the VS Code and ES Lint settings I ended up using.

Here are the issues with Prettyhtml, Prettier and VS Code.

  1. Prettyhtml removes all whitespace, even the whitespace you need, and there’s no way to disable it (from what I could find).
  2. Prettier always wraps attributes and there’s no way to disable it.
  3. Prettyhtml allows you to disable the wrap attributes, but removes all whitespace.

So, when open a .vue file in VS Code and Format Document with Vetur, it uses Prettyhtml by default, which violates prettier ES Lint rules.

For example,

When you open App.vue, you get this…notice the space after router-view and the space after router-link>

<template>
 <div id="app">
  <div id="nav">
   <router-link to="/">Home</router-link> |
   <router-link to="/about">About</router-link>
  </div>
  <router-view />
 </div>
</template>

Then you format the doc and get this:

<template>
 <div id="app">
  <div id="nav">
   <router-link to="/">Home</router-link>|
   <router-link to="/about">About</router-link>
  </div>
  <router-view/>
 </div>
</template>

As you can see, those spaces are removed after router-view and /router-link>

This is bad for me. I need the first space for proper formatting and I prefer the whitespace before the self-closing bracket.

Then when you run yarn serve you get this warning.

Module Warning (from ./node_modules/eslint-loader/index.js):
warning: Insert `·` (prettier/prettier) at src\App.vue:7:17:
  5 |    <router-link to="/about">About</router-link>
  6 |   </div>
> 7 |   <router-view/>
   |         ^
  8 |  </div>
  9 | </template>
 10 |

I searched for many hours and could not find a way to tell Prettyhtml to preserve those spaces. I could tell ES Lint to ignore those rules, but I want those spaces.

So, I had to abandon Prettyhtml.

I tried to go with Prettier directly using this VS Code setting:

"vetur.format.defaultFormatter.html": "prettier"

(VS Code will tell you it is not a valid setting, but it actually runs Prettier)

But Prettier always wraps attributes and there’s no way to tell it to not do that.

For example, Prettier, will turn this:

<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.

Into this:

<a
  href="https://cli.vuejs.org"
  target="_blank"
  rel="noopener"
  >vue-cli documentation</a>.

Which, to me, is not desirable. I have a huge monitor and don’t want to wrap every single attribute.

From what I could find, there’s no way to tell Prettier to not wrap.

So, I had to abandon Prettier as well.

Here’s where I’m at at this point:

  • Prettyhtml is out because it strips all whitespace - without a way to disable.
  • Prettier is out because it always wraps attributes - without a way to disable it. Prettyhtml can disable the attribute wrap, but it removes all whitespace.

I decided that I want to continue to use Vetur (The VS Code Extension) and the the only remaining option is js-beautify-html instead of Prettyhtml or Prettier.

I added this setting:

"vetur.format.defaultFormatter.html": "js-beautify-html"

Did a format…and whitespace was retained in my .vue files, but attributes were still being wrapped, so I dug and found the wrap_attributes settings and added this:

{
  "vetur.format.defaultFormatter.html": "js-beautify-html",
  "vetur.format.defaultFormatterOptions": {
    "js-beautify-html": {
     "wrap_attributes": "auto"
    }
   }
}

And that setting disables wrap_attributes.

So, I’m golden with Formatting…now onto ES Lint issues.

Then I run yarn serve and get this:

warning: Replace `·href="https://github.com/vuejs/awesome-vue"·target="_blank"·rel="noopener">awesome-vue</a` with `⏎··········href="https://github.com/vuejs/awesome-vue"⏎··········target="_blank"⏎··········rel="noopener"⏎··········>awesome-vue</a⏎········` (prettier/prettier) at src\components\HelloWorld.vue:63:11:
 61 |    </li>
 62 |    <li>
> 63 |     <a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a>
   |      ^
 64 |    </li>
 65 |   </ul>
 66 |  </div>

The issue is that ES Lint is configured to use Prettier, which wants attributes on new lines.

I could have disabled that setting, but I’m now using es-beautifer, so I better switch to the ES Lint rules for that.

I found the ES Lint beautifier plugin here: https://github.com/dai-shi/es-beautifier#usage-eslint-plugin

And I installed it:

yarn add eslint eslint-plugin-es-beautifier

And then opened .eslintrc.js and added that plugin:

plugins: ["es-beautifier"],
extends: ["plugin:vue/essential", "plugin:es-beautifier/standard", "@vue/typescript"],

Re-ran yarn serve

And got this error:

error: Missing trailing comma (comma-dangle) at src\views\Home.vue:15:4:
 13 |  components: &#123;
 14 |   HelloWorld
> 15 |  &#125;
   |  ^
 16 | &#125;)
 17 | export default class Home extends Vue &#123;&#125;
 18 | </script>

It’s looking for a dangling comman, which I don’t want to add to my code, so I disable it with this:

rules: {
  "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
  "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
  "comma-dangle": ["error", "never"]
 },

I re-run yarn serve and get this:

error: Expected linebreaks to be 'LF' but found 'CRLF' (linebreak-style) at src\main.ts:13:19:
 11 |  store,
 12 |  render: h => h(App)
> 13 | &#125;).$mount("#app");
   |          ^
 14 |

It is checking for consistent linebreaks. At this point, I don’t really care, so I just disable that rule with this:

 rules: {
  "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
  "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
  "comma-dangle": ["error", "never"],
  "linebreak-style": "off"
 },

AND BOOM!

DONE Compiled successfully in 5661ms                                                                              10:20:40 AM
No type errors found
Version: typescript 3.2.4
Time: 3210ms

 App running at:
 - Local:  http://localhost:8080/
 - Network: unavailable

 Note that the development build is not optimized.
 To create a production build, run yarn build.

As I went about creating more sophisticated TypeScript, the default Prettier formatter didn’t work for me there either, so I swapped that out with this:

  "vetur.format.defaultFormatter.ts": "vscode-typescript"

I now finally have .vue files being formatted correctly (at least the way I want them to be) and ES Lint passing successfully.

Now, time to get to the fun part. Actually coding the app.

I REALLY hope this post saves you some time.

Jon

Here are the files:

.eslintrc.js

module.exports = {
 root: true,
 env: {
  node: true
 },
 plugins: ["es-beautifier"],
 extends: ["plugin:vue/essential", "plugin:es-beautifier/standard", "@vue/typescript"],
 rules: {
  "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
  "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
  "comma-dangle": ["error", "never"],
  "linebreak-style": "off"
 },
 parserOptions: {
  parser: "@typescript-eslint/parser"
 }
};

VS Code User Settings

{
  "window.zoomLevel": 2,
  "vetur.format.defaultFormatter.html": "js-beautify-html",
  "vetur.format.defaultFormatterOptions": {
    "js-beautify-html": {
      "wrap_attributes": "auto"
    }
  },
  "vetur.format.defaultFormatter.ts": "vscode-typescript"
}