Web フロントエンドにて、Canvas を使った View のテストを書きたいことがたまにあります。ブラウザであれば以下のようにして Canvas を利用できますが、テストが実行される Node.js ではそのような API は生えていません。
const canvas = document.createElement('canvas'); canvas.width = 200; canvas.height = 200; const ctx = canvas.getContext('2d'); ctx.fillStyle = 'green'; ctx.fillRect(10, 10, 150, 100);
そこで Node.js では、node-canvas
という npm パッケージがよく使われます。これを使うと、Web Canvas API 互換な API を用いて、Node.js でも Canvas を利用できます。
import { createCanvas } from 'canvas'; const canvas = createCanvas(200, 200); const ctx = canvas.getContext('2d'); ctx.fillStyle = 'green'; ctx.fillRect(10, 10, 150, 100);
余談ですが、Node.js に DOM API の互換実装を提供するライブラリ「jsdom」では、HTMLCanvasElement
や HTMLImageElement
の実装に、この node-canvas
を利用しています。
- https://github.com/jsdom/jsdom/blob/8e3a568d504353270691b5955af505155ae368bf/lib/jsdom/utils.js#L162
const Canvas = require("canvas");
して node-canvas を import してる
- https://github.com/jsdom/jsdom/blob/8e3a568d504353270691b5955af505155ae368bf/lib/jsdom/living/nodes/HTMLCanvasElement-impl.js#L5
- https://github.com/jsdom/jsdom/blob/8e3a568d504353270691b5955af505155ae368bf/lib/jsdom/living/nodes/HTMLImageElement-impl.js#L5
ネイティブモジュールの依存問題
そんな node-canvas
ですが、実はネイティブモジュールに依存しています。そのため、いくつかのモジュールがマシンにインストールされていなければ実行できません。
一度マシンにインストールしてしまえば良いものではありますが、複数人で開発するようなプロジェクトで node-canvas
に依存していると、新しくチームメンバーがやってくる度にインストール方法を案内する必要があります。加えて、CI 上でテストを走らせる際も CI 環境にインストールが必要になってきます。
ちょっとした手間ではあるのですが、面倒なのでなんとかして解消できると嬉しいです。
@napi-rs/canvas
を使う
@napi-rs/canvas
という、同じく Web Canvas API 互換な API を実装したライブラリがあります。このパッケージはネイティブモジュールに依存しない作りになっていて、ネイティブモジュールをインストールせずとも node-canvas
と同等のことができます。
$ npm i -D @napi-rs/canvas
import { createCanvas } from '@napi-rs/canvas'; // ^^^^^^^^^^^^^^^ // ここが canvas から変わっただけ const canvas = createCanvas(200, 200); const ctx = canvas.getContext('2d'); ctx.fillStyle = 'green'; ctx.fillRect(10, 10, 150, 100);
jsdom
と組み合わせるにはもう一手間必要
先程ちょっと触れましたが、jsdom
からは const Canvas = require("canvas");
で node-canvas が参照されています。従って、jsdom の HTMLCanvasElement
を呼び出すと node-canvas が使われてしまう…ということになってしまいます。
そこで id:mizdra は @napi-rs/canvas
を以下のようなコマンドでインストールしています。
$ npm i -D canvas@npm:@napi-rs/canvas
このコマンドを実行すると、package.json
上では以下のような依存関係の情報が書き込まれます。
{ // ... "devDependencies": { // ... "canvas": "npm:@napi-rs/canvas@^0.1.34", } }
これで require('canvas')
というコードが require('@napi-rs/canvas')
へと解決され、node-canvas
の代わりに @napi-rs/canvas
が利用されるようになります。jsdom の HTMLCanvasElement
からも @napi-rs/canvas
が使われるようになり、ネイティブモジュール無しでテストを実行できるようになります。
こことかここを見るとわかるのですが、実際には @napi-rs/canvas
は node-canvas
の API を完全に模倣している訳ではないので、もしかしたらうまく動かないこともあるかもしれません… とりあえず手元の HTMLCanvasElement
を使ったテストケースは正常に動作していますが、凝ったテストを書いてると動かないとかあるかもです。