Webpackのcss loaderでcssファイルをstring型に変換、またはCSS Modules的に扱えるようにする。

# モチベーション
nodejs(typescript)でcssを以下のようにstring型で書いていたけど、`.css`ファイルで書いた方がVSCodeとかでcssの補完も効くよな。。と思い、実験してみました。

```css.ts
const css = `
.description {
  overflow: hidden;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  color: #777;
  font-size: 12px;
  line-height: 19px;
  font-weight: 700;
  height: 57px;
}
`
```

```hoge.ts
import css from './css';

const style = document.createElement('style');
style.textContent = css;

```

# Webpackでできそう

色々と方法をググっていたら、webpackの公式ドキュメントで以下のような記述を発見。

（実装方法などは後述）

> css-loader
> toString
>You can also use the css-loader results directly as a string, such as in Angular's component style.
> [css-loader | webpack](https://webpack.js.org/loaders/css-loader/)

元々broserifyでjsなどbundleをしていましたが、ドキュメントの充実度とstackoverflowでの検索ヒット率でwebpackに乗り換えることを決意しました。

browserifyではbroserify-cssというモジュールでcssをトランスフォーム出来るそうです。（今回は試していません）

# 環境構築

## ディレクトリ構成

```directory
root/
    ├ package.json
    ├ node_modules
    ├ tsconfig.json
    ├ webpack.config.js
    ├ sample.html
    ├ yarn.lock
    ├ src/
         ├ index.ts
         ├ css.css
         ├ css.text.css
```

## package.jsonの設定
package.jsonに以下のmoduleを含める。

```package.json
  "devDependencies": {
    "@types/node": "^12.11.6",
    "css-loader": "^3.4.2",
    "to-string-loader": "^1.1.6",
    "ts-loader": "^6.2.1",
    "typescript": "^3.6.4",
    "webpack": "^4.42.0",
    "webpack-cli": "^3.3.11"
  }
```

## tsconfig.json

各自の設定で大丈夫かと思われます。

```tsconfig.json
{
    "compilerOptions": {
        "noImplicitAny": false,
        "strict": true, 
        "target": "es5",
        "removeComments": true,    
        "rootDir": "./src/",
        "module": "es6",
        "sourceMap": false
    },
    "include": ["src"]
}
```

## webpack.config.jsの設定

今回はmodule.rulesの中で、ファイルの末尾が`.css`の場合はモジュールで読み込み、`.text.css`のように`.text`がファイル名に入っている場合は、string形で読み込めるように設定。

```webpack.config.js
const helpers = require('./webpack-helpers/helpers');

module.exports = {
  mode: 'production',
  entry: './src/index.ts',
  output: {
    path: __dirname,
    filename: 'bundle.js',
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: 'ts-loader', //ts-loader使うよ
      },
      {
        test: /\.text\.css$/i,
        use: ['to-string-loader', 'css-loader'],
      },
      {
        test: /\.css$/i,
        exclude: /text/,
        use: [
          'style-loader', 
          {
            loader: 'css-loader',
            options: {
              modules: true,
            }
          }],
      },
    ],
  },
};

```

通常通りcssを書く。

```css.text.css
#target1{
    width: 100px;
    height: 100px;
    background-color: red;
}
```

```css.css
#target2{
    width: 100px;
    height: 100px;
    background-color: red; 
    opacity: 0.5;
}
```

## cssをtsファイルの中で読み込み

typescirptファイル内で以下のようにcssをインポートすると、string型、またはCSS Modulesで扱えるようになる。

```index.ts
const exec = (): void => {
  const cssText: string = require('./main.text.css').toString(); // string
  const styles = require('./main.css'); // CSS Modules
  const target1 = document.querySelector('#target1');
  const target2 = document.querySelector('#target2');

  if (target1) {
    target1.textContent = 'fuga';
    const style = document.createElement('style');
    style.textContent = cssText;
    document.body.appendChild(style);
  }

  if (target2) {
    const ptag = document.createElement('p');
    ptag.textContent = 'fugafuga';
    target2.appendChild(ptag);
    target2.id = styles.target2;
  }
};

exec();
```

注意点としては、cssをインポートする時は`import`ではなく、`require`を使用しなければいけません。
（調査中なので追記をしたいと思います。）

```sample.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="target1">
        hoge
    </div>
    <div id="target2">
        hogehoge
    </div>
    <script src="bundle.js"></script>
</body>
</html>
```

これで`webpack`を実行すればファイルがbundleされて、以下のような表示になります。

<img width="356" alt="スクリーンショット 2020-03-09 13.52.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211967/19c8b3f0-aba3-350d-1289-6e94fcf09feb.png">


これでcssファイルで編集できるようになります。

以上。