Chrome Extensions を TypeScript で開発する

April 29, 2020

概要

Chrome Extensions(以下、Chrome 拡張)をTypeScript で開発するための webpack の設定やファイル構成について記載しています。

この記事では、Chrome 拡張の作り方については説明しません。作成に必要なファイルや作成した Chrome 拡張の読み込み方は、Chrome Extension - Getting Started Tutorial を参照してください。

環境

  • Node.js v12.14.0
  • TypeScript v3.8.3
  • webpack v4.42.1
  • TypeScript v3.8.3

プロジェクト作成

  1. Chrome 拡張のプロジェクトを作成します。

    mkdir chrome-extension-sample && cd $_
    npm init -y
  2. webpack や webpack のプラグインをインストールします。

    npm install --save-dev webpack webpack-cli copy-webpack-plugin
  3. TypeScript や、TypeScript を webpack で利用するための ts-loader をインストールします。

    npm install --save-dev typescript ts-loader
  4. Chrome の型定義をインストールします。

    npm install --save-dev @types/chrome

ファイル構成

次のファイル構成となるように、必要なファイルを作成します。

.
├── node_modules
├── public # Chrome 拡張に必要なファイル
│   ├── image
│   │   └── icon128.png # Chrome 拡張のアイコン
│   └── manifest.json # Chrome 拡張の設定ファイル
├── src # トライスパイル対象のファイル
│   └── background.ts # ブラウザ起動時に読み込まれるファイル
├── package.json
├── package-lock.json
├── tsconfig.json # TypeScript の設定
└── webpack.config.js # webpack の設定

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "strict": true,
    "rootDir": "src",
    "esModuleInterop": true,
    "typeRoots": [ "node_modules/@types"]
  },
  "exclude": [
    "node_modules"
  ]
}

webpack.config.js

const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  mode: process.env.NODE_ENV || "development",
  entry: {
    background: path.join(__dirname, "src/background.ts"),
  },
  output: {
    path: path.join(__dirname, "dist/js"),
    filename: "[name].js",
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".ts", ".js"],
  },
  plugins: [
    new CopyPlugin([{ from: ".", to: "../" }], { context: "public" })
  ],
};
  • トランスパイル対象のファイルを entry で指定します。
  • トランスパイル後の JavaScript ファイルを dist/js の下に出力します。
  • CopyPlugin を使って、「public」ディレクトリに置いたアイコンファイルや manifest.json をコピーします。

background.ts

今回は、選択した文字列を Google 検索するコンテキストメニュー(右クリックしたときに開くメニュー)を実装します。

const openTab = (query?: string) => {
  if(query) {
    chrome.tabs.create({ url: `https://www.google.com/search?q=${query}` });
  }
}

chrome.runtime.onInstalled.addListener((): void => {
  chrome.contextMenus.create({
    id: "sample",
    title: "選択した文字列を検索する",
    contexts: ["selection"]
  });
});

chrome.contextMenus.onClicked.addListener((info, tab): void => {
  openTab(info.selectionText);
});

manifest.json

{
  "manifest_version": 2,
  "name": "chrome-extension-sample",
  "description": "A sample Chrome Extension.",
  "version": "0.0.1",
  "icons": { "128": "image/icon128.png" },
  "background": {
    "scripts": ["js/background.js"],
  },
  "permissions": ["contextMenus", "tabs"]
}
  • manifest.json は webpack 実行時に「dist」ディレクトリの下にコピーされるので、各ファイルのパスは、「dist」ディレクトリからの相対パスを指定します。

ビルド

production モードでビルドします。

npx webpack --mode production

ビルド後のファイル構成は次のようになります。

.
├── dist
│   ├── image
│   │   └── icon128.png
│   ├── js
│   │   └── background.js
│   └── manifest.json
├── node_modules
├── public
│   └── ... 略 ...
├── src
│   └── ... 略 ...
├── package-lock.json
├── package.json
├── tsconfig.json
└── webpack.config.js

動作確認

  1. Chrome Extension - Getting Started Tutorial の記載の手順で、Chrome 拡張を読み込みます。 読み込みするディレクトリは「dist」です。

  2. 新しくタブを開きます(既存のタブの場合は、リロードしてください)。

  3. Web ページの文字を選択し、右クリックします。「選択した文字列を検索する」をクリックします。

  4. 選択した文字について Google 検索した結果が新しいタブで表示されます。