ããã«ã¡ã¯ãããã³ãã¨ã³ãã¨ã³ã¸ãã¢ã®ä½ã æ¨ã§ãã
å æ¥ãESLintã¾ãããææ°ã«ã¢ãããã¼ãããã®ã§ãããçµæ§ä¿®æ£ãããã¨ãå¤ãã£ãã®ã§åå¿é²ãå ¼ãã¦æ®ãã¦ããã¾ãã
ã¡ãã£ã¨å¤ãã§ãããESLintã®å°å ¥ã«é¢ãã¦ã¯ãã¡ãã®è¨äºã§ãç´¹ä»ãã¾ããã
ESLint3ã¸ã®ã¢ãããã¼ã
ESLintå°å ¥æã«ã¯ä¸è¨ã®ãã¼ã¸ã§ã³ã使ã£ã¦ãã¾ããã
"eslint": "^2.9.0", "eslint-config-airbnb": "^9.0.1", "eslint-plugin-import": "^1.12.0", "eslint-plugin-jsx-a11y": "^1.2.0", "eslint-plugin-react": "^5.1.1", "babel-eslint": "^6.0.4",
ããããææ°ã®ãã¼ã¸ã§ã³ã調ã¹ã¦ã¤ã³ã¹ãã¼ã«ãã¾ããã
"eslint": "^3.8.1", "eslint-config-airbnb": "^12.0.0", "eslint-plugin-import": "^1.16.0", "eslint-plugin-jsx-a11y": "^2.2.2", "eslint-plugin-react": "^6.4.1", "babel-eslint": "^7.0.0",
ãã®ç¶æ
ã§ESLintãèµ°ããã㨠â 108 problems (99 errors, 9 warnings)
ã¨ãçµæ§ãªæ°ã®ã¨ã©ã¼ãçºçãã¦ãã¾ãã¾ããã
ãã¼ã¸ã§ã³ãä¸ããå㯠0 errors
ã ã£ãã®ã§ãã¢ãããã¼ãã«ä¼´ã£ã¦ã«ã¼ã«ãå³æ ¼ã«ãªã£ã¦ããã®ããããã¾ãã
.eslintrc ã .eslintrc.yml ã«å¤æ
ESLint3ããè¨å®ãyamlã§æ¸ãããããªã®ã§æ¸ãæãã¾ããã
ããã¯å¥½ã¿ã®åé¡ã§ãããjsonãããyamlã®æ¹ãã¡ã³ãããããã§ãã
.eslintrc (å¤æ´å)
{ "parser": "babel-eslint", "extends": "airbnb", "env": { "browser": true, "node": true }, "globals": { "ga": true, "$": true, "I18n": true, "describe": true, "it": true, "before": true, "after": true }, "rules": { "new-cap": [2, { "capIsNewExceptions": ["Map", "List", "Set", "OrderedSet"] // ããã¯ImmutableJSç¨ã®è¨è¿° }] } }
.eslintrc.yml (å¤æ´å¾)
--- parser: babel-eslint extends: airbnb env: browser: true node: true globals: ga: true $: true I18n: true describe: true it: true before: true after: true rules: new-cap: [2, { capIsNewExceptions: [Map, List, Set, OrderedSet] }]
Lintããã¹ããããä¿®æ£
import/extensions
6:18 error Unexpected use of file extension "jsx" for "./views/base.jsx" import/extensions
ãã®ã¨ã©ã¼ã¯ import Base from './views/base.jsx'
ã®ããã« .jsx
ãèªã¿è¾¼ãã§ããç®æã§èµ·ãã¦ã¾ããã
eslint-plugin-import ãè¦ã㨠import/resolver ãå¿ è¦ã ã¨ããã®ã§ã¤ã³ã¹ãã¼ã«ãã¾ãã
$ npm i -D eslint-import-resolver-node # ãã webpack ã使ã£ã¦ããã eslint-import-resolver-webpack ãå¿ è¦ããã§ã
.eslintrc.yml ã«è¿½è¨ãã¾ãã
# rules ã¨åãã¤ã³ãã³ãã¬ãã«ã« settings ã追å settings: import/resolver: node
ããã§ESLintãèµ°ãããã° import/extensions ã®ã¨ã©ã¼ã¯åé¿ã§ãã¾ãã
class-methods-use-this
34:22 error Expected 'this' to be used by class method 'comonentWillUnmount' class-methods-use-this
ãã®ã¨ã©ã¼ã®å½è©²ç®æã¯æ¬¡ã®ãããªã³ã¼ãã«ãªã£ã¦ã¾ããã
class Base extends PureComponent { componentDidMount() { $(window).on('resize', throttle(this.handleResize, 200)); } comonentWillUnmount() { // NG $(window).off('resize'); } // 以ä¸ç¥ }
class methodã¨ãã¦å®ç¾©ãããã¡ã½ããã®ã¹ã³ã¼ãå
㧠this
ã¸ã®åç
§ãç¡ãã¨ã¨ã©ã¼ã«ãªãããã§ãã
componentDidMount ã§ã¤ãã³ããè³¼èªãã¦ãcomponentWillUnmount ã§ã¤ãã³ãã®è³¼èªã解é¤ããã³ã¼ããªã®ã§ããã以ä¸ç´ããããç¡ãã¨æã£ãã®ã§ warning ãåºãããã«å¤æ´ãã¾ããã
.eslintrc.yml ã® rules ã« class-methods-use-this: 1
ã追è¨ããã¨ã¨ã©ã¼ããè¦åã«å¤ããã¾ãã
æ ¹æ¬çã«è§£æ±ºããã«ã¯ãç¶æ ãæããªãJSX㯠é¢æ°ã¨ãã¦å®ç¾©ããã¨ããã§ãã
jsx-a11y/no-static-element-interactions
89:7 error Visible, non-interactive elements should not have mouse or keyboard event listeners jsx-a11y/no-static-element-interactions
ãã®ã¨ã©ã¼ã¯æ¬¡ã®ãããªJSXãæ¸ãã¨çºçãã¾ãã
render() { return <div onClick="this.handleClick" /> }
div
è¦ç´ ãªã©ã®æ¬æ¥ã¦ã¼ã¶ã¼ã®ã¤ã³ã¿ã©ã¯ã·ã§ã³ãæ³å®ãã¦ããªãè¦ç´ ã«å¯¾ãã¦ãonClick
ãªã©ã§ãã³ããªã³ã°ãããã¨ããã¨çºçãã¾ãã
ãã®å ´åã¯HTMLã®ãã¼ã¯ã¢ããä¸ã§ã¯ãªãã«ãã«ãªè¦ç´ ã¨ãããã¨ã示ããªãã¨ãããªãã®ã§ãdiv
ã®ä»£ããã« button
ãªã©ã§ä»£æ¿ããã®ãããã§ãããã
render() { return <button onClick="this.handleClick" /> }
react/forbid-prop-types
6:5 error Prop type `object` is forbidden react/forbid-prop-types
ãããä¸çªä¿®æ£ç®æãå¤ãã¨ã©ã¼ã§ããã
ãã®ã¨ã©ã¼ã¯ ReactComponent ã® propTypes
㧠React.PropTypes.array
ã React.PropTypes.object
ã使ã£ã¦ããã¨çºçãã¾ãã
props ã« React.PropTypes.array
ã¨æå®ããã¦ããArrayåãªã®ã¯ãããã¾ããé
åã®ä¸èº«ãä½ã®åãªã®ãããããã¾ããã
ä½ã®åãæã£ãé
åãªã®ããæ示çã«ç¤ºããªãã¨ã¨ã©ã¼ã«ãªãããã«ãªãã¾ããã
ãã®Lintããã¹ããã«ã¯ PropTypes.array
ã®ä»£ããã« PropTypes.arrayOf()
, PropTypes.object
ã®ä»£ããã« PropTypes.shape()
ã使ãã¾ãã
ãã®ãããã®æ¸ãæ¹ã¯æ¬å®¶ã®ããã¥ã¡ã³ããåèã«ãªãã¾ããã
Typechecking With PropTypes - React
ä¾ãã°æ¬¡ã®ãããªã³ã¼ããããå ´åã
// @file sites.jsx import React, { Component, PropTypes } from 'react'; export default class Sites extends Component { static propTypes = { sites: PropTypes.array.isRequired, // NG currentSite: PropTypes.object, // NG } // 以ä¸ç¥ }
ãã®ããã«ç´ãã¾ãã
// @file sites.jsx import React, { Component, PropTypes } from 'react'; export default class Sites extends Component { static propTypes = { sites: PropTypes.arrayOf(PropTypes.shape({ // OK name: PropTypes.string.isRequired, url: PropTypes.string, })).isRequired, currentSite: PropTypes.shape({ // OK name: PropTypes.string.isRequired, url: PropTypes.string, }), } // 以ä¸ç¥ }
currentSite
ã site
åã®ãªãã¸ã§ã¯ããå¤ã«æã¤ã¨ããã°ãsites
㯠site
åã®é
åãå¤ã«æã¤ã¨è¨ãããã§ãã
ä¸è¨ã®ã³ã¼ãã¯åé·ãªã®ã§ãåå®ç¾©ãã¾ã¨ãããã¡ã¤ã«ã constants/prop_types.js
ã¨ãã¦ä½ãã¾ããã
// @file prop_types.js import { PropTypes } from 'react'; export const site = PropTypes.shape({ name: PropTypes.string.isRequired, url: PropTypes.string, }); export sites = PropTypes.arrayOf(site);
åå®ç¾©ãã¡ã¤ã«ãåç §ããããã«å¤æ´ããã¨ãã£ãããã¾ãã
// @file sites.jsx import React, { Component } from 'react'; import * as pTypes from '../constants/prop_types'; export default class Sites extends Component { static propTypes = { sites: pTypes.sites.isRequired, currentSite: pTypes.site, } // 以ä¸ç¥ }
react/no-unused-prop-types
PropTypes.shape()
ãå
¨ã¦å¤é¨ã®ãã¡ã¤ã«(constants/prop_types.js
)ã§å®ç¾©ããã°ããã®ã§ããã.jsx
ãã¡ã¤ã«å
㧠PropTypes.shape()
ãå®ç¾©ããã¨ä»¥ä¸ã®ãããªã¨ã©ã¼ãåºã¾ãã
6:12 error 'item.key' PropType is defined but prop is never used react/no-unused-prop-types
ãã®ã¨ã©ã¼ãæå¶ããããã®ãªãã·ã§ã³ãç¨æããã¦ããã®ã§ã.eslintrc.yml ã® rules ã«ä¸è¨ã追è¨ãã¦ç¡å¹åãã¾ãããã
rules: react/no-unused-prop-types: [2, { skipShapeProps: true }]
Code Climate
ESLintã®ãã¼ã¸ã§ã³ã¢ããã«ä¼´ããCodeClimateã®è¨å®ã§å°ãæ©ãã ã®ã§æ¸ãã¦ããã¾ãã
CodeClimateä¸ã§å®è¡ããESLintã®ãã¼ã¸ã§ã³ã¯ããã©ã«ãã§1ç³»ã使ãããããã§ãã
Note:
If no channel is specified, ESLint v1.10.3 is used for analysis.
https://docs.codeclimate.com/docs/eslint
ã¨ãããã¨ã§ãESLint 3ç³»ã使ã£ã¦æ¬²ããå ´å㯠.codeclimate.yml
ã«è¿½è¨ãã¾ãã
--- engines: eslint: enabled: true channel: eslint-3 # ESLint3ç³» ã使ãããã«ãã
ã¡ãªã¿ã«ãã® eslint-3
ã§ä½¿ããã ESLint ã®ãã¼ã¸ã§ã³ã¯ç¾æç¹ã§ã¯ 3.6.1
ã§ããã
ããã§ãã¾ãè¡ãã¯ãã ã£ãã®ã§ããããã¼ã«ã«ã§ESLintãéã£ã¦ãã¦ã CodeClimate ä¸ã§ã¯ import/no-unresolved
ã®ã¨ã©ã¼ãåºãç¾è±¡ã«æ©ã¾ããã¾ããã
import/no-unresolved
ãåºã¦ããç®æ㯠import React from 'react';
ã®ããã«ãnpm module ãèªã¿è¾¼ãã§ããç®æã§ãåç
§å
ã絶対ãã¹ã§å§ã¾ãã¨ã¨ã©ã¼ã«ãªãããã§ããã
ããã¯CodeClimateä¸ã§ã®çµæãæ£ãããªãã®ã§ã.codeclimate.yml ã«ä¸è¨ã追å ãã¦ãã§ãã¯ãç¡å¹åãã¾ããã
--- engines: eslint: enabled: true channel: eslint-3 checks: import/no-unresolved: # ãã®ãã§ãã¯ãç¡å¹åãã enabled: false
ã¾ã¨ã
ESLintã¾ãããææ°ã«ã¢ãããã¼ããã¦çºçããã¨ã©ã¼ãç¡ããã¦ããéç¨ããç´¹ä»ãã¾ããã
è¨äºãé·ããªãã®ã§ç´¹ä»ã§ããªãã£ãã«ã¼ã«ããã£ãã®ã§ãããReact ã使ããªã eslint-config-airbnb
ã¯å
¥ãã¦ãããã»ããããã¨æ¹ãã¦æãã¾ããã
ãã®Lintè¨å®ã§æ¸ãã¦ããã°æä½éã®ã³ã¼ãã®å質ã¯ä¿ã¦ãã¨æãã¾ãã
Wondershake ã§ã¯ Web ã¨ã³ã¸ãã¢ã iOSãAndroid ãã£ããããã¼ãåéãã¦ãã¾ãã èå³ã湧ããæ¹ã¯æ¯éãã¡ããããå¿åä¸ããï¼