Edited at

Class Propertiesを使ったReactコンポーネントをESLintで静的検証

More than 3 years have passed since last update.

ある程度Babelとか知ってる人向きです。


要約


  1. Class Property DeclarationsでReactのPropTypes指定が捗る

  2. ESLintそのままではClass Property Declarationsに対応してない

  3. babel-eslintパーサ使えば解決(ただしestraverse-fbは手動で入れろ)

以上。estraverse-fb周りはそのうち修正されそう。


Class Property DeclarationsでReactのPropTypes指定が捗る

ES.nextのClass Property Declarationsでクラスにプロパティ生やせるよう提案されている。

それを使えばReactのpropTypes指定が捗るようになる。やったぜ。


MyButton.jsx

import React from 'react';

export default class MyButton extends React.Component {
static propTypes = {
onClick: React.PropTypes.func.isRequired
};

handleClick(){
this.props.onClick();
}
render(){
return (
<button onClick={::this.handleClick}>ClickMe!</button>
);
}
}


使うにはBabelのstage-2stage-1以上の指定が必要。

(イベントハンドラってbindする必要あったんだっけ…?)


ESLintそのままではClass Property Declarationsに対応してない

ESLintは2.3.0からES7に対応したらしい。

ES7(ES2016)で新たに入るのは以下の二つのみ。


  • Array.prototype.includes

  • Exponentiation Operator(**)

仕様は以下から見られる。

ES7が固まった以上、昔ES7 async/awaitなんて言ってたりしたのはもはや誤用なのだが、

Class Property Declarationsが入ると勘違いして試してコケた。

誤用については以下のリンクも参考になる。

※指摘をいただいたので上記を修正


.eslintrc

{

"parserOptions": {
"ecmaVersion": 7
},
"env": {
"browser": true,
"es6": true
},
"ecmaFeatures": {
"classes": true,
"jsx": true
},
"plugins": [
"react"
],
"rules": {
//...
}
}

$ eslint MyButton.jsx

/src/MyButton.jsx
4:20 error Parsing error: Unexpected token =

/(^o^)\


babel-eslintパーサ使えば解決

というわけで、babel-eslintを入れよう。

babelパーサならES.nextの仕様のコードでも読んでくれる。

パーサ自体がbabel用なためか、いろいろオプション抜いても平気で動く。

$ npm i -D babel-eslint


.eslintrc

{

"parser": "babel-eslint",
"env": {
"browser": true
},
"plugins": [
"react"
],
"rules": {
//...
}
}

ここで、estraverse-fbが存在しないと怒られたら手動で入れる

$ npm i -D estraverse-fb

estraverse-fbは2.3.0から利用しなくなったESLintの依存モジュールらしい。

babel-eslintがESLintのためにそれを読んでるとかなんとか。

上のリンクでそのうちbabel-eslintが対応すると思うので-Dは付けてないよ、ってコメントあるけど、

めんどいので-Dつけたよ。お好みで。


ビルド環境

実際はeslint直接ぶっ叩いてるわけじゃなくてwebpackのpreLoaderとして使ってる。

現状の趣味プロジェクトのビルド環境晒しとく。多分そのうち変わる。

しかし最近のjs環境って本当にカオスですよねー

みんなHaxe使えばいいのに


webpack.config.js

const path = require('path');

module.exports = [{
entry: {
bundle: './src/entry.jsx'
},
resolve: {
extensions: ['', '.js', '.jsx']
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
preLoaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'eslint-loader'
}
],
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.jade$/,
loader: 'jade-react-loader'
}
]
}
}];



.eslintrc

{

"parser": "babel-eslint",
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"env": {
"browser": true
},
"plugins": [
"react"
],
"rules": {
"indent": [
"error",
2
],
"quotes": [
"error",
"single"
],
"linebreak-style": [
"error",
"unix"
],
"semi": "error",
"no-unused-vars": "warn",
"no-console": "warn"
}
}


.babelrc

{

"presets": ["es2015", "stage-0", "react"]
}