あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

React Native+ExpoでESLintとPrettierを使いコードチェックとフォーマット整形を行う方法

はじめに

React Native + Expo の開発を行おうとして、そういえば React のプロジェクトで ESLint と Prettier を導入したけど定型化しておらず再利用ができないなぁと思いました。

じゃあ、React プロジェクトの ESLint と Prettier の設定を React Native + Expo に持ってくればよいのではないかと思い、この記事でその設定を公開します。

もちろん、React から持ってくるので、この設定は React でも活用できると思います。(一部パスが違いますが、そこは注記します)

Mac での環境構築ですが、Windows でも問題ないでしょう。(React はともかく、React Native + Expo を Windows で開発する人は少ないでしょうが。できますけどね?)

なお、プロジェクトの言語は TypeScript になります。

プロジェクト設定概要

このプロジェクト設定は、Visual Studio Code で開発を前提としています。

プログラムを書いているときに、リアルタイムで ESLint でコードチェックを行いエラーを表示し、ファイルの保存時に Prettier で自動で(強制的に)コードフォーマットをかけます。

また、Git のコミット時には、husky を使用して pre-commit のイベントで Lint のチェックをかけて不正なコードがコミットされないようにします。

この設定をプロジェクトで共有すれば、コードの品質を向上されることができるでしょう。

なお、TSLint はすでに時代にそぐわないのでご注意ください。

qiita.com

Expo プロジェクトの作成

React Native + Expo のプロジェクトを作成します。

$ expo init config-test

プロジェクトのテンプレートは、TypeScript を使用するので TypeScript を選択します。

? Choose a template: 
  ----- Managed workflow -----
  blank                 a minimal app as clean as an empty canvas 
❯ blank (TypeScript)    same as blank but with TypeScript configuration 
  tabs                  several example screens and tabs using react-navigation 
  ----- Bare workflow -----
  minimal               bare and minimal, just the essentials to get you started
 
  minimal (TypeScript)  same as minimal but with TypeScript configuration 

プロジェクト名は今回はなんでもいいので、「test」としました。

  Read more: https://docs.expo.io/versions/latest/workflow/configuration/ › 100% completed
  {
    "expo": {
      "name": "test",
      "slug": "config-test"
    }
  }

Yarn をパッケージマネージャとして使用するか聞いてくるので、Y を選択します。

? Yarn v1.17.3 found. Use Yarn to install dependencies? (Y/n) Y

するとプロジェクトの雛形が作成されるので、コミットします。

$ cd config-test
$ git add .
$ git commit -m "initial commit"

関連パッケージのインストール

Yarn で関連パッケージを一気にインストールします。

$ yarn add -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-jest eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-prefer-arrow eslint-plugin-import husky lint-staged

プロジェクト設定

プロジェクトのルートに.vscodeというフォルダを作成し、その下に以下の2ファイルを作成します。

.vscode/.extensions.json

{
    "recommendations": [
      "coenraads.bracket-pair-colorizer",
      "dbaeumer.vscode-eslint",
      "esbenp.prettier-vscode",
      "msjsdiag.debugger-for-chrome",
      "ms-vsliveshare.vsliveshare",
      "oderwat.indent-rainbow",
      "shinnn.stylelint",
      "VisualStudioExptTeam.vscodeintellicode",
      "vscode-icons-team.vscode-icons"
    ],
    "unwantedRecommendations": []
  }

.vscode/settings.json

{
  "css.validate": true,
  "editor.formatOnSave": false,
  "eslint.autoFixOnSave": true,
  "eslint.enable": true,
  "eslint.validate": [
    {
      "language": "javascript",
      "autoFix": true
    },
    {
      "language": "javascriptreact",
      "autoFix": true
    },
    {
      "language": "typescript",
      "autoFix": true
    },
    {
      "language": "typescriptreact",
      "autoFix": true
    }
  ],
  "files.insertFinalNewline": true,
  "files.trimTrailingWhitespace": true,
  "stylelint.enable": false,
  "typescript.tsserver.trace": "verbose",
  "[markdown]": {
    "files.trimTrailingWhitespace": false
  }
}

.eslintrc.js の設定

プロジェクトのルートに.eslintrc.jsファイルを作成します。

