Firefox拡張機能でChart.jsを使う方法

# 概要

Firefoxのブラウザ拡張機能で、Chart.jsを使ってグラフ表示機能を持たせたいと思ったときに行う手順を記載します。

Javascriptでモジュールをインポートする方法がわかりにくい点、ブラウザ拡張機能だと余計に複雑なため実現できた方法を紹介します。

:::note
推奨の方法は調べた限りだとないため、一つの方法として参考になればと思います。
:::

# Chart.jsを拡張機能で使うまでの流れ

全体の流れを記載します。

- Chart.jsをnpmで開発環境へインストールする
- 拡張機能で使うJavascriptのコード内でChart.jsを絶対パスでインポートする
- 拡張機能を公開するときにChart.jsのコード自体も含めてビルドする

## Chart.jsを使う上での注意点

### CDNで公開されているChart.jsを直接使うことはできない

コンテンツセキュリティポリシー（CSP）によってMozillaで公開する拡張機能ではリモートソースが使えません。

:::note warn
注: 有効な例は正しい CSP のキーの使い方を表しますが、'unsafe-eval', 'unsafe-inline', リモートスクリプト、リモートソースを CSP に指定する拡張機能は、主なセキュリティの問題から、addons.mozilla.org に載せる拡張機能には許可されません。
[content_security_policy](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_security_policy)
:::

### Chart.js内のコードを一部修正する

詳細は後ほど記載しますが、モジュールを読み込む際にパスを変更します。

拡張機能で使うコード内だけでなく、Chart.jsのコードが他のモジュールを読み込んでいるため同じようにパスを絶対パスに変更する必要があります。

# Chart.jsを拡張機能で使う方法

## Chart.jsをnpmで開発環境へインストールする

通常通りnpmでChart.jsをローカルにインストールします。

```bash
npm init
npm install chart.js
```

## 拡張機能で使うJavascriptのコード内でChart.jsを絶対パスでインポートする

以下のようなフォルダ構造になっている想定です。

```bash
node_modules
 ┝ @kurkle
 └ chart.js
    └ dist
       └ chart.js
```

この状態で`node_modules\chart.js\dist\chart.js`を編集します。
`@kurkle/color`を`import '/node_modules/@kurkle/color/dist/color.esm.js';`でインポートします。

```diff_javascript
/*!
 * Chart.js v4.4.8
 * https://www.chartjs.org
 * (c) 2025 Chart.js Contributors
 * Released under the MIT License
 */
import { r as requestAnimFrame, a as resolve, e as effects, c as color, i as isObject, d as defaults, b as isArray, v as valueOrDefault, u as unlistenArrayEvents, l as listenArrayEvents, f as resolveObjectKey, g as isNumberFinite, h as defined, s as sign, j as createContext, k as isNullOrUndef, _ as _arrayUnique, t as toRadians, m as toPercentage, n as toDimension, T as TAU, o as formatNumber, p as _angleBetween, H as HALF_PI, P as PI, q as _getStartAndCountOfVisiblePoints, w as _scaleRangesChanged, x as isNumber, y as _parseObjectDataRadialScale, z as getRelativePosition, A as _rlookupByKey, B as _lookupByKey, C as _isPointInArea, D as getAngleFromPoint, E as toPadding, F as each, G as getMaximumSize, I as _getParentNode, J as readUsedSize, K as supportsEventListenerOptions, L as throttled, M as _isDomSupported, N as _factorize, O as finiteOrDefault, Q as callback, R as _addGrace, S as _limitValue, U as toDegrees, V as _measureText, W as _int16Range, X as _alignPixel, Y as clipArea, Z as renderText, $ as unclipArea, a0 as toFont, a1 as _toLeftRightCenter, a2 as _alignStartEnd, a3 as overrides, a4 as merge, a5 as _capitalize, a6 as descriptors, a7 as isFunction, a8 as _attachContext, a9 as _createResolver, aa as _descriptors, ab as mergeIf, ac as uid, ad as debounce, ae as retinaScale, af as clearCanvas, ag as setsEqual, ah as _elementsEqual, ai as _isClickEvent, aj as _isBetween, ak as _readValueToProps, al as _updateBezierControlPoints, am as _computeSegments, an as _boundSegments, ao as _steppedInterpolation, ap as _bezierInterpolation, aq as _pointInLine, ar as _steppedLineTo, as as _bezierCurveTo, at as drawPoint, au as addRoundedRectPath, av as toTRBL, aw as toTRBLCorners, ax as _boundSegment, ay as _normalizeAngle, az as getRtlAdapter, aA as overrideTextDirection, aB as _textX, aC as restoreTextDirection, aD as drawPointLegend, aE as distanceBetweenPoints, aF as noop, aG as _setMinAndMaxByKey, aH as niceNum, aI as almostWhole, aJ as almostEquals, aK as _decimalPlaces, aL as Ticks, aM as log10, aN as _longestText, aO as _filterBetween, aP as _lookup } from './chunks/helpers.segment.js';
- import '@kurkle/color';
+ import '/node_modules/@kurkle/color/dist/color.esm.js';
```

また自身で書いた拡張機能用のコードからChart.jsを読み込む際も同じようにパスを変えます。

```javascript:hogehoge.js
import { Chart } from '/node_modules/chart.js/auto/auto.js';

mychart = new Chart(...)
```


## 拡張機能を公開するときにChart.jsのコード自体も含めてビルドする

あとはすべてのコードをまとめて拡張機能としてアップロードします。
Zip化してアップロードする方法を例にすると、以下フォルダをビルド用に作ります。

```
project
 ┝ node_modules
 │ ┝ @kurkle
 │ └ chart.js
 ┝ manifest.json
 └ hogehoge.js
```

これらのフォルダをすべてZip化します。
解凍したときに`manifest.json`が直下にないとエラーとなるため、`project`内のファイルをすべて選択してZip化します。

:::note warn
projectフォルダをZip化するのではなく、project内のファイルとフォルダをすべてZip化です。
:::

このZipファイルをリリースします。
詳しい手順は筆者が書いた以下記事を参照ください。

[Firefox拡張機能を開発者センターに限定公開する手順](https://qiita.com/The-town/items/c30d58ee4103abd1b978)

:::note info
開発者センターへリリースする際に、`\node_modules\@kurkle\LICENSE.md`ファイルがあるとエラーになる場合がありました。同様にエラーとなる場合はこちらのファイルを削除してください。
:::

# 参考

https://qiita.com/sugurutakahashi12345/items/3cc49926faeaf25d3051#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

https://github.com/chartjs/Chart.js/discussions/11024#discussioncomment-4773271