Preact × Webpack 環境で Emotion を使う

2020.09.12

Reactの軽量版という立ち位置のPreactでEmotionを使おうとして少しハマったので備忘録として書き残します。

Fast 3kB alternative to React with the same modern API

Preactは上記のように書かれているのでライブラリとしてはかなり軽量な部類に入るのではないでしょうか。バンドルサイズを軽くしたいときはReactではなくこちらを使うと良いかと思います。今回もその理由でPreactを採用しました。

React × TypeScript 環境でEmotionを使うのは以下を参考にすれば良かったです。

Emotion - TypeScript

インストール

Preact × Webpack × TypeScriptのプロジェクトを作っていきます。

yarn add @emotion/core @emotion/styled preact
yarn add -D @types/react ts-loader tslint typescript webpack webpack-cli webpack-dev-server
./node_modules/.bin/tsc --init

ディレクトリはこんな感じです。

$ tree -I node_modules
.
├── build
│   └── index.html
├── package.json
├── src
│   └── index.tsx
├── tsconfig.json
├── webpack.config.js
└── yarn.lock

src/index.tsxはこのようにしました。

import { h, render } from "preact";

const App = () => {
  return <div>Hello Preact Emotion!</div>
};

render(<App />, document.getElementById("app") as Element);

React × Webpack × TypeScriptの雛形はこちらに置いてあります。ほぼこちらの雛形通りです。

sandbox/webpack-react-typescript at master · SatoshiKawabata/sandbox · GitHub

コンパイルエラーが出た

JSXがTypeScriptで認識されていないエラーが出たのでtsconfig.jsonに設定を追加します。

ERROR in ./src/index.tsx 5:11
Module parse failed: Unexpected token (5:11)
File was processed with these loaders:
 * ./node_modules/ts-loader/index.js
You may need an additional loader to handle the result of these loaders.
| var preact_1 = require("preact");
| var App = function () {
>     return <div>Hello Preact Emotion!</div>;
| };
| preact_1.render(<App />, document.getElementById("app"));

ERROR in /src/index.tsx
./src/index.tsx
[tsl] ERROR in /src/index.tsx(4,10)
      TS17004: Cannot use JSX unless the '--jsx' flag is provided.

ERROR in /src/index.tsx
./src/index.tsx
[tsl] ERROR in /src/index.tsx(7,8)
      TS17004: Cannot use JSX unless the '--jsx' flag is provided.
ℹ 「wdm」: Failed to compile.

tsconfig.jsonに下記を追記してJSXを認識させます。Preactはh関数を使うためjsxFactoryhに設定しています。

"compilerOptions": {
    "jsx": "react",
    "jsxFactory": "h",
}

Emotionを追加

Emotionを追加するためにindex.tsxに下記のようにします。@emotion/coreを使おうと思ったのですが、JSXの認識がうまくいかなかったので@emotion/styledを使います。

import { h, render } from "preact";
import styled from "@emotion/styled";

const SomeComp = styled.h1`
  color: hotpink;
`;

const App = () => {
  return <SomeComp>Hello Preact Emotion!</SomeComp>
};

render(<App />, document.getElementById("app") as Element);

Module not found: Error: Can't resolve 'react' in '/node_modules/@emotion/core/dist' エラーが出た

Emotion内でReactを使っているらしく下記のエラーが出ました。

ERROR in ./node_modules/@emotion/core/dist/core.browser.esm.js
Module not found: Error: Can't resolve 'react' in '/node_modules/@emotion/core/dist'
 @ ./node_modules/@emotion/core/dist/core.browser.esm.js 2:0-76 10:26-39 17:19-32 22:11-24 28:9-19 82:12-25 92:11-24 110:11-24 156:9-22 175:11-24 177:13-26 185:9-22 257:2-11 332:9-22
 @ ./node_modules/@emotion/styled-base/dist/styled-base.browser.esm.js
 @ ./node_modules/@emotion/styled/dist/styled.browser.esm.js
 @ ./src/worker/WorkerLogin.tsx
 @ ./src/login.tsx

ERROR in ./node_modules/@emotion/styled-base/dist/styled-base.browser.esm.js
Module not found: Error: Can't resolve 'react' in '/node_modules/@emotion/styled-base/dist'
 @ ./node_modules/@emotion/styled-base/dist/styled-base.browser.esm.js 2:0-38 85:13-26 134:18-31
 @ ./node_modules/@emotion/styled/dist/styled.browser.esm.js
 @ ./src/worker/WorkerLogin.tsx
 @ ./src/login.tsx
ℹ 「wdm」: Failed to compile.

解決方法

PreactにはReactとの互換性を保つためのモジュールがあるようで、そちらをWebpackのaliasとして使うように変更しました。

Preact X, Preact render to string & Styled Components · Issue #1417 · preactjs/preact · GitHub

こちらにも詳しく書かれているみたいです。

Switching to Preact – Preact

resolve: {
  alias: {
     react: 'preact/compat',
  }
}

これで動くと思います。

ソースコード

今回のプロジェクトはこちらに置いておきました。

sandbox/webpack-prect-typescript-emotion at master · SatoshiKawabata/sandbox · GitHub

他の記事

Emotion関連ではこちらの記事も書いていますのでよかったら覗いてください。

© 2019-2020 kwst.site.