.eslintrc.js

module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true,
  },
  extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],

  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    project: './tsconfig.json',
    sourceType: 'module',
  },
  plugins: [
    '@typescript-eslint',
    'jest',
    'prettier',
    'prefer-arrow',
    'react',
    'react-hooks',
  ],
  root: true,
  settings: {
    'import/resolver': {
      node: {
        extensions: ['.js', 'jsx', '.ts', '.tsx'],
      },
    },
  },
  rules: {
    // eslint official
    'newline-before-return': 'error',
    'no-console': 'warn',
    'require-yield': 'error',

    // @typescript-eslint
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-member-accessibility': 'off',
    indent: 'off',
    '@typescript-eslint/indent': 'off',
    '@typescript-eslint/no-unnecessary-type-assertion': 'error',

    // prefer-arrow
    'prefer-arrow/prefer-arrow-functions': [
      'error',
      {
        disallowPrototype: true,
        singleReturnOnly: true,
        classPropertiesAllowed: false,
      },
    ],

    // react
    'react/jsx-filename-extension': [
      'error',
      {
        extensions: ['jsx', 'tsx'],
      },
    ],
    'react/jsx-one-expression-per-line': 'off',
    'react/jsx-uses-react': 'error',
    'react/jsx-uses-vars': 'error',
    'react/prop-types': 'off',
    'react/prefer-stateless-function': 'off',

    // react hooks
    'react-hooks/rules-of-hooks': 'error',

    // prettier
    'prettier/prettier': [
      'error',
      {
        bracketSpacing: true,
        printWidth: 80,
        semi: true,
        singleQuote: true,
        trailingComma: 'all',
        useTabs: false,
      },
    ],
  },
};

package.json の設定

React Native + Expo の場合は、"scripts"の下に以下の設定を行います。カンマの位置に気をつけてください。

"scripts": {
    ...
    "lint": "eslint --ext  './**/*.{js,jsx,ts,tsx}'; stylelint './**/*.css'"
}

React の場合は、この部分のパスを以下のようにします。

"scripts": {
    ...
    "lint": "eslint --ext ./ 'src/**/*.{js,jsx,ts,tsx}'; stylelint 'src/**/*.css'"
}

そして、package.jsonの終わりに以下の設定を追加します。

"lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "git add"
    ],
    "*.css": [
      "stylelint --fix",
      "git add"
    ]
  },
  "prettier": {
    "bracketSpacing": false,
    "singleQuote": true,
    "trailingComma": "es5"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }

これで設定は完了です。

実際の動作

Prettier の機能で、以下のようにフォーマットが崩れたプログラムが、

export default function App() {
  return (
<View 
style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
</View>
  );
}

ファイルを保存するだけで、フォーマットが整形されます。すばらしいですね。

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.tsx to start working on your app!</Text>
    </View>
  );
}

また、コミット時に ESLint のエラーがあると、以下のようなエラーが出力されコミットが中断されます。これもすばらしい。

husky > pre-commit (node v12.4.0)
  ↓ Stashing changes... [skipped]
    → No partially staged files found...
  ❯ Running tasks...
    ❯ Running tasks for *.{js,jsx,ts,tsx}
      ✖ eslint --fix
        git add
    ↓ Running tasks for *.css [skipped]
      → No staged files match *.css

✖ eslint --fix found some errors. Please fix them and try committing again.

/Users/xxxxx/Projects/config-test/App.tsx
  6:18  error  'styles' was used before it was defined  @typescript-eslint/no-use-before-define

✖ 1 problem (1 error, 0 warnings)

これによってプロジェクトのコード品質は上がること間違いなし?ですね。

おわりに

この設定は以下の 「りあクト!」というReact の書籍を参考にさせていただきました。

booth.pm

この「りあクト!」という書籍は、React の入門に最適だと思うので、React で挫折したことのある方にもおすすめです。

自分もこの書籍のおかげで、 単語帳.com という React で構築したサービスをベータリリースまでこぎつけることができました。問題が起きなければ、このまま正式リリースしたいと思ってます。

さて、React や React Native (+ Expo)のプロジェクト設定って、なかなかまとまったものがないのでこの記事がお役にたてば幸いです。