シャッフル主任の進捗報告

興味のあるものを作ります。進捗を不定期にご報告します。

そうだ、教祖になろう。出エジプト記 第4章4節 ESLint設定を整える

我々の街と塔を作ろう。塔の先が天に届くほどの。


これまで第4章1節 Cloud9にVue.js開発環境を導入するなどでトランスパイラに触れました。
Vue.jsのトランスパイラは「Babel」です。
BabelはJavaScriptトランスパイラで、本家サイトでは「次世代JavaScriptコンパイラ」と説明されています。

github.com

"babble"と発音するらしいです。

見よ、その塔はシナルの地、アッシリアとバビロンの中間にあった。


第4章3節 Vue.jsでAjax通信するで出たWarningを解消するのに、このBabelが関係してきます。

Vue.jsプロジェクトをトランスパイルする前にESLintというリンタを使います。
リンタとは、プログラムソースがコーディング規約に則しているかチェックするツールです。
vue create プロジェクト名でVue CLIを導入したときにESLint + Prettierを導入しました。
Prettierはコードフォーマッターです。
登場人物が多いですね。

Prettierはコーディング規約に沿うようにコードを整形してくれます。
ESLintもオプションでコードを整形できますが、Prettierの方が簡単により見やすくしてくれるといったところです。

とりあえずPrettierのことはおいといて、ESLintを使ってWarningが出ないようにコードを修正していきます。
参考にさせていただいたサイトです。

qiita.com

.vueファイルでESLintが利くようにeslint-config-vueパッケージを導入します。
参考サイトにある他のパッケージはvue createで入っているようです。

$ cd clientside/
$ yarn add -D eslint-config-vue
$ ./node_modules/.bin/eslint src/**/*.js src/**/*.vue 

デフォルト設定だとこんな感じの結果になります。

f:id:chief-shuffle:20191223191031j:plain

設定をカスタマイズするため、プロジェクト直下に'.eslintrc.js`というファイルを作ります。
Vueの推奨設定を読み込みます。

module.exports = {
  "extends": ["vue", "plugin:vue/recommended"],
  "env": {
    "browser": true
  },
  "rules": {}
}

これで走らせると80個ほどエラーと警告が出ます。

f:id:chief-shuffle:20191223191421j:plain

f:id:chief-shuffle:20191223191410j:plain

1つずつエラーを確認しながら修正したいので、今出たエラーと警告の右側に表示されているルール名を全部”rules”"off"で追加します。

module.exports = {
  "extends": ["vue", "plugin:vue/recommended"],
  "env": {
    "browser": true
  },
  "rules": {
    "indent": "off",
    "semi": "off",
    "space-in-parens": "off",
    "quotes": "off",
    "vue/html-closing-bracket-newline": "off",
    "vue/html-indent": "off",
    "vue/html-self-closing": "off",
    "vue/max-attributes-per-line": "off",
    "vue/name-property-casing": "off",
    "vue/require-default-prop": "off",
  }
}

一回走らせます。

f:id:chief-shuffle:20191223191722j:plain

まだ1つ出てます。
これは”rules”では消せないのでbabel-eslintで抑止します。
babel-eslintはESLintのためのBabel Parserのラッパーです。
Babel ParserはBabelの中にあるJavaScriptパーサーで、昔はBabylonと呼ばれていたそうです。
いよいよ混乱してきましたね。

"parserOptions"を追加してもう一回走らせます。

module.exports = {"parserOptions": {
    "parser": "babel-eslint",
  },
  …
}

babel-eslintはデフォルトで"allowImportExportEverywhere": falseになっており、Importに対するエラーが消えます。
これで一旦エラーがない状態になりました。

その為に、この街はバベルと名付けられた。


では、ルールを1つずつ修正していきたいと思います。
デフォルト以外にしたい設定は"off"の部分を修正したい設定値に変えていきます。

インデントは2にしてみます。

    "indent": "indent": ["error", 2],

リントします。

$ ./node_modules/.bin/eslint src/**/*.js src/**/*.vue

f:id:chief-shuffle:20191223192731j:plain

17か所エラーになってますね。
--fixオプションをつけて修正します。

$ ./node_modules/.bin/eslint --fix src/**/*.js src/**/*.vue

これが、

...
const routes = [{
    path: "/",
    name: "home",
    component: Home
  },
...

こうなりました。

...
const routes = [{
  path: "/",
  name: "home",
  component: Home
},
...

続いて、セミコロンをなしにします。

    "semi": ["error", "never"],
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";

import Vue from "vue"
import VueRouter from "vue-router"
import Home from "../views/Home.vue"

どんどんいきます。
好き嫌いが分かれるでしょうが、複数行の場合の末尾のカンマを必須にします。

    "comma-dangle": ["error", "always-multiline"],
export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
})

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {},
})

()の内側のスペースなしはデフォルトなのでこの行は消します。

    "space-in-parens": "off",
  component: () =>
    import ( /* webpackChunkName: "about" */ "../views/About.vue"),

  component: () =>
      import (/* webpackChunkName: "about" */ "../views/About.vue"),

クォーテーションはダブルクォーテーション。

    "quotes": ["error", "double"],

これはエラーなし。シングルクォーテーションはなかったようです。

ここから.vueファイルのHTML部分の規則です。
HTMLの>位置はデフォルトなので削除。

    "vue/html-closing-bracket-newline": "off",
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener"
        >vue-cli documentation</a
      >.

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

HTML属性位置はデフォルトなので削除。

    "vue/max-attributes-per-line": "off",
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.

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

