Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

webpack5 + React 17.x + TypeScript 4.x その0003 CSS Modulesでscopedな感じのCSSを書く

参考にさせていただいたページ

CSS Modulesについて

CSS Modulesの型定義について

webpack.config.jsについて

VSCodeでTS/JSの静的解析を強制的に再実行したい場合について

必要なパッケージのインストール

devDependenciesに追加します。

yarn add -D style-loader css-loader sass-loader node-sass

webpack.config.jsに.scssのルールを追記する

最新版で学ぶwebpack 5入門 - スタイルシート(CSS/Sass)を取り込む方法 - ICS MEDIA」を参考に、webpack.config.jsのrulesに以下のような感じで.scssのルールを追記しました。

rules: [
      〜略〜
      {
        test: /\.scss/,
        use: [
          // linkタグに出力する機能
          {
            loader: 'style-loader',
          },
          // CSSをバンドルするための機能
          {
            loader: 'css-loader',
            options: {
              // オプションでCSS内のurl()メソッドを取り込む
              url: true,
              // ソースマップの利用有無
              sourceMap: enabledSourceMap,

              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
              importLoaders: 2,
            },
          },
          // Sassをバンドルするための機能
          {
            loader: 'sass-loader',
            options: {
              // ソースマップの利用有無
              sourceMap: true,
            },
          },
        ],
      },
      〜略〜
],

この段階でのwebpack.config.jsの全容

一応書いておくとこんな感じです。

module.exports = {
  mode: 'development',
  entry: {
    'pages/index': './src/entry-points/index.tsx',
  },
  // 出力するパスは絶対パスで書きます
  output: {
    path: `${__dirname}/public/javascripts`,
    filename: (arg) => {
      return '[name].bundle.js';
    },
  },
  module: {
    rules: [
      // 拡張子 .ts もしくは .tsx の場合
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
      },
      // 拡張子 .scss の場合
      {
        test: /\.scss/,
        use: [
          // linkタグに出力する機能
          {
            loader: 'style-loader',
          },
          // CSSをバンドルするための機能
          {
            loader: 'css-loader',
            options: {
              // オプションでCSS内のurl()メソッドを取り込む
              url: true,
              // ソースマップの利用有無
              sourceMap: enabledSourceMap,

              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
              importLoaders: 2,
            },
          },
          // Sassをバンドルするための機能
          {
            loader: 'sass-loader',
            options: {
              // ソースマップの利用有無
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  resolve: {
    // import時に省略できる拡張子を設定
    extensions: ['.ts', '.tsx', '.js', '.json'],
  },
  // ES5(IE11等)向けの指定(webpack 5以上で必要)
  target: ['web', 'es5'],
};

src/components/App.tsxの作成とその記述内容

App.tsxは以下のようにしています。同じディレクトリ内にあるApp.module.scssをimportしています。

import * as ReactDOM from 'react-dom';
import styles from './App.module.scss';

const App = () => (
  <div className={styles.App}>
    <h1 className={`${styles.App__Heading} ${styles['App__Heading--hoge']}`}>
      Hello React!
    </h1>
  </div>
);

src/components/App.module.scssの作成とその記述内容

以下のように記述しました。

.App {
  display: block;

  .App__Heading {
    font-size: 32px;

    &.App__Heading--hoge {
      font-size: 10px;
    }
  }
}

src/types/style.d.tsの作成とその記述内容

VS Codeで以下のようなエラーが表示されたので「 TypeScript + CSS Modules をWebpackで構築 」を参考に型定義ファイルを作成しました。

Cannot find module './App.module.scss' or its corresponding type declarations.ts(2307)

すると今度は以下のようなエラーが表示されました。

Module '"*.scss"' can only be default-imported using the 'allowSyntheticDefaultImports' flagts(1259)
style.d.ts(6, 3): This module is declared with using 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag.
import styles

参考にさせていただいたページでexport = classNamesとなっていた部分をexport default classNames;にして、以下のようにしたら解消されました。

declare module '*.scss' {
  interface IClassNames {
    [className: string]: string;
  }
  const classNames: IClassNames;
  export default classNames;
}

この段階でまだ型がおかしい場合は、Shift+Command+Pでコマンドパレットを開いてTypeScript: Restart TS serverコマンドを実行するか、VS Codeを一旦終了して、再起動すると型が正常に適用されるかもしれません。

src/entry-points/index.tsxの記述内容

以下の通り記述しています。ただ単にApp.tsxをimportしているだけです。

import '../components/App';

おわりに

こんな感じでたぶん動くはず。

この記事が誰かの役に立ったら嬉しいです。