Vite + React + TypeScriptで、Qiitaの記事を書きたくなるChrome拡張を爆速で作った

## はじめに

QiitaでLGTMをもらえると、紙吹雪で称賛してもらえる拡張機能を作りました。 (**非公式**)

Viteでの実装について説明する前に、軽く今回作った拡張機能の機能説明をします。

## 作った拡張はこちら

https://twitter.com/kyntk_1128/status/1513306191782608898

<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="ja" dir="ltr">LGTMが伸びるともっと浸れるようになった <a href="https://t.co/8gbIz1pyeN">pic.twitter.com/8gbIz1pyeN</a></p>&mdash; 金子 優斗 / Qiita (@kyntk_1128) <a href="https://twitter.com/kyntk_1128/status/1513306804604010496?ref_src=twsrc%5Etfw">April 11, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

長い時間と労力をかけて記事を投稿したものの、思ったよりもバズらず、悲しい思いをしたことがある人は多いと思います。

そこで、この拡張を使えば、1LGTMつくだけで、盛大に終わりのない称賛を得ることができます。
止めどない称賛の雨を受けて、自己満足に浸ることができます。
大バズリしたり、たくさんのLGTMがついたりしなくても、また次に記事を書きたいと思えるでしょう。

また、もしも多くの人に見てもらい、たくさんのLGTMがついた記事にはさらなる称賛を得ることができるようになっています。

## 使ってみたい方は、こちらからダウンロード

https://chrome.google.com/webstore/detail/qiita-confetti/bfcplfkoiieodnphcjpgcdmpibcjeoge

## 拡張機能の説明

この拡張では、WebページのDOMを読み込んで、ReactのコンポーネントをDOMに挿入しています。

機能がとてもシンプルなのと、Viteのbuildがとても簡単なため、すぐ拡張機能を作ることができました。

### 実装が必要なもの

拡張機能を作るために最低限必要なのはこちらです

1. 機能を実装したJavaScriptファイル
1. 拡張機能の設定ファイル (`manifest.json`)

これさえあれば、自分で実装した拡張を自分のChromeで動かすことができます。
ストアに公開するために、（任意ですが）その他にiconを用意する必要があります。

今回は @degudegu2510 が作ってくれました!
![icon128.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/baf769d6-924e-e645-ac41-403622ed8292.png)

ソースコードはGitHubにて公開しているので、詳しく知りたい方はこちらからご確認ください!

https://github.com/kyntk/qiita-confetti

## 実装の詳細

### 前提 Viteについて

https://ja.vitejs.dev/guide/

> より速く無駄のない開発体験を提供することを目的としたビルドツールです

特徴としては、以下の2つが挙げられています

- 非常に高速な Hot Module Replacement (HMR) など、ネイティブESモジュールを利用した豊富な機能拡張を提供する開発サーバ。
- Rollupでコードをバンドルするビルドコマンド。プロダクション用に高度に最適化された静的アセットを出力するように事前に設定されています。

Viteの特徴については、すでに色々な記事で紹介されているのでここでは詳しい説明を省きますが、ネイティブESモジュールを利用し、バンドルをしないことによって、Webpackなどと比較しても高速なbuildスピードを出しています。
開発環境のbuildには他のツールと比較して10〜100倍速いと言われている[^esbuild]esbuildを使っているようです。
これにより、高速な開発体験が得られます。

[^esbuild]: https://esbuild.github.io

また、プロダクション環境はRollupを用いてバンドルをしています。

https://ja.vitejs.dev/guide/why.html

### Viteでアプリケーションの構築

ViteはReact, Vue, Svelteなどのアプリケーションを簡単に構築することができます。

https://vitejs.dev/guide/

```bash
$ yarn create vite sample_extension
yarn create v1.22.18
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Installed "create-vite@2.9.1" with binaries:
      - create-vite
      - cva
```

create viteを実行すると、どのアプリケーションを使うかを選択できます。

```bash
? Select a framework: › - Use arrow-keys. Return to submit.
    vanilla
    vue
❯   react
    preact
    lit
    svelte
```

Reactを選択すると、次にTypeScriptの環境も選択することができます。

