react,ts,redux,eslint,prettier,webpackな環境を一から作る

# はじめに
react、ts、redux、eslint、prettier、webpackな環境を一から作った時の作業メモ。
create-react-appなどは用いずに作る。
いろんなnpmをインストールしていくことになるが、あちこちの記事を見て回るのがちょっとめんどかったので、作業記録を残しておく。

# typescript,webpackの環境を作る。

webpackの環境は以下のような感じ。

- sass,css-moduleが使える
- 画像がバンドルできる。
- 開発環境では、webpack-dev-serverを利用し、ソースマップがある
- 本番環境では、build時にclean-webpack-pluginでdist配下を消す

```terminal
$ npm init
```

```terminal
$ npm i -D typescript ts-loader webpack webpack-cli
$ ./node_modules/.bin/tsc --init # tsconfig.jsonの作成
$ npm i -D node-sass style-loader css-loader sass-loader url-loader file-loader
$ npm i -D clean-webpack-plugin html-webpack-plugin webpack-dev-server webpack-merge
```

```js:webpack.base.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: [path.resolve(__dirname, 'src/index.ts')],
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
  },
  module: {
    rules: [
      {
        test: /\.(js|ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'ts-loader',
        options: {
          transpileOnly: true,
        },
      },
      {
        test: /\.(scss|css)$/,
        use: ['style-loader', 'css-loader?modules', 'sass-loader'],
      },
      {
        test: /\.(png|jpg|gif|svg)$/i,
        loader: 'url-loader',
        options: {
          limit: 8192,
          name: '[name].[ext]',
        },
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.ts', '.tsx', '.json', '.mjs', '.wasm'],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'src/index.html'),
    }),
  ],
};

```

```js:webpack.dev.config.js
const path = require('path');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js');

module.exports = merge(baseConfig, {
  mode: 'development',
  devServer: {
    open: true,
    port: 9000,
    contentBase: path.resolve(process.cwd(), 'dist'),
    publicPath: '/',
  },
  devtool: 'cheap-module-eval-source-map',
});
```


```js:webpack.prod.config
const { merge } = require('webpack-merge');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const baseConfig = require('./webpack.base.config.js');

module.exports = merge(baseConfig, {
  mode: 'production',
  plugins: [new CleanWebpackPlugin()],
});

```


```js:src/index.ts
document.querySelector('body').innerText='HelloWorld';
```

```json:package.json
  "scripts": {
    "start": "webpack-dev-server --config ./webpack.dev.config.js",
    "build": "webpack --config ./webpack.prod.config.js",
    "typecheck": "tsc -p . --noEmit",
  },
```

```terminal
$ npm start
```

ここまでの成果
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131625/b0eae9e4-73bd-5ec1-a45d-dd1aa2042fa1.png">

# lintの環境を整備

```terminal
$ npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
$ npm i -D prettier eslint-config-prettier
$ npx mrm lint-staged
```

手順は[こちらの記事](https://qiita.com/notakaos/items/85fd2f5c549f247585b1)を参考に。

# reactの導入

```terminal
$ npm i -S react react-dom @types/react @types/react-dom
$ npm i -D eslint-plugin-react
```

[こちらの記事](https://qiita.com/notakaos/items/85fd2f5c549f247585b1)で作成したeslintrc.jsにreactの設定を追加

```js:.eslintrc.js
module.exports = {
  root: true,
  env: {
    es6: true,
    node: true,
  },
  parser: '@typescript-eslint/parser',
  parserOptions: {
    sourceType: 'module',
    ecmaVersion: 2019,
    tsconfigRootDir: __dirname,
    project: ['./tsconfig.eslint.json'],
  },
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:react/recommended', // 追加
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'prettier',
    'prettier/@typescript-eslint',
    'prettier/react',           // 追加
  ],
  rules: {},
};

```

```tsx:src/index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';

ReactDOM.render(
  <div>
    <h1>Hello React!</h1>
  </div>,
  document.getElementById('root')
);
```

```html:src/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Webpack App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
```

```js:webpack.base.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: [path.resolve(__dirname, 'src/index.tsx')], // index.ts => index.tsx に変更
```

```terminal
$ npm start
```

ここまでの成果

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131625/d098ed17-2e06-4b71-df66-be2da3a91362.png">

# typesyncの導入
typesyncを使えば、@typesを手動で導入する必要がなくなる。

```terminal
$ npm i -D typesync
```

```json:package.json
  "scripts": {
    "preinstall": "typesync || :",
```

# redux、redux toolkitの導入

```terminal
$ npm i -D @reduxjs/toolkit react-redux
```

reduxのコードはcraのボイラープレートからコピペで拝借する。以下を現在作成しているプロジェクトとは別の場所で実行する。

```terminal
$ npx create-react-app tmp-react-app --template redux-typescript
```
実行すると、こんな感じのコードが展開される。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131625/86443c36-be5d-9499-4c09-469ca61133f5.png">
上記のコードから

- appディレクトリ
- featuresディレクトリ
- App.css
- App.tsx
- index.css
- index.tsx
- logo.svg

をコピペしてくる。

注意するところとして、コピペする際、コピペ元のindex.tsxで以下のようにcssを読み込んでいるところは

```ts
import './index.css';
```

このように読み込むようにする。

```ts
import '!style-loader!css-loader!./index.css';
```

```terminal
$ npm start
```

ここまでの成果
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131625/4f417cef-83e7-ffa6-ba16-cad0befeddad.png">


最終的なコードはこちら。
細かなところなどは端折ったので、参考にする際は直接コードを見てもらった方がいいかも

https://github.com/pokotyan/redux-boilerplate