HTMLのインデントもデフォルトで削除。

    "vue/html-indent": "off",
      <li>
        <a
          href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
          target="_blank"
          rel="noopener"
          >babel</a>
      </li>

↓ 微妙!(>が2つ上がってる)

      <li>
        <a
          href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
          target="_blank"
          rel="noopener"
        >babel</a>
      </li>

HTMLタグの閉じタグ許可。

    "vue/html-self-closing": ["error", {
      "html": { "normal": "always", "void": "always", "component": "always" },
    }],

これもエラーなし。

プロパティ名はデフォルト(キャメルケース)なので削除。

    "vue/name-property-casing": "off",
  name: "ajax",

  name: "Ajax",

プロパティのrequired属性必須はデフォルトなので削除。

    "vue/require-default-prop": "off",
  props: {
    msg: String
  }

あれ、これは--fixで修正されないようです。
手で修正。

    msg: {
      type: String,
      required: true
    }

最終的に.eslintrc.jsはこうです。

module.exports = {
  "extends": ["vue", "plugin:vue/recommended"],
  "env": {
    "browser": true
  },
  "parserOptions": {
    "parser": "babel-eslint",
  },
  "rules": {
    "comma-dangle": ["error", "always"],
    "indent": ["error", 2],
    "semi": ["error", "never"],
    "quotes": ["error", "double"],
    "vue/html-self-closing": ["error", {
      "html": { "normal": "always", "void": "always", "component": "always" },
    }],
  }
}

主がそこで、全地の言葉を乱し、そこから人を全地に散らされたからである。


手間はかかりましたが一応設定が整いましたね。
では改めてsrc/router/index.jsをいじって保存すると、

f:id:chief-shuffle:20191223203729j:plain

おっと、設定と異なるインデントで自動フォーマットされてエラーが大量発生しますね。
あれ、これ涙?

Cloud9のJSBeutify設定とESLintの設定が合っていないからっぽいです。
ので、保存時にESLintを使ってフォーマットしてくれるように変更します。

PreferenceのProject Setting > JavaScript Support > Custom Code Formatterの欄に以下のコマンドを設定します。
保存したファイルのディレクトリに下りてって、eslint --fixを実行するコマンドです。
npmのパッケージ実行コマンドであるnpxで起動しています。
うまく動いてないときのトレースのためにホームディレクトリにログファイルを吐いてます。

cd `dirname "$file"`;npx eslint --fix `basename "$file"` &> ~/eslint.log

これでsrc/router/index.jsを保存しなおすと、エラーがなくなりました。
npxを起動するのが遅いのか、若干タイムラグがあるのが気になります。

Cloud9にJavaScriptと認識されてるファイルでしかフォーマッタのトリガがかからないので、.vueファイルのエディタの右下にあるファイル種別を「JavaScript」に変更します。

これで保存時に.vueファイルもフォーマットされますが、JavaScriptファイルには存在しない<script>のあたりでParsing errorが発生します。
ので、Hints&Warning > Ignore Messages Matchingの欄にParsing errorと入力して無視します。
これやっちゃうと例えば<script>が抜けたりしてもコーディング時点では分からないのですが、ビルド時点で気づけるのでまあよしとしましょう。

ついでに、Mark missing semicolonsを外してエディタの横に出ている「i」マークが表示されないようにします。

見よ、人の子らはシナルの地に自分たちの都市と塔を建てようというそのふとどきなはかりごとのゆえに邪悪になった。


なんとかESLintの設定を整えてきたのですが、はっきり言って結構手間です。
Prettierだともちょっと楽に設定できるようで、今後のためにPrettier設定をESLintに読ませる形に全面見直したいと思います。
つまり、今の設定はほぼ全部壊します。

大変参考になりました。

qiita.com

修正した.eslintrc.jsがこれ。
ESLint、Prettier、Vueの推奨を利用。

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: 'babel-eslint',
    sourceType: 'module',
  },
  env: {
    browser: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:prettier/recommended',
    'plugin:vue/recommended',
    'prettier/vue',
  ],
  rules: {},
}

新規作成したPrettierの設定である.prettierrc.jsがこれ。
シングルクォーテーションにしました。

module.exports = {
  printWidth: 80,
  tabWidth: 2,
  singleQuote: true,
  semi: false,
  trailingComma: 'es5',
  bracketSpacing: false,
}

上記の2ファイルも整形できるよう、PreferenceのCustom Code Formatterに--ignore-patternオプションを追加しました。
デフォルトだと対象外のファイルもリントしてくれます。

cd `dirname "$file"`;npx eslint --fix `basename "$file"` --ignore-pattern '!.*.js' &> ~/eslint.log

苦労しましたが、これでやっとESLint設定が整いました。

次回はHTMLをスッキリ書けるPugを入れたかったので、またフォーマッタ設定を変えないといけないんかいとビクビクでしたが、色々サポートが利かなくなるし、特段コーディング量も減らないし、あまりメリットがなさそうなのでやめておこうと思います。

qiita.com

2019/12/27追記

Pugをちょっと入れてみましたが、eslint --fixが利かないようで断念しました。残念。

yarn add -D pug pug-plain-loader eslint-plugin-pug
  plugins: ['pug'],
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Welcome to Your Vue.js App" />
  </div>
</template>

<template lang="pug">
  div.home
    img(alt="Vue logo" src="../assets/logo.png"
    HelloWorld(msg="Welcome to Your Vue.js App")
</template>