やりたかったこと、経緯
- TypeScript + React + Vite で Chrome 拡張機能を作りたい
- その開発環境を WSL2 上に Docker で構築したい
- 現行の crxjs はライブラリの更新が滞っているため、wxt を使ってみたい
- しかし、wxt を Docker コンテナ上で動かす知見がない(技術的には有用でないのかも)
この記事のセールスポイント
- 現時点のwxtをコンテナで動かすための、コンテナ設定、wxt.config.ts の設定項目を解説
最終成果物とその解説
ディレクトリ構造(抜粋)
.
├── Dockerfile
├── docker-compose.yml
└── host-frontend-root # コンテナの/opt/frontend-container-app-rootにマウントされるディレクトリ
└── frontend-src-root # wxtで作成したプロジェクト
├── .output # chromeから読み込むディレクトリ
├── .wxt
├── assets
├── entrypoints # 開発で編集するファイル
├── package-lock.json
├── package.json
├── public
├── tsconfig.json
└── wxt.config.ts # docker対応のために編集
各ファイル
# 実際には wxt の初期化を行うため、作業時にいくつかのコードをコメントアウトしている
FROM node:23.0-alpine3.19
WORKDIR /opt/frontend-container-app-root/frontend-src-root
# Chromium のインストール
RUN apk add --no-cache \
chromium
COPY ./host-frontend-root/frontend-src-root /opt/frontend-container-app-root/frontend-src-root
RUN npm install
services:
frontend:
image: frontend-image
build: .
restart: always
tty: true
volumes:
- ./host-frontend-root:/opt/frontend-container-app-root/
environment:
- HOST=localhost
network_mode: host
import { defineConfig } from 'wxt';
// See https://wxt.dev/api/config.html
export default defineConfig({
extensionApi: 'chrome',
modules: ['@wxt-dev/module-react'],
dev: {
server: {
hostname: 'localhost',
port: 3000,
}
},
runner: {
disabled: true,
},
vite:() => ({
server: {
host: 'localhost',
port: 3000,
strictPort: true,
hmr: {
port: 3000,
}
}
}),
});
前提
- Windows 11
- WSL2
- 空のリポジトリが作成済み
- Docker、Docker Compose がインストール済み
やったこと
初期ディレクトリ、ファイル構成
プロジェクトルートにフォルダを作成
mkdir host-frontend-root
プロジェクトルートに Dockerfile、docker-compose.yml を作成
echo "FROM node:23.0-alpine3.19" > Dockerfile
echo "services:" > docker-compose.yml
この時点のディレクトリ構造は下記の通り
tree -I .
.
├── Dockerfile
├── README.md
├── docker-compose.yml
└── host-frontend-root
2 directories, 3 files
初期コンテナ作成
あとで使う箇所をコメントアウトしておく
FROM node:23.0-alpine3.19
WORKDIR /opt/frontend-container-app-root/frontend-src-root
# Chromium のインストール
RUN apk add --no-cache \
chromium
# COPY ./host-frontend-root/frontend-src-root /opt/frontend-container-app-root/frontend-src-root
# RUN npm install
services:
frontend:
image: frontend-image
build: .
restart: always
tty: true
volumes:
- ./host-frontend-root:/opt/frontend-container-app-root/
environment:
- HOST=localhost
network_mode: host
コンテナを起動する
docker compose build --no-cache && docker compose up -d && docker compose ps
wxtを使ってプロジェクトを作成
VS Code のリモートエクスプローラーで起動したコンテナに接続する
pwd
/opt/frontend-container-app-root
wxt を使ってプロジェクトを作成する
npx wxt@latest init frontend-src-root
を実行。今回はreact、npmを選んでいるが、目的に応じて変えて良い
npx wxt@latest init frontend-src-root
WXT 0.19.23
ℹ Initalizing new project
✔ Choose a template › react
✔ Package Manager › npm
✔ Downloading template
✨ WXT project created with the react template.
Next steps:
1. cd frontend-src-root
2. npm install
ログが示してくれた通りコマンドを実行
cd frontend-src-root
npm install
> wxt-react-starter@0.0.0 postinstall
> wxt prepare
WXT 0.19.23
types...
✔ Finished in 3.013 s
added 546 packages in 8m
148 packages are looking for funding
run `npm fund` for details
wxt.config.ts を編集
import { defineConfig } from 'wxt';
// See https://wxt.dev/api/config.html
export default defineConfig({
extensionApi: 'chrome',
modules: ['@wxt-dev/module-react'],
dev: {
server: {
hostname: 'localhost',
port: 3000,
}
},
runner: {
disabled: true,
},
vite: () => ({
server: {
host: 'localhost',
port: 3000,
strictPort: true,
hmr: {
port: 3000,
}
}
}),
});
/opt/frontend-container-app-root/frontend-src-root # npm run dev
> wxt-react-starter@0.0.0 dev
> wxt
WXT 0.19.23
✔ Started dev server @ http://localhost:3000
ℹ Pre-rendering chrome-mv3 for development with Vite 6.0.7
✔ Built extension in 1.747 s
├─ .output/chrome-mv3/manifest.json 1.05 kB
├─ .output/chrome-mv3/popup.html 744 B
├─ .output/chrome-mv3/background.js 19.96 kB
├─ .output/chrome-mv3/chunks/popup-BxzUToPe.js 8.24 kB
├─ .output/chrome-mv3/content-scripts/content.js 36.77 kB
├─ .output/chrome-mv3/icon/128.png 3.07 kB
├─ .output/chrome-mv3/icon/16.png 559 B
├─ .output/chrome-mv3/icon/32.png 916 B
├─ .output/chrome-mv3/icon/48.png 1.33 kB
├─ .output/chrome-mv3/icon/96.png 2.37 kB
└─ .output/chrome-mv3/wxt.svg 1.07 kB
Σ Total size: 76.08 kB
ℹ Load ".output/chrome-mv3" as an unpacked extension manually
ホスト側でもファイル修正できるように権限を修正
sudo chown -R (wslユーザー名):(wslユーザー名) frontend-src-root/
chrome-mv3 を手動で読み込む
wxt.config.ts で
runner: {
disabled: true,
},
としているため、chrome-mv3 を手動で読み込む必要がある。
Chrome で下記を開く
chrome://extensions/ > パッケージ化されていない拡張機能を読み込む
\wsl.localhost(Ubuntuのディストリビューション)\home(ユーザー名)(プロジェクトルート)\host-frontend-root\frontend-src-root.output\chrome-mv3
上記のような初期画面が表示され、カウント機能とホットリロードが正常に動作していることを確認。
リモートエクスプローラーを閉じ、ここまでを一旦コミットする
Dockerfile による永続化
FROM node:23.0-alpine3.19
WORKDIR /opt/frontend-container-app-root/frontend-src-root
# Chromium のインストール
RUN apk add --no-cache \
chromium
COPY ./host-frontend-root/frontend-src-root /opt/frontend-container-app-root/frontend-src-root
RUN npm install
再ビルド
docker compose build --no-cache && docker compose up -d && docker compose ps
docker コマンドでコンテナに接続して、npm run dev を実行
docker compose exec frontend npm run dev
再びポップアップとホットリロードの動作を確認して、環境構築は完了。
解説
今回の設定値の肝
- wxt.config.tsのdevプロパティとviteプロパティでのhostname、portの値を一致させる必要がある。
- wxt.config.tsのrunnerプロパティを無効にする。
- また、hostnameの値はdocker-compose.ymlのenvironmentプロパティのHOSTにも合わせる必要がある。
- コンテナ内のViteサーバーとホストマシンのChrome拡張機能が通信できるようにするため。
- 現時点では、docker-compose.ymlのnetwork_modeをhostにしておく必要がある(後述)。
vite.config.tsの内容はwxt.config.tsに記述する
公式ドキュメント
https://wxt.dev/guide/essentials/config/vite.html#change-vite-config
によれば、通常vite.config.tsのdefineConfigに記述する内容を、wxt.config.tsに記述することで、Viteの設定を変更できる。
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
vite: () => ({
// Override config here, same as `defineConfig({ ... })`
// inside vite.config.ts files
}),
});
未解決事項
network_mode: host について
本来はdocker-compose.ymlでnetwork_modeをhostにしないほうが望ましいが、WebSocket関連のエラーを回避できなかったため、一時的にhostを使用している。
Dockerfileにおけるレイヤーキャッシュの利用
ビルド速度向上のため、ホスト側からのソースコードコピー前にnpm installを実行したい。
# 1. package.json と lockファイルのみコピー
COPY ./host-frontend-root/frontend-src-root/package*.json ./
# 2. 先に依存関係をインストール
RUN npm install
# 3. 全ソースコードをコピー
COPY ./host-frontend-root/frontend-src-root ./
しかし、wxtの場合、npm installを実行すると、npx wxt@latest init
コマンド実行時にデフォルトで用意される
"postinstall": "wxt prepare"
が実行される。これにより、
ERROR No entrypoints found in /opt/frontend-container-app-root/frontend-src-root/entrypoints
というエラーが発生するため、 RUN npm install
前にソースコードが存在している必要がある。
回避するにはpackage.json
からpostinstall
を削除し、以下のように書き換える方法がある。
# 1. package.json と lockファイルのみコピー
COPY ./host-frontend-root/frontend-src-root/package*.json ./
# 2. 先に依存関係をインストール (キャッシュが効きやすい)
RUN npm install
# 3. 全ソースコードをコピー
COPY ./host-frontend-root/frontend-src-root ./
# postinstallで指定されているコマンドを実行
RUN npx wxt prepare
これでレイヤーキャッシュを活用できるが、デフォルトのpostinstall
を削除する必要があり、対応を検討中。
wxt.config.tsのrunner: { disabled: true } について
wxt.config.ts で
runner: {
disabled: true,
},
と設定しているが、本来はfalseやオプション削除が望ましい。しかし、これを削除すると下記エラーが発生する。
✖ Command failed after 34.1 s
ERROR connect ECONNREFUSED 127.0.0.1:32885
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1608:16)
chromiumPortなどの設定を試しても改善せず、一時的にrunnerを無効にして手動ロードを行っている。
改善できそうな箇所
- port等の設定値を.envにまとめる
- ボイラーテンプレート化