mcpField
C
crxjs

crxjs

github.com/samber開発2414.5k 回インストール4 週間前に更新
リポジトリ

crxjs について

CRXJSは、マニフェストを基にChrome拡張機能を構築するViteプラグインです。各種拡張機能の画面に加え、コンテンツスクリプトにも真のHMRを提供し、ブラウザーAPIを薄く扱えます。

主な機能

  1. 01マニフェストに基づく拡張機能ビルド
  2. 02ポップアップ、オプション、サイドパネルのHMR
  3. 03コンテンツスクリプトのHMR
  4. 04動的なコンテンツスクリプトのインポート
  5. 05型安全な動的マニフェスト

活用シーン

  1. 01Viteフレームワークでの拡張機能開発
  2. 02開発中のコンテンツスクリプト反復
  3. 03リリース用拡張機能バンドルの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/samber/cc-skills --skill crxjs

インストール後、Claude Code で/crxjsを入力して読み込まれたか確認します。

詳細

CRXJS

CRXJS は、ポップアップ、オプションページ、コンテンツスクリプト、サイドパネルに真の HMR を提供する Chrome 拡張機能開発ツールです。マニフェストを読み取って拡張機能の出力を自動生成し、コンテンツスクリプトの挿入を処理し、サービスワーカーのビルドを管理します。内部では Vite プラグイン(@crxjs/vite-plugin)として動作します。

現在の状況

  • パッケージ:@crxjs/vite-plugin(v2.x は安定版、2026年3月時点の最新は v2.4.0)
  • スキャフォールディング:npm create crxjs@latest(常に @latest を使用してください)
  • メンテナンス担当:@Toumash と @FliPPeDround(2025年半ばから)
  • GitHub:github.com/crxjs/chrome-extension-tools(約4kスター)
  • Vite との互換性:v3 から v8-beta まで

クイックスタート

# Scaffold new project (picks framework interactively)
npm create crxjs@latest

# Or add to existing Vite project
npm install @crxjs/vite-plugin -D

フレームワーク別の Vite 設定

CRXJS は Vite プラグインとして追加します。セットアップ方法はフレームワークによって少し異なります。

React
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [react(), crx({ manifest })],
});

HMR との互換性を最大限に高めるには、@vitejs/plugin-react(plugin-react-swc ではありません)を使用してください。SWC を使用する必要がある場合は、マニフェストをキャストしてください。

import { ManifestV3Export } from "@crxjs/vite-plugin";
const manifest = manifestJson as ManifestV3Export;
Vue
import vue from "@vitejs/plugin-vue";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [vue(), crx({ manifest })],
});
Svelte
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [svelte(), crx({ manifest })],
});
Vanilla TypeScript
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [crx({ manifest })],
});

defineManifest — 型安全な動的マニフェスト

静的な JSON ファイルの代わりに、CRXJS の defineManifest を使用すると、動的な値と完全な TypeScript の自動補完が利用できます。

// manifest.ts
import { defineManifest } from "@crxjs/vite-plugin";
import pkg from "./package.json";

export default defineManifest((config) => ({
  manifest_version: 3,
  name: config.command === "serve" ? `[DEV] ${pkg.name}` : pkg.name,
  version: pkg.version,
  description: pkg.description,
  permissions: ["storage", "activeTab", "scripting"],
  action: {
    default_popup: "src/popup/index.html",
    default_icon: {
      "16": "public/icons/icon16.png",
      "48": "public/icons/icon48.png",
    },
  },
  background: {
    service_worker: "src/background/index.ts",
    type: "module",
  },
  content_scripts: [
    {
      matches: ["https://*/*"],
      js: ["src/content/index.ts"],
      css: ["src/content/styles.css"],
    },
  ],
  options_page: "src/options/index.html",
  side_panel: { default_path: "src/sidepanel/index.html" },
  icons: {
    "16": "public/icons/icon16.png",
    "48": "public/icons/icon48.png",
    "128": "public/icons/icon128.png",
  },
}));

vite.config.ts でインポートします。

import manifest from "./manifest";
// ... crx({ manifest })

型宣言

src/vite-env.d.ts または src/crxjs.d.ts に追加します。

/// <reference types="@crxjs/vite-plugin/client" />

これにより、?script と ?script&module のインポートで型が有効になります。

コンテキスト別の HMR の動作

コンテキストHMR動作の仕組み
ポップアップ完全な HMRWebSocket ベースで、状態が保持されます
オプションページ完全な HMRポップアップと同じです
サイドパネル完全な HMRポップアップと同じです
コンテンツスクリプト(マニフェスト)真の HMRCRXJS がローダーと HMR クライアントを挿入します
コンテンツスクリプト(動的)真の HMR?script インポート経由です
サービスワーカー自動再読み込み変更すると拡張機能全体が再読み込みされます
メインワールドスクリプトHMR なしCRXJS ローダーによってスキップされます

コンテンツスクリプトで HMR が機能するのは、CRXJS がローダースクリプトを生成し、HMR のプリアンブル、HMR クライアント、実際のスクリプトをインポートするためです。これにより、ページ全体を再読み込みせずに、真のモジュール単位の HMR が可能になります。これが CRXJS の主な差別化要因です。

動的なコンテンツスクリプトのインポート

プログラムで挿入するコンテンツスクリプト(マニフェストには記載しないもの)には、CRXJS が特殊なインポートサフィックスを用意しています。

