サクッと作れる! React でブラウザ拡張機能のテンプレートを作ってみた

Reactを用いてブラウザ拡張機能を作成する際に、サクッと作り始められる目的でテンプレートを作ったので紹介します。

基本の構成としては、React + TypeScript + esbuildを使ったものとなっており、@htlsneさんのテンプレートをベースに、開発を始める際に個人的にそろっていてほしい環境を追加で導入したものになります。 zenn.dev

※ベースの段階でbuild周りの実装など綺麗にまとまっており素晴らしいテンプレートとなっていますので、環境周りは自分でやるから最低限でいいよという方にはベースのこちらをお勧めします。

成果物

github.com

想定する利用場面

  • Reactで環境構築を考えずにブラウザ拡張機能を作り始めたい時にお使いいただけます。
  • それ以外の場合は...

モジュールバンドラー

冒頭でも説明しましたが、esbuildを利用しています。 buildの実行、ターミナルで以下コマンドを実行することで、build.tsをエントリーポイントとしてビルドが走ります。

npm run build

バンドル周りの詳細な実装については、ベースとしたテンプレートを説明している記事で詳しく解説されていたので、そちらをご覧ください。 content-script.jsファイルなど、popup.html以外の実装が必要になった場合にも、build.tsaddBuildFile()addBuildFile()にパスを追加するだけで簡単にビルドができるような設定になっているようです。

zenn.dev

スタイリング

tailwindcssのようなCSSフレームワークを使いたかったのですが、2022/07時点では、esbuild本家でPostCSSがサポートされておらず、自分で拡張する必要があったので今回は導入しませんでした。 その代わり、手軽に導入できることもあり、emotionを導入することでCSS-in-JSでスタイリングするようにしました。

emotion.sh

なお、CSS ModulesはComponentファイル内に配置しないためにCSS-in-JSと比較して可読性が劣ったり、localスコープでのスタイルの適用が難しかったりという理由から、インラインでstyleタグを利用することについては、レンダリングの度にスタイルを読み込む必要があることからDOM Elements - Reactで非推奨とされており、それぞれ候補から外しました。

CSS-in-JSを実現するライブラリには、古くから使われているstyled-componentsがあったり、zero runtime CSS-in-JSとして実行時ではなくbuild時にCSSを生成してくれるlinariaもありましたが、linariaは動的にスタイルを生成することができず、この辺はもう少し知見を溜めてから導入したいこともあり、後発で多機能なemotionを採用することとしました。

開発補助

テスト

テストについては、プロジェクト毎にユニットテストからE2Eテストなどをどこまで必要とするのか状況次第であることを想定して導入していません。 また、Jestなどであれば後から簡単に導入できるため、上述の理由も含めてあえて事前に導入する必要はないと判断しました。

linter/formatter

linter/formatterについてはベースのテンプレートを踏襲して、eslint、prettierを使っています。

eslintはtypescript-eslintreact/recommendedなど、基本的にReactで導入する際にデフォルトで推奨されている設定がメインになります。

追加している要素として、emotionのeslintルールを追加しています。

.eslint.js

module.exports = {
  plugins: ['@typescript-eslint', '@emotion'],
  ...
  rules: {
    ...
    // emotion
    '@emotion/jsx-import': 'error',
    '@emotion/pkg-renaming': 'error',
    '@emotion/no-vanilla': 'error',
    '@emotion/import-from-emotion': 'error',
    '@emotion/styled-import': 'error',
  },
  ...
};

emotionのStyledを利用する際に、構文のルールを統一してくれたりするルールなどが含まれています。

CI/CD

CI

huskylint-stagedを利用して、pre-commit時にコードフォーマットと型の静的解析を行なっています。

github.com

その他テストを導入したら、Github Actionsを使ってPRを作成した際にテストを回すように設定してもよさそうですね。

CD

デリバリーに関しては、Github ActionsでChromeFirefox用のdistファイルをそれぞれ.zipファイルに圧縮して、tagでリリースを作成することを想定していますが、こちらは現在未対応となっています。

Tips

スタイルのテーマ管理

@emotion/reactのライブラリ側で定義されたTheme型を上書くことで、独自テーマを一貫して利用することができます。

emotion.sh

popup/Popup.tsx

const Title = styled.h1((props) => {
  const { theme } = props;
  return {
    ...theme.typography.h1,
    color: theme.palette.text.primary, // props.themeオブジェクトから呼び出せる
  };
});
emotion.d.ts

// @emotion/react で定義されている Theme を上書き
declare module '@emotion/react' {
  export interface Theme {
    typography: {
      h1: TypographyType;
      h2: TypographyType;
      h3: TypographyType;
    };
    palette: PaletteType;
  }
}
popup/index.tsx

// 独自テーマの定義
const theme = {...}

ReactDOM.render(
  <ThemeProvider theme={theme}>
    <Popup />
  </ThemeProvider>,
  document.getElementById('root')
);

詳細な使い方については、以下の記事にわかりやすくまとまっていたのでこちらを参照ください。

zenn.dev

作成したChrome拡張機能を利用する方法

npm run run:chromeコマンドなどでデバックはできますが、これだけでは実際に使える状態にはなりません。 ここでは作成したブラウザ拡張機能Chromeで利用する方法を簡単にご紹介します。

  1. npm run buildもしくはnpm run build:chromeコマンドを実行すると、ルートディレクトリにdist-chromeというビルドの成果物が含まれたディレクトリが生成されます。
  2. chrome://extensions にアクセスします。
  3. デベロッパーモード」と表記のあるトグルスイッチをクリックして、デベロッパーモードを有効にします。
  4. 「パッケージ化されていない拡張機能を読み込む」ボタンを押すと選択モーダルが開くので、ビルドしたdist-chromeディレクトリを選択します。

developer.chrome.com

以上で作成したChrome拡張機能が利用できるようになります。

まとめ

以上、さくっと作りたい時に使えるブラウザ拡張機能のテンプレートの紹介でした。 デリバリー周りなどもう少し調整したい箇所はありますが、サクッと作り始めるという目的は達成できるテンプレートになったかと思います。 今後リポジトリを更新した際は、合わせてこちらのドキュメントも随時更新していこうと思っています。

はじめてブラウザ拡張機能を作る方なども、手軽に作れるので是非お試しください。