【ネタ】<img /> と書いてESLintに怒られたValid HTMLおじさんの話
ESLintとPrettierが喧嘩してる
最近、個人でちょっとしたプロダクトを作ろうとしている。
モックをせっせと作っていたら、ESLint に怒られた。
<img /> を <img> に直せ、と。
「え、そうなん?」と思いつつ直したら、
保存した瞬間、Prettierが、即座に「/」を生やした。
……え、なにこれ。
20年前、僕は Webデザインの世界にいた
怒られるまで、僕は「正しいHTMLってこうだよね」と思ってた。
<img />
<br />
……かれこれ20年間。
20年前は、
いまみたいにフレームワークもなければ、
ビルドもトランスパイルもなかった。
代わりにあったのが、
- XHTML
- W3C Validator
- 「Valid」っていう緑の文字
HTMLを書いて、
CSSを書いて、
最後に Validator に投げる。
エラーが0になるまで直す。
「Valid を出すこと」に、なぜか燃えてた。
そして、有名なサイトをこっそりValidatorにかけて、
Invalidじゃねえかw
と優越感に浸ったこともある。
たぶん、同世代には心当たりがあるはず。やってたでしょ?
今思うと、かなりしょうもない。
でも当時は本気だった。
-
<img />を忘れたらエラー -
<br />も忘れたらエラー - warning が1個でも出ると落ち着かない
だから <img /> は、
身体に染みついた書き方になった。
時代は HTML5 に進んだ
HTML5の存在はもちろん知っていた。
ただ、システムエンジニアの世界に来ていたので、
正直、フロントエンドの世界は浦島太郎。
今や職業エンジニアでもなくて、ただの経営企画のおじさんになった。
気持ち悪いのでちゃんと調べてみたら衝撃だった。
HTML5 では、
<img>
<br>
が正解らしい。
<img /> は
エラーではないけど、
スラッシュに意味はない。
ただ、無視されるだけ。
けんかをやめて ふたりをとめて
わたしのためにあらそわないで もうこれいじょう
今回、僕が直面した問題は、単純に言えばこういうこと。
-
ESLint は言う
「HTML的に正しいのは<img>」 -
Prettier は言う
「Vueっぽいし<img />でしょ」
どっちも間違ってない。
結論としては、どっちでもいい。
-
<img>でも -
<img />でも
動くならOK。
20年前に、他人が書いていたHTMLをあざ笑っていた僕にはわかる。
大事なのは、
ツール同士が喧嘩しないこと
人が決めると揉めるから、
ESLint と Prettier に決めさせる。
それがいちばん平和。
僕は、VS Codeのsettings.jsonをそっと修正した。
{
// VueはESLintが整形の最終決定権を持つ(Prettierに戻されない)
"[vue]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
// TS/JSはPrettierでOK
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
// ESLint拡張の基本設定
"eslint.useFlatConfig": true,
"eslint.validate": [
"javascript",
"typescript",
"vue"
],
"eslint.format.enable": true,
"files.eol": "\n"
}
おわり。
終わったつもりが終わってなかった(最終決着)
ぜんぶ、dbaeumer.vscode-eslintに寄せれば良いのでは…。
ということで、もう少し、修正を続けた。
pnpm add -D @stylistic/eslint-plugin
// eslint.config.mjs
import withNuxt from './.nuxt/eslint.config.mjs'
import stylistic from '@stylistic/eslint-plugin'
export default withNuxt(
stylistic.configs.customize({
semi: false,
quotes: 'single',
indent: 2,
jsx: false,
}),
)
{
// ESLint(コード)はESLintに任せる
"eslint.useFlatConfig": true,
"eslint.validate": [
"javascript",
"typescript",
"vue"
],
"eslint.format.enable": true,
// --- Code (ESLint) ---
"[vue]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
// --- Docs/Config (Prettier) ---
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"files.eol": "\n"
}
MarkdownとJSONだけは引き続きPrettierさんにがんばってもらうことにしました。
今度こそ ー完ー 。
次回予告:<br /> も怒られるのか?(書きません)
Discussion