```bash
? Select a variant: › - Use arrow-keys. Return to submit.
    react
❯   react-ts


Scaffolding project in /Users/kyntk/git/sample_extension...

Done. Now run:

  cd sample_extension
  yarn
  yarn dev

✨  Done in 2.33s.
```

これだけですぐにVite + React + TypeScriptでアプリケーションを構築する準備ができます。 (React18にも対応したようです)

あとは、`src/App.tsx`に必要な実装をしていくだけです。

### JavaScriptのbuild

:::note
追記) ここで説明するのはプレーンなviteでの構築の説明です。
こちらの記事にある方法を使うと、HMRを使いながら開発することができ、とても快適です！

https://dev.to/jacksteamdev/rpce-is-moving-to-crxjsvite-plugin-2lmi
:::

Viteのbuildコマンドは、デフォルトでは`index.html`にbuildした結果をimportするようにしてくれます。
今回はJavaScript部分のみが必要になるので、Viteの`ライブラリモード`を使います。

https://ja.vitejs.dev/config/#build-lib

vite.config.tsにentryファイルを設定します。
formatではESモジュールも選択することはできるのですが、ライブラリモードで`es`を選択すると、minifyが無効化されてしまい、ファイルサイズが大きくなってしまうので、今回は`cjs`を選択しました

```diff_typescript:vite.config.ts
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
+ const path = require('path')

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
+  build: {
+    outDir: './extension/js',
+    lib: {
+      entry: path.resolve(__dirname, 'src/main.tsx'),
+      name: 'confetti',
+      formats: ['cjs'],
+    },
+  },
})
```

必要な設定はこれだけです！

`yarn build`をすると、`./extension/js`ディレクトリにファイルが出力されます。

### manifest.jsonの作成

拡張機能の設定ファイルであるmanifest.jsonを作成します。
今回は`./extension`を拡張機能としてアップロードするためのディレクトリにしたので、以下のようなディレクトリ構成にしています。

```bash
$ tree ./extension
./extension
├── icons
│   ├── icon128.png
│   ├── icon16.png
│   └── icon48.png
├── js
│   └── qiita-confetti.cjs.js
└── manifest.json
```

content_scriptsに出力したファイルを記載します。

https://developer.chrome.com/docs/extensions/mv3/content_scripts/

```manifest.json
{
  "manifest_version": 3,
  "name": "Qiita Confetti",
  "description": "Best praises you for writing articles on Qiita!",
  "version": "1.1",
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "author": "kyntk",
  "permissions": ["storage"],
  "content_scripts": [
    {
      "matches": ["https://qiita.com/*"],
      "js": ["js/qiita-confetti.cjs.js"]
    }
  ]
}
```

そして、 `./extension`ディレクトリをChrome拡張機能の「パッケージ化されていない拡張機能」の読み込みを使うことで挙動の確認をすることができます。

https://developer.chrome.com/docs/extensions/mv3/getstarted/#unpacked

## まとめ

拡張機能を作りたいと思った時に、Viteを使って爆速な開発体験を得ながらすぐに実装することができるのはとても便利です。

また、以下の記事を参考に、 `@crxjs/vite-plugin`を使うと、HMRを使いながら、より快適に開発することができましたので、ぜひこちらもご利用ください!

https://dev.to/jacksteamdev/rpce-is-moving-to-crxjsvite-plugin-2lmi

## 改めて拡張の宣伝

拡張機能をこちらからダウンロードお願いします！

https://chrome.google.com/webstore/detail/qiita-confetti/bfcplfkoiieodnphcjpgcdmpibcjeoge

https://twitter.com/kyntk_1128/status/1513306191782608898

<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="ja" dir="ltr">LGTMが伸びるともっと浸れるようになった <a href="https://t.co/8gbIz1pyeN">pic.twitter.com/8gbIz1pyeN</a></p>&mdash; 金子 優斗 / Qiita (@kyntk_1128) <a href="https://twitter.com/kyntk_1128/status/1513306804604010496?ref_src=twsrc%5Etfw">April 11, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## ソースコード

https://github.com/kyntk/qiita-confetti

## 参考

- https://vitejs.dev
- https://developer.chrome.com/docs/extensions/mv3/
- https://esbuild.github.io