// background.ts — ?script gives you a resolved path for executeScript
import contentScript from "./content?script";

chrome.action.onClicked.addListener(async (tab) => {
  await chrome.scripting.executeScript({
    target: { tabId: tab.id! },
    files: [contentScript],
  });
});

メインワールドへの挿入(HMR なし)には、次を使用します。

import mainWorldScript from "./inject?script&module";

await chrome.scripting.executeScript({
  target: { tabId },
  world: "MAIN",
  files: [mainWorldScript],
});

CRXJS プラグインのオプション

crx({
  manifest,
  browser: "chrome", // 'chrome' | 'firefox'
  contentScripts: {
    injectCss: true, // auto-inject CSS for content scripts
    hmrTimeout: 5000, // HMR connection timeout (ms)
  },
});

開発ワークフロー

# Start dev server (outputs to dist/ with HMR)
npm run dev

# 1. Open chrome://extensions
# 2. Enable "Developer mode"
# 3. Click "Load unpacked"
# 4. Select the dist/ directory
# 5. Edit code — popup/content scripts update instantly via HMR
# 6. Service worker changes trigger automatic extension reload

一度読み込めば、以降の npm run dev セッションは自動的に再接続します。manifest.json に変更がない限り、拡張機能を再読み込みする必要はありません。

本番ビルド

npm run build    # outputs to dist/

dist/ ディレクトリは、zip に圧縮して Chrome ウェブストアにアップロードできる状態です。

cd dist && zip -r ../extension.zip .

インラインスクリプトが原因で CWS に拒否されないよう、Vite のモジュールプリロードを無効にします。

build: {
  modulePreload: false;
}

既知の問題と回避策

コンテンツスクリプトでの Tailwind CSS HMR

新しい Tailwind クラスを追加しても、コンテンツスクリプトの CSS が更新されないことがあります。回避策:新しいユーティリティクラスを追加した後、開発サーバーを再起動してください。v2.4.0 で改善されていますが、完全には解決していません。設定で injectCss: true が有効になっていることを確認してください。

WebSocket 接続エラー(ws://localhost:undefined/)

原因:開発サーバーと HMR 設定のポートが一致していません。修正方法:両方に同じ値を明示的に設定してください。

server: {
  port: 5173,
  strictPort: true,
  hmr: { port: 5173 },
}
「マニフェスト バージョン 2 は非推奨です」という警告

この警告が表示された場合、マニフェストが MV2 として解釈されています。修正方法:"manifest_version": 3 が設定されていることを確認してください。

file:// URL でコンテンツスクリプトが挿入されない

Chrome では、拡張機能の設定(chrome://extensions)でユーザーが「ファイル URL へのアクセスを許可する」を有効にする必要があります。CRXJS ではこの設定を変更できません。

Chrome の更新後に HMR が機能しなくなる

CRXJS の HMR は、開発サーバーの WebSocket に接続するコンテンツスクリプトの挿入に依存しています。Chrome のセキュリティ更新によって、これが機能しなくなることがあります。修正方法:Chrome の変更に対応した最新バージョンの CRXJS に更新してください。

CRXJS と代替手段の比較

機能CRXJSWXTPlasmo
コンテンツスクリプトの HMR真の HMRファイルベースのリロード部分的
フレームワークのサポートあらゆる Vite フレームワークあらゆるものReact に特化
抽象化のレベル薄い(Vite プラグイン)フルフレームワークフルフレームワーク
メッセージングヘルパーなし(chrome.* を直接使用)組み込み組み込み
ストレージラッパーなし組み込み組み込み
クロスブラウザー対応Chrome + FirefoxChrome + Firefox + SafariChrome + Firefox
ファイルベースのルーティングなしありあり
学習曲線低い(Vite と CRXJS の知識が必要)中程度中程度

CRXJS を選ぶ場合:Chrome の生の API に対する抽象化を最小限に抑え、コンテンツスクリプトの HMR を何よりも重視する場合に適しています。CRXJS は邪魔になりません。魔法のようなルーティングも、ラッパー API もなく、HMR を備えたあなたのコードだけです。

WXT を選ぶ場合:規約、組み込みユーティリティ、クロスブラウザー対応を求める場合に適しています。

Plasmo を選ぶ場合:React を中心に開発し、最も高水準の抽象化を求める場合に適しています。

プロジェクト構成(推奨)

my-extension/
├── src/
│   ├── background/
│   │   └── index.ts
│   ├── content/
│   │   ├── index.ts
│   │   └── styles.css
│   ├── popup/
│   │   ├── index.html        <- CRXJS resolves HTML entry points
│   │   ├── App.tsx
│   │   └── main.tsx
│   ├── options/
│   │   ├── index.html
│   │   └── main.tsx
│   ├── sidepanel/
│   │   ├── index.html
│   │   └── main.tsx
│   └── shared/
│       ├── messages.ts
│       └── storage.ts
├── public/
│   └── icons/
├── manifest.ts               <- or manifest.json
├── vite.config.ts
├── tsconfig.json
└── package.json

CRXJS は、マニフェストで参照される HTML ファイルを自動的に解決します。popup.html では標準の <script type="module" src="./main.tsx"> を使用でき、そのまま動作します。

CRXJS でバグや予期しない動作が発生した場合は、github.com/crxjs/chrome-extension-tools/issues で issue を作成してください。

コメント