React で eject せずに Scoped SASS (.scss) を使う

## 概要
- scoped sass (ファイル内限定で適用されるスタイル) を使いたいでござる
- でも`npm run eject`はしたくないでござる
- `cra-sass` を導入するとかんたんにできるでござる

## 参考文献
- [How to use Sass and CSS Modules with create-react-app](https://blog.bitsrc.io/how-to-use-sass-and-css-modules-with-create-react-app-83fa8b805e5e)
- [leighs-hammer/cra-sass](https://github.com/leighs-hammer/cra-sass)

## 実行環境
- `create-react-app` で作った react project
    - 既存プロジェクトなのでversionわからん　すまん
- TypeScript

## サンプルコード (変更前)
`node-sass` を入れてふつーにscssを使うとこうなる。

### Sample.tsx
```tsx:Sample.tsx
import * as React from "react";
import "./Sample.scss";

export const Sample: React.FC = () => {
  return (
    <div className="outer">
      OUTER
      <div className="inner">INNER</div>
      <ul>
        {["red", "blue", "green"].map((each, index) => (
          <li className={each} key={index}>
            {each.toUpperCase()}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Sample;
```

### Sample.scss
```scss:Sample.scss
.outer {
  &,
  * {
    display: flex;
    flex-direction: column;
    padding: 8px;
    border-left: 1px solid gray;
  }

  font-size: 1.2rem;
  .inner {
    font-weight: bold;
  }
  ul {
    li {
      &.red {
        color: red;
      }
      &.green {
        color: green;
      }
      &.blue {
        color: blue;
      }
    }
  }
}
```

### ビルド結果(html)
```html
<div class="outer">
  OUTER
  <div class="inner">INNER</div>
  <ul>
    <li class="red">RED</li>
    <li class="blue">BLUE</li>
    <li class="green">GREEN</li>
  </ul>
</div>
```

### 実行結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545251/1ed341a0-0381-c7ea-c223-e52337fac149.png" width="200">

### この実装の問題点
Sample.scss に記述したスタイルのscopeはグローバルである。
すなわち、Sample.tsx と同時にロードされるコンポーネントに、
同じclassName(例えば.outer)が割りあたっていると、互いに影響を受け合いバグの原因となる

### 解決策
閉じたscopeを扱うことのできるsass loaderを導入する

## 導入手順
cra-sass を実行
`npx cra-sass`

するとなんかいっぱいインストールしてプロジェクトが魔改造される

```diff:package.json
@ devDependencies
+    "cra-sass": "0.0.5",

@ dependencies
+    "node-sass-chokidar": "^1.4.0",
+    "npm-add-script": "^1.1.0",
+    "npm-run-all": "^4.1.5",

@ scripts
-    "start": "react-scripts start",
-    "build": "react-scripts --max-old-space-size=2048 build",
+    "start": "npm-run-all -p watch-css start-js",
+    "build": "npm run build-css && react-scripts build",
     "test": "react-scripts test",
-    "eject": "react-scripts eject"
+    "eject": "react-scripts eject",
+    "build-css": "node-sass-chokidar src/ -o src/",
+    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
+    "start-js": "react-scripts start"
```

`--max-old-space-size=2048` とか無くなってぶっ壊れてんじゃん！
ってことで無駄にぶっこわされたとこは直しておく

```diff:package.json
-    "build": "react-scripts --max-old-space-size=2048 build",
+    "build": "npm run build-css && react-scripts --max-old-space-size=2048 build",
```

## サンプルコード(リファクタ後)

### Sample.scss
`Sample.module.scss` に改名する

### Sample.tsx

- scssのimport
- classNameの割当てのしかた

だけを変更

```tsx:Sample.tsx
import * as React from "react";
import styles from "./Sample.module.scss";

export const Sample: React.FC = () => {
  return (
    <div className={styles.outer}>
      OUTER
      <div className={styles.inner}>INNER</div>
      <ul>
        {["red", "blue", "green"].map((each, index) => (
          <li className={styles[each]} key={index}>
            {each.toUpperCase()}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Sample;
```

### ビルド結果
```html
<div class="Sample_outer__144wv">
  OUTER
  <div class="Sample_inner__EiBfI">INNER</div>
  <ul>
    <li class="Sample_red__1ktYQ">RED</li>
    <li class="Sample_blue__32ZOZ">BLUE</li>
    <li class="Sample_green__2OrZU">GREEN</li>
  </ul>
</div>
```

```css:css部分の抜粋
.Sample_outer__144wv {
  font-size: 1.2rem; }
  .Sample_outer__144wv,
  .Sample_outer__144wv * {
    display: flex;
    flex-direction: column;
    padding: 8px;
    border-left: 1px solid gray; }
  .Sample_outer__144wv .Sample_inner__EiBfI {
    font-weight: bold; }
  .Sample_outer__144wv ul li.Sample_red__1ktYQ {
    color: red; }
  .Sample_outer__144wv ul li.Sample_green__2OrZU {
    color: green; }
  .Sample_outer__144wv ul li.Sample_blue__32ZOZ {
    color: blue; }
```

## その他やったこと

### scriptsが壊されてないかチェックしよう
start, build, test が、 cra-sass によって破壊されている恐れがある
特にdefaultから変更している場合注意しよう

### .cssが.scssと同階層に出力されるようになってうっおとしい
- node-sass-chokidar のしわざくさい
- でもoutputしなくするオプションとかなさげ
- めんどいから、別階層に吐かせて、ignoreすることにした

```diff:package.json
-    "build-css": "node-sass-chokidar src/ -o src/",
-    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
+    "build-css": "node-sass-chokidar src/ -o built-css/",
+    "watch-css": "npm run build-css && node-sass-chokidar src/ -o built-css/ --watch --recursive",
```

```diff:.gitignore
+/built-css
```

### `node-sass` をすでに使っていた場合、不要になる
`npm r node-sass`

## おしまい
これで快適な React x Scoped SASS 生活が始まる