Angular v19 で始める、Chrome拡張機能作り
これは Angular Advent Calendar 2024 5日目の記事です。
普段みなさんはどのような拡張機能を使われていますか?
Chrome ウェブストア - 拡張機能
公開されている便利な拡張機能は様々ありますが、個人で楽しむ拡張機能を自分で作ることも可能です。
今回は、毎日開くWebサイト上にある画像を自分好みに置き換える、というとても小さな拡張機能を作ってみました。
Angularを使って作ってみたので、簡単に作り方を記述します。
拡張機能の Hello world
ng new するまで。
❯ mise install node@22
mise node@22.11.0 ✓ installed
❯ mise use node@22
mise ~/opt/.mise.toml tools: node@22.11.0
❯ node -v
v22.11.0
❯ npm install -g @angular/cli
added 296 packages in 15s
52 packages are looking for funding
run `npm fund` for details
Reshimming mise 22...
❯ ng version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 19.0.2
Node: 22.11.0
Package Manager: npm 10.9.0
OS: darwin arm64
Angular:
...
Package Version
------------------------------------------------------
@angular-devkit/architect 0.1900.2 (cli-only)
@angular-devkit/core 19.0.2 (cli-only)
@angular-devkit/schematics 19.0.2 (cli-only)
@schematics/angular 19.0.2 (cli-only)
❯ ng new image-change-chrome-extension
✔ Which stylesheet format would you like to use? CSS [ https://developer.mozilla.org/docs/Web/CSS
]
✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? no
CREATE image-change-chrome-extension/README.md (1489 bytes)
CREATE image-change-chrome-extension/.editorconfig (314 bytes)
CREATE image-change-chrome-extension/.gitignore (587 bytes)
CREATE image-change-chrome-extension/angular.json (2682 bytes)
CREATE image-change-chrome-extension/package.json (1060 bytes)
CREATE image-change-chrome-extension/tsconfig.json (915 bytes)
CREATE image-change-chrome-extension/tsconfig.app.json (424 bytes)
CREATE image-change-chrome-extension/tsconfig.spec.json (434 bytes)
CREATE image-change-chrome-extension/.vscode/extensions.json (130 bytes)
CREATE image-change-chrome-extension/.vscode/launch.json (470 bytes)
CREATE image-change-chrome-extension/.vscode/tasks.json (938 bytes)
CREATE image-change-chrome-extension/src/main.ts (250 bytes)
CREATE image-change-chrome-extension/src/index.html (312 bytes)
CREATE image-change-chrome-extension/src/styles.css (80 bytes)
CREATE image-change-chrome-extension/src/app/app.component.css (0 bytes)
CREATE image-change-chrome-extension/src/app/app.component.html (19903 bytes)
CREATE image-change-chrome-extension/src/app/app.component.spec.ts (985 bytes)
CREATE image-change-chrome-extension/src/app/app.component.ts (305 bytes)
CREATE image-change-chrome-extension/src/app/app.config.ts (310 bytes)
CREATE image-change-chrome-extension/src/app/app.routes.ts (77 bytes)
CREATE image-change-chrome-extension/public/favicon.ico (15086 bytes)
✔ Packages installed successfully.
Successfully initialized git.
-
src/manifest.json
を設置 - build 時に、上記を含むようにする
diff --git a/angular.json b/angular.json
index 79879b6..c570663 100644
--- a/angular.json
+++ b/angular.json
@@ -24,7 +24,8 @@
{
"glob": "**/*",
"input": "public"
- }
+ },
+ "src/manifest.json"
],
"styles": [
"src/styles.css"
-
chrome://extensions/
にアクセスして、Load unpacked
をクリック
開発中の拡張機能を読み込む
-
dist/browser
を選択 - すると、拡張機能がロードされる
拡張機能が読み込まれた様子
- 拡張機能の一覧からPinして、
拡張機能をツールバーに表示させる
- 拡張機能のアイコンをクリックすると、Angularの初期ページが表示される
拡張機能クリック時の様子
99割できた。
ちなみに、build ではなく、watch しながらファイルを編集した場合、都度の拡張機能ロードは必要ありません。(便利!)
ただし、manifest.json 等を編集した場合は、再ロードが必要になります。
拡張機能の見た目を変更する
拡張機能をクリックした時に表示される領域を編集しましょう。
ここは通常のAngularアプリの作成と同じです。
src/app/app.component.html
を編集します。
画像を選択させたいので、input タグを追加します。
あとでファイル選択時の処理を追加するので、onChangeイベントも設定しておきます。
<div class="container">
<div class="item">
<input type="file" accept="image/png, image/jpeg" (change)="onFileSelected($event)" />
</div>
</div>
適宜 css も追加します。
HTMLを編集して拡張機能をクリックした様子
拡張機能側から、Chrome の ActiveTab を取得する
拡張機能上で画像を選択させ、特定サイトの画像を上書きするため、拡張機能側でアクティブなタブを取得します。
そのために、chrome
のAPIを使います。
Angular上で使用できるようにするために、npm install @types/chrome
しておき、
compilerOptions
に chrome
を追加します。
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 3775b37..c4f4ede 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -4,7 +4,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
- "types": []
+ "types": ["chrome"]
},
"files": [
"src/main.ts"
これで、以下のAPIが使えるようになりました。
アクティブタブ内のDOMを変化させる
例として、https://www.google.com/ の画像を変化させてみましょう。
変化前のgoogle.com
chrome.scripting.executeScript
を使ってActive Tabに対してスクリプトを実行します。
ざっくりとしたイメージはこんな感じ。
▼ 拡張機能側
import { Component } from '@angular/core';
import { setImage } from '../../set-image';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'image-change-chrome-extension';
onFileSelected(event: Event): void {
const input = event.target as HTMLInputElement;
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = (e) => {
const base64Image = e.target?.result as string | ArrayBuffer;
if (typeof base64Image === 'string') {
this.executeScriptOnTargetPage(base64Image);
}
};
// 選択されたファイルをData URLとして読み込む
reader.readAsDataURL(input.files[0]);
}
}
executeScriptOnTargetPage(base64Image: string): void {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
// 対象のタブ内で動作させるスクリプト
chrome.scripting.executeScript({
target: { tabId: tabs[0].id! },
func: setImage,
args: [base64Image]
});
});
}
}
▼ アクティブなタブで実行されるスクリプト
export const setImage = (base64Image: string): void => {
const element = document.querySelector('.lnXdpd');
if (element) {
const newImg = document.createElement('img');
newImg.className = 'lnXdpd';
newImg.src = base64Image;
element.replaceWith(newImg);
}
}
画像が差し替わりました。
google.comの画像が差し替わった様子
実際には
拡張機能上で選択した画像ファイルは、拡張機能を閉じると消えてしまいます。
永続的に保持しておくためには、クラウド上に保存したり工夫が必要です。
特に懸念事項がなければ LocalStorage も使えるかもしれません。
また、画像を都度選択するのは面倒です。特定のページを開いたときに、保存された画像を自動で適用させたいです。
特定ページを開いたときに実行させるスクリプトを組み込みたい、そんな時は以下が使えます。
コンテンツ スクリプト | Chrome Extensions | Chrome for Developers
- 特定サイトで動作するスクリプトを設置
- 例)
src/content_script.ts
- 対象のページでDOMを書き換えます
- 例)
-
@angular-builders/custom-webpack
等を使って、ビルド時に上記ファイルも含めるようにする -
src/manifest.json
コンテンツスクリプトの定義を記述
diff --git a/src/manifest.json b/src/manifest.json
index 09a34e1..6446634 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -9,5 +9,11 @@
],
"action": {
"default_popup": "index.html"
- }
+ },
+ "content_scripts": [
+ {
+ "matches": ["https://google.com/"],
+ "js": ["contentScript.js"]
-
google.com
にアクセス - スクリプトが動作し、DOMにアクセスし画像が書き換わる
このあたりは設定など複雑になるのでここでは割愛します。
拡張機能を作ってみようと思ったときの一助になれば幸いです。
良き拡張機能ライフを!
明日は、@nishitakuさんです。
